Code Signing a Java Applet - Demo and Tutorial

Part I:  Create an Unsigned Applet That Just Needs to be Signed to Work

  1. Create and compile a working applet.  Make sure your applet is in a package.  Don't use the default, nameless package for signed code.  (It may be possible, but it can't be a good idea for applets you plan on sharing with the world.)  For this demo, let's use “mypackage” for the package name, and “MyApplet” for the applet name.

    Here's the Java source code for the sample applet:

  2. Java code must be packaged in a JAR file (including applets), in order to be signed.  When creating a JAR file for signed code, you will need to add some manifest entries to it.  Without these, your signed applet runs without restrictions and could be run from any website (depending on the version of Java used).  The jar utility automatically creates and adds a default manifest file to your JAR, but it won't include the entries needed for a signed applet (or signed WebStart application).  So the next step is to create a text file with the correct entries.

    To add (or override) manifest entries, put your entries in a text file and use an option with jar to have it include those entries.  Here's how to add the correct manifest entries for our demo applet:

    Create a UTF-8 text file (let's call it “MyAppletManifest.txt”) with these three lines in it:

    Permissions: all-permissions
    Codebase: *
    Application-Name: My Applet
    

    Make sure each line ends with a NEWLINE (the “Enter” key).  A common error is to forget to hit Enter after that last line.

    Previously (Java 7U45 and older), the default was to run unsigned applets in the sandbox, and signed applets with all permissions.  But Oracle made a change for the next version, Java 7U51, and the Permissions manifest entry is now required for all applets and WebStart apps (Oracle calls these Rich Internet Applications, or “RIAs”).  If your applet doesn't require extra permissions, change “all-permissions” to “sandbox”.

    The “Codebase” entry lists the places from where this applet can be loaded.  The entry shown says the applet can be loaded from anywhere, but you shouldn't use that for a real applet.  Instead, you should list your testing computer(s), usually localhost, and the real website(s) to which you plan on deploying your applet.  Here's a sample entry for an applet I plan on testing locally (on my computer), and then deploying on a webserver at example.com:

    Codebase: localhost *.localdomain 127.0.0.1 *.example.com 192.0.2.2
    

    You need to list both the hostname and its IP address (localhost and 127.0.0.1), since one doesn't imply the other for some reason.  (For IPv6, you would need to add ::1 for localhost as well.  However, IPv6 addresses are not supported in the JAR file manifest specification as of Java 8U31.)  Additional entries can be included to enhance security further; those won't be discussed here (but see the manifest resources at the bottom of this web page).

  3. Now we're ready to package the applet into a JAR file.  The applet must be in a JAR file before a certificate can be attached to it.  Let's call the unsigned JAR file “MyApplet.jar”.  Use the jar utility to create a new JAR (Java ARchive) file, and include the manifest entries from the file you created:
    jar -cvfm MyApplet.jar MyAppletManifest.txt mypackage
    

    (Additional files, such as media files, could also be included in the JAR; simply add file or folder names to the end of the command line.)

  4. Now that the applet is packaged in a JAR, the HTML file's <applet> tag needs to be updated.  (The change is similar if you use the Java Deployment JavaScript, or a WebStart JNLP file.)  In the applet tag of your HTML file, add the “archive” attribute.  The value of the archive attribute is the URL of the JAR file.  If the JAR file is in the same folder as the HTML file, just use the file name as the URL:
    <APPLET CODE="mypackage.MyApplet" ARCHIVE="MyApplet.jar"
       HEIGHT="120" WIDTH="400">
    

    This should result in a non-signed applet.  If the Java 7 control-panel security settings are set to “medium”, you should be able to run this applet locally.  But not if you use the default security settings (or have a more recent version of Java that eliminated the medium setting).  For that, you must sign the applet, or add it's codebase (location) to the Java Exception List (discussed below).

    Since this applet is unsigned. it runs in a sandbox regardless of the manifest entries we used.  You will see an “Access Control Exception” (ACE) when it attempts a restricted operation:

    java.security.AccessControlException: access denied
        ("java.util.PropertyPermission" "user.dir" "read")
    

    Here is a sample HTML file showing the correct applet tag:

    <HTML><HEAD>
    <TITLE>My Applet - Demo signed applet</TITLE></HEAD>
    <BODY>
     <APPLET CODE="mypackage.MyApplet" WIDTH="400" HEIGHT="120"
             ARCHIVE="MyApplet.jar">
     </APPLET>
    </BODY></HTML>
    

    Here's what this looks like when run (a PNG image if Java is disabled in your browser):
    screen-shot of unsigned applet ACE

Part II:  Sign the Unsigned Applet

This is easier to do than to describe.  There's only two commands to run, and the first one only needs to be run once.  (The second command is run whenever you need to sign a new or updated JAR file.)  Here's what you need to know, and how to do it:

A digital signature is constructed from a private key, plus the code (.class files) and other data to be signed.  What's great about that is any changes to the code/data or the digital signature will result in an invalid signature.  A valid signature provides assurance that the code you got was unaltered since it was signed.  The additional data includes the identity of the signer, so that can't be changed either. 

To verify a digital signature, your system needs the signed code and data, and a public key that works with the private key used to sign the code.  You generate these keys in pairs, and keep one private, backed-up, and secure.  The other is the public key, which you can freely share.  The two keys have a special mathematical relationship, so that you can encrypt or sign data with one key, but it takes the other to decrypt or verify.  The public key is included in the JAR file with your code, so the recipient can easily verify the signature.

These keys are huge, and are generated using random numbers.  Even knowing the method used and the public key, nobody can figure out the corresponding private key.  But if an attacker wanted to forge a digital signature, they would need to determine the private key used.  The keys are so large that guessing numbers to find the private key is futile.  So this digital signature is unforgeable, provided nobody gets a copy of your private key.  To prevent this, private keys are kept in password-protected files.

When you sign your JAR file, both the signature and the public key are added to the JAR file.  But not just the public key; that wouldn't prove much.  (An attacker could generate their own key pair, sign their modified code with their private key, and include their public key in the JAR; if you checked, the digital signature would appear valid.)  The public key is part of a certificate; it is the certificate that is added to the JAR.  A certificate includes: your public key, the algorithm used to sign the JAR, the signer's ID, and other information.

To prevent false claims of identity (“This Applet was signed by Bill Gates”), the certificate itself must be signed by a well-known, trusted third party called a certificate authority (or “CA”).  Your computer has a list of the public keys of CAs, updated regularly.  (Note that the Java JRE, your operating system, web browsers, and other software all contain separate lists.)  So your system first verifies the digital signature on the certificate, showing the true identity of the code signer.  Then it verifies the digital signature on the code from the public key in the certificate.  Thus, your system not only knows the code wasn't modified since it was signed, but also it has assurance that it knows the true identity of the signer.

CAs charge money for this service.  As of 12/2014, code signing certificates that work for any code, not just Java JARs, are available from Comodo resellers for about $80/year.  Both cheaper and (much) more expensive certificates from various CAs are available, but not all of them are included in the current JRE's trusted CA list.

Instead, you can become your own CA, and then sign your own certificate.  Such certificates are called self-signed.  While you should never trust self-signed code from the Internet, you can use them yourself for testing your code and for educational purposes.  When ready to deploy your code to the world, use a real CA-provided code signing certificate to sign code.

With that background, here's the two steps to sign a JAR file:

  1. Create a key pair in user.home/.keystore:
    keytool -genkeypair -alias mykey
    

    This will create a private key to sign your code with, and a public key that can be used to validate the digital signature.  Since Java 6, it will also generate a self-signed certificate.  The keys will be put in the default file in the default location (the file “.keystore” within your “HOME” directory).  A file containing a set of keys is called a keystore.  You can specify an option to use a different keystore if you want.

    The alias you give is the name for the pair of keys.  (Sometimes, you may have multiple key pairs, and might use different keys for different applications.  So you need to give each pair a name, or alias.)  There are defaults for everything (including the alias, which does default to “mykey”), but you can use additional command line options to override the defaults.

    One default to consider changing is the validity period of 90 days.  After that, the certificate is expired and won't (or shouldn't) work to sign any code; you would have to generate a new certificate (or pay to renew an existing one).  If you're generating keys for use in a one semester Java course (four months), or a longer course, you will probably want to change this default.  Here's the option to add for a one year certificate:

    keytool -genkeypair -alias mykey -validity 364
    

    (For code signing certificates obtained from CAs, the validity period depends on how much you pay.  It generally ranges from one to five years.)

    The keytool utility is interactive.  It will ask you for your name, your organization's name, your department (“organizational unit”), and the city, state, and country of your organization.  You can include all that on the command line, but this way is often easier. You will also be prompted for a password to protect the private key, and another for the keystore file.  You should pick good, different passwords; access to your private keys have legal implications!  You can write these down, but please don't put the passwords on a note taped to your monitor, or store them in an unsecured or public place.  Keep the passwords safe.  Note, you can also put the passwords as options on the command line (if you include everything, keytool won't prompt you for any additional data), but generally, that isn't a good idea.

    You can view the contents of your keystore with:

    keytool -list
    

    Or, if you want the gory details:

    keytool -v -list
    

    If you are happy with your key pair, be sure to back it up someplace, away from your computer.  A small USB flash disk can be used to back up your keystore, and that disk can be put in a secure location.  Saving your keystore on a CD-ROM would be a better idea.

    To sign your JAR file with a real, non-self-signed certificate, use keytool to export your certificate data in a form ready to be signed by some CA.  This file is called a “certificate signing request”, or CSR.  Use the command (assuming your key pair is named mykey):

       keytool -certreq -alias mykey -file mykey.csr
    

    You generally email mykey.csr or upload it to the CAs website, submit proofs of identity, and pay them.  (Some CAs have web forms to fill out, and they create the CSR and key pair for you.  Generating your own CSR has the advantage of keeping your private key private and off of the Internet.)

    Sometime later, they send back the signed certificate.  You can then import that into your keystore file with:

       keytool -importcert -alias mykey -file cert_file
    

    (You must specify the alias with this command even if using the default alias name, or the certificate won't import correctly!)  After that, use the imported certificate the same way as you would the self-signed certificate we use in this demo.

    Self-signed JAR files will display a warning message about an UNKNOWN publisher.  You must use a CA-signed certificate to have your name appear.  When the publisher is UNKNOWN, some security settings may prevent the applet from running at all.

    (See using a real certificate for more information.)

  2. Now that you have a signed certificate, you can sign any JAR file with it:
    jarsigner -signedjar MySignedApplet.jar MyApplet.jar mykey
    

    This will ask you for the password(s) needed to access your private key.  (If you don't include the option “-signedjar newJarFilename.jar”, jarsigner will overwrite the original JAR file listed!)  The process is fast.

    As of Java 7U51, this will issue a warning that you didn't use a time stamp when you signed your code.  Time stamps are useful when certificates are revoked by a CA or have expired, so Java can check if the code was signed before or after revocation/expiration.  Without a trusted, valid time stamp, the system needs to assume it was signed after, and is therefore invalid.  You need to list the URL of a trusted Time Stamp Authority, or “TSA”, when signing a JAR.  Just as with Certificate Authorities, these generally charge money for the service (most CAs operate a TSA for their customers' use, free of charge).  This service is usually cheap, and some TSAs do offer some limited, but free, time-stamping services.

    To have your signature time stamped, include an option to identify a TSA; there are several ways to identify one.  For example:

    jarsigner -signedjar MySignedApplet.jar -tsa URL MyApplet.jar mykey
    

    The European Union maintains (as of 2014) a list of approved and trusted TSAs, at https://ec.europa.eu/.../eu-trusted-lists-certification-service-providers.  A Google search for “public time stamp authority free” will turn up some, such as these (as of 2015):

    For example, to use the Aloaha TSA, the command would be (one long line):

    jarsigner -signedjar MySignedApplet.jar
       -tsa http://card.aloaha.com:8081/tsa.aspx MyApplet.jar mykey
    

    (This is not an endorsement of this TSA.)

    When done, you should check the result:

    jarsigner -verify -certs -verbose MySignedApplet.jar
    

    Finally, you need to update the applet tag in the HTML file to refer to the new (signed) JAR file:

    <APPLET CODE="mypackage.MyApplet" ARCHIVE="MySignedApplet.jar"
       HEIGHT="120" WIDTH="400">
    

    That should be it.  The result is (shows as a PNG image if Java is disabled in your browser):
    screen-shot of signed applet, showing current directory

    And here is the result when using a valid code-signing certificate (the result is (a PNG image if Java is disabled in your browser).  Notice the difference security dialog that shows, and how you can make it remember your decision to allow this applet:
    screen-shot of signed applet, showing current directory

    To make this signed (with a real certificate) applet, I used a different manifest file:

    Permissions: all-permissions
    Codebase: localhost *.localdomain 127.0.0.1 ::1 *.wpollock.com *.hccfl.edu
    Application-Name: My Applet
    

    In the future, you can use the same key and certificate to sign other JAR files (or newer versions of this one).  You'll only need to re-run the one jarsigner command.

Exception List:

Starting with Java 7U51, the user can add exceptions to the rule that prevents unsigned or self-signed apps from running, by white-listing URLs using the Java control panel.  The exception list is a list of sites from which you will allow unsigned or self-signed code to run.  Note that unsigned applets, even if their codebases are added to the exception list, will run in the security sandbox.  So if your applet or WebStart app require extra permissions, you must sign them.  You should add a couple of entries to enable running code from your local computer:

http://localhost/
http://127.0.0.1/
https://localhost/
https://127.0.0.1/
file:///

Only the file:/// entry is needed unless you're running a local server such as a web server, Tomcat, Glassfish, etc., and plan on serving up Java applets and/or WebStart applications.  In that case, you may need to specify a non-standard port number.  For example, by default the Glassfish server uses port 8080, so you need to add “http://localhost:8080/” and “https://localhost:8080/”.

IPv6 isn't supported by this exception list as of Java 8.  Also the JRE doesn't resolve DNS names to IP addresses, so you should have both the host name as well as the IP addresses listed.  Note you will get a warning with http:// and file: protocols.  Since you are using localhost for those protocols, it doesn't matter (no security implication) so you should ignore those warnings.

Web Browser that Support Applets

The browser plug-in API, NPAPI, was pioneered by Netscape long ago and is no longer considered secure.  This is why most browsers today no longer support such plugins, used for Flash and for Java.  Soon, Applets will be a dead technology; we'll all have to learn to use Java WebStart I suppose.  (All Java applications, including WebStart and others, support code signing as described here.)

There are still some web browsers that support plugins using NPAPI, and thus support Java Applets.  One such browser is Pale Moon, a Mozilla browser fork that keeps the original Firefox appearance (menus, status bar, etc.).

As usual, you must have the same type of browser as your JRE, 32-bit or 64-bit.  When most browsers supported Flash and Java, I used 32-bit versions (since there is no 64-bit Flash).  Now that most browsers don't, I've noticed many of the ones that do support NPAPI come in either 32-bit or 64-bit versions.  So my advice today would be to install a 64-bit JDK and JRE, and use the 64-bit version of Pale Moon.  (You can install two different JREs if you want to support both.)

Testing Signed Applets

To test, move the signed JAR file and the HTML file to another location.  Otherwise, the original package may be found on CLASSPATH, and your signed JAR would be ignored.

Don't use appletviewer to test!  In older versions of JDK, appletviewer defaulted to “all-permissions”, whether or not the applet was signed.  Now it defaults to “sandbox”, and there is no way to override that, even with signed applets!  Appleteer is a non-Oracle alternative that uses the older “all-permissions” mode.  The best plan may be to test signed apps using a web browser.

References:

docs.oracle.com/javase/tutorial/security/toolsign/
docs.oracle.com/javase/tutorial/deployment/jar/signindex.html
docs.oracle.com/javase/8/docs/technotes/tools/windows/keytool.html
docs.oracle.com/javase/8/docs/technotes/tools/windows/jarsigner.html
docs.oracle.com/javase/tutorial/deployment/jar/signing.html
docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html
docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#JAR_Manifest
docs.oracle.com/javase/tutorial/deployment/jar/secman.html
docs.oracle.com/javase/8/docs/technotes/guides/deploy/manifest.html
www.cert.org/blogs/certcc/2013/09/signed_java_applet_security_im.html
docs.oracle.com/javase/8/docs/technotes/guides/jweb/security/manifest.html
docs.oracle.com/javase/tutorial/deployment/applet/html.html
blogs.oracle.com/java-platform-group/entry/code_signing_understanding_who_and?msgid=3-9171379647
docs.oracle.com/javase/8/docs/technotes/guides/deploy/exception_site_list.html
docs.oracle.com/javase/8/docs/technotes/guides/deploy/deployment_flow.html
pscode.org/appleteer/ (from archive.org)
How to sign and timestame a Java Jar fileK Software (A Comodo partner)
Java code signing how-to — www.GoDaddy.com
How to remove private password from PKCS12 certificate — ServerFault.com