ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Programmatically Signing JAR Files
Pages: 1, 2, 3

Creating the signed JAR

Now we can actually create and output the signed JAR file. The signJarFile method is the publicly accessible method which propagates all the exceptions which may occur when signing the JAR file. First we need to compute all the necessary files to go into the META-INF directory for the signed JAR. Next we write out the contents of the META-INF directory (the MANIFEST.MF file, the SF, the DSA file, and the rest of the files that were originally in the META-INF directory), and then we will iterate, writing out the rest of the JAR.



// a helper function that can take entries from one jar file and
// write it to another jar stream
private static void writeJarEntry( JarEntry je, JarFile jarFile, JarOutputStream jos )
throws IOException {
jos.putNextEntry( je );
byte[] buffer = new byte[2048];
int read = 0;
InputStream is = jarFile.getInputStream( je );
while( ( read = is.read( buffer ) ) > 0 )
jos.write( buffer, 0, read );
jos.closeEntry();

}

// the actual JAR signing method -- this is the method which
// will be called by those wrapping the JARSigner class
public void signJarFile( JarFile jarFile, OutputStream outputStream )
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException {

// calculate the necessary files for the signed jAR

// get the manifest out of the jar and verify that
// all the entries in the manifest are correct
Manifest manifest = getManifestFile( jarFile );
Map entries = createEntries( manifest, jarFile );

// create the message digest and start updating the
// the attributes in the manifest to contain the SHA1
// digests
MessageDigest messageDigest = MessageDigest.getInstance( "SHA1" );
updateManifestDigest( manifest, jarFile, messageDigest, entries );

// construct the signature file object and the
// signature block objects
SignatureFile signatureFile = createSignatureFile( manifest, messageDigest );
SignatureFile.Block block = signatureFile.generateBlock( privateKey, certChain, true );


// start writing out the signed JAR file

// write out the manifest to the output jar stream
String manifestFileName = "META-INF/MANIFEST.MF";
JarOutputStream jos = new JarOutputStream( outputStream );
JarEntry manifestFile = new JarEntry( manifestFileName );
jos.putNextEntry( manifestFile );
jos.write( manifestBytes, 0, manifestBytes.length );
jos.closeEntry();

// write out the signature file -- the signatureFile
// object will name itself appropriately
String signatureFileName = signatureFile.getMetaName();
JarEntry signatureFileEntry = new JarEntry( signatureFileName );
jos.putNextEntry( signatureFileEntry );
signatureFile.write( jos );
jos.closeEntry();

// write out the signature block file -- again, the block
// will name itself appropriately
String signatureBlockName = block.getMetaName();
JarEntry signatureBlockEntry = new JarEntry( signatureBlockName );
jos.putNextEntry( signatureBlockEntry );
block.write( jos );
jos.closeEntry();

// commit the rest of the original entries in the
// META-INF directory. if any of their names conflict
// with one that we created for the signed JAR file, then
// we simply ignore it
Enumeration metaEntries = jarFile.entries();
while( metaEntries.hasMoreElements() ) {
JarEntry metaEntry = (JarEntry)metaEntries.nextElement();
if( metaEntry.getName().startsWith( "META-INF" ) &&
!( manifestFileName.equalsIgnoreCase( metaEntry.getName() ) ||
signatureFileName.equalsIgnoreCase( metaEntry.getName() ) ||
signatureBlockName.equalsIgnoreCase( metaEntry.getName() ) ) )
writeJarEntry( metaEntry, jarFile, jos );

}

// now write out the rest of the files to the stream
Enumeration allEntries = jarFile.entries();
while( allEntries.hasMoreElements() ) {
JarEntry entry = (JarEntry)allEntries.nextElement();
if( !entry.getName().startsWith( "META-INF" ) )
writeJarEntry( entry, jarFile, jos );

}

// finish the stream that we have been writing to
jos.flush();
jos.finish();

// close the JAR file that we have been using
jarFile.close();

}

}

And that's it. Using this class, JAR files can be signed from within different applications; their error conditions are exposed so they may be caught and dealt with in a more robust way than that which is provided by the Java libraries themselves.

Raffi Krikorian makes a career of hacking everything and anything. Professionally, he is the founding partner at Synthesis Studios: a technological design and consulting firm that orchestrates his disjointed train of thought.

  Related Reading:
Java Security, 2nd Edition

Java Security, 2nd Edition
By Scott Oaks
2nd Edition May 2001 (est.)
0-596-00157-6, Order Number: 1576
550 pages (est.), $39.95 (est.)


Return to ONJava.com.