Signing applets
Because of the sandbox security model, unsigned applets are prevented from performing certain tasks on the client, like writing to a file or connecting to a local network. [83] A signed applet verifies to the user that the person who claims to have created the applet actually did, and that the contents of the JAR file have not been tampered with since that file left the server. Without this minimum guarantee, the applet will not be allowed to do anything that could damage a person’s machine or violate their privacy. This is a restriction that is vital for the safe use of applets through the Internet, but which also makes applets relatively powerless.
Since the release of the Java Plugin, the process of signing applets has become simpler and more standardized, and applets have become a more viable means of deploying your application. Signing an applet has become a reasonably straightforward process and uses standard Java tools.
Prior to the plugin, you had to sign a .jar file with the Netscape tools for a Netscape client, a .cab file with the Microsoft tools for an Internet Explorer client, and create an applet tag in the HTML file for both platforms. The user would then have to install a certificate on the browser so that the applet would be trusted.
The plugin not only provides a standard approach to applet signing and deployment, but it also provides the end user with a better experience by making certificate installation automatic.
Consider an applet that wants to have access to the client’s file system and read and write some files. This is very similar to FileChooserTest.java, but because this is an applet, it will only be able to open the Swing JFileChooser dialog if it is running from a signed JAR file. Otherwise, the showOpenDialog( ) method will throw a SecurityException.
//: c14:signedapplet:FileAccessApplet.java
// Demonstration of File dialog boxes.
package c14.signedapplet;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import com.bruceeckel.swing.*;
public class FileAccessApplet extends JApplet {
private JTextField
filename = new JTextField(),
dir = new JTextField();
private JButton
open = new JButton("Open"),
save = new JButton("Save");
private JEditorPane ep = new JEditorPane();
private JScrollPane jsp = new JScrollPane();
private File file;
public void init() {
JPanel p = new JPanel();
open.addActionListener(new OpenL());
p.add(open);
save.addActionListener(new SaveL());
p.add(save);
Container cp = getContentPane();
jsp.getViewport().add(ep);
cp.add(jsp, BorderLayout.CENTER);
cp.add(p, BorderLayout.SOUTH);
dir.setEditable(false);
save.setEnabled(false);
ep.setContentType("text/html");
filename.setEditable(false);
p = new JPanel();
p.setLayout(new GridLayout(2, 1));
p.add(filename);
p.add(dir);
cp.add(p, BorderLayout.NORTH);
}
class OpenL implements ActionListener {
public void actionPerformed(ActionEvent e) {
JFileChooser c = new JFileChooser();
c.setFileFilter(new TextFileFilter());
// Demonstrate "Open" dialog:
int rVal = c.showOpenDialog(FileAccessApplet.this);
if(rVal == JFileChooser.APPROVE_OPTION) {
file = c.getSelectedFile();
filename.setText(file.getName());
dir.setText(c.getCurrentDirectory().toString());
try {
System.out.println("Url is " + file.toURL());
ep.setPage(file.toURL());
// ep.repaint();
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
if(rVal == JFileChooser.CANCEL_OPTION) {
filename.setText("You pressed cancel");
dir.setText("");
} else {
save.setEnabled(true);
}
}
}
class SaveL implements ActionListener {
public void actionPerformed(ActionEvent e) {
JFileChooser c = new JFileChooser(file);
c.setSelectedFile(file);
// Demonstrate "Save" dialog:
int rVal = c.showSaveDialog(FileAccessApplet.this);
if(rVal == JFileChooser.APPROVE_OPTION) {
filename.setText(c.getSelectedFile().getName());
dir.setText(c.getCurrentDirectory().toString());
try {
FileWriter fw = new FileWriter(file);
ep.write(fw);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
if(rVal == JFileChooser.CANCEL_OPTION) {
filename.setText("You pressed cancel");
dir.setText("");
}
}
}
public class TextFileFilter extends
javax.swing.filechooser.FileFilter {
public boolean accept(File f) {
return f.getName().endsWith(".txt")
|| f.isDirectory();
}
public String getDescription() {
return "Text Files (*.txt)";
}
}
public static void main(String[] args) {
Console.run(new FileAccessApplet(), 500, 500);
}
} ///:~
It appears to be an ordinary applet. However, as it stands, it would not be allowed to open and close files on a client’s system. To make this run as a signed applet, you need to put it into a JAR file (see the section on the jar utility, earlier in this chapter) and sign the JAR file.
Once you have a JAR file, you will need a certificate or a key to sign it with. If you were a large corporation, you would apply to a signing authority like Verisign or Thawte, and they would issue you a certificate. This is used to sign code and thus identify to a user that you are indeed the provider of the code they are downloading, and that the code that has been deployed hasn’t been modified since you signed it. Essentially, the digital signature is a load of bits, and the signing authority vouches for you when someone downloads that signature.
A certificate from a signing authority costs money and requires regular renewal. In our case we can just make a little self-signed one. This needs to be stored in a file somewhere (it is usually called the keychain). If you type:
keytool –list
then it will try to access the default file. If there is no file, then you need to create one, or specify an existing one. You might try to search for a file called “cacerts,” and then try
keytool -list -file <path/filename>
The default location is usually
{java.home}/lib/security/cacerts
where the java.home property points to the JRE home.
You can also easily make a self-signed certificate for testing purposes using the keytool. If you have your Java “bin” directory in your executable path, you can type:
keytool –genkey –alias <keyname> -keystore <url>
where keyname is the alias name that you want to give the key, say “mykeyname,” and url is the location of the file that stores your keys, usually the cacerts file as described above.
You will now be prompted for the password. Unless you have changed the default, this will be “changeit” (a hint to do just that). Next you will be asked for your name, the organizational unit, the organization, city, state, and country. This information is stored in the certificate. Lastly, you will be asked for a password for that key. If you are really security conscious, you can give it a separate password, but the default password is the same as the keystore itself, and is usually adequate. The above information can be specified on the command line from within a build tool such as Ant.
If you invoke the keytool utility with no parameters at the command prompt, it will give you a list of its numerous options. You might like to use the –valid option, for example, which enables you to specify how many days the key will be valid for.
To confirm that your key is now in the cacerts file, type:
keytool –list –keystore <url>
and enter the password as before. Your key may be hidden among the other keys already in your certificate files.
Your new certificate is self-signed and thus not actually trusted by a signing authority. If you use this certificate to sign a JAR file, the end user will get a warning, and a strong recommendation not to use your software. You and your users will have to tolerate this until you are prepared to pay for a trusted certificate for commercial purposes.
To sign your JAR file, use the standard Java jarsigner tool as follows:
jarsigner –keystore <url> <jarfile> <keyname>
where url is the location of your cacerts file, jarfile is the name of your JAR file, and keyname is the alias that you gave to your key. You will again be prompted for the password.
You now have a JAR file that can be identified as being signed with your key, and that can guarantee it has not been tampered with (i.e., no files have been changed, added, or removed) since you signed it.
All you have to do now is make sure that the applet tag in your HTML file has an “archive” element, which specifies the name of your JAR file.
The applet tag is somewhat more complicated for the plugin, but if you create a simple tag like:
<APPLET
CODE=package.AppletSubclass.class
ARCHIVE = myjar.jar
WIDTH=300
HEIGHT=200>
</APPLET>
and run the HTMLConverter tool on it (this is packaged with the freely downloadable JDK), it will create the correct applet tag for you.
Now, when your applet is downloaded by a client, they will be informed that a signed applet is being loaded, and given the option of trusting the signer. As previously mentioned, your test certificate doesn’t have a very high degree of trust, and the user will get a warning to this effect. If they opt to trust your applet, it will have full access to their system and behave as if it were an ordinary application.
The source code for this book, downloadable from www.BruceEckel.com, contains complete working configuration files and an Ant build script to properly compile and build this project.