I need to generate a CMS with SHA1+RSA detached signature in PEM format over a xml file input. I need to make this from Java code in runtime. I need to avoid the use of an external tool like OpenSSL. This is because we need invoke some services with the generated signature from Java and manage error properly if an exception ocurred in the signature process (the input xml changes every day).
The file I have to sign looks like this:
<header> <generationTime>2017-04-17T00:00:01-03:00</generationTime> <expirationTime>2017-04-17T23:59:59-03:00</expirationTime> </header>
Using OpenSSL, with a private key and a certificate, the signature is generated executing this command:
openssl cms -sign -in tra.xml -inkey MyPrivateKey -signer myCertificate.pem -out tra.xml.cms -outform PEM -nodetach
The generated PEM signature in that case is:
-----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-----
Thats is what I need generate over Java 7.
I already read and make many test with Bouncy Castle and java.security.cert
standard API, but I can’t generate the same signature result. I checked internal representation from Bouncy Castle and the API is using DER format to hold the signature. And in the examples are always showing how to verify a signature, but no how to generate one and save it in a file or print it in PEM form.
Here is an example of how I generate a BC signature, but the result is pretty different from what I need:
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())); }
Is different also if I encode the result with base64:
return Base64Util.encodeBase64(new String(sigData.getEncoded()));
Any tips will be very apreciated
Advertisement
Answer
I’ve made a test using BouncyCastle (bcprov-jdk15on) 1.56 and Java 1.7
To convert your signature to PEM format, you can use BouncyCastle’s JcaPEMWriter
(or just PEMWriter
for older versions), like this:
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();
The result is slightly different, because BouncyCastle generates a file with BEGIN PKCS7 and END PKCS7 headers (instead of BEGIN CMS and END CMS):
-----BEGIN PKCS7----- MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCA JIAEgYg8aGVhZGVyPgo8Z2VuZXJhdGlvblRpbWU+MjAxNy0wNC0xN1QwMDowMDow ... lots of base64 lines ... WHkpUQDxQj+v/SbMGa5+U7VC8+HNOfgFOba+U56QLhbhDEeaaozwATXveRkqhsdn AAAAAAAA -----END PKCS7-----
But the output file is a valid digital signature anyway. And both (PKCS7 and CMS headers) can be read by OpenSSL and BouncyCastle. So, unless you need exactly BEGIN CMS header, I believe this will be enough.
If you don’t want to write to a file and get a String
instead, you can use java.io.StringWriter
combined with 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)