Code Signing for Authenticode, Java and Netscape

JavaScience Consulting April 05, 2007


Digital code-signing, based on industry standard PKI technology standards, is a powerful approach for secure distribution of software over the Internet with authentication and integrity verification capability. This technology also facilitates more powerful web-based client-side applications which rely on the end user making a trust decision in the downloaded software, particularly for signed Java applets, installable ActiveX controls etc... However, the situation for developers is complicated by various approaches, tools and implementations of code-authentication verification, the main approaches being Microsoft Authenticode (based on cryptoAPI win32technology), Netscape based "object signing" and Sun Java code signing. Each of these vendors offer different tools and technologies, with generally incompatible "keystore" implementation technologies for key/certificate storage, even though code signed with each technology involves some elements that are based on public PKI standards (e.g. pkcs#7 signature blocks, X509v3 certificates etc..).

This document describes how a *single* code signing certificate can be used with the three vendor technologies and tools to sign code targeting Microsoft Authenticode (signed cabs, exe, dll, ocs etc..), Netscape (signed applets in JARs) and Sun (signed JARs for JavaPlugin, Netscape etc..). Only one particular type of commercial certificate has been tested for this "3-ply" capability.

Demo of applet signed 3 ways

The Thawte Microsoft Authenticode - (Multi-Purpose Certificate) has been tested for compatibility, using the procedure detailed below. See the official Thawte site for official documentation. The information is being provided to developers who must sign code targeting an audience using several vendor technologies. The results below have been verified on only one platform:


The Authenticode Process
[Note: The Thawte certificate registration process for IE (Authenticode) currently generates ONLY 512 bit RSA long key, which is generally viewed as inadequate from a security perspective. This problem arises from inadequate implementation of the Microsoft "Enrollment Control" (an ActiveX Control) used by the web-page for key generation/registration. The process for Netscape and Java key registration however generates larger RSA keys. ] [Update note: In the Feb. 2003 PSDK release, the Signcode.exe signing tool has been replaced with Signtool.exe which provides similar levels of Authenticode signing support with more robust signature verification capabilities.]

[Note on Timestamp support: Currently VeriSign and Certum both offer free Internet timestamp services based on IETF RFC 3161 protocol and compatible with Microsoft Authenticode request format.]

Porting To Netscape Environment

Porting To Sun Java Environment 3rd Party Solutions:

Currently, at release j2se v1.4 b3, the Sun keystore provider implementation does not include the ability to WRITE pkcs12 keystores. Thus keys generated with keytool.exe into a JKS type keystore cannot be written out to a pkcs12 keystore file, either with keytool.exe or using the standard API. However, a freely available (see license) jce provider implementation which DOES support writing pkcs12 stores is available via Legion of the BouncyCastle. Uwe Guenther has written a Java application utility BCMain.java which exports a user-specified JKS keystore to a pkcs12 file which can subsequently be imported into either Microsoft Authenticode or Netscape object-signing key/cert stores essentially completing the "reverse porting" process, complementing the information presented previously on this page.
To use the "Bouncy Castle Provider" extension, download the relevant JDK 1.x bcprov-jdk1x-112.jar archive and place it in the following extension directory locations (one for compiler visibility, one for runtime visibility):

${jdk.home}/jre/lib/ext/
${java.home}/jre/lib/ext/

The sample code can be compiled with j2se v1.3.1, but requires jce to execute. Jce is included with j2se v1.4+ but must be added as an extension to j2se v1.3.x. Note that the BouncyCastle provider is only being used here to export the keys from JKS to PKCS12 keystores. The code-signing process using jarsigner.exe and RSA keys is of course fully supported by j2se v1.3. The "Bouncy Castle" jse provider has been verified to work with j2se v1.4b3, but j2se v.1.3.1 cannot properly read the exported pkcs12 file. The advantage of using the Sun keytool.exe for key generation and PKCS10 Certificate Request processing is that RSA key strengths up to 2048 bits can be generated with the j2se keytool, and the Thawte/Java key registration process correctly handles this key strength (see note above on current Thawte/Authenticode IE 512 bit generation process limitation). Further, test (self-signed) certificates can be generated with keytool.exe and then, after exporting to pkcs12 keystores using this 3rd party jse provider, used with MS Authenticode signcode.exe or Netscape signtool.exe, as an alternative to using test certificates generated by these vendors.

A simple self-signed example:
j2se v1.3.1 was used to generate a new 2048-bit RSA self-signed certificate:
keytool -genkey -keyalg rsa -keysize 2048 -validity 730 -alias mig -keystore .keystore

This key was exported to a pkcs12 store file mig.p12 using the sample java application BCMain.java above. This pkcs12 file, mig.p12 was imported into "Personal" Authenticode store (by double-clicking it) . The Authenticode 5 tool signcode.exe was then used in GUI mode to sign a simple win32 application sguitartuner.exe (a little guitar tuner utility for Win2000, XP) successfully. If this application is downloaded using IE4+, then a security dialog appears prompting the user to Open, Save or Cancel the download. If Open is selected, a Security Warning dialog appears (screen shop), indicating that the "authenticity of this content cannot be verified, therefore it cannot be trusted". This is because the code was signed with a self-signed certificate which is its own root CA, which most end users will not have installed (in a real issued Thawte/Verisign certificate, the certificate and signature would be trusted since the root CA issuer would already be present in the "Trusted Root Certification Authorities" list. However, the end user would still be prompted with a choice to trust subsequent code (without any prompting) signed with the same certificate.). If one of the links in the Security Warning panel is clicked, it allows the end user to view the certificate contents and potentially import the public certificate included with the signature within the .exe file. (Note: be extremely cautious about doing this for a ROOT CERTIFICATE).

Download sguitartuner.exe


Java SE 6 support for Windows CryptoAPI Stores/Keys

Starting at Java SE 6 (developer version 1.6.0), there is support for accessing native Windows CrypoAPI services such as certificate stores and keys.

RSA key sizes up to the maximum size supported in Windows (16,384 bits = 2048 bytes) are supported. Note that the .rsa pkcs#7 component in the jar is a detached signature covering the manifest and sf file which lists hashes of all JAR'd files. (note that the VeriSign timestamp service does not seem to support the timestamp protocol used by jarsigner .. only certum which is 2048 bit timestamp service).

Sample code for signing using Windows CryptoAPI certificate:

import java.io.*;
import java.util.*;
import java.security.*;
import java.security.Security;
import java.security.cert.Certificate;
import java.util.*;

class javasig {

 public static void main(String args[]) {

 KeyStore ks = null;
 PublicKey pub = null;
 PrivateKey priv = null;
 String pkcs1file = "pkcs1";
 byte[] data = null;
 String filename = null;

   if (args.length != 1) {
	System.out.println("Usage: java  javasig   ");
	return;
	}
	filename = args[0];
	data = getFileData(filename) ;
	if (data == null){
	System.out.println("Couldn't get filedata for " + filename) ;
		return;
	}
 try{
        ks = KeyStore.getInstance("Windows-MY", "SunMSCAPI");
        ks.load(null, null); 

        String alias = "My CryptAPI store alias";	// either Friendly Name (if present) or CN or certificate

        PrivateKey privKey = (PrivateKey) ks.getKey(alias, null);
        Certificate cert = ks.getCertificate(alias);
	System.out.println("Got private key and cert for alias '" + alias + "'") ;
        Provider p = ks.getProvider();
        Signature sig = Signature.getInstance("SHA1withRSA", p);
        sig.initSign(privKey);
        sig.update(data);
        byte[] signature = sig.sign();
        System.out.println("Generated signature for file '" + filename + "'");
	FileOutputStream fos = new FileOutputStream(pkcs1file);
	fos.write(signature);
	fos.close();
	System.out.println("Wrote PKCS#1 signature file  '" + pkcs1file + "'  (" + signature.length + " bytes)") ;
        sig.initVerify(cert);
        sig.update(data);
       if (sig.verify(signature))
             System.out.println("Signature verified!");
 }

 catch(Exception exc){
  System.out.println("Problem with keystore access: " + exc.toString()) ;
  return;
  }
}

 public static byte[] getFileData (String filename)
 {
	File blobfile = new File(filename) ;
	if (!blobfile.exists())
		return null;	
	int blobsize = ((int) blobfile.length());
	byte[] blobdata = new byte[blobsize];
	try 
	{
	 FileInputStream freader = new FileInputStream(blobfile);
	 freader.read(blobdata, 0, blobsize) ;
	 freader.close();
	 return blobdata;
	}
	catch(IOException ioe)
	 {
	 return null;
	}
 }
}

Java SE 6 Security (general)

JDK Tools and Utilities for detailed information on javac, jar, and jarsigner.

JSSE Reference Guide for information on pkcs12 support issues.

JCA and JCE Documentation (combined) for information on keystores, ciphers etc.