我需要在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。
我已经阅读并进行了许多带有弹性城堡和java.security.cert
标准API的测试,但是我无法生成相同的签名结果。我检查了来自弹力城堡的内部表示形式,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用开始生成一个文件>开始PKCS7 和结束PKCS7 标题(而不是 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标头(。因此,除非您完全需要开始CMS 标题,我相信这足够了。
如果您不想写入文件并获得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)