`Python`的`加密`:`cryptography`库的`高级`用法,如`AES`和`RSA`。

Python cryptography 库高级用法:AES 和 RSA

大家好,今天我们来深入探讨 Python cryptography 库的高级用法,重点关注对称加密算法 AES (Advanced Encryption Standard) 和非对称加密算法 RSA (Rivest-Shamir-Adleman)。我们将不仅学习如何使用它们,还会深入理解它们背后的原理和最佳实践。

一、cryptography 库简介

cryptography 是一个强大的 Python 库,提供了各种加密算法和安全协议的实现。它建立在 OpenSSL 之上,提供了易于使用的 API,同时保持了安全性和性能。安装方式非常简单:

pip install cryptography

二、AES 加密

AES 是一种对称加密算法,意味着加密和解密使用相同的密钥。它被广泛应用于各种安全应用中,因为其速度快、安全性高。

2.1 基本原理

AES 将明文数据分成固定大小的块(通常是 128 位),并使用密钥对每个块进行加密。AES 支持不同的密钥长度:128 位、192 位和 256 位,密钥长度越长,安全性越高,但计算成本也越高。

AES 的加密过程包含多个轮次的变换,包括字节替换 (SubBytes)、行移位 (ShiftRows)、列混淆 (MixColumns) 和轮密钥加 (AddRoundKey)。这些变换的目的是为了将明文数据充分混淆和扩散,使得攻击者难以通过统计分析来破解密码。

2.2 使用 cryptography 库进行 AES 加密

cryptography 库提供了多种 AES 模式,包括 CBC (Cipher Block Chaining)、CTR (Counter) 和 GCM (Galois/Counter Mode)。每种模式都有其优缺点,选择哪种模式取决于具体的应用场景。

  • CBC 模式: CBC 模式需要一个初始化向量 (IV),每个块的加密依赖于前一个块的加密结果。这使得 CBC 模式具有较强的抗攻击能力,但需要保证 IV 的唯一性。
  • CTR 模式: CTR 模式将一个计数器与密钥结合,生成一个密钥流,然后将密钥流与明文进行异或运算。CTR 模式可以并行加密和解密,效率较高,并且不需要填充。
  • GCM 模式: GCM 模式是一种认证加密模式,它不仅可以加密数据,还可以生成一个认证标签,用于验证数据的完整性和真实性。GCM 模式被广泛应用于网络安全协议中。

下面是一个使用 AES-CBC 模式加密和解密的例子:

from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, hmac
import os

# 生成一个随机的密钥 (Key Generation)
key = Fernet.generate_key()
print("Generated Key:", key.hex())

# 创建一个 Fernet 对象 (Fernet Object Creation)
f = Fernet(key)

# 要加密的明文数据 (Plaintext Data)
plaintext = b"This is a secret message."

# 加密数据 (Data Encryption)
ciphertext = f.encrypt(plaintext)
print("Ciphertext:", ciphertext.hex())

# 解密数据 (Data Decryption)
decrypted_plaintext = f.decrypt(ciphertext)
print("Decrypted Plaintext:", decrypted_plaintext.decode())

# 使用 AES-CBC 模式进行加密和解密 (AES-CBC Encryption and Decryption)
def aes_cbc_encrypt(plaintext, key, iv):
    padder = padding.PKCS7(algorithms.AES.block_size).padder()
    padded_data = padder.update(plaintext) + padder.finalize()
    cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(padded_data) + encryptor.finalize()
    return ciphertext

def aes_cbc_decrypt(ciphertext, key, iv):
    cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
    decryptor = cipher.decryptor()
    unpadded_data = decryptor.update(ciphertext) + decryptor.finalize()
    unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
    plaintext = unpadder.update(unpadded_data) + unpadder.finalize()
    return plaintext

# 生成一个随机的密钥 (Key Generation)
key = os.urandom(32)  # 256-bit key
# 生成一个随机的初始化向量 (IV Generation)
iv = os.urandom(16)  # 128-bit IV

# 加密数据 (Data Encryption)
ciphertext = aes_cbc_encrypt(plaintext, key, iv)
print("AES-CBC Ciphertext:", ciphertext.hex())

# 解密数据 (Data Decryption)
decrypted_plaintext = aes_cbc_decrypt(ciphertext, key, iv)
print("AES-CBC Decrypted Plaintext:", decrypted_plaintext.decode())

# 使用 AES-CTR 模式进行加密和解密 (AES-CTR Encryption and Decryption)
def aes_ctr_encrypt(plaintext, key, nonce):
    cipher = Cipher(algorithms.AES(key), modes.CTR(nonce), backend=default_backend())
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(plaintext) + encryptor.finalize()
    return ciphertext

def aes_ctr_decrypt(ciphertext, key, nonce):
    cipher = Cipher(algorithms.AES(key), modes.CTR(nonce), backend=default_backend())
    decryptor = cipher.decryptor()
    plaintext = decryptor.update(ciphertext) + decryptor.finalize()
    return plaintext

# 生成一个随机的密钥 (Key Generation)
key = os.urandom(32)  # 256-bit key
# 生成一个随机的 nonce (Nonce Generation)
nonce = os.urandom(16)  # 128-bit nonce

# 加密数据 (Data Encryption)
ciphertext = aes_ctr_encrypt(plaintext, key, nonce)
print("AES-CTR Ciphertext:", ciphertext.hex())

# 解密数据 (Data Decryption)
decrypted_plaintext = aes_ctr_decrypt(ciphertext, key, nonce)
print("AES-CTR Decrypted Plaintext:", decrypted_plaintext.decode())

# 使用 AES-GCM 模式进行加密和解密 (AES-GCM Encryption and Decryption)
def aes_gcm_encrypt(plaintext, key, nonce):
    cipher = Cipher(algorithms.AES(key), modes.GCM(nonce), backend=default_backend())
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(plaintext) + encryptor.finalize()
    return ciphertext, encryptor.tag

def aes_gcm_decrypt(ciphertext, key, nonce, tag):
    cipher = Cipher(algorithms.AES(key), modes.GCM(nonce, tag), backend=default_backend())
    decryptor = cipher.decryptor()
    plaintext = decryptor.update(ciphertext) + decryptor.finalize()
    return plaintext

# 生成一个随机的密钥 (Key Generation)
key = os.urandom(32)  # 256-bit key
# 生成一个随机的 nonce (Nonce Generation)
nonce = os.urandom(16)  # 128-bit nonce

# 加密数据 (Data Encryption)
ciphertext, tag = aes_gcm_encrypt(plaintext, key, nonce)
print("AES-GCM Ciphertext:", ciphertext.hex())
print("AES-GCM Tag:", tag.hex())

# 解密数据 (Data Decryption)
decrypted_plaintext = aes_gcm_decrypt(ciphertext, key, nonce, tag)
print("AES-GCM Decrypted Plaintext:", decrypted_plaintext.decode())

代码解释:

  1. 密钥生成: 使用 os.urandom() 生成随机的密钥和 IV/Nonce。密钥长度取决于 AES 的密钥大小 (128, 192, 或 256 位)。
  2. 加密: 创建一个 Cipher 对象,指定加密算法 (AES)、模式 (CBC, CTR, GCM) 和密钥/IV/Nonce。创建一个 encryptor 对象,并使用 update()finalize() 方法对数据进行加密。
  3. 解密: 创建一个 Cipher 对象,指定加密算法、模式和密钥/IV/Nonce。创建一个 decryptor 对象,并使用 update()finalize() 方法对数据进行解密。
  4. 填充: CBC 模式需要对数据进行填充,以确保数据长度是块大小的倍数。PKCS7 是一种常用的填充方案。
  5. 认证标签: GCM 模式会生成一个认证标签,用于验证数据的完整性和真实性。解密时需要提供该标签。

2.3 密钥管理

AES 加密的安全性取决于密钥的安全性。因此,密钥管理至关重要。

  • 密钥生成: 使用安全的随机数生成器来生成密钥。os.urandom() 是一个不错的选择。
  • 密钥存储: 不要将密钥存储在代码中或明文中。可以使用密钥管理系统 (KMS) 或硬件安全模块 (HSM) 来安全地存储密钥。
  • 密钥交换: 如果需要在不同的系统之间共享密钥,可以使用密钥交换协议,如 Diffie-Hellman 密钥交换。

三、RSA 加密

RSA 是一种非对称加密算法,意味着加密和解密使用不同的密钥:公钥和私钥。公钥可以公开分发,而私钥必须保密。

3.1 基本原理

RSA 算法基于大数分解的数学难题。其核心思想是:给定两个大素数 pq,计算它们的乘积 n = p * q。已知 n,很难分解出 pq

RSA 密钥生成过程如下:

  1. 选择两个大素数 pq
  2. 计算 n = p * q
  3. 计算欧拉函数 φ(n) = (p – 1) * (q – 1)。
  4. 选择一个整数 e,满足 1 < e < φ(n),且 e 与 φ(n) 互质。e 作为公钥的指数。
  5. 计算 e 关于 φ(n) 的模反元素 d,即 d * e ≡ 1 (mod φ(n))。d 作为私钥的指数。

公钥为 (n, e),私钥为 (n, d)。

加密过程:将明文 m (小于 n) 加密成密文 c,计算 c = me mod n

解密过程:将密文 c 解密成明文 m,计算 m = cd mod n

3.2 使用 cryptography 库进行 RSA 加密

from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend

# 生成 RSA 密钥对 (RSA Key Pair Generation)
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)
public_key = private_key.public_key()

# 将公钥序列化为 PEM 格式 (Public Key Serialization to PEM)
public_key_pem = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)
print("Public Key:n", public_key_pem.decode())

# 将私钥序列化为 PEM 格式 (Private Key Serialization to PEM)
private_key_pem = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.NoEncryption()
)
print("Private Key:n", private_key_pem.decode())

# 要加密的明文数据 (Plaintext Data)
plaintext = b"This is a secret message."

# 使用公钥加密数据 (Data Encryption with Public Key)
ciphertext = public_key.encrypt(
    plaintext,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)
print("Ciphertext:", ciphertext.hex())

# 使用私钥解密数据 (Data Decryption with Private Key)
decrypted_plaintext = private_key.decrypt(
    ciphertext,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)
print("Decrypted Plaintext:", decrypted_plaintext.decode())

# 使用 RSA 进行签名和验证 (RSA Signing and Verification)
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

# 签名数据 (Data Signing)
signature = private_key.sign(
    plaintext,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA256()
)
print("Signature:", signature.hex())

# 验证签名 (Signature Verification)
try:
    public_key.verify(
        signature,
        plaintext,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    print("Signature is valid.")
except Exception as e:
    print("Signature is invalid:", e)

代码解释:

  1. 密钥生成: 使用 rsa.generate_private_key() 生成 RSA 密钥对。需要指定公钥指数 e (通常为 65537) 和密钥长度 (通常为 2048 位或更高)。
  2. 密钥序列化: 使用 private_key.private_bytes()public_key.public_bytes() 将私钥和公钥序列化为 PEM 格式。PEM 格式是一种常用的密钥存储格式。
  3. 加密: 使用 public_key.encrypt() 使用公钥加密数据。需要指定填充方案,例如 OAEP (Optimal Asymmetric Encryption Padding)。
  4. 解密: 使用 private_key.decrypt() 使用私钥解密数据。需要使用与加密时相同的填充方案。
  5. 签名: 使用 private_key.sign() 使用私钥对数据进行签名。需要指定签名方案,例如 PSS (Probabilistic Signature Scheme)。
  6. 验证: 使用 public_key.verify() 使用公钥验证签名。需要使用与签名时相同的签名方案。

3.3 密钥管理

RSA 密钥管理比 AES 更加复杂,因为需要安全地存储和管理私钥。

  • 私钥存储: 绝不能将私钥存储在代码中或明文中。可以使用 KMS 或 HSM 来安全地存储私钥。
  • 密钥轮换: 定期轮换密钥,以降低密钥泄露的风险。
  • 证书: 使用数字证书来验证公钥的身份。数字证书由受信任的证书颁发机构 (CA) 颁发,可以证明公钥属于特定的实体。

3.4 填充方案

RSA 加密需要使用填充方案,以防止某些攻击。常用的填充方案包括 PKCS#1 v1.5 和 OAEP。OAEP 比 PKCS#1 v1.5 更加安全,建议使用 OAEP。

3.5 应用场景

RSA 广泛应用于数字签名、密钥交换和身份验证等领域。

  • 数字签名: 用于验证数据的完整性和真实性。
  • 密钥交换: 用于在不安全的信道上安全地交换密钥。
  • 身份验证: 用于验证用户的身份。

四、选择合适的加密算法

选择合适的加密算法取决于具体的应用场景。

  • 对称加密算法 (AES): 速度快,适合加密大量数据。需要安全地管理密钥。
  • 非对称加密算法 (RSA): 速度慢,适合加密少量数据,例如密钥或数字签名。需要安全地管理私钥。

在实际应用中,通常将对称加密算法和非对称加密算法结合使用。例如,可以使用 RSA 加密 AES 密钥,然后使用 AES 加密数据。这样既可以保证数据的安全性,又可以提高加密速度。

五、其他注意事项

  • 随机数生成: 使用安全的随机数生成器来生成密钥、IV 和 Nonce。
  • 错误处理: 妥善处理加密和解密过程中可能出现的错误。
  • 代码审查: 对加密代码进行严格的代码审查,以确保其安全性和正确性。
  • 定期更新: 定期更新 cryptography 库,以获取最新的安全补丁。

六、不同加密模式的特性

加密模式 优点 缺点 适用场景
CBC 较强的抗攻击能力 需要填充,不能并行加密和解密,需要保证 IV 的唯一性 需要保证数据完整性和机密性,不要求高速并行处理
CTR 可以并行加密和解密,效率较高,不需要填充 需要保证 Nonce 的唯一性,如果 Nonce 重复使用,会导致安全问题 需要高速并行处理,对填充不敏感
GCM 认证加密模式,可以同时保证数据的完整性和机密性,效率较高 实现相对复杂 需要同时保证数据的完整性和机密性,例如网络安全协议
ECB 简单易用 容易受到模式攻击,不建议使用 尽量避免使用
CFB 可以处理任意长度的数据,不需要填充 实现相对复杂,安全性略低于 CBC 处理流数据,对填充不敏感
OFB 可以并行加密和解密,效率较高,不需要填充 需要保证初始向量的唯一性,如果初始向量重复使用,会导致安全问题 需要高速并行处理,对填充不敏感

七、代码安全最佳实践

  • 使用参数化查询: 避免 SQL 注入攻击。
  • 输入验证: 验证所有用户输入,防止恶意输入。
  • 最小权限原则: 只授予用户完成任务所需的最小权限。
  • 安全配置: 确保应用程序和服务器的配置是安全的。
  • 日志记录和监控: 记录所有重要的事件,并定期监控系统安全。
  • 定期安全审计: 定期进行安全审计,发现和修复安全漏洞。

八、使用场景案例

1. 安全存储用户密码:

不应该直接存储用户的明文密码。应该使用哈希算法(如 SHA256 或 bcrypt)对密码进行哈希处理,并存储哈希值。

import bcrypt

def hash_password(password):
  """Hashes the password using bcrypt."""
  hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
  return hashed_password

def verify_password(password, hashed_password):
  """Verifies the password against the stored hash."""
  return bcrypt.checkpw(password.encode('utf-8'), hashed_password)

# 示例
password = "mysecretpassword"
hashed_password = hash_password(password)
print("Hashed password:", hashed_password)

# 验证密码
if verify_password(password, hashed_password):
  print("Password verified successfully.")
else:
  print("Password verification failed.")

2. 安全通信:

可以使用 TLS/SSL 协议来加密客户端和服务器之间的通信。cryptography 库可以用于实现 TLS/SSL 协议。

3. 数据加密存储:

可以使用 AES 或其他对称加密算法来加密存储在数据库或文件系统中的敏感数据。

# 示例 (AES-GCM)
import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend

def encrypt_data(data, key, nonce):
    cipher = Cipher(algorithms.AES(key), modes.GCM(nonce), backend=default_backend())
    encryptor = cipher.encryptor()
    ciphertext = encryptor.update(data) + encryptor.finalize()
    return ciphertext, encryptor.tag

def decrypt_data(ciphertext, key, nonce, tag):
    cipher = Cipher(algorithms.AES(key), modes.GCM(nonce, tag), backend=default_backend())
    decryptor = cipher.decryptor()
    plaintext = decryptor.update(ciphertext) + decryptor.finalize()
    return plaintext

# 示例
key = os.urandom(32) # 256-bit key
nonce = os.urandom(16) # 128-bit nonce
data = b"Sensitive data to be encrypted"

ciphertext, tag = encrypt_data(data, key, nonce)
print("Ciphertext:", ciphertext.hex())

decrypted_data = decrypt_data(ciphertext, key, nonce, tag)
print("Decrypted data:", decrypted_data)

九、持续学习,不断进步

加密技术是一个不断发展的领域,新的攻击方法和防御技术层出不穷。因此,我们需要不断学习新的知识,才能确保我们的系统安全。 关注最新的安全漏洞、安全新闻和最佳实践。

总而言之,cryptography 库为 Python 开发者提供了强大的加密工具。通过理解 AES 和 RSA 的原理和用法,并遵循最佳实践,我们可以构建更安全的应用程序。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注