java – 使用较新版本的Bouncy Castle时,接收器无法验证SMIME

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

由于我们使用JDK 1.7(和1.8),我一直在尝试将其更新为更新版本的BC,java-mail等.我已将所有充气城堡升级到bcmail-jdk15on:1.51和bcprov-jdk15on: 1.51,以及java邮件,并按照bcmail包中的示例进行操作.但是,我仍然从Cyclone那里得到一个错误,说完整性检查失败了.

我相当肯定错误在于我如何签名.当我禁用签名并仅使用加密时,它会正确处理.此外,我可以正确地从远程服务器接收签名的响应并验证签名,这是我如何获取错误消息(来自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--

最佳答案 经过大量的调试和转储文件等等,我证明了它与摘要计算有关.在签名正文部分中的特定位置是内容MIC(摘要的base64).不知何故,这个价值与另一方所做的不符……

有一次我有这个,还有更多的时间与谷歌,我终于出现more information on sourceforge确认这一点.有用,因为它提到我的特定版本的BC.报价:

The problem is that BC >= 1.27 will “canonicalize” all messages that
are not sent with content-transfer-encoding binary.

What does this mean?

In the S/MIME rfc it says that all messages should be converted to a
“canonical” form before computing the MIC. The “canonical” form for
text messages is that EOL is indicated by CR, LF. The rfc’s are
silent on what the canonical form for other content types is. Many
S/MIME implementations (e.g. openssl, Bouncy Castle after 1.27)
incorrectly assume that the canonical form for all messages except
those sent with content-transfer-encoding binary is that every LF
should be preceeded by a CR.

So if a BC 1.25 used sends a message including bare LF characters then
the MIC validation will fail if the message is received by an
application using BC >= 1.27, or openssl smime, or many other S/MIME
implementations.

OpenAS2 should be fixed to use content-transfer-encoding binary.

只有通过手动执行JCA路由,轻量级路由以及CMS路由,在MIME Body Part上设置了我已经验证了相同结果(在服务器上失败)的编码时,这似乎才有效.

有了这些信息,我对发送者做了一个简单的改变….

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之后根据预期的消化值进行验证.也许我错过了它)

点赞