libcrypto 别忽略这行代码,否则加密会失败
2026/9/15 8:25:18 网站建设 项目流程

代码对比

/* 第一组:3行,显式设置两个哈希 */ EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING); EVP_PKEY_CTX_set_rsa_oaep_md(ctx, EVP_sha256()); // OAEP 哈希 = SHA-256 EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, EVP_sha256()); // MGF1 哈希 = SHA-256 ← 多了这行 /* 第二组:2行,只设置 OAEP 哈希 */ EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING); EVP_PKEY_CTX_set_rsa_oaep_md(ctx, EVP_sha256()); // OAEP 哈希 = SHA-256 // MGF1 哈希 = ???(未显式设置)

RSA OAEP 的两个哈希算法

RSA OAEP 填充(RFC 8017 / PKCS#1 v2.2)内部有两个独立的哈希算法
OAEP 编码结构: ┌─────────────────────────────────────────────────────┐ │ Hash(L) │ PS(全零) │ 0x01 │ M(消息) │ ← 用 oaep_md 计算 Hash(L) │ (hLen) │ │ │ │ ├─────────────────────────────────────────────────────┤ │ DB = Hash(L) || PS || 0x01 || M │ │ maskedDB = DB XOR MGF(seed, mLen) │ ← 用 mgf1_md 计算 MGF │ maskedSeed = seed XOR MGF(maskedDB, hLen) │ ← 用 mgf1_md 计算 MGF └─────────────────────────────────────────────────────┘
OAEP 编码结构:
关键点:这两个哈希是独立的,可以相同也可以不同。标准允许 oaep_md ≠ mgf1_md,但实际工程中通常设为相同。

第二组代码的 MGF1 哈希到底是什么?

这取决于OpenSSL 版本

OpenSSL 版本

只设 oaep_md=SHA-256 时,mgf1_md 的值

1.0.2 及更早

保持默认SHA-1(不跟随 oaep_md)

1.1.0 / 1.1.1

保持默认SHA-1(不跟随 oaep_md)

3.0+

文档说"通常跟随 oaep_md",但不保证,建议显式设置

结论:第二组代码在大多数 OpenSSL 版本中,MGF1 哈希仍然是SHA-1,而不是你期望的 SHA-256!
/* 第二组代码实际效果(OpenSSL 1.1.x): oaep_md = SHA-256 (设置的) mgf1_md = SHA-1 (默认值,你没设置) */
这是一个非常隐蔽的坑——你以为用了 SHA-256,实际上 MGF1 还在用 SHA-1。

实际影响:跨平台解密失败

场景1:和对端都用第一组代码(显式 SHA-256 + SHA-256)

加密端:oaep=SHA-256, mgf1=SHA-256
解密端:oaep=SHA-256, mgf1=SHA-256
结果: 解密成功

场景2:加密端用第一组,解密端用第二组(OpenSSL 1.1.x)

加密端:oaep=SHA-256, mgf1=SHA-256
解密端:oaep=SHA-256, mgf1=SHA-1 ← MGF1 不匹配!
结果: 解密失败(padding check failed / RSA_OAEP_DECRYPT_ERROR)

场景3:和 Java / Python / 嵌入式设备交互

语言/平台

OAEP 默认配置

注意事项

Java JCE

OAEPWithSHA-256AndMGF1Padding→ oaep=SHA-256, mgf1=SHA-1(!)

Java 的 MGF1 默认是 SHA-1,必须用

OAEPParameterSpec

显式指定 MGF1=SHA-256

Python cryptography

OAEP(mgf=MGF1(algorithm=SHA256()),algorithm=SHA256())

必须显式指定两者

嵌入式 mbedTLS

MBEDTLS_RSA_PKCS_V21+ hash_id

两个哈希通常绑定为同一个

OpenSSL 命令行

openssl pkeyutl -encrypt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256 -pkeyopt rsa_mgf1_md:sha256

必须两个都指定

Java 的坑最大:OAEPWithSHA-256AndMGF1Padding 这个名字看起来两个都是 SHA-256,但实际上 MGF1 默认是 SHA-1!必须这样写:
OAEPParameterSpec spec = new OAEPParameterSpec( "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, // ← 必须显式指定 MGF1=SHA-256 PSource.PSpecified.DEFAULT );

为什么第一组是最佳实践

1. 行为确定,不依赖 OpenSSL 版本

显式设置两个哈希,无论 OpenSSL 1.0.2 / 1.1.x / 3.x,行为完全一致。

2. 跨平台兼容性可控

和 Java/Python/嵌入式设备联调时,双方明确约定 oaep=SHA-256, mgf1=SHA-256,不会出现"我这边是 SHA-256,你那边 MGF1 是 SHA-1"的诡异问题。

3. 安全强度一致

如果 oaep=SHA-256 但 mgf1=SHA-1,整体安全强度被 SHA-1 拖后腿。PCI PTS v7.0 要求设备安全功能 ≥128-bit,SHA-1(160-bit 但已被攻破)不满足要求,必须全部用 SHA-256。

4. 审计友好

安全审计时,显式设置的代码一目了然;只设一个的代码需要审计员去查 OpenSSL 版本默认值,容易遗漏。

pos项目中的建议

涉及 RSA 的场景(TR-34 远程密钥加载、脱机 PIN 加密等),统一用第一组写法:

/*RSA OAEP 标准配置 */ EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING); EVP_PKEY_CTX_set_rsa_oaep_md(ctx, EVP_sha256()); EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, EVP_sha256()); // 必须显式设置
检查清单
  • 所有 RSA 加密/解密都用 OAEP(禁止 PKCS#1 v1.5)
  • oaep_md 和 mgf1_md 都显式设置为 SHA-256
  • 和对端(银联/TMS/密钥注入系统)确认双方配置一致
  • Java 端用 OAEPParameterSpec 显式指定 MGF1=SHA-256
  • 密钥长度 ≥2048-bit(PCI PTS v7.0 建议 3072-bit 或 ECC P-256)

总结

对比项

第一组(3行)

第二组(2行)

OAEP 哈希

SHA-256(显式)

SHA-256(显式)

MGF1 哈希

SHA-256(显式)

取决于 OpenSSL 版本,通常是 SHA-1

行为确定性

跨版本一致

依赖默认值

跨平台兼容

可控

容易和 Java/Python 不匹配

安全强度

全 SHA-256

MGF1 可能是 SHA-1

PCI PTS v7.0

合规

可能不合规(SHA-1)

推荐度

推荐

不推荐

一句话:第一组多的那行 EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, EVP_sha256()) 不是多余的,它确保 MGF1 掩码生成函数也用 SHA-256,避免 OpenSSL 默认值带来的隐蔽兼容性和安全问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询