netnix.org
Networking and Unix


Self-Signed CA with CRL for Java Code Signing

 April 6th, 2014Apr 6th, 2014        1

I am going to start off by saying that since Java 7 Update 51, that using self-signed certificates is pretty much a waste of time for Internet deployments. With Update 51, Oracle has beefed up the security aspects of Java, which now blocks self-signed JAR files from being run via the web – even for applications which only want to run within the restricted “Sandbox”.

There are a couple of ways around this. The first is to reduce your security settings from the recommended (and default) High option to Medium (in the Java Control Panel), which allows self-signed JAR files to be executed after presenting a scary warning message (for the time being anyway). When set to High you don’t even get a choice, it was just blocked. The second is to get your clients to import your self-signed root certificate into their trust store – this approach could work within a closed or corporate environment but won’t on the Internet. The third, and recommended approach, is to sign your JAR file with a code signing certificate which has been signed by a trusted CA, but this will set you back a few pennies.

With this in the mind, the following approach is for people looking to adopt the second option above, which could work in closed or corporate environments. The following instructions have been tested with OpenSSL 1.0.1g (configuration file) and Java 8 on Windows 7.

Step 1 – Initialise Certificate Authority (CA)

The first step is to create the necessary directories and files that will be used for your CA. I have placed most of the files into a single “ca” directory, but you may wish to adopt a different layout.

mkdir ca
mkdir cacerts
touch caindex.txt

Step 2 – Generate a Certificate Authority (CA)

The next step is to generate your self-signed root certificate. This can be done in two steps, but I prefer the single step using the “-x509” parameter which generates a self-signed certificate.

openssl req -new -x509 -newkey rsa:4096 -keyout caca.key -out caca.crt -days 9125 -extensions v3_ca -config openssl.cfg

Country Name (2 letter code) []: Your Country
State or Province Name (full name) []: Your State or Province
Locality Name (eg, city) []: Your Locality
Organization Name (eg, company) []: Your Organisation
Organizational Unit Name (eg, section) []: Your Organisational Unit
Common Name (e.g. server FQDN or YOUR name) []: Root CA
Email Address []:

This generates a new private key “ca.key” and a self-signed public certificate “ca.crt” which is valid for 9125 days (25 years). I have specified a key length of 4096 bits and the following x509 extensions by referencing the “v3_ca” section in the configuration file:

[ v3_ca ]
basicConstraints          = critical, CA:TRUE, pathlen:0
keyUsage                  = critical, cRLSign, keyCertSign
subjectKeyIdentifier      = hash
authorityKeyIdentifier    = keyid:always, issuer

Step 3 – Generate a Certificate Signing Request (CSR)

The next step is to generate a certificate signing request that will be signed by our CA.

openssl req -new -newkey rsa:2048 -nodes -keyout localhost.key -out localhost.csr -config openssl.cfg

Country Name (2 letter code) []: Your Country
State or Province Name (full name) []: Your State or Province
Locality Name (eg, city) []: Your Locality
Organization Name (eg, company) []: Your Organisation
Organizational Unit Name (eg, section) []: Your Organisational Unit
Common Name (e.g. server FQDN or YOUR name) []: Your Name
Email Address []: Your Email Address

Step 4 – Sign Certificate Signing Request (CSR) with Certificate Authority (CA)

Once you have your certificate signing request you need to sign it with a CA. At this point you could send off your CSR to a trusted CA for them to sign, but for the purpose of this article we are going to use our newly created CA.

openssl ca -create_serial -days 1095 -in localhost.csr -out localhost.crt -notext -extensions v3_req_sign -config openssl.cfg

The above command will sign our CSR with our CA that will be valid for 1095 days (3 years) and will also add it to the CA database “caindex.txt”. We will add the following x509 extensions by referencing the “v3_req_sign” section in the configuration file:

[ v3_req_sign ]
basicConstraints          = CA:FALSE
keyUsage                  = digitalSignature
extendedKeyUsage          = codeSigning
crlDistributionPoints     = URI:http://mydomain.com/ca.crl
subjectKeyIdentifier      = hash
authorityKeyIdentifier    = keyid, issuer

If you wish to use a Certificate Revocation List (CRL) then you will need to define the “crlDistributionPoints” parameter which tells the application checking the certificate where it can find the CRL. You need to ensure you have a valid CRL provided at the location or the CRL validation will fail. The combination of “keyUsage” and “extendedKeyUsage” means that our certificate can only be used for code signing – there are other usages which could be specified.

You now have a working code signing certificate that has been signed by your newly created CA. The next step is optional, but can be used to generate a CRL.

Step 5 – Generate Certificate Revocation List (CRL)

If you want the ability to revoke certificates that you have issued then you need to reference a CRL. Java also enforces that your certificates define a CRL or the user will still see a scary warning message when launching your application. The CRL contains a list of certificate serial numbers that you have revoked – if your certificate is on the list then it can’t be used. Certificate revocation is used if your private key has been comprised, etc.

openssl ca -gencrl -crldays 30 -out caca.crl -keyfile caca.key -cert caca.crt -config openssl.cfg

The above command will generate a CRL and place it into the file “caca.crl”. The CRL is downloaded from the “crlDistributionPoints” URL in the certificate on a periodic basis and a new copy must be obtained before the local cached copy expires. I have specified a validity period of 30 days in the example above, but care should be taken when choosing a value – the shorter the duration the quicker certificate revocation happens but it means the CRL needs to be highly available – the longer the duration the less emphasis on availability but the longer it takes for certificate revocation to occur. I am not 100% sure of the behaviour with Java, but some applications will only attempt to fetch a new copy of the CRL when the cached copy is about to expire – with Java it might attempt to obtain a new copy every time, which means revocation should be fairly instant as long as a new copy can be downloaded.

Once the CRL is generated (or updated) it needs to be uploaded to the “crlDistributionPoints” URL to ensure a non-expired copy is always available.

Step 6 – Revoke a Certificate and Generate an Updated CRL

The following commands are used to revoke a compromised certificate.

openssl ca -keyfile caca.key -cert caca.crt -revoke cacerts<serial>.pem -config openssl.cfg

openssl ca -gencrl -crldays 30 -out caca.crl -keyfile caca.key -cert caca.crt -config openssl.cfg

Once the certificate has been revoked and the CRL has been updated then it needs to be uploaded so a new version can be downloaded before the old CRL expires. The following command can be used to view the contents of the CRL to see what certificates the CRL has revoked.

openssl crl -in caca.crl -text -noout

Step 7 – Combine Certificate and Key into PKCS12 Format

This step is used so we are able to import our new code signing certificate into our Java KeyStore and use it to sign JAR files.

openssl pkcs12 -export -in localhost.crt -inkey localhost.key -out localhost.pfx

The above command will generate a PKCS12 certificate called “localhost.pfx” that can be used in the next step when it is imported into your local Java KeyStore.

Step 8 – Import Certificate (and CA Public Certificate) into Java KeyStore

The following commands are used to import your certificate and any intermediate certificates into a local Java KeyStore called “keyStore.jks”. The intermediate certificates are required so Java can verify the chain all the way up to the root – if it can’t find all the chained certificates then it will output a warning message.

keytool -importcert -keystore keyStore.jks -file caca.crt

keytool -importkeystore -destkeystore keyStore.jks -srckeystore localhost.pfx -srcstoretype PKCS12

We can then view the contents of the KeyStore using the following command – you will see your newly imported code signing certificate has an alias of “1”.

keytool -list -v -keystore keyStore.jks

Step 9 – Sign JAR File using Code Signing Certificate

Once you have created a local Java KeyStore that contains your new code signing certificate you can use the “jarsigner” tool to sign your JAR file.

jarsigner -verbose -keystore keyStore.jks -tsa https://timestamp.geotrust.com/tsa -signedjar Signed.jar Original.jar 1

I have specified the “-tsa” parameter which is a Time Stamping Authority – this places a timestamp into the JAR file which has been signed by a TSA. The purpose of a TSA is to ensure a JAR can still be used after the code signing certificate expires. Normally, once your certificate expires Java doesn’t know if your JAR was signed before or after the certificate expired, so it assumes it was after. By using a TSA it knows exactly when your JAR was signed and potentially that your JAR was signed before it expired and it will still run. In the same sense if your code signing certificate was revoked then a TSA will be used to determine if your code was signed before or after your certificate was revoked – if it was before then it will still run.

Step 10 – Import CA Public Certificate into Java Trust Store

If you are working in a closed or corporate environment then the final step is to install your root certificate into the Java Trust Store on all the client PCs – this will have the same effect as using a trusted CA to sign your JAR file.

keytool -importcert -v -file caca.crt -keystore "C:Program FilesJavajre8libsecuritycacerts" -storepass changeit

The path above will depend on the version of Java you have installed – in the example above I am using Java 8 – it also assumes you haven’t changed the default Java password of “changeit”.

General CA Java OpenSSL


Thoughts on "Self-Signed CA with CRL for Java Code Signing"...

by S Card on August 6, 2014 at 21:37

Excellent article. Only issue I had was that keytool imported the certs into the keystore.jks with 200 character aliases instead of something friendly like “1”, so I had to “keytool -changealias” them to something manageable. Thanks!