CMS签名SHA1 + RSA与PEM格式 - Java

时间:2017-04-17 15:30:04

标签: java openssl cryptography

我需要通过xml文件输入生成具有PEM格式的SHA1 + RSA分离签名的CMS。我需要在运行时从Java代码中创建它。我需要避免使用像OpenSSL这样的外部工具。这是因为我们需要使用Java生成的签名调用某些服务,并在签名过程中出现异常(输入xml每天更改)时正确管理错误。

我必须签名的文件如下:

select t1.id, t2.code3
from table1 t1 join
     table1 tt1
     on t1.id = tt1.id and
        t1.code < t2.code join
     table2 t2
     on t2.code1 = t1.code and
        t2.code2 = tt1.code;

使用OpenSSL,使用私钥和证书,签名将在执行此命令时生成:

<header>    
 <generationTime>2017-04-17T00:00:01-03:00</generationTime>
 <expirationTime>2017-04-17T23:59:59-03:00</expirationTime>
</header>

在这种情况下生成的PEM签名是:

openssl cms -sign -in tra.xml -inkey MyPrivateKey -signer myCertificate.pem -out tra.xml.cms -outform PEM -nodetach

这就是我需要通过Java 7生成的东西。

我已经阅读并使用Bouncy Castle和-----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----- 标准API进行了许多测试,但我无法生成相同的签名结果。我检查了Bouncy Castle的内部表示,API使用DER格式来保存签名。并且在示例中始终显示如何验证签名,但没有如何生成签名并将其保存在文件中或以PEM格式打印。

以下是我如何生成BC签名的示例,但结果与我需要的完全不同:

java.security.cert

如果我使用base64编码结果也不同:

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()));
}

任何提示都会非常苛刻

1 个答案:

答案 0 :(得分:2)

我使用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 标头(而不是 BEGIN CMS END CMS ):

-----BEGIN PKCS7-----
MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCA
JIAEgYg8aGVhZGVyPgo8Z2VuZXJhdGlvblRpbWU+MjAxNy0wNC0xN1QwMDowMDow

... lots of base64 lines ...

WHkpUQDxQj+v/SbMGa5+U7VC8+HNOfgFOba+U56QLhbhDEeaaozwATXveRkqhsdn
AAAAAAAA
-----END PKCS7-----

但是输出文件无论如何都是有效的数字签名。 OpenSSL和BouncyCastle都可以读取(PKCS7和CMS标头)。因此,除非您需要完全 BEGIN CMS 标题,否则我相信这就足够了。

如果您不想写入文件并获得String,则可以将java.io.StringWriterJcaPEMWriter结合使用:

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)