It is known that encryption / decryption by public key method (RSA) takes time, and hybrid encryption is used. I actually measured the speed of encryption and decryption, also as a memo of how to encrypt and decrypt with a program (Python).
A method that uses the same key for encryption and decryption.
The block length is fixed at 128 bits, and the key length can be 128 bits, 192 bits, or 256 bits.
A method for encrypting information longer than the block length with a block cipher.
ECB (Electronic CodeBook) mode Simply divide into blocks and encrypt. The same plaintext becomes the same ciphertext. Deprecated.
CBC (Cipher Block Chaining) mode XOR with the ciphertext of the previous block to encrypt. Most often used. Since it is encrypted sequentially, it cannot be processed in parallel at the time of encryption.
CTR (CounTeR) mode The counter is incremented for each block to encrypt, and XOR with plaintext to create a block cipher.
A method of encrypting with a public key and decrypting with a private key.
Cryptography that utilizes the difficulty of discrete logarithms and prime factorization of large numbers.
Ciphertext = (plaintext ** E)% N
The {E, N} pair of is the public key at the time of encryption.
Plaintext = (ciphertext ** D)% N
The {D, N} pair of is the private key at the time of decryption.
Use a number of 2048 bits or more as N. 4096bit or more (ref. NIST SP800-57) for new use after 2031.
Even with public key cryptography, communication can be interrupted if a malicious person enters between the sender and receiver of the communication (MITM (man-in-the-middle) attack). A public key certificate is used to prevent this.
RSA-OAEP uses random numbers to generate different ciphertexts for the same plaintext each time.
Even shorter keys are stronger than RSA (the elliptic curve cryptography of 224 to 225 bit keys is equivalent to the same strength as 2048 bit RSA).
When encrypting a large file of several MB or more, the common key method is used because it takes a long time to encrypt and decrypt the public key method. The public key method is used to communicate the common key used at this time. It is said that the same level of encryption strength is preferable for both, and NIST Special Publication 800-57 Part 1 Table 2 of Revision 4, "Recommendations for Key Management" lists the security strengths that can be used as a reference.
Security strength | Private key (common key) algorithm | IFC (eg RSA) |
---|---|---|
128 | AES-128 | k = 3072 |
192 | AES-192 | k = 7680 |
256 | AES-256 | k = 15360 |
The processing times of AES-256 and RSA-2048 were compared. (Although the strength is different ...)
pip install pycrypto pyOpenSSL
Refer to this site and look like AES256.py below. (It is necessary to delete the padding when actually using it for file encryption / decryption).
When you run AES256.py shown below,
$ python3 AES256.py
File size = 10.0 [MB]
Encode:
AES_encrypt_time:0.12138915061950684[sec]
Decode:
AES_decrypt_time:0.12209415435791016[sec]
The result is as follows. You can see that it takes about 0.12 seconds to encrypt and decrypt a 10MB file.
AES256.py
import sys,struct,random
from Crypto.Cipher import AES
from hashlib import sha256
import time
import os
def generate_salt(digit_num):
DIGITS_AND_ALPHABETS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
return "".join(random.sample(DIGITS_AND_ALPHABETS, digit_num))
# AES-256
def derive_key_and_iv(password, salt, bs):
salted = ''.encode()
dx = ''.encode()
#AES from password-Key for 256 and initialization vector for CBC(iv)Generate a
while len(salted) < 48: # 48 =AES256 key length(32byte)+IV length(16byte)
hash = dx + password.encode() + salt.encode()
dx = sha256(hash).digest()
salted = salted + dx
key = salted[0:32] # 32byte -> AES-256 key lengths
iv = salted[32:48] # 16byte (AES.block_Same size as size,128bit in AES(=16byte)Fixed)
return key, iv
#encryption
def encrypt(in_file, out_file, password):
bs = AES.block_size
#salt = generate_salt() #Random.new().read(bs - len('Salted__'))
salt = generate_salt(AES.block_size)
key, iv = derive_key_and_iv(password, salt, bs)
cipher = AES.new(key, AES.MODE_CBC, iv) #Set CBC mode. Get the AESCipher class.
out_file.write(('Salted__' + salt).encode()) #salt writes to encrypted file
finished = False
while not finished:
chunk = in_file.read(1024 * bs)
orgChunkLen = len(chunk)
if len(chunk) == 0 or len(chunk) % bs != 0:
padding_length = (bs - len(chunk) % bs) or bs
padding = padding_length * chr(padding_length)
chunk += padding.encode()
finished = True
if len(chunk) > 0:
out_file.write(cipher.encrypt(chunk))
#Decryption
def decrypt(in_file, out_file, password):
bs = AES.block_size
in_file.seek(len('Salted__'))
salt = in_file.read(16).decode()
#key from salt and password,Get iv.
key, iv = derive_key_and_iv(password, salt, bs)
cipher = AES.new(key, AES.MODE_CBC, iv) #Set CBC mode. Get the AESCipher class.
finished = False
while not finished:
chunk = in_file.read(1024 * bs)
orgChunkLen = len(chunk)
if orgChunkLen == 0 or orgChunkLen % bs != 0:
padding_length = (bs - orgChunkLen % bs) or bs
padding = padding_length * chr(padding_length)
chunk += padding.encode()
finished = True
if orgChunkLen > 0:
out_file.write(cipher.decrypt(chunk)[0:orgChunkLen])
def main(filename):
infile = open(filename, "rb")
outfile = open(filename+"_AES.bin", "wb")
print("File size = ", os.path.getsize(filename) /1024/1024, "[MB]")
print("Encode:")
start = time.time()
encrypt(infile,outfile,"password")
# openssl enc -e -aes-256-cbc -salt -k "password" -in practice.bin -out practice_aes.bin
elapsed_time = time.time() - start
print ("AES_encrypt_time:{0}".format(elapsed_time) + "[sec]")
infile.close()
outfile.close()
print("Decode:")
outinfile = open(filename+"_AES.bin", "rb")
outfile2 = open(filename+"_dec_AES.bin", "wb")
start = time.time()
decrypt(outinfile,outfile2,"password")
elapsed_time = time.time() - start
print ("AES_decrypt_time:{0}".format(elapsed_time) + "[sec]")
outinfile.close()
outfile2.close()
if __name__== "__main__":
filename = "practice.bin"
main(filename)
Basically RSA is not intended to be used to encrypt large amounts of data. It was implemented in chunks for speed measurement, but it is not generally recommended. Large size data is encrypted using symmetric encryption such as AES.
Code modified with reference to this site and chunked 196 bytes. Changed to encrypt in units.
When you run RSA2048.py shown below,
$ python3 RSA2048.py
max_data_len= 196
Generate Key:
privatePem len = 1674
publicPem len = 450
Load Key:
File size = 10.0 [MB]
Encode:
RSA_encrypt_time:46.78378772735596[sec]
Decode:
RSA_decrypt_time:123.22586274147034[sec]
The result is as follows. You can see that it takes about 47 seconds to encrypt a 10MB file and about 123 seconds to decrypt it. Since each AES took about 0.12 seconds, it can be seen that RSA is about 400 times slower for encryption and about 1000 times slower for decryption.
RSA2048.py
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64
import time
import os
modulus_length = 2048 # bit
max_data_len = int((int(modulus_length/8.0) - 11 )*0.8) #The maximum size that can be encrypted with RSA is smaller than the key size. Depends on what you use for padding.
print("max_data_len=",max_data_len)
def generate_keys():
key = RSA.generate(modulus_length)
pub_key = key.publickey()
return key, pub_key
def encrypt_private_key(a_message, private_key):
encryptor = PKCS1_OAEP.new(private_key)
encrypted_msg = encryptor.encrypt(a_message)
encoded_encrypted_msg = base64.b64encode(encrypted_msg)
return encoded_encrypted_msg
def decrypt_public_key(encoded_encrypted_msg, public_key):
encryptor = PKCS1_OAEP.new(public_key)
decoded_encrypted_msg = base64.b64decode(encoded_encrypted_msg)
decoded_decrypted_msg = encryptor.decrypt(decoded_encrypted_msg)
return decoded_decrypted_msg
def encrypt_file(in_file, out_file, key):
finished =False
while not finished:
chunk = in_file.read(max_data_len)
if len(chunk) == 0 or len(chunk)%max_data_len:
finished = True
encdata = encrypt_private_key(chunk, key)
a_number = len(encdata)
out_file.write(a_number.to_bytes(4, byteorder='little'))
out_file.write(encdata)
out_file.close()
def decrypt_file(in_file, out_file, key):
finished =False
while not finished:
bnum = in_file.read(4)
inum = int.from_bytes(bnum, byteorder='little')
chunk = in_file.read(inum)
if len(chunk) == 0 or len(chunk)%inum:
finished = True
if len(chunk) != 0:
decdata = decrypt_public_key(chunk, key)
out_file.write(decdata[0:len(chunk)])
out_file.close()
def main(filename):
print("Generate Key:")
private, public = generate_keys()
privateFile = open("private.pem","wb")
privatePem = private.exportKey(format='PEM')
print("privatePem len = ", len(privatePem))
privateFile.write(privatePem)
privateFile.close()
publicFile = open("public.pem","wb")
publicPem = public.exportKey(format='PEM')
print("publicPem len = ", len(publicPem))
publicFile.write(publicPem)
publicFile.close()
#print (private)
#message = b'AES password or key'
#print(message)
#encoded = encrypt_private_key(message, public)
#decrypt_public_key(encoded, private)
print("Load Key:")
privateFile = open("private.pem","rb")
private_pem = privateFile.read()
privateFile.close()
private_key = RSA.importKey(private_pem)
publicFile = open("public.pem","rb")
public_pem = publicFile.read()
publicFile.close()
public_key = RSA.importKey(public_pem)
print("File size = ", os.path.getsize(filename) /1024/1024, "[MB]")
print("Encode:")
infile = open(filename, "rb")
outfile = open(filename+"_RSA.bin", "wb")
start = time.time()
encrypt_file(infile,outfile,public_key)
elapsed_time = time.time() - start
print ("RSA_encrypt_time:{0}".format(elapsed_time) + "[sec]")
infile.close()
outfile.close()
print("Decode:")
infile = open(filename+"_RSA.bin", "rb")
outfile = open(filename+"_dec_RSA.bin", "wb")
start = time.time()
decrypt_file(infile,outfile,private_key)
elapsed_time = time.time() - start
print ("RSA_decrypt_time:{0}".format(elapsed_time) + "[sec]")
infile.close()
outfile.close()
if __name__== "__main__":
filename = "practice.bin"
main(filename)
In actual TSL and PGP (GnuPG), the key of symmetric cryptography (equivalent data) is exchanged by public key method, and symmetric cryptography is used for the actual data, but this has a big difference in processing speed and is open to the public. It is said that encryption / decryption of the key method takes time. Here, in order to confirm how much the processing speed actually differs at the order level, we created and compared Python test code for symmetric cryptography (AES) and public key cryptography (RSA). As a result of actual measurement, it took about 0.12 seconds for AES to encrypt / decrypt a 10MB file, while RSA took about 47 seconds to encrypt and about 400 times slower, and decryption was about 123. It turned out to be about 1000 times slower in seconds.