Skip to content
Advertisement

CMS signature SHA1+RSA with PEM format – Java

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)
Advertisement