pyca/cryptography库的学习(7)——python

RSA

RSA是一种用于加密和签名消息的公钥算法。

Generation

与对称密码学不同,在对称密码学中,密钥通常只是一系列随机字节,而RSA密钥具有复杂的内部结构和特定的数学属性。

python 复制代码
cryptography.hazmat.primitives.asymmetric.rsa.generate_private_key(public_exponent, key_size)

生成新的RSA私钥。key_size描述了密钥的长度。密钥越大,安全性越高;目前1024及以下被认为是不安全的,而2048或4096是新密钥的合理默认密钥大小。public_export表示密钥生成的一个数学属性是什么。除非您有特殊原因,否则应始终使用65537。

python 复制代码
from cryptography.hazmat.primitives.asymmetric import rsa
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
)

参数(parameters):

  • public_exponent (int) ------新密钥的公开指数。65537或3(用于遗留目的)。几乎每个人都应该使用65537。

  • key_size (int) ------ 模数的长度,单位为比特。对于2015年生成的密钥,强烈建议至少为2048。不得小于512。

返回: RSAPrivateKey的一个实例。

Key loading

如果你已经有一个PEM格式的磁盘密钥(可以通过独特的BEGIN识别)-----BEGIN {format}----- 和-----END {format}-----,您可以加载它:

python 复制代码
from cryptography.hazmat.primitives import serialization

with open("path/to/key.pem", "rb") as key_file:
    private_key = serialization.load_pem_private_key(
        key_file.read(),
        password=None,
    )

序列化密钥可以选择在磁盘上使用密码进行加密。在这个例子中,我们加载了一个未加密的密钥,因此我们没有提供密码。如果密钥是加密的,我们可以传递一个bytes对象作为password参数。还支持loading public keys in the SSH format.

Key serialization

如果你已经加载了私钥,你可以使用private_bytes()来序列化密钥。

python 复制代码
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

# 生成 RSA 私钥
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
)

# 使用密码加密私钥
pem = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.PKCS8,
    encryption_algorithm=serialization.BestAvailableEncryption(b'mypassword'),
)

# 打印加密后的 PEM 私钥
print("Encrypted Private Key:")
print(pem.decode('utf-8'))

# 提取 PEM 格式私钥的第一行(即开始行)
print("\nFirst line of the PEM:")
print(pem.splitlines()[0])  # 这里输出的是 '-----BEGIN ENCRYPTED PRIVATE KEY-----'

也可以使用NoEncryption在不加密的情况下进行序列化。

python 复制代码
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization

# 生成 RSA 私钥
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
)

# 将私钥序列化为 PEM 格式,且不加密
pem = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.TraditionalOpenSSL,
    encryption_algorithm=serialization.NoEncryption(),
)

# 打印 PEM 格式私钥
print("Private Key (PEM format):")
print(pem.decode('utf-8'))

# 提取并打印 PEM 私钥的第一行(即开始行)
print("\nFirst line of the PEM:")
print(pem.splitlines()[0])  # 输出:'-----BEGIN RSA PRIVATE KEY-----'

对于公钥,您可以使用public_bytes()对密钥进行序列化。

python 复制代码
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization

# 生成 RSA 私钥
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
)

# 从私钥获取公钥
public_key = private_key.public_key()

# 将公钥序列化为 PEM 格式
pem = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)

# 打印 PEM 格式公钥
print("Public Key (PEM format):")
print(pem.decode('utf-8'))

# 提取并打印 PEM 公钥的第一行(即开始行)
print("\nFirst line of the PEM:")
print(pem.splitlines()[0])  # 输出:'-----BEGIN PUBLIC KEY-----'

Signing

私钥可用于对消息进行签名。这允许任何拥有公钥的人验证消息是由拥有相应私钥的人创建的。RSA签名需要使用特定的哈希函数和填充。以下是一个使用RSA对消息进行签名的示例,其中包含一个安全的哈希函数和填充:

python 复制代码
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
message = b"A message I want to sign"
signature = private_key.sign(
    message,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA256()
)

签名的有效填充是PSSPKCS1v15PSS是任何新协议或应用程序的推荐选择,PKCS1v15应仅用于支持传统协议。如果您的数据太大,无法在一次调用中传递,您可以单独对其进行哈希处理,并使用Prehash传递该值。

python 复制代码
from cryptography.hazmat.primitives.asymmetric import utils
chosen_hash = hashes.SHA256()
hasher = hashes.Hash(chosen_hash)
hasher.update(b"data & ")
hasher.update(b"more data")
digest = hasher.finalize()
sig = private_key.sign(
    digest,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    utils.Prehashed(chosen_hash)
)

Verification

上一节描述了如果你有私钥并想签名,该怎么办。如果您有公钥、消息、签名和使用的签名算法,则可以检查与给定公钥关联的私钥是否用于对特定消息进行签名。您可以使用 load_pem_public_key(), load_der_public_key(), public_key() , 或者 public_key().

python 复制代码
public_key = private_key.public_key()
public_key.verify(
    signature,
    message,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    hashes.SHA256()
)

如果签名不匹配,verify() 将引发InvalidSignature异常。

如果您的数据太大,无法在一次调用中传递,您可以单独对其进行哈希处理,并使用Prehash传递该值。

python 复制代码
chosen_hash = hashes.SHA256()
hasher = hashes.Hash(chosen_hash)
hasher.update(b"data & ")
hasher.update(b"more data")
digest = hasher.finalize()
public_key.verify(
    sig,
    digest,
    padding.PSS(
        mgf=padding.MGF1(hashes.SHA256()),
        salt_length=padding.PSS.MAX_LENGTH
    ),
    utils.Prehashed(chosen_hash)

Encryption

RSA加密很有趣,因为加密是使用公钥执行的,这意味着任何人都可以加密数据。然后使用私钥对数据进行解密。与签名一样,RSA支持使用多种不同填充选项的加密。下面是一个使用安全填充和哈希函数的示例:

python 复制代码
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes

# 生成 RSA 私钥
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
)

# 从私钥获取公钥
public_key = private_key.public_key()

# 定义要加密的消息
message = b"encrypted data"

# 使用公钥加密消息
ciphertext = public_key.encrypt(
    message,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),  # Mask Generation Function 使用 SHA-256
        algorithm=hashes.SHA256(),                    # 使用 SHA-256 哈希算法
        label=None                                    # 不使用标签
    )
)

# 打印加密后的密文
print("Ciphertext (Encrypted Message):")
print(ciphertext)

加密的有效填充是OAEPPKCS1v15OAEP是任何新协议或应用程序的推荐选择,PKCS1v15应仅用于支持传统协议。

Decryption

一旦你有了加密的消息,就可以使用私钥解密:

python 复制代码
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes

# 生成 RSA 密钥对
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
)

# 从私钥获取公钥
public_key = private_key.public_key()

# 定义要加密的消息
message = b"encrypted data"

# 使用公钥加密消息
ciphertext = public_key.encrypt(
    message,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),  # Mask Generation Function 使用 SHA-256
        algorithm=hashes.SHA256(),                    # 使用 SHA-256 哈希算法
        label=None                                    # 不使用标签
    )
)

# 使用私钥解密消息
plaintext = private_key.decrypt(
    ciphertext,
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),  # Mask Generation Function 使用 SHA-256
        algorithm=hashes.SHA256(),                    # 使用 SHA-256 哈希算法
        label=None                                    # 不使用标签
    )
)

# 打印解密后的明文和验证结果
print("Decrypted Message:", plaintext)
print("Is the decrypted message same as original?", plaintext == message)

Padding

python 复制代码
class cryptography.hazmat.primitives.asymmetric.padding.AsymmetricPadding

方法:name

python 复制代码
class cryptography.hazmat.primitives.asymmetric.padding.PSS(mgf, salt_length)

PSS(概率签名方案)是RFC 3447中定义的签名方案。它比PKCS1更复杂,但具有安全证明。这是RSA签名的推荐填充算法。它不能与RSA加密一起使用。

参数(parameters):

  • mgf ------ 掩码生成函数对象。目前唯一支持的MGF是MGF1。

  • salt_length (int) ------ 盐的长度。建议将其设置为PSS.DIGEST_LENGTHPSS.MAX_LENGTH

类方法:

  1. MAX_LENGTH ------ 将此属性传递给salt_length以获得可用的最大盐长度。
  2. DIGEST_LENGTH ------ 将此属性传递给salt_length,将salt长度设置为调用sign时传递的摘要的字节长度。请注意,这不是传递给MGF1的摘要的长度。
  3. AUTO ------ 将此属性传递给salt_length,以便在验证时自动确定盐长度。如果在签名时使用,则引发ValueError
  4. mgf ------ 类型:MGF。填充的掩码生成功能(MGF)。
python 复制代码
class cryptography.hazmat.primitives.asymmetric.padding.OAEP(mgf, algorithm, label)

OAEP(最优非对称加密填充)是RFC 3447中定义的填充方案。它提供概率加密,并被证明对几种攻击类型是安全的。这是RSA加密的推荐填充算法。它不能与RSA签名一起使用。

参数(parameters):

  • mgf ------ 掩码生成函数对象。目前唯一支持的MGF是MGF1。

  • algorithm ------ HashAlgorithm的一个实例。

  • label (bytes) ------ 要贴的标签。这是一个很少使用的字段,通常应设置为None"b",它们是等效的。

类方法:

  1. algorithm ------ 填充的哈希算法。
  2. mgf ------ 类型:MGF。填充的掩码生成功能(MGF)。
python 复制代码
class cryptography.hazmat.primitives.asymmetric.padding.PKCS1v15

PKCS1 v1.5(也称为简单的PKCS1)是一个为RSA密钥开发的简单填充方案。它在RFC 3447中定义。此填充可用于签名和加密。不建议将PKCS1v15用于新应用程序,应首选OAEP进行加密,首选PSS进行签名。
Waring:我们实现的PKCS1 v1.5解密不是恒定时间的。有关详细信息,请参阅已知安全限制

python 复制代码
cryptography.hazmat.primitives.asymmetric.padding.calculate_max_pss_salt_length(key, hash_algorithm)

参数(parameters):

返回: 计算出的盐长度。

计算在使用 PSS.MAX_LENGTH 时,PSS 将使用的盐的长度。

Mask generation functions

python 复制代码
class cryptography.hazmat.primitives.asymmetric.padding.MGF
python 复制代码
class cryptography.hazmat.primitives.asymmetric.padding.MGF1(algorithm)

MGF1(掩码生成函数1)用作PSS和OAEP填充中的掩码生成函数。它需要一个哈希算法。
参数(parameters): algorithm ------ 一个HashAlgorithm实例

Numbers

这些类包含RSA密钥的组成部分。只有当更传统的密钥序列化不可用时,它们才有用。

python 复制代码
class cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicNumbers(e, n)

组成RSA公钥的整数集合。

参数:n(int):公共模块。 e(int):公共幂,public_key():返回一个新的RSAPublicKey实例

python 复制代码
class cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateNumbers(p, q, d, dmp1, dmq1, iqmp, public_numbers)

组成RSA私钥的整数集合。

Waring:除了RSAPublicNumbers中包含的整数外,此类的所有属性都必须保密。暴露它们将危及使用从它们加载的密钥执行的任何加密操作的安全性。

  1. public_numbers(RSAPublicNumbers):组成与此RSA私钥关联的RSA公钥的RSAPublicNumbers
  2. p(int): 构成n的两个素数之一。
  3. q(int): 构成n的两个素数之一。
  4. d(int): 私有指数。
  5. dmp1(int): 一种用于加速RSA运算的中国剩余定理系数。计算公式为: d m o d ( p − 1 ) d \ mod \ (p-1) d mod (p−1)
  6. iqmp(int): 一种用于加速RSA运算的中国剩余定理系数。计算公式为: q − 1 m o d p q^{-1} \ mod \ p q−1 mod p
  7. private_key(*, unsafe_skip_rsa_key_validation=False):
    参数: unsafe_skip_rsa_key_validation (bool) -- 默认为False的仅关键字参数。如果True RSA私钥将不会被验证。这大大加快了加载密钥的速度,但除非您确定密钥有效,否则是不安全的。在加载用户提供的密钥时,不应将此参数设置为True。如果您确实以这种方式加载无效密钥并尝试使用它,OpenSSL可能会挂起、崩溃或出现其他异常行为。
    返回: 一个 RSAPrivateKey实例

Handling partial RSA private keys(处理部分RSA私钥)

如果您试图自己加载RSA私钥,您可能会发现并非RSAPrivateNumbers所需的所有参数都可用。特别是,中国剩余定理(CRT)值dmp1、dmq1、iqmp可能缺失或以不同形式存在。例如,OpenPGP不包括iqmp、dmp1或dmq1参数。

以下功能是为那些想使用这样的按键而不必自己计算的用户提供的。

python 复制代码
cryptography.hazmat.primitives.asymmetric.rsa.rsa_crt_iqmp(p, q)

根据RSA素数p和q计算iqmp(也称为qInv)参数

python 复制代码
cryptography.hazmat.primitives.asymmetric.rsa.rsa_crt_dmp1(private_exponent, p)

根据RSA私有指数(d)和素数p计算dmp1参数

python 复制代码
cryptography.hazmat.primitives.asymmetric.rsa.rsa_crt_dmq1(private_exponent, q)

根据RSA私有指数(d)和素数q计算dmq1参数。

python 复制代码
cryptography.hazmat.primitives.asymmetric.rsa.rsa_recover_private_exponent(e, p, q)

在给定公共指数(e)和RSA素数p和q的情况下计算RSA private_exportion(d)。

Note:此实现使用Carmichael totient函数返回d的最小工作值。较旧的RSA实现,包括最初的RSA论文,通常使用Euler totient函数,这会产生更大但功能相同的私人指数。这里返回的由Carmichael totient函数得到的私人指数在计算效率上略高,一些现代标准要求使用它们。

python 复制代码
cryptography.hazmat.primitives.asymmetric.rsa.rsa_recover_prime_factors(n, e, d)

根据模数、公共指数和私有指数计算素数因子(p,q)。

Note:当恢复素数因子时,该算法将始终返回p和q,使得p>q。注意:在1.5之前,该函数始终返回p,使得p<q。由于库通常要求p>q,因此它被更改了。

返回: 一个数组(p,q)

Key interfaces(关键接口)

python 复制代码
class cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey

RSA私钥。

  1. decrypt(ciphertext, padding)
    Warning:我们实现的PKCS1 v1.5解密不是恒定时间的。有关详细信息,请参阅已知安全限制。

    解密用公钥加密的数据。
    参数:

    -ciphertext (bytes) -- 要解密的密文。

    -padding --- 一个AsymmetricPadding实例

    返回: 解密的数据。

  2. public_key():返回RSAPublicKey------ 与私钥值对应的RSA公钥对象。

  3. key_size(int) ------ 模数的位长。

  4. sign(data, padding, algorithm): 对一个数据块进行签名,稍后其他人可以使用公钥对其进行验证。
    参数:

    -data (bytes-like) -- 要签名的消息字符串。

    -padding --- 一个AsymmetricPadding实例

    -algorithm --- 如果要签名的数据已经过哈希处理,则可以使用HashAlgorithmPrehash的实例。

    返回: 签名。

  5. private_numbers():创建一个RSAPrivateNumbers对象。 返回一个RSAPrivateNumbers实例

  6. private_bytes(encoding, format, encryption_algorithm):允许将密钥序列化为字节。选择编码(PEM或DER)、格式(传统OpenSSL、OpenSSH或PKCS8)和加密算法(如BestAvailableEncryption或NoEncryption)来定义确切的序列化。
    参数:

    -encoding -- Encoding枚举中的值。

    -format--- PrivateFormat枚举中的值

    -encryption_algorithm --- 符合KeySerialization Encryption接口的对象实例。

    返回: 序列化密钥。

python 复制代码
class cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey

一个RSA公钥

  1. encrypt(plaintext, padding)

    用公钥加密数据。
    参数:

    -plaintext (bytes)-- 需要加密的明文。

    -padding --- AsymmetricPadding的一个实例

    返回: 加密数据。
    异常: ValueError --- 数据无法加密。一个可能的原因是数据太大;RSA密钥只能加密小于密钥大小的数据。

  2. key_size(int):模数的位长。

  3. public_numbers():创建RSAPublicNumbers对象。返回一个RSAPublicNumbers实例

  4. public_bytes(encoding, format):允许将密钥序列化为字节。选择编码(PEM或DER)和格式(SubjectPublicKeyInfo或PKCS1)来定义精确的序列化。
    参数:

    -encoding -- Encoding枚举中的值。

    -format--- PrivateFormat枚举中的值

    返回: 序列化密钥。

  5. verify(signature, data, padding, algorithm)

    验证一个数据块是否由与此公钥关联的私钥签名。
    参数:

    -signature (bytes-like)-- 需要验证的签名。

    -data (bytes-like)--- 已签名的消息字符串。

    -padding --- AsymmetricPadding的一个实例

    -algorithm --- 如果要验证的数据已经过哈希处理,则使用HashAlgorithmPrehash的实例。

    返回: None。
    异常: cryptography.exceptions.InvalidSignature --- 如果签名无效。

  6. recover_data_from_signature(signature, padding, algorithm):

    从签名中恢复已签名的数据。数据通常包含原始消息字符串的摘要。填充和算法参数必须与创建签名时使用的参数相匹配,以便恢复成功。算法参数也可以设置为None,以恢复签名中存在的所有数据,而不管其格式或用于创建签名的哈希算法。对于PKCS1v15填充,此方法在删除填充层后返回数据。对于标准签名,数据包含完整的DigestInfo结构。对于非标准签名,可以返回任何数据,包括零长度数据。通常,您应该使用verify()函数来验证签名。但对于某些非标准签名格式,您可能需要显式恢复和验证签名数据。以下是一些示例:

  • 一些没有DigestInfo的旧Thawte和Verisign时间戳证书。

  • TLS 1.1或更早版本中的签名MD5/SHA1哈希(RFC 4346,第4.7节)。

  • 不带DigestInfo的IKE版本1签名(RFC 2409,第5.1节)。'

    参数:

    -signature (bytes) -- 签名。

    -padding--不对称填充的一个实例。恢复仅支持某些填充类型。(目前仅适用于PKCS1v15)。

    -algorithm是 --- HashAlgorithm的一个实例。可以为None以返回签名中存在的所有数据。
    返回: 签名的数据。
    异常: cryptography.exceptions.InvalidSignature --- 如果签名无效。

    cryptography.exceptions.UnsupportedAlgorithm --- 如果提供的填充类型不支持签名数据恢复。

相关推荐
飞yu流星27 分钟前
c++ stl 遍历算法和查找算法
开发语言·c++·算法
山海青风1 小时前
OpenAI 实战进阶教程 - 第七节: 与数据库集成 - 生成 SQL 查询与优化
数据库·人工智能·python·sql
Swift社区3 小时前
LeetCode - #197 Swift 实现找出温度更高的日期
算法·leetcode·swift
NPE~3 小时前
[漏洞篇]SQL注入漏洞详解
数据库·安全·渗透测试·教程·漏洞·sql注入
嗯嗯你说的对3 小时前
记忆化搜索和动态规划 --最长回文子串为例
算法·动态规划
玥轩_5213 小时前
《Linux服务与安全管理》| 数据库服务器安装和配置
linux·运维·服务器·数据库·安全·网络安全·redhat
金融OG3 小时前
98.2 AI量化开发:基于DeepSeek打造个人专属金融消息面-AI量化分析师(理论+全套Python代码)
人工智能·python·算法·机器学习·数学建模·金融
山海青风4 小时前
OpenAI 实战进阶教程 - 第四节: 结合 Web 服务:构建 Flask API 网关
前端·人工智能·python·chatgpt·flask
白嫖勇者4 小时前
Python(Pandas)数据分析学习
python·数据分析·pandas
dal118网工任子仪4 小时前
88.[4]攻防世界 web php_rce
安全·web安全