4

我正在使用 BC 加密和签署 SMIME 消息以用于 AS2。我们的代码适用于绝对古老的充气城堡版本,bcmail-1.4:125. 升级到任何更新的东西会导致消息的接收者(不是太古老的 Cyclone 服务器)无法验证消息。(例如,maven 中最早的 v也会导致这种情况。这些是没有 API 更改的版本(例如 1.38)。

由于我们使用 JDK 1.7(和 1.8),我一直在尝试将其更新为更新版本的 BC、java-mail 等。我已经将所有 bouncy castle 升级到bcmail-jdk15on:1.51bcprov-jdk15on:1.51,以及 java 邮件,并遵循包中的示例bcmail。但是,我仍然收到 Cyclone 的错误提示integrity-check-failed

我相当肯定错误与我的签名方式有关。当我禁用签名并仅使用加密时,它会正确处理。此外,我可以正确接收来自远程服务器的签名响应并验证签名,这就是我获取错误消息的方式(来自 MimeMultiPart 上的内容处置)。

  • 证书由 openssl/self signed/etc 创建,存储在 pkcs12 文件中
  • 无限强度政策到位
  • senderKey是一个BCRSAPrivateCrtKey
  • senderCert
    • org.bouncycastle.jcajce.provider.asymmetric.x509.X509CertificateObject

失败:当前代码是这个,使用bcmail-jdk15on:1.51& 等

SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder()
           .setProvider("BC")
           .build("SHA1withRSA", senderKey, senderCert));
// gen.addCertificates(new JcaCertStore(list(senderCert))); old v. doesn't add certs
MimeMultipart smime = gen.generate(part); // MimeBodyPart passed in to function
MimeBodyPart tmpBody = new MimeBodyPart();
tmpBody.setContent(signedData);
tmpBody.setHeader("Content-Type", signedData.getContentType()

以前的工作代码看起来像这样并使用bcmail-1.4:1.25. 解密时升级到 1.3x 也会导致另一端失败(无论我在哪个 jdk 上运行,1.6 - 1.8)

MimeBodyPart body = new MimeBodyPart();
body.setDataHandler(new DataHandler(new ByteArrayDataSource(bytes[], contentType, null);));
SMIMESignedGenerator sGen = new SMIMESignedGenerator();
// SHA1 resolves to "1.3.14.3.2.26", FWIW
sGen.addSigner(senderKey, senderCert, getBouncyCastleAlgorithmId("SHA1"));
MimeMultipart signedData = sGen.generate(part, "BC");
// this is then encrypted & streamed, no issues there

通用设置代码

byte[] data = Files.readAllBytes(filePath);
MimeBodyPart part = new MimeBodyPart();
ByteArrayDataSource dataSource = new ByteArrayDataSource(data, "application/EDIFACT", null);
part.setDataHandler(new DataHandler(dataSource));
part.setHeader("Content-Transfer-Encoding", "8bit");
part.setHeader("Content-Type", "application/EDIFACT");

我感觉这与我添加(或操作)senderCert本地应用程序的 X509 的方式有关。

更新

我通过删除证书使新代码更符合旧代码:

  • 它不再在签名消息中包含证书。老版本没有
  • 整个 mime 多部分内容现在的长度(1095 字节)与以前完全相同
  • 格式(标题等)现在完全相同
  • 签名部分现在几乎相同。但是,有一部分似乎会根据时间(???)而变化,并且每次都会发生变化。我还无法让 openssl 验证此消息,不知道为什么。

这是示例输出,FWIW。中的文本[]是唯一更改的部分。

------=_Part_1_1448572667.1409621469842
Content-Type: application/EDIFACT
Content-Transfer-Encoding: 8bit

this is a test

------=_Part_1_1448572667.1409621469842
Content-Type: application/pkcs7-signature; name=smime.p7s; smime-type=signed-data
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"
Content-Description: S/MIME Cryptographic Signature

MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAMYIBpDCCAaAC
AQEwgZ4wgZAxCzAJBgNVBAYTAmNuMREwDwYDVQQIDAhzaGFuZ2hhaTESMBAGA1UEBwwJY2hhbmdu
aW5nMREwDwYDVQQKDAhwb3dlcmUyZTEOMAwGA1UECwwFaXRkZXYxEjAQBgNVBAMMCWFiLWNsaWVu
dDEjMCEGCSqGSIb3DQEJARYUYWItY2xpZW50QG15Q29ycC5jb20CCQClDAGwq37A/jAJBgUrDgMC
GgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTQwOTAyMDEz
M[TA5]WjAjBgkqhkiG9w0BCQQxFgQUG6KkoqPBvE7Kd9dB0eop/aUTya0wDQYJKoZIhvcNAQEBBQAE
gYB[h9N4maow9aoTQ8QBGgXEYE+xgXSmRPy+ufIsMpuS0Yys/1t3AfXSSI7WKgLMRKYXve8gdb4Gn
dqecHzkBwBq4hebt9YK+E30E6DpZpCwErsgDVaU/ExBA5gauPWneysy+s2bE5Y6pNZ7Qf3kGU5kI
UjlOF/LUNkCsgT5z//]5N6QAAAAAAAA==
------=_Part_1_1448572667.1409621469842--
4

1 回答 1

4

经过大量的调试和文件转储等,我证明它与摘要计算有关。在签名正文部分的特定位置是内容 MIC(摘要的 base64)。不知何故,这个值与对方所做的不匹配......

一旦我有了这个,并在谷歌上呆了一段时间,我终于在 sourceforge 上找到了更多信息来证实这一点。很有帮助,因为它提到了我的特定版本的 BC。去引用:

问题是 BC >= 1.27 将“规范化”所有未使用内容传输编码二进制文件发送的消息。

这是什么意思?

在 S/MIME rfc 中,它说在计算 MIC 之前应将所有消息转换为“规范”形式。文本消息的“规范”形式是 EOL 由 CR、LF 表示。rfc 没有说明其他内容类型的规范形式是什么。许多 S/MIME 实现(例如 openssl、1.27 之后的 Bouncy Castle)错误地假设所有消息的规范形式(使用内容传输编码二进制发送的消息除外)是每个 LF 前面都应该有一个 CR。

因此,如果使用的 BC 1.25 发送包含裸 LF 字符的消息,则如果应用程序使用 BC >= 1.27、openssl smime 或许多其他 S/MIME 实现接收到消息,则 MIC 验证将失败。

OpenAS2 应该被修复为使用内容传输编码二进制文件。

仅当您在MIME 正文部分上设置编码时,这似乎才有效, 我通过手动执行 JCA 路由、轻量级路由以及 CMS 路由验证了相同的结果(在服务器上失败)。

有了这些信息,我对发件人进行了简单的更改......

MimeBodyPart part = //.. make mime body part from file
part.setHeader("Content-Transfer-Encoding", "binary");

有趣的是,改变与 相关的任何东西SMIMESignedGenerator()似乎都没有效果:

gen = SMIMESignedGenerator("binary");  // nothing, even though the docs say to set this

对于任何感兴趣的人,我的最终签名功能如下所示:

SMIMESignedGenerator gen = new SMIMESignedGenerator();
SignerInfoGenerator sigGen = new JcaSimpleSignerInfoGeneratorBuilder()
        .setProvider(BC)
        .build("SHA1withRSA", senderKey, senderCert);
gen.addSignerInfoGenerator(sigGen);
MimeMultipart smime = gen.generate(part);
MimeBodyPart tmpBody = new MimeBodyPart();
tmpBody.setContent(smime);
tmpBody.setHeader("Content-Type", smime.getContentType());
return tmpBody;

原始文件只有一行:

this is a test

得到签名的输入是这样的:

Content-Type: application/EDIFACT
Content-Transfer-Encoding: binary

this is a test

调试信息:

data bytes:
436F6E74656E742D547970653A206170706C69636174696F6E2F454449464143540D0
A436F6E74656E742D5472616E736665722D456E636F64696E673A2062696E6172790D
0A0D0A74686973206973206120746573740A

digest mic: {
   "algorithmId":   "1.3.14.3.2.26"
   "digest bytes":  "CEC2C6614A481DFDF45C801FD6F2A51BC53D3FDF"
   "digest base64": "zsLGYUpIHf30XIAf1vKlG8U9P98="
}

不是这不附加签名或添加任何功能,并使用 v1 x509 证书。现在我可能会改变这些东西,因为这一切都再次起作用了。

我真的希望这一切都更加透明...... BC 内部是间接间接,虽然我明白为什么。它在内部仍然比旧版本更好。我不能说我没有找到很多示例,但是 BC 测试用例似乎不是最好的(例如,我找不到在 SMIME 之后根据预期的摘要值进行验证的示例。也许我错过了它)

于 2014-09-03T05:46:20.570 回答