Using 'openssl cms verify/decrypt' to retrieve content encrypted and signed by a foreign Java library

First, you should know that a part of the answer to your question is an openssl bug report I've just filed.

Both openssl cms -sign and openssl cms -verify only handle S/MIME content. They, by coincidence, can also process any text data (though there's no promise that this functionality will be kept in future releases), but they are not designed to sign or verify any encrypted binary data, including, but not limited to, encrypted DER-encoded messages, as it is in your case (though the cms(1) manpage sort of assumes it is still possible).

What is no less important, those two would not detect if the input is in any other format, silently failing instead with a (somewhat obscure) error message:

$ cat msg.txt
This is a secret message from Alice for Bob written at Thu Mar 13 12:39:12 CET 2014
$ 
$ openssl cms -encrypt -outform DER -aes-256-cbc -in msg.txt cert.pem \
| openssl cms -sign -md sha256 -signer cert.pem -inkey key.pem -nodetach -outform DER \
| openssl cms -verify -inform DER -CAfile ca.pem -out msg_ver.txt
Verification successful
$
$ openssl cms -cmsout -in msg_ver.txt -print
Error reading S/MIME message
4404864620:error:0D0D40D1:asn1 encoding routines:SMIME_read_ASN1:no content type:asn_mime.c:440:
$ 
$ openssl cms -cmsout -in msg_ver.txt -inform DER -print
Error reading S/MIME message
4527457900:error:0D06B08E:asn1 encoding routines:ASN1_D2I_READ_BIO:not enough data:a_d2i_fp.c:247:
$

NB: this is as of 1.0.2r as well as 1.1.1b openssl versions — the latest ones at the time of writing.

It would probably stay the same way in future releases, though you'd better check that again if you're reading this circa 2020.

I will try to update the answer once the behaviour changes, though I hold no responsibility for not doing so.

Second, the output you see is a correct decrypted message in its entirety.

However, for reasons unbeknowst to me, whoever has provided you with the encrypted file did not just go the ordinary encrypt[1] -> sign[2] route, but instead did sign[1] -> encrypt[2] -> sign[3] instead. The signing in the steps 1 and 3 was done with different certificates, with serial numbers a2:30:b4:be:2b:89:05:27 and a2:30:b4:be:2b:89:05:23, respectively.

You have correctly handled backwards steps 3 and 2. To verify the last signature from the step 1, you need to find a certificate with the serial number a2:30:b4:be:2b:89:05:27, and the certificate you've provided has a serial number a2:30:b4:be:2b:89:05:23, four issued certificates away from what you should have had.

Here's how you can figure out the serial number of a certificate:

$ openssl x509 -in ehealth_root_ca.cer -noout -text | grep -A1 'Serial Number:'
        Serial Number:
            a2:30:b4:be:2b:89:05:23
$

Maybe the certificate necessary is located somewhere in the sources or examples of the library you're referring to.

(Here's a short openssl cms cookbook in case you need it — though it doesn't seem like you do.)

Tags:

Openssl