Replicating 'openssl smime' in Java with B

2019-05-10 20:10发布

问题:

I have a problem in hand. My colleague who doesn't know Java is using OpenSSL commands to sign a file as follows:

openssl smime -binary -sign -certfile WWDR.pem -signer passcertificate.pem \
  -inkey passkey.pem -in manifest.json -out signature -outform DER \
  -passin pass:12345

As you can see there are three files here that are given to the openssl command to generate the signature.

Now we want to replicate the same functionality using Java because the content that we are suppose to be signed will be dynamic and is server side in nature. I read that BouncyCastle is the way to go. But I am not sure how to go about using that library. I am not very familiar with cryptography technologies too. I am not able to understand how do I use all the three files above to sign the content in manifest.json.

If someone can please guide me to the right code or give me a start I will be very appreciative of your efforts.

回答1:

I also had to replicate that openssl command in java and this is how I accomplished it. No need to use Runtime.

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

This is my files mapping:

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


回答2:

Firstly, do not feel bad about struggling to understand BouncyCastle. It's a really useful API but it's poorly documented. The best bet is to search around for examples that will teach you how to use the API.

As I've not used BouncyCastle for SMIME before (I've mostly used it for PGP and/or JCE) a brief hunt for "bouncycastle smime example" has bought me to this page and specifically, this example.

Hopefully this is a good start, from which further Googling will help understand the API classes in use. I suspect that example alone will get you 80% of the way there.


In case there is any confusion about the purpose of your input files:

-certfile WWDR.pem - This is an additional certificate to specify in the message. The recipient of the signed message will consider this certificate when validating the signature.

-signer passcertificate.pem - This is the certificate that directly corresponds to your signing key.

-inkey passkey.pem - This is your signing key



回答3:

So if someone is wondering how I solved my problem above here is what I did:

I used Java's Runtime object!

String openSSLCommand = openssl smime -binary 
-sign -certfile WWDR.pem -signer passcertificate.pem
-inkey passkey.pem -in manifest.json -out signature -outform DER
-passin pass:12345

Process process = Runtime.getRuntime().exec(openSSLCommand);

Thank you everyone