跳到主要内容位置

Workshop-0x02-Cryptography

Recorded workshop:

Becoming Familiar with OpenSSL

OpenSSL is a library and command-line tool for both symmetric and asymmetric (RSA) encryption. OpenSSL is included as part of most Linux builds, and exists in Kali Linux. 

  1. To get help and list the the commands, type openssl help
    openssl help

Note that list of standard commands, message digest (hashing) formats supported, and cipher commands (encryption algorithms) supported.

2. We will be using the `"enc"`, `"dgst"`, `"genrsa"`, `"rsa"`, `"pkeyutl"`, commands. To get options for each of these commands, use the "-help" option

openssl help

3. To list all the ciphers supported execute this command

# openssl enc -ciphers
```bash
openssl enc -ciphers

Symmetric Encryption

  1. Create a sample file -- just a small text file is fine
echo "Ethical Hacking is Fun" > plaintext
  1. Open the text file in a Hex editor to confirm its binary contents
hexeditor plaintext
  1. Encrypt the file using AES128 CBC mode. The 128-bit key and IV (initialisation vector) should be chosen randomly for real use, but for the purpose of this exercise anything simple is OK.
openssl enc -aes-128-cbc -in plaintext -out ciphertext -k 123 -iv 456
  1. Check the content of the ciphertext
hexeditor ciphertext

Note that the ciphertext has the "Salted__" prefix, indicating that a random salt has been used.

  1. Decrypt the ciphertext and check that you get the original text back
openssl enc -d -aes-128-cbc -in ciphertext -k 123 -iv 456

ECB vs CBC

As explained in the lecture, ECB (electronic code book) operation mode of AES encryption encrypts blocks of binary independently, so identical blocks encrypt to the same ciphertext. This is bad if you are trying to encrypt something like an image. CBC (cipher block chaining) mode, on the other hand, spreads or diffuses the information over all blocks by chaining.

  1. Download this cbc.bmp to Kali and create a copy of it with the filename original.bmp
  2. Encrypt this file using AES128 in ECB mode as follows:
openssl enc -aes-128-ecb -in cbc.bmp -out encrypted.bmp -K 123
  1. Now copy the bitmap header (first 54 bytes) from the original to the encrypted file using this dd command
dd if=cbc.bmp count=54 ibs=1 >> out.bmp  
dd if=encrypted.bmp skip=54 ibs=1 >> out.bmp

or this Python script.

```
original = open('cbc.bmp','rb')  
encrypted = open('encrypted.bmp','rb')
output = open('output.bmp','wb')
data_original = original.read()
data_encrypted = encrypted.read()
output.write(data_original[0:55]) **# copy the first 54 bytes from original**
output.write(data_encrypted[55:len(data_encrypted)]) **# write the remainder from encrypted**
original.close()
encrypted.close()
output.close()
  1. View the encrypted file with the modified header, and confirm that you can make out the original image like this:

  2. Next, encrypt the original BMP file again using CBC this time. Note that you need to specify the initialisation vector (IV) for the CBC mode.

openssl enc -aes-128-cbc -in cbc.bmp -out encrypted_cbc.bmp -K 123 -iv 456
  1. Repeat the header modification as per Step 3 for the CBC-encrypted image, and confirm that the information now looks random. 

Textbook RSA using Python

In this step you will perform "textbook" RSA encryption and decryption using the following given primes. The key difference between actual PKCS RSA implementation and textbook implementation is padding of messages before encryption. PKCS#1.5 padding includes random bytes, so that ciphertext is actually different each time the same plaintext is encrypted, whereas it is identical each time with textbook RSA. This is a cryptographic weakness. Since a given plaintext always produces the same ciphertext we say that textbook RSA is deterministic. How is this a problem? Think about how you could find the plaintext of a ciphertext encrypted with textbook RSA if you knew that the original plaintext was a number between 0 and 99.

p=0xE017  
q=0xD20D
e=0x010001
  1. Run the following python code to verify that RSA "works". Experiment with different "messages". Make sure you understand what each line is doing!! You can also download the script from here Download here. Note: You should also try doing it the other way: encrypt with private exponent d, then decrypt with public exponent e.

       # function to calculate inverse modular
    # using the extended Euclidean algorithm
    def invmod(a,n):
    i=1
    while True:
    c = n * i + 1;
    if(c%a==0):
    c = c//a
    break;
    i = i+1
    # note: indent before the following statement
    return c

    p = int("E017",16) # first prime
    q = int("D20D",16) # second prime
    e = int("010001",16) # a number that is co-prime with (p-1)*(q-1)

    # calculate modulus n
    n = p*q
    print("n is: " + str(n))

    # calculate inverse modular d of exponent e and (p-1)*(q-1)
    d = invmod(e, (p-1)*(q-1))
    print("d is: " + str(d))

    # check that d*e mod (p-1)*(q-1) is indeed 1
    print("checking d*e mod (p-1)*(q-1): " + str(((d*e) % ((p-1)*(q-1)))))

    # encrypting short message using public exponent e
    msg = 12345
    enc = pow(msg,e, n) # supplying the 3rd parameter in pow() efficiently computes the mod
    print("Message " + str(msg) + " is encrypted to: " + str(enc))

    # decrypt message using private exponent d
    plain = pow(enc,int(d), n) # supplying the 3rd parameter efficiently computes the mod
    print("Cipher " + str(enc) + " is decrypted to: " + str(plain))

RSA Encryption with OpenSSL

  1. Generate a 512-bit RSA private key (default is 2048 bits) and check the key (encoded in Base64). Also note that standard exponent (65537 or 0x10001) is again used here.
openssl genrsa 512 > private.key  
cat private.key
  1. Generate the corresponding public key. 
    openssl rsa -pubout < private.key > public.key  
    cat public.key
2. You can get more info on the public key by using the following command
```bash
openssl rsa -text -pubin < public.key

Note the exponent (e) and the modulus (n) are encoded in the key to allow encryption.

  1. Let's encrypt a short text. The key size is 512bits (64 bytes) so the maximum message size after taking away padding should be 53 byes.
echo "Ethical hacking is fun" | openssl pkeyutl -encrypt -pubin -inkey public.key > message.dat
  1. Trying to encrypt something too long gives you this error (please try)
RSA operation error  
3080140544:error:0406D06E:rsa routines:RSA_padding_add_PKCS1_type_2:data too large for key size:../crypto/rsa/rsa_pk1.c:125:

This is why RSA is not used to encrypt long messages. RSA can only encrypt input data that is smaller than the modulus value of the RSA key. RSA is most commonly used to encrypt temporary session keys (e.g., AES encryption key) which are then used to encrypt long messages.

  1. Now we can decrypt using the private key as follows. Verify that you get the same message back.
openssl pkeyutl -decrypt -inkey **private.key** -in message.dat

Examining the RSA Private Key

(Update: This part won't work since a 512-bit key was generated in the previous section. The example below is demonstrated with a 128-bit key, which can't be created nowadays as it has been depreciated due to being vulnerable.)

The RSA private.key generated in the last exercise is formatted using PEM and has a structure called "ASN.1"(see https://tools.ietf.org/html/rfc3641). You can use the openssl asn1parse command to understand what is included in the private.key to obtain the modulus (n), p, q, public exponent (e), and the private exponent (d).

  1. Execute the following command (the example below is for 128 bits, so the one you will see is much longer). Note: If you do not get the modulus/exponents/secrets shown below you may have to use openssl's "-strparse" switch to drill down deeper into the private.key (see OpenSSL man page).
openssl asn1parse -i -in private.key  
0:d=0 hl=2 l= 97 cons: SEQUENCE
2:d=1 hl=2 l= 1 prim: INTEGER :00
5:d=1 hl=2 l= 17 prim: INTEGER :AB32AD57BC713D952BE8D30099B41BEF **# this is the modulus n**
24:d=1 hl=2 l= 3 prim: INTEGER :010001 **# public exponent e**
29:d=1 hl=2 l= 16 prim: INTEGER :76B72FA9358DB94B835AF9B0F4C0D721 **# private exponent d**
47:d=1 hl=2 l= 9 prim: INTEGER :DD454679C3CA4C77 **# secret p**
58:d=1 hl=2 l= 9 prim: INTEGER :C611776FD9A7A249 **# secret q**
69:d=1 hl=2 l= 8 prim: INTEGER :75081A01773BFD7B
79:d=1 hl=2 l= 8 prim: INTEGER :2A37B4DE89651AC9
89:d=1 hl=2 l= 8 prim: INTEGER :228DBCF687C89CD5
  1. Now we know n, p, q, e and d, you can use this Python code to verify their relationships (n=pq and de mod (p-1)*(q-1) = 1 as follows
p = int("00DD454679C3CA4C77",16)  
q = int("00C611776FD9A7A249", 16)
n = int("00AB32AD57BC713D952BE8D30099B41BEF", 16)
e = int("010001",16)
d = int("76B72FA9358DB94B835AF9B0F4C0D721", 16)

# Verify n = p*q
print("[n]:" + str(n) + " [p*q]: " + str(p*q))

# verify e*d mod{(p-1)*(q-1)} = 1
print("[e*d mod{(p-1)*(q-1)}]: " + str((e*d)%((p-1)*(q-1))))

Cryptographic Hashing

OpenSSL includes command for calculating cryptographic hashes using various algorithms. 

  1. Calculate the SHA256 hash of the original bitmap file used in the previous exercise as follows
openssl dgst cbc.bmp  
SHA256(original.bmp)= 61d9ed6f632e3db1be665fc3c02446d0a76ba153517054bfc3a8855ca9b05322
  1. Use hexeditor to change 1 bit in the original.bmp file, save, and re-calculate SHA256

  2. Confirm that the generated SHA256 is COMPLETELY different from the original one.

openssl dgst cbc.bmp  
SHA256(original.bmp)= 7bf73e1e98c11d3f2233e8f46fc328663405b5d1035bbff0d87e83421823609b

Digital Signature

As covered in the lecture, digital signature involves signing the message digest (hash) of a message with the signer's private key. The recipient verifies that (a) the message has not been modified and (b) the signer is the owner of the private key corresponding to the public key. For digitally signing a document, we will generate a new X.509 certificate as follows

  1. Run the following command to generate signing key and certificate (containing the public key)
    openssl req -nodes -x509 -sha256 -newkey rsa:2048 -keyout signing.key -out signing.crt

When prompted, supply additional details about the signer.
2. Create some message.
```bash
echo "Ethical hacking is cool" > message.txt
  1. Sign the message with the generated private key as follows
openssl dgst -sha256 -sign signing.key -out sign.txt.sha256 message.txt
  1. Imagine that you have sent the message along with the digital signature. The recipient would look up the sender/signer's public X.509 certificate, extract the public key, then use that key to validate the signature.
openssl x509 -in signing.crt -pubkey -noout > signing.pub.key  
openssl dgst -sha256 -verify signing.pub.key -signature sign.txt.sha256 message.txt

Verified OK
  1. Try tampering with either the message or the signature. Again, changing a single bit in either of the files will result in a failed verification.
sed -i "s/o/0/g" message.txt  
# openssl dgst -sha256 -verify signing.pub.key -signature sign.txt.sha256 message.txt
Verification Failure

Comparing the Performance between RSA and AES

One reason why RSA is mainly used just to encrypt the secret key and not the whole message, is that RSA is much slower than AES. OpenSSL comes with a tool to measure the performance of RSA and AES. Give it a try.

  1. Test the performance of RSA
openssl speed rsa
  1. Test the performance of AES
openssl speed aes
  1. What can you say about the speed of the two protocols?

Manually Verifying an X.509 Certificate

  1. Download a certificate from a real server using openssl 
    Links to an external site.](http://www.spotify.com:443/) -showcerts  
CONNECTED(00000003)
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, CN = DigiCert SHA2 Secure Server CA
verify return:1
depth=0 C = SE, L = Stockholm, O = Spotify AB, CN = *.spotify.com
verify return:1
---
Certificate chain
0 s:/C=SE/L=Stockholm/O=Spotify AB/CN=*.spotify.com
i:/C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
-----BEGIN CERTIFICATE-----
MIIFCDCCA/CgAwIBAgIQA2PTtH+jtsZRxsc3jVOALDANBgkqhkiG9w0BAQsFADBN
_**___SNIP____**_
-----END CERTIFICATE-----
1 s:/C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert Global Root CA
-----BEGIN CERTIFICATE-----
MIIElDCCA3ygAwIBAgIQAf2j627KdciIQ4tyS8+8kTANBgkqhkiG9w0BAQsFADBh
_**___SNIP___**_
-----END CERTIFICATE-----
---
Server certificate
subject=/C=SE/L=Stockholm/O=Spotify AB/CN=*.spotify.com
issuer=/C=US/O=DigiCert Inc/CN=DigiCert SHA2 Secure Server CA
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3141 bytes and written 302 bytes
Verification: OK
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
_**___SNIP__**_
  1. There are two certificates in the output: (1) one for *.spotify.com wildcard URI and (2) issuer cert by DigiCert. Let's verify that both of these are valid by checking the signature embedded in these certificates against the root CA certificate.

Save each one to files server.pem and issuer.pem (copy-paste from ----BEGIN CERTIFICATE---- to ----END CERTIFICATE----. Make sure the "begin" and "end" lines are included.)

  1. Open Firefox, open settings, go to "Privacy & Security", "Certificates" and click on [View Certificates]

  2. Find the "DigiCert Global Root CA" and export it to file (the default file name DigiCertGlobalRootCA.crt is OK)

  3. Now use the following command to verify the issuer and server certs

openssl verify -verbose -CAfile <(cat DigiCertGlobalRootCA.crt issuer.pem) server.pem

We basically need to use cat to concatenate the root CA certificate and the issuer certificate to validate the server certificate.

Password Hashing 1 - SHA512

  1. Create two dummy test accounts, both with with password of "password" 
$sudo adduser test1
(type "password")$sudo adduser test2
(type "password")
  1. Examine the content of the shadow file (/etc/shadow). 
$ sudo cat /etc/shadow​test1:$y$j9T$x23oBZ3tUzqPTScTsIKud1$g/XJAFGEITAfrugMpYH3hxMCS9RLz9bDr/T1H9omrYC:19405:0:99999:7:::test2:$y$j9T$gLNja5MeJWEXiECKgrqh50$MObX0SyUO6qsZZT9DJXE4F7JU6DKBh./yTu46HCvKR.:19405:0:99999:7:::*

Note: The default hashing mechanism in Kali is now Yescrypt (yy) however OpenSSL does not currently support this hash. We'll instead demonstrate using SHA512 (66) below. 

Note:

  • The 66 at the beginning indicates that the password is encrypted using SHA512 with salt
  • Although test1 and test2 have the same password, the hash is different due to the different salt
  1. Use openssl with "passwd" command to manually create SHA512 hashes of password "password" with salt and check that they match with the values in the shadow file.
a1112407@kali:~$ openssl passwd -6 -salt 9VPMbK3ALIhniJHW password  

**$6$9VPMbK3ALIhniJHW$bJMWzyPJEeZaVWR9Um/X3TdDs4C9WvLnp6RsnRPA.5CltBhjO4GbxLESwqFUh8YbjreRpxw7NPHjrzfxEaqz6/**

a1112407@kali:~$ openssl passwd -6 -salt ZGKaIrrMWYnPQSSM password

**$6$ZGKaIrrMWYnPQSSM$yFnz8v0k4vCoOqNQSRihMIGbG6lexlkgA73wdDgXj8xBxrvTpdNAII3pcQLyFkXVUkKar8B18BQFc/wiireET/**
  1. How does the salt help to prevent password hash pre-computation attacks?

(Optional) Creating an MD5 collision with fastcoll

  1. Download this binary executable for fastcoll Download fastcollto your Kali instance and make it executable (chmod +x). You can also compile it from source using this repository (https://github.com/upbit/clone-fastcollLinks to an external site.) but you would need to install the C++ Boost library first.

  2. Create some "prefix" file to start with, and run fastcoll to generate two files with identical MD5 hashes, and confirm that the MD5 are indeed identical. How would you go about creating two programs (say scripts) with identical MD5 hash but with different execution behaviour?

**root@kali:~# echo "This is a test prefix" > prefix.txt**  
**root@kali:~# ./fastcoll prefix.txt**
Generating first block: .
Generating second block: W.................
use 'md5sum md5_data*' check MD5
**root@kali:~# ls md5_data***
md5_data1 md5_data2
**root@kali:~# cat md5_data1**
This is a test prefix
���>�6*�u���{e�:W�E0N�9��Ʋ����r�)�f�s����am�B�V����VLO;c�+�M��P���զ�E���H������7���r�792�ĩ���H
��c��WVJIo**root@kali:~# cat md5_data2**
This is a test prefix
���>�6*�u���{e�:W��E0N�9��Ʋ����r�)�f�s�}��am�B�V�(��VLO;c�+�M��P����&�E���H������7���r�792�D����H
��c��W�**root@kali:~# md5sum md5_data***
926459c620ba6651ba0ce6d223ca4e25 md5_data1
926459c620ba6651ba0ce6d223ca4e25 md5_data2

(Optional) Learning more about cryptography

If you're interested in learning more about cryptography and doing more fun challenges, CryptoHackLinks to an external site. is a great resource.