我需要通过 xml 文件输入生成具有 PEM 格式的 SHA1+RSA 分离签名的 CMS。我需要在运行时通过 Java 代码来实现这一点。我需要避免使用 OpenSSL 等外部工具。这是因为我们需要使用Java生成的签名来调用一些服务,并在签名过程中发生异常(输入的xml每天都在变化)时正确管理错误。
我必须签署的文件如下所示:
<header>
<generationTime>2017-04-17T00:00:01-03:00</generationTime>
<expirationTime>2017-04-17T23:59:59-03:00</expirationTime>
</header>
使用 OpenSSL,使用私钥和证书,执行以下命令生成签名:
openssl cms -sign -in tra.xml -inkey MyPrivateKey -signer myCertificate.pem -out tra.xml.cms -outform PEM -nodetach
在这种情况下生成的 PEM 签名是:
-----BEGIN CMS-----
MIIGdAYJKoZIhvcNAQcCoIIGZTCCBmECAQExDTALBglghkgBZQMEAgEwgaIGCSqG
SIb3DQEHAaCBlASBkTxoZWFkZXI+ICAgIA0KPGdlbmVyYXRpb25UaW1lPjIwMTct
MDQtMTdUMDA6MDA6MDEtMDM6MDA8L2dlbmVyYXRpb25UaW1lPg0KPGV4cGlyYXRp
b25UaW1lPjIwMTctMDQtMTdUMjM6NTk6NTktMDM6MDA8L2V4cGlyYXRpb25UaW1l
Pg0KPC9oZWFkZXI+DQqgggNOMIIDSjCCAjKgAwIBAgIII0Or3JGYSY4wDQYJKoZI
hvcNAQENBQAwODEaMBgGA1UEAwwRQ29tcHV0YWRvcmVzIFRlc3QxDTALBgNVBAoM
BEFGSVAxCzAJBgNVBAYTAkFSMB4XDTE3MDEyNjE2MDIwNFoXDTE5MDEyNjE2MDIw
NFowMDETMBEGA1UEAwwKYWNjZXNvQUZJUDEZMBcGA1UEBRMQQ1VJVCAyMDI5OTUw
Mzk2OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPhtRXT8FQPvcFvQ
CUSZaHTtcc864DsvP3zedpcr1gDLyJRMMlKnV0mZVJXEeC6eo6AV71kv2QpFUUp3
OGUAS/zJGXByCJ2trV/pXrvppmJvAJARlfw6KoqQBY+YYoIinIzCbUHdvoPwub2K
o7081VlmLxUffiDElbAi3gi41z/W6pD57i3U1uPjS45HRvIn7Vcv4epcH3x9+IDC
DEbZ0hsKIiuJrH4RO1k50gSSaXjvAQSG8kbEXMQ89AxAeynI8jk964JpHc0qLj6y
1sfvAyCSPq8ZFURribdboZi8G6oAccIM1pyMKA13+AcPkOFy0SyotjnFgrK2MMVZ
+vEgNwECAwEAAaNgMF4wDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBSzstP//em6
3t6NrxEhnNYgffJPbzAdBgNVHQ4EFgQUEuAlczdaDyI7hGuwyqR6ipLvTikwDgYD
VR0PAQH/BAQDAgXgMA0GCSqGSIb3DQEBDQUAA4IBAQA+Pg0RQ7J5qiViZMk94tgD
WAgTT0iIoVm65Xn2/czlBgefhxY6l4SKqQCONJpAMCUI2mEG6qgOg/u+GbN3pR+p
1FSC2yDETRIpf9nekooTEot6A9r2Huykd4Sp3QHZEly9Sx3+3ek+w7Mg0k/+AtgT
JodP0ArzCQyvBJCR8ZTTHjUazf2/9o0iEqQIKyp1vn2vv3JlMONBb7+ALqzCXgCb
FVjFpF8PpZyWM/+J6WVrU19hB3wsdyhLh0M5CiBQ19aGC8R/0bWm2w2P3awOn8r4
r/duYqdGzK/7zTpjtvk0VKax6/Pe5WIFLKXTP9LGpxbCQjxKpbVxMbzx1pDPGBjF
MYICVDCCAlACAQEwRDA4MRowGAYDVQQDDBFDb21wdXRhZG9yZXMgVGVzdDENMAsG
A1UECgwEQUZJUDELMAkGA1UEBhMCQVICCCNDq9yRmEmOMAsGCWCGSAFlAwQCAaCB
5DAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0xNzA0
MTcxMzM3MDVaMC8GCSqGSIb3DQEJBDEiBCBL5i3jl4+rfSfo/Pcu/CbI6JHGj0jg
UGI/EucH7LBM6jB5BgkqhkiG9w0BCQ8xbDBqMAsGCWCGSAFlAwQBKjALBglghkgB
ZQMEARYwCwYJYIZIAWUDBAECMAoGCCqGSIb3DQMHMA4GCCqGSIb3DQMCAgIAgDAN
BggqhkiG9w0DAgIBQDAHBgUrDgMCBzANBggqhkiG9w0DAgIBKDANBgkqhkiG9w0B
AQEFAASCAQAMNHskWrhZCu/DmFQLCrAweTEacCwTJdYOx+704PS6DkflXQLpD9q4
B0Psxx6gmN7HkHkrY4bD250TefZpKyD7IfJjdNQEz4SzgmtgMTl2a0JvlgpWSNjq
au0WMkFXnoSo0oJ3s4FSHWAe15DlNFQn9HbKfjI/sIHpkhgA0u/Kr6ZHUSIEnfxS
KVNxQ224uvFPGCggHnPIdtBRFgGn44J1hRyiYm0BLqJO5sAwV23gWB8OztsuBHqj
imi4WWXnCVPk7/6BMGNuLpUH3bH6nfIPDfSL7bb7vXRhcQrjTU8o38/C3gDsJr2A
4JNHkIjPMoo4l+wlS66MJQpOXadjYaFi
-----END CMS-----
这就是我需要通过 Java 7 生成的内容。
我已经阅读并使用 Bouncy CaSTLe 和 java.security.cert 标准 API 进行了多次测试,但我无法生成相同的签名结果。我检查了 Bouncy CaSTLe 的内部表示,API 使用 DER 格式来保存签名。并且在示例中始终显示如何验证签名,但没有显示如何生成签名并将其保存在文件中或以 PEM 形式打印。
这是我如何生成 BC 签名的示例,但结果与我需要的有很大不同:
public static String encryptSHA1RSA(String xmlPayload) throws Exception {
List certList = new ArrayList();
CMSTypedData msg = new CMSProcessableByteArray(xmlPayload.getBytes());
certList.add(CMSEncryptor.getSingCert());
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(CMSEncryptor.getPrivateKey());
gen.addSignerInfoGenerator( new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build())
.build(sha1Signer, CMSEncryptor.getSingCert()));
gen.addCertificates(certs);
CMSSignedData sigData = gen.generate(msg, true);
ASN1InputStream asn1 = new ASN1InputStream(sigData.getEncoded());
FileOutputStream fos = new FileOutputStream(getSecurityFolderPath() + "/tra.test.cms");
DEROutputStream dos = new DEROutputStream(fos);
dos.writeObject(asn1.readObject());
dos.flush();
dos.close();
asn1.close();
return Base64Util.encodeBase64(new String(sigData.getEncoded()));
}
如果我用 base64 编码结果也会不同:
return Base64Util.encodeBase64(new String(sigData.getEncoded()));
任何提示将不胜感激
最佳答案
我使用 BouncyCaSTLe (bcprov-jdk15on) 1.56 和 Java 1.7 进行了测试
要将您的签名转换为 PEM 格式,您可以使用 BouncyCaSTLe 的 JcaPEMWriter
(或者只是 PEMWriter
对于旧版本),如下所示:
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
// ... used the same code you posted above ...
// *** NOTE: if you want a detached signature, change the second parameter to false ***
CMSSignedData sigData = gen.generate(msg, false);
// write sigData to output.pem file, using a pem writer
ContentInfo ci = ContentInfo.getInstance(ASN1Sequence.fromByteArray(sigData.getEncoded()));
JcaPEMWriter writer = new JcaPEMWriter(new FileWriter("output.pem"));
writer.writeObject(ci);
writer.close();
结果略有不同,因为 BouncyCaSTLe 生成的文件带有 BEGIN PKCS7 和 END PKCS7 header (而不是 BEGIN CMS 和 >结束 CMS):
-----BEGIN PKCS7-----
MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCA
JIAEgYg8aGVhZGVyPgo8Z2VuZXJhdGlvblRpbWU+MjAxNy0wNC0xN1QwMDowMDow
... lots of base64 lines ...
WHkpUQDxQj+v/SbMGa5+U7VC8+HNOfgFOba+U56QLhbhDEeaaozwATXveRkqhsdn
AAAAAAAA
-----END PKCS7-----
但输出文件无论如何都是有效的数字签名。 OpenSSL 和 BouncyCaSTLe 都可以读取两者(PKCS7 和 CMS header )。因此,除非您确实需要 BEGIN CMS header ,否则我相信这就足够了。
如果您不想写入文件并获取 String
相反,您可以使用 java.io.StringWriter
与 JcaPEMWriter
结合:
StringWriter sw = new StringWriter();
JcaPEMWriter writer = new JcaPEMWriter(sw);
writer.writeObject(ci);
writer.close();
String pemString = sw.toString(); // pemString will be the PEM formatted string (with BEGIN PKCS7 header)
关于java - CMS 签名 SHA1+RSA 与 PEM 格式 - Java,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43454473/