nvanwyen
nvanwyen

Reputation: 81

Using Java 7 to Verify OpenSSL Generated S/MIME Digital Signature Files

We have a process that uses OpenSSL to generate S/MIME digital signatures which need to be verified later using Java 7. On one side we use OpenSSL to read in text files and generate a signed digital output which is verified later.

We used to have the verification using OpenSSL but now we need Java (note: we cannot count on OpenSSL being available now).

We use this to sign: openssl smime -sign -inkey private.key -signer public.key -in ${f} > ${f}.signed and this to verify: openssl smime -verify -noverify -in ${f}.signed

Note: that the verify does not validate the certificates, only checks the signature/contents.

I need to change the verify part of this process to be a java application, preferably with Java 7 (which I think now has the JCE built in).

A sample output is something like ...

MIME-Version: 1.0
Content-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha-256"; boundary="----185C6C544BB34D30B0835B915C158544"

This is an S/MIME signed message

------185C6C544BB34D30B0835B915C158544
Four score and seven years ago our fathers brought forth on this continent, a
new nation, conceived in Liberty, and dedicated to the proposition that all
men are created equal.

Now we are engaged in a great civil war, testing whether that nation, ...
------185C6C544BB34D30B0835B915C158544
Content-Type: application/x-pkcs7-signature; name="smime.p7s"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7s"

MIIKAAYJKoZIhvcNAQcCoIIJ8TCCCe0CAQExDzANBglghkgBZQMEAgEFADALBgkq
hkiG9w0BBwGgggchMIIHHTCCBgWgAwIBAgIEUzuEpzANBgkqhkiG9w0BAQsFADCB
hzELMAkGA1UEBhMCVVMxGDAWBgNVBAoTD1UuUy4gR292ZXJubWVudDEoMCYGA1UE
CxMfRGVwYXJ0bWVudCBvZiBIb21lbGFuZCBTZWN1cml0eTEiMCAGA1UECxMZQ2Vy
dGlm ...
... JIQeeE=

------185C6C544BB34D30B0835B915C158544--

The signature algorithm is sha256WithRSAEncryption; example ...

openssl smime -pk7out -in message.signed | openssl pkcs7 -text -noout -print_certs
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1396409511 (0x533b84a7)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=Corp, OU=Acme, OU=CA
        Validity
            Not Before: May 16 15:27:56 2014 GMT
            Not After : May 16 15:57:56 2015 GMT
        Subject: C=US, O=Corp, OU=Acme, OU=CBP, CN=foo.acme.corp.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:00:00:00:00:b1:b6:49:6e:ca:d7:61:07:a0:18:
                    ...
                    c9:de:ab:a7:2f:97:e4:f6:64:37:ec:3a:9d:ae:c0:
                    16:03
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Certificate Policies: 
                Policy: 2.16.840.1.101.3.2.1.3.8
...

I have looked at many, many examples, tried multiple ones without success. I wish had had some source to share with you, but the success I have had so far would not be helpful in the least.

Upvotes: 4

Views: 1031

Answers (1)

Jaime Hablutzel
Jaime Hablutzel

Reputation: 6343

This can be done using the Bouncy Castle Crypto APIs where you can use the following official example as reference, https://github.com/bcgit/bc-java/blob/master/mail/src/main/java/org/bouncycastle/mail/smime/examples/ValidateSignedMail.java.

For a simpler example to perform a full validation of a signed email including the certification chain you would do something like this with org.bouncycastle:bcmail-jdk15on:1.52:

import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.mail.smime.validator.SignedMailValidator;

import javax.mail.internet.MimeMessage;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.PKIXParameters;

public class SignedMailValidatorExample {
    public static void main(String[] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());
        FileInputStream signedEmailInputStream = new FileInputStream("signed_email.eml");
        MimeMessage signedEmailMimeMessage = new MimeMessage(null, signedEmailInputStream);
        KeyStore trustStore = KeyStore.getInstance("JKS");
        trustStore.load(new FileInputStream("truststore.jks"), "changeit".toCharArray());
        PKIXParameters pkixParameters = new PKIXParameters(trustStore);
        pkixParameters.setRevocationEnabled(false);
        SignedMailValidator signedMailValidator = new SignedMailValidator(signedEmailMimeMessage, pkixParameters);
        boolean successfulValidation = true;
        for (SignerInformation signerInformation : signedMailValidator.getSignerInformationStore().getSigners()) {
            SignedMailValidator.ValidationResult signerValidationResult = signedMailValidator
                    .getValidationResult(signerInformation);
            if (!signerValidationResult.isValidSignature()) {
                successfulValidation = false;
                break;
            }
        }
        if (successfulValidation) {
            System.out.println("Signed email validated correctly.");
        } else {
            System.out.println("Signed email validation failed.");
        }
    }
}

Where truststore.jks should contain a CA certificate (e.g. the issuing CA) that chains to the certificate used to sign the email. Now, you can easily created this file using a software like https://keystore-explorer.org/.

Upvotes: 1

Related Questions