Replicate OpenSSL command to sign a file in Java

爱⌒轻易说出口 提交于 2019-12-12 02:53:16

问题


I need to sign a file and so far I've used the openssl comman which is working fine (file is signed and verified).

openssl smime -sign -in unsigned.mobileconfig -out signed.mobileconfig -signer myCrtFile.crt -inkey myKeyFile.key -certfile bundleCertificate.crt -outform der -nodetach

But now I need to do this on runtime, so I need to sign the file programmatically. I'm using BouncyCastle but I'm open to switch to another library.

I'm not skilled with certificates and even less with BouncyCastle. This is what I came with.

The corresponding files from the openssl command to the code below are:

myCrtFile.crt -> signerCertHolder
myKeyFile.key -> privateKeyInfo
bundleCertificate.crt -> certificateHolder

public byte[] sign(String message) throws IOException, CMSException, OperatorCreationException, CertificateEncodingException, MessagingException, CertificateException {
    Security.addProvider(new BouncyCastleProvider());

    PrivateKeyInfo privateKeyInfo = loadInKey();
    X509CertificateHolder signerCertHolder = loadSigner();
    X509CertificateHolder certificateCertHolder = loadCertfile();

    PrivateKey inKey = new JcaPEMKeyConverter().getPrivateKey(privateKeyInfo);

    JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
    X509Certificate signer = certificateConverter.getCertificate(signerCertHolder);
    X509Certificate certificate = certificateConverter.getCertificate(certificateCertHolder);

    List<X509Certificate> certificateList = new ArrayList();
    certificateList.add(signer);
    certificateList.add(certificate);

    Store<?> certs = new JcaCertStore(certificateList);
    ContentSigner sha1signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(inKey);
    JcaSignerInfoGeneratorBuilder jcaSignerInfoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build());

    CMSSignedDataGenerator signGen = new CMSSignedDataGenerator();
    signGen.addSignerInfoGenerator(jcaSignerInfoGeneratorBuilder.build(sha1signer, certificate));
    signGen.addCertificates(certs);

    CMSTypedData content = new CMSProcessableByteArray(message.getBytes());
    CMSSignedData signedData = signGen.generate(content, false);
    byte[] signeddata = signedData.getEncoded();

    return signeddata;
} 

Then I save the byte[] to a file. When I open the file (it is a MDM mobileConfig file) it says "The file is signed but could not be verified". I feel I'm close to the solution but I don't know what am I doing wrong.

Can someone help me figuring out what am I missing to make the file verified?

PS; The certs are from an SSL certificate (GoDaddy) and the bundleCertificate.crt is the GoDaddy bundle certificate.


回答1:


I managed to sign the file. As suspected, I was close to the final solution. Here's the complete code.

public byte[] signMobileConfig(byte[] mobileconfig) 
        throws CertificateEncodingException, PEMException, FileNotFoundException, IOException, CertificateException, OperatorCreationException, CMSException {
    Security.addProvider(new BouncyCastleProvider());

    X509CertificateHolder caCertificate = loadCertfile();

    JcaX509CertificateConverter certificateConverter = new JcaX509CertificateConverter();
    X509Certificate serverCertificate = certificateConverter.getCertificate(loadSigner());

    PrivateKeyInfo privateKeyInfo = loadInKey();
    PrivateKey inKey = new JcaPEMKeyConverter().getPrivateKey(privateKeyInfo);
    ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(inKey);

    CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
    JcaDigestCalculatorProviderBuilder digestProviderBuilder = new JcaDigestCalculatorProviderBuilder().setProvider("BC");
    JcaSignerInfoGeneratorBuilder generatotBuilder = new JcaSignerInfoGeneratorBuilder(digestProviderBuilder.build());

    generator.addSignerInfoGenerator(generatotBuilder.build(sha1Signer, serverCertificate));
    generator.addCertificate(new X509CertificateHolder(serverCertificate.getEncoded()));
    generator.addCertificate(new X509CertificateHolder(caCertificate.getEncoded()));

    CMSProcessableByteArray bytes = new CMSProcessableByteArray(mobileconfig);
    CMSSignedData signedData = generator.generate(bytes, true);

    return signedData.getEncoded();
}

And here's how I load the files:

public X509CertificateHolder loadSigner() throws FileNotFoundException, IOException {
    InputStream inputStream = externalResourcesFacade.getResourceAsStream("path/to/.crt");
    PEMParser parser = new PEMParser(new InputStreamReader(inputStream));
    return (X509CertificateHolder) parser.readObject();
}

public PrivateKeyInfo loadInKey() throws FileNotFoundException, IOException {
    InputStream inputStream = externalResourcesFacade.getResourceAsStream("path/to/.key");
    PEMParser parser = new PEMParser(new InputStreamReader(inputStream));
    return (PrivateKeyInfo) parser.readObject();
}

public X509CertificateHolder loadCertfile() throws FileNotFoundException, IOException {
    InputStream inputStream = externalResourcesFacade.getResourceAsStream("path/to/.crt");
    PEMParser parser = new PEMParser(new InputStreamReader(inputStream));
    return (X509CertificateHolder) parser.readObject();
}


来源:https://stackoverflow.com/questions/36595294/replicate-openssl-command-to-sign-a-file-in-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!