Sunday, December 21, 2014

Support for elliptic curves by jarsigner

Summary: Support for cryptography features by jarsigner depends on available Java crypto providers.

Suppose you are defining a PKI profile. You naturally want to use the stronger algorithms with better performance, which (as of year 2014) most likely means elliptic curves. Besides bit strength and performance, you want to be absolutely sure that the curve is supported by your software. If the latter includes jarsigner, you'll be surprised to find that Oracle documentation seems to not mention at all, what elliptic curves does jarsigner support.

Signing a JAR means adding digests of the JAR data to the manifest file, adding digest of the latter to the manifest signature file, and then creating the JAR signature block file. The last step involves two operations:
  1. calculating a digest over the manifest signature file;
  2. signing - meaning, encrypting with the private key - that digest. 
Jarsigner has an option '-sigalg', which is supposed to specify the two algorithms used in these two steps. (There is also '-digestalg' option, but it is not used for the signature block file; it defines the algorithm used in the two initial steps.) Well, this option is irrelevant for the signing step: the curve is in fact defined by the provided private key. So jarsigner will either do the job or choke on the key which comes from an unsupported curve.

A curve may "not work" because it is unknown to jarsigner itself, or to an underlying crypto provider. (The latter case was a reason to a bug 1006776; only three curves actually worked, while many returned a totally unclear error "certificate exception: java.io.IOException: subject key, Could not create EC public key".)

In a particular setup the support can be tested. For curves, supported by OpenSSL, the test can be done by creating the keypair on each curve and attempting the signing. Create the list of curves with 'openssl ecparam -list_curves', remove manually some extra words openssl puts there, and feed it to the stdin:

#!/bin/bash
# Test, which OpenSSL-supported elliptic curves from the list are supported also by jarsigner.

result="supported-curves.txt"
source_data="data.txt"
jar="data.jar"
key="key.pem"
cert="cert.pem"
pfx="keystore.pfx"
key_alias="foo"         # Identificator of the key in the keystore
storepass="123456"      # jarsigner requires some

touch $source_data

while read curve; do
        # Generate an ECDSA private key for the selected curve:
        openssl ecparam -name $curve -genkey -out $key

        # Generate the certificate for the key; give some dummy subject:
        openssl req -new -x509 -nodes -key $key -out $cert -subj /CN=foo

        # Wrap key+cert in a PKCS12, so that jarsigner can use it:
        openssl pkcs12 -export -in $cert -inkey $key -passout pass:$storepass -out $pfx -name $key_alias

        # Create a fresh jar and attempt to sign it
        jar cf $jar $source_data
        jarsigner -keystore $pfx -storetype PKCS12 -storepass $storepass $jar $key_alias
        [ $? -eq 0 ] && echo $curve >> $result
done

rm $source_data $key $cert $pfx $jar

And enjoy the list in supported-curves.txt.


Conclusion: support of elliptic curves by jarsigner depends on jarsigner itself and on the JRE configuration. There is no command-line option to list all supported curves. For a particular system, support of curves supported by OpenSSL can be easily tested.

Monday, December 8, 2014

JAR signature block file format


Summary: this post explains the content of the "JAR signature block file" - that is, the file "META-INF/*.RSA", "META-INF/*.DSA" or "META-INF/*.EC" inside the JAR.

Oracle does not document it

Signed JAR file contains the following additions over a non-signed JAR:
  1. checksums over the JAR content, stored in text files "META-INF/MANIFEST.MF" and "META-INF/*.SF"
  2. the actual cryptographic signature (created with the private key of a signer) over the checksums in a binary signature block file.
Surprisingly, format of the latter does not seem to be documented by Oracle. JAR file specification provides only a useful knowledge that "These are binary files not intended to be interpreted by humans".

Here, the content of this "signature block file" is explained. We show how it can be created and verified with a non-Java tool: OpenSSL.

Create a sample signature block file

For our investigation, generate such file by signing some data with jarsigner:
  • Make an RSA private key (and store it unencrypted), corresponding self-signed certificate, pack them in a format jarsigner understands:
openssl genrsa -out key.pem
openssl req -x509 -new -key key.pem -out cert.pem -subj '/CN=foo'
openssl pkcs12 -export -in cert.pem -inkey key.pem -out keystore.pfx -passout pass:123456 -name SEC_PAD
  • Create the data, jar it, sign the JAR, and unpack the resulting "META-INF" directory:
echo 'Hello, world!' > data
jar cf data.jar data
jarsigner -keystore keystore.pfx -storetype PKCS12 -storepass 123456 data.jar SEC_PAD
unzip data.jar META-INF/*
 
The "signature block file" is META-INF/SEC_PAD.RSA.

What does this block contain

The file appears to be a DER-encoded ASN.1 PKCS#7 data structure. DER-encoded ASN.1 file can be examined with asn1parse subcommand of the OpenSSL:

openssl asn1parse -in META-INF/SEC_PAD.RSA -inform der -i > jarsigner.txt


For more verbosity, you may use some ASN.1 decoder such as one at lapo.it.

You'll see that the two top-level components are:
  • The certificate.
  • 256-byte RSA signature.
You can extract the signature bytes from the binary data and verify (=decrypt) them with openssl rsautl. That includes some "low-level" operations and brings you one more step to understanding the file's content. A simple "high-level" verification command would be:

openssl cms -verify -noverify -content META-INF/SEC_PAD.SF -in META-INF/SEC_PAD.RSA -inform der

This command tells: "Check that the CMS structure in META-INF/SEC_PAD.RSA is really a signature of META-INF/SEC_PAD.SF; do not attempt to validate the certificate".

Creating the signature block file with OpenSSL

For this example, we created the signature block file with jarsigner. Knowing the file's content, we can look for other ways to produce or verify such structure. It may be not that hard to construct it "manually", although authorities and  illustrations all recommend against implementing own crypto.

There are at least two OpenSSL commands which can produce similar structures: cms and smime. Options make the signature closer to that by jarsigner:

openssl cms -sign -binary -noattr -in META-INF/SEC_PAD.SF -outform der -out openssl-cms.der -signer cert.pem -inkey key.pem -md sha256
openssl smime -sign -noattr -in META-INF/SEC_PAD.SF -outform der -out openssl-smime.der -signer cert.pem -inkey key.pem -md sha256

To satisfy the curiosity, peek into these files and compare them to jarsigner.txt with your favorite diff tool:
 
openssl asn1parse -inform der -in openssl-cms.der -i >  openssl-cms.txt
openssl asn1parse -inform der -in openssl-smime.der -i >  openssl-smime.txt 

 

Testing the "DIY signature"

Underlying ASN.1 structures are, in both cms and smime cases, very close but not identical to those made by jarsigner. As the format of the signature block file is not documented, we can do tests to have some ground to say that "it works". Just replace the original signature block file with our signature created by OpenSSL:

cp openssl-cms.der META-INF/SEC_PAD.RSA
zip -u data.jar META-INF/SEC_PAD.RSA
jarsigner -verify -keystore keystore.pfx -storetype PKCS12 -storepass 123456 data.jar SEC_PAD

Lucky strike: a signature produced by 'openssl cms' is recognized by jarsigner (that is, at least by some particular version).

Note that the data which is signed is SEC_PAD.SF, and it was created by jarsigner. If not using the latter, you'll need to produce that file in some way, for example with python-javatools.

What's the use for this knowledge?

Besides better understanding your data, one can think of at least two reasons to sign JARs with non-native tools. Both are somewhat untypical, but not completely irrelevant:

1. The signature must be produced in a system, where native Java tools are not available.
Such system must have access to private key (in one form or another), and security administrators may not like the idea of having such overbloated software as JRE in a tightly controlled environment.
2. The signature must be produced or verified in a system, where available tools do not support the required signature algorithm.
There can be reasons that restrict tools, algorithms, or both; examples include compliance with regulations or compatibility with legacy systems. On a certain system, testing which elliptic curves are supported by jarsigner reveals just three curves (which is not much).

 

 Conclusion

  • JAR signature block file is a DER-encoded PKCS#7 structure, representing a detached signature over the .SF file.
  • Its exact content can be viewed with "openssl asn1parse" or with any ASN.1 decoder.
  • OpenSSL can verify signatures in signature block files and create almost identical structures.
  • Java tools have been shown to accept these "almost identical" structures.