python module – cryptography

1. cryptography module

The cryptography module is the core module in Python for cryptographic operations. It provides various cryptographic algorithms, key generation, encryption, decryption, signing and verification functions.

Official website address: cryptography · PyPI

Version requirements: Python 3.7+

Installation: pip install cryptography

Documentation: Welcome to pyca/cryptography – Cryptography 42.0.0.dev1 documentation

The following are the core modules of the cryptography module:

  • `cryptography.hazmat.primitives`: This module contains cryptography primitives (primitives), such as hash functions, symmetric encryption, asymmetric encryption, message authentication codes, etc. These primitives are for direct use by developers.
  • `cryptography.hazmat.primitives.asymmetric`: This module contains primitives related to asymmetric encryption, such as RSA, elliptic curve cryptography (ECC), etc.
  • `cryptography.hazmat.primitives.symmetric`: This module contains primitives related to symmetric encryption, such as AES, DES, etc.
  • `cryptography.hazmat.primitives.hashes`: This module contains various hash functions, such as SHA-256, SHA-512, MD5, etc.
  • `cryptography.hazmat.primitives.serialization`: This module contains functions for serializing and deserializing cryptographic objects, such as converting private keys, public keys, and certificates into byte strings in different formats.
  • `cryptography.hazmat.primitives.padding`: This module contains various padding schemes for padding data blocks when encrypting and decrypting data.
  • `cryptography.hazmat.primitives.ciphers`: This module contains low-level encryption primitives, such as the underlying interfaces of encryption algorithms such as AES and DES.
  • `cryptography.hazmat.primitives.keywrap`: This module contains symmetric key wrapping and unwrapping functions for encrypting and decrypting symmetric keys.

2. Generate secret key

2.1 Generate private key

In the cryptography module, an RSA private key object is generated using the generate_private_key() method of the cryptography.hazmat.primitives.asymmetric.rsa module.

Syntax format:

def generate_private_key(
    public_exponent: int, key_size: int, backend=None
) -> RSAPrivateKey

Parameter Description:

  • `public_exponent`: Public key exponent, which is part of the public key in the RSA algorithm, usually chooses a fixed value, such as 65537 (i.e. 0x10001). This is a safe and common option.
  • `key_size`: Key length, used to determine the strength of the generated RSA private key, that is, the number of bits. Common key lengths include 2048, 3072, 4096, etc. Longer key lengths provide greater security, but also require longer encryption and decryption times.
  • `backend`: Optional parameter, the default value is `None`, this parameter is used to specify the backend implementation of the encryption library. If no backend parameter is provided, the appropriate backend will be used by default.
  • Return value: `RSAPrivateKey`, which is the class in the `cryptography.hazmat.primitives.asymmetric.rsa` module that represents the RSA private key.

Here is an example using the `generate_private_key()` method:

from cryptography.hazmat.primitives.asymmetric import rsa

# Generate RSA private key
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048
)

In the above example, the `generate_private_key()` method generates an RSA private key object. We use the parameters `public_exponent=65537` and `key_size=2048` to define the public key exponent and key length of the generated private key.

Note that private keys are very sensitive information and need to be properly kept and subject to strict access control when necessary.

2.2 Generate public key

In the `cryptography` library, the corresponding public key can be generated from the private key. The public key can be obtained from the public key object through the `public_key()` method of the private key object.

Syntax format:

def public_key(self) -> "RSAPublicKey":
    """
    The RSAPublicKey associated with this private key.
    """

The following is sample code to generate a public key using the ‘public_key()’ method of a private key object:

from cryptography.hazmat.primitives.asymmetric import rsa

# Generate RSA private key
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
)

# Generate RSA public key
public_key = private_key.public_key()

In the above example, we used the `public_key()` method of the private key object to obtain the corresponding public key object.

Note that the private key and the public key are used in pairs, the private key is used for encryption and signing, and the public key is used for decryption and verification of signatures. Private keys are sensitive information and need to be kept properly and should not be disclosed to others. The public key can be disseminated publicly to others for use by others.

3. Save the secret key

Serialize the generated secret key into a byte sequence and save it in pem format.

3.1 Serialized private key

In the cryptography library, private keys can be serialized into a byte stream representation using the ‘private_bytes()’ function in the serialization module.

Syntax format:

 def private_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PrivateFormat,
        encryption_algorithm: _serialization.KeySerializationEncryption,
    ) -> bytes:
        """
        Returns the key serialized as bytes.
        """

Parameter Description:

`encoding`: Encoding, this parameter is used to specify the encoding method used by the private key when serializing. `_serialization.Encoding` is an enumeration class. Commonly used values include:
– `PEM`: Serialize the private key to text format using Base64 encoding.
– `DER`: Serialize private key to binary format.

`format`: Secret key format, this parameter is used to specify the format when serializing the private key. `_serialization.PrivateFormat` is an enumeration class. Commonly used values include:
-`PKCS8`: Serialize private keys using PKCS#8 format.
– `TraditionalOpenSSL`: Serialize private keys using traditional OpenSSL format.

`encryption_algorithm`: Encryption algorithm, this parameter is used to specify whether to encrypt the private key. `_serialization.KeySerializationEncryption` is an enumeration class. Commonly used values include:
-`NoEncryption`: The private key will not be encrypted and will be serialized in clear text.
– `BestAvailableEncryption`: Encrypt the private key using the best encryption algorithm available.

Return value: `bytes`: serialized byte stream representation of the private key.

The following is sample code to serialize a private key to a byte stream:

from cryptography.hazmat.primitives import serialization

# Assume the private key object is private_key

# Serialize the private key into text format using PEM format
private_key_bytes = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.NoEncryption()
)

print(private_key_bytes)

In the above example, we use the `private_bytes()` method to serialize the private key into a byte stream representation. We specified the parameters `encoding` as `PEM`, `format` as `PKCS8`, and `encryption_algorithm` as `NoEncryption`, indicating that the private key will not be encrypted. A byte stream representation of the private key can be viewed by printing `private_key_bytes`.

3.2 Serialized public key

In the `cryptography` library, public keys can be serialized into a byte stream representation using the `public_bytes()` function in the serialization module.

Syntax format:

 def public_bytes(
        self,
        encoding: _serialization.Encoding,
        format: _serialization.PublicFormat,
    ) -> bytes:
        """
        Returns the key serialized as bytes.
        """

Parameter Description:

`encoding`: Encoding, this parameter is used to specify the encoding method used when serializing the public key. `_serialization.Encoding` is an enumeration class. Commonly used values include:
– `PEM`: Serialize the public key to text format using Base64 encoding.
– `DER`: Serialize public key to binary format.

`format`: Public key format, this parameter is used to specify the format when serializing the public key. `_serialization.PublicFormat` is an enumeration class. Commonly used values include:
– `SubjectPublicKeyInfo`: SubjectPublicKeyInfo is a structure used to represent public keys in the PKCS#8 format.

Return value: `bytes`: byte stream representation of the serialized public key.

The following is sample code to serialize a public key to a byte stream:

from cryptography.hazmat.primitives import serialization

# Assume the public key object is public_key

# Serialize the public key into a byte stream using PEM format
public_key_bytes = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)

print(public_key_bytes)

In the above example, we serialize the public key into a byte stream using the public_bytes() method of the public key object. Among them, we specified the serialization format as PEM and used the `SubjectPublicKeyInfo` format to represent the public key.

A public key can be serialized into a byte stream by choosing the appropriate encoding (such as PEM or DER) and the format of the public key (such as SubjectPublicKeyInfo). This way you can save the public key to a file or transfer it over the network so that others can use it.

3.3 Save public and private keys

The following is a sample code to save the public and private keys in pem format:

from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization

# Generate RSA private key
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
)

# Get the public key
public_key = private_key.public_key()

# Save the private key in pem format
with open('private_key.pem', 'wb') as file:
    # Serialize the private key into a byte stream
    private_key_bytes = private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption()
    )
    file.write(private_key_bytes)

# Save the public key in pem format
with open('public_key.pem', 'wb') as file:
    # Serialize the public key into a byte stream
    public_key_bytes = public_key.public_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PublicFormat.SubjectPublicKeyInfo
    )
    file.write(public_key_bytes)

4. Load the secret key

4.1 Load the public key in pem format

In the cryptography library, use the `load_pem_public_key()` method of the serialization module to load the public key in pem format.

Syntax format:

def load_pem_public_key(data: bytes, backend=None) -> _PUBLIC_KEY_TYPES:

Parameter Description:

  • data: The public key data in PEM format to be loaded, which is an object of type `bytes`. This parameter can be the content of a public key file or a public key string in PEM format.
  • backend: (optional) An instance of the encryption backend implementation to use.
  • Return value: `_PUBLIC_KEY_TYPES`: The returned object will be different depending on the type of public key loaded. For example, if an RSA public key is loaded, a `cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` object is returned.

Here is a sample code that demonstrates how to load a public key in PEM format:

from cryptography.hazmat.primitives import serialization


# Read the public key in PEM format
def loader_public_key():
    with open('public_key.pem', 'rb') as file:
        public_key = serialization.load_pem_public_key(
            file.read()
        )
    return public_key

4.2 Load the private key in pem format

In the cryptography library, use the ‘load_pem_private_key()’ method of the serialization module to load the private key in pem format.

Syntax format:

def load_pem_private_key(
    data: bytes, password: typing.Optional[bytes], backend=None
) -> _PRIVATE_KEY_TYPES:

Parameter Description:

  • data: The private key data in PEM format to be loaded, which is an object of type `bytes`. It can be the contents of a private key file or a private key string in PEM format.
  • Password: (optional) The password of the private key, which is an object of type `bytes`. If the private key is password protected, you need to provide the password to decrypt the private key. If the private key does not have a password, this parameter can be set to `None`.
  • backend: (optional) An instance of the encryption backend implementation to use.
  • Return value: _PRIVATE_KEY_TYPES: The returned object will be different depending on the type of private key loaded. For example, if an RSA private key is loaded, a `cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey` object is returned.

The following is a sample code that demonstrates how to load a private key in PEM format using the `load_pem_private_key` method:

from cryptography.hazmat.primitives import serialization


# Read the private key in PEM format
def loader_private_key():
    with open('private_key.pem', 'rb') as file:
        private_key = serialization.load_pem_private_key(
            file.read(),
            password=None
        )
    return private_key

5. Encryption, decryption and decryption

5.1 Encryption

In the cryptography module, use the RSAPublicKey.encrypt() method of the cryptography.hazmat.primitives.asymmetric.rsa module to encrypt plaintext data.

Syntax format:

def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes:

Parameter Description:

  • plaintext: The original data to be encrypted is an object of type `bytes`.
  • padding: The padding scheme used, which is an object of type `AsymmetricPadding`. Padding schemes such as `padding.PKCS1v15` and `padding.OAEP` can be used.
  • Return value: The encrypted data is an object of type `bytes`.

The following is a sample code that demonstrates how to use the cryptography module to perform public key encryption of data:

from cryptography.hazmat.primitives.asymmetric import rsa, padding

# Assume you already have an RSA public key for encryption
public_key = ...

#Data to be encrypted
data = b"Hello, world!"

# Use public key to encrypt data
ciphertext = public_key.encrypt(
    data,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)

# Store or transfer the encrypted data to the place that needs to be decrypted

5.2 Decryption

In the cryptography module, use the cryptography.hazmat.primitives.asymmetric.rsa module RSAPrivateKey.decrypt() method to decrypt the encrypted ciphertext.

Syntax format:

def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes:

Parameter Description:

  • ciphertext: The ciphertext data to be decrypted is an object of type bytes.
  • padding: The padding scheme used, which is an object of type AsymmetricPadding. You can use padding.PKCS1v15, padding.OAEP and other padding schemes.
  • Return value: The decrypted original data, which is an object of type bytes.

The following is a sample code that demonstrates how to use the cryptography module to decrypt data with a private key:

from cryptography.hazmat.primitives.asymmetric import rsa, padding

# Assume you already have an RSA private key for decryption
private_key = ...

#Data to be decrypted
ciphertext = ...

# Decrypt data using private key
plaintext = private_key.decrypt(
    ciphertext,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)

6. Signature and verification

6.1 Signature

In the cryptography module, use the RSAPrivateKey.sign() method of the cryptography.hazmat.primitives.asymmetric.rsa module to sign.

Syntax format:

 def sign(
        self,
        data: bytes,
        padding: AsymmetricPadding,
        algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm],
    ) -> bytes:

Parameter Description:

  • data: The data to be signed is an object of type bytes.
  • padding: The padding scheme used, which is an object of type AsymmetricPadding. You can use padding.PKCS1v15, padding.PSS and other padding schemes.
  • algorithm: The hash algorithm used, which can be an object of type asym_utils.Prehashed or an object of type hashes.HashAlgorithm.
  • Return value: signature result, which is an object of type bytes.

Here is a sample code that shows how to use the cryptography module to sign data:

from cryptography.hazmat.primitives import hashing, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding

# Assume you already have an RSA private key for signing
private_key = ...

#Data to be signed
data = b"Hello, world!"

# Use private key to sign data
signature = private_key.sign(
    data,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashing.SHA256()
)

# Store or pass the signature to the place where the signature needs to be verified

6.2 Verify

In the cryptography module, use the RSAPublicKey.verify() method of the cryptography.hazmat.primitives.asymmetric.rsa module to verify the signature.

Syntax format:

 def verify(
        self,
        signature: bytes,
        data: bytes,
        padding: AsymmetricPadding,
        algorithm: typing.Union[asym_utils.Prehashed, hashes.HashAlgorithm],
    ) ->None:

Parameter Description:

  • signature: The signature to be verified is an object of type `bytes`.
  • data: The original data to be verified is an object of type `bytes`.
  • padding: The padding scheme used, which is an object of type `AsymmetricPadding`. You can use `padding.PKCS1v15`, `padding.PSS` and other padding schemes.
  • algorithm: The hash algorithm used, which can be an object of type `asym_utils.Prehashed` or an object of type `hashes.HashAlgorithm`.
  • Return value: None, if the verification passes, nothing will be returned. If verification fails, an `InvalidSignature` exception will be triggered.

The following is a sample code that demonstrates how to use the cryptography module to verify a signature:

from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding

# Assume you already have an RSA public key for signature verification
public_key = ...

# Data and signature to verify
data = b"Hello, world!"
signature = ...

# Use public key for signature verification
try:
    public_key.verify(
        signature,
        data,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    print("Signature is valid.")
except InvalidSignature:
    print("Signature is invalid.")

Note: When doing signature verification, you need to use the same padding scheme to verify the received signature. If the padding scheme does not match, the verification process will fail.

reference:

cryptography · PyPI

Welcome to pyca/cryptography – Cryptography 42.0.0.dev1 documentation