Original article, welcome to forward the circle of friends, reproduced please indicate the source
Cryptography is a very well-known addition and decryption library in the Python language, providing a high level of abstraction at the algorithm level, very simple, intuitive, and pythonic, while preserving the low-level interfaces of various algorithms, preserving flexibility.
We know that encryption is generally divided into symmetric encryption (symmetric key encryption) and asymmetric encryption (asymmetric key encryption). , each corresponding to a number of different algorithms, each algorithm has different key length requirements, in addition to different packet encryption mode, as well as the end of the way. So it takes a high level of abstraction to encapsulate these parameters so that we don't have to worry about so many parameters when we use them, as long as we know it's safe enough.
Symmetric encryption is divided into packet encryption and sequence encryption, this article only discusses symmetric packet encryption.
Mainstream symmetric packet encryption algorithm: DES, 3DES, AES
Mainstream symmetric packet encryption mode: ECB, CBC, CFB, OFB
Mainstream fill standards: PKCS7, ISO 10126, ANSI x.923, Zero padding
In cryptography Library, the abstraction of symmetric encryption algorithm is Fernet module, including the encryption and decryption of data, signature verification function, and key expiration mechanism.
The module uses the following definition:
- Encryption and decryption algorithm for AES, key bit length 128,CBC mode, fill the standard PKCS7
- The signature algorithm is an HMAC of SHA256, with a key bit length of 128 bits
- Key can set expiration time
examples of using Fernet Plus decryption are as follows:
>>>ImportOS>>> fromCryptography.hazmat.primitives.ciphersImportCipher, algorithms, modes>>> fromCryptography.hazmat.backendsImportDefault_backend>>> backend =default_backend ()>>> key = Os.urandom (32)>>> IV = Os.urandom (16)>>> cipher = cipher (algorithms. AES (key), modes. CBC (iv), backend=backend)>>> encryptor =Cipher.encryptor ()>>> ct = encryptor.update (b"a secret message") +encryptor.finalize ()>>> decryptor =Cipher.decryptor ()>>> Decryptor.update (CT) +decryptor.finalize ()'a secret message'
in addition to specifying algorithms and patterns, and generating random keys, the CBC mode needs to generate a random initial vector IV, as well as an IV when decrypting. The fernet module of the Cryptography library encapsulates the operation of symmetric encryption and provides three basic operations:generate symmetric key: Generate_keyEncrypt with symmetric key: Encryptdecryption with symmetric key: Decrypt Generate_key: Visible only generates a 32-bit random number and encodes it with Base64
@classmethod def Generate_key (CLS): return Base64.urlsafe_b64encode (Os.urandom (32))
after generating the 32-bit key, the first 16 bits are used to compute the HMAC, and the last 16 bits are used to decrypt
Self._signing_key = key[:16] = key[16:] = Backend
Encrypt:1. Obtain the Current_time and randomly generate a 16-bit CBC initial Vector IV2. Specify padding mode as PKCS73. Use the Padding method to encrypt the original data4. Specify the AES algorithm for CBC mode encryption5. Encrypt to get ciphertext6. Merging Current_time, IV and ciphertext to get a basic_parts
Basic_parts = ( b"\x80" + struct.pack (">q", Current_time) + IV + ciphertext )
7. Calculating the HMAC value of the Basic_parts8. Basic_parts + HMac to do base64 to return, this is what we finally get encrypted data, containing the timestamp, IV, ciphertext, HMAC
defEncrypt (self, data): Current_time=Int (time.time ()) IV= Os.urandom (16) returnself._encrypt_from_parts (data, Current_time, IV)def_encrypt_from_parts (self, data, Current_time, iv):if notisinstance (data, bytes):RaiseTypeError ("data must be bytes.") Padder=padding. PKCS7 (algorithms. aes.block_size). Padder () Padded_data= Padder.update (data) +padder.finalize () encryptor=Cipher (algorithms. AES (Self._encryption_key), modes. CBC (iv), Self._backend). Encryptor () ciphertext= Encryptor.update (padded_data) +encryptor.finalize () basic_parts=(b"\x80"+ Struct.pack (">q", Current_time) + IV +ciphertext) H= HMAC (Self._signing_key, hashes. SHA256 (), backend=self._backend) h.update (basic_parts) HMAC=h.finalize ()returnBase64.urlsafe_b64encode (Basic_parts + HMAC)
Decrypt:completely opposite operation of encrypt1. Get Current_time2. Base64 decode token, get data containing timestamp, IV, ciphertext, HMAC3. Determine if the key has expired based on time stamp and TTL4. Calculate the HMAC and verify the key validity with the previous HMAC5. Get IV, and ciphertext, and decrypt through the key, get through the pad's plaintext6. Unpaid operation via PKCS7 to get the clear text removed7. Return to final result
defDecrypt (self, token, ttl=None):if notisinstance (token, bytes):RaiseTypeError ("token must be bytes.") Current_time=Int (time.time ())Try: Data=Base64.urlsafe_b64decode (token)except(TypeError, Binascii. Error):RaiseInvalidtokenif notDataorSix.indexbytes (data, 0)! = 0x80: RaiseInvalidtokenTry: Timestamp,= Struct.unpack (">q", Data[1:9]) exceptStruct.error:RaiseInvalidtokenifTtl is notNone:ifTimestamp + TTL <Current_time:RaiseInvalidtokenifCurrent_time + _max_clock_skew <Timestamp:RaiseInvalidtoken H= HMAC (Self._signing_key, hashes. SHA256 (), backend=self._backend) h.update (data[:-32]) Try: h.verify (data[-32:]) exceptinvalidsignature:RaiseInvalidtoken IV= data[9:25] Ciphertext= data[25:-32] Decryptor=Cipher (algorithms. AES (Self._encryption_key), modes. CBC (iv), Self._backend). Decryptor () plaintext_padded=decryptor.update (ciphertext)Try: plaintext_padded+=decryptor.finalize ()exceptValueError:RaiseInvalidtoken Unpadder=padding. PKCS7 (algorithms. aes.block_size). Unpadder () unpadded=unpadder.update (plaintext_padded)Try: unpadded+=unpadder.finalize ()exceptValueError:RaiseInvalidtokenreturnunpadded
Symmetric key encryption and decryption in cryptography: A Study of Fernet algorithm