TOC PREV NEXT INDEX




Step 4 - Trusted Sites, Signlists, and Passwords


In the preceding steps, some major features of the ICEssl module have been presented. However, having more features can be convenient for some applications. Such features include the option for the user to trust a certificate from a particular server without being asked each time, or to allow the ICEssl module to sign with a particular client certificate each time it is asked for. Also, in many cases it is useful to be able to encrypt the private keys of the client certificates. The following example adds these features.

Trusted Sites

When an invalid certificate is encountered, the user can choose to trust a certificate for this session or to permanently trust the certificate. To get this feature, a trusted sites list must be installed. A list of server certificates is read from the file trustedsites.crt at startup.

Add the following code in the constructor of SSLConnect just before the connection to a server is made:

    //Load the list of trusted sites.

     try{

         InputStream in = new FileInputStream(TRUSTEDSITES_FILE_NAME);

         cm.setTrustedSites(cm.getTrustedSites().restoreListFromPEM(in));

         in.close();

     } catch(IOException ex){

         System.err.println("Could not load trusted sites list");

     }
 

When the user has chosen to trust a certificate in the acceptCertificate( ) call, it must be added to the trusted sites list, and saved to disk if the user selected 'Accept forever'. The option 'Save for this session' does not make sense in this example, since we only connect once during the execution of the program, but it will often be useful in an actual application.

The switch statement in acceptCertificate( ) is changed to:

    switch(acceptance){

     case CERT_ALWAYSACCEPT:

         CertificateManager cm = CertificateManager.getCertificateManager();

         cm.getTrustedSites().addCertificate(certs[0]);

         try{

         //Load trusted sites from disk.

         InputStream in = new FileInputStream(TRUSTEDSITES_FILE_NAME);

         ServerCertificateList saved =

            ServerCertificateList.restoreListFromPEM(in);

         in.close();

         //Add the new certificate

         saved.addCertificate(certs[0]);

         //Write list back

         OutputStream out = new FileOutputStream(TRUSTEDSITES_FILE_NAME); 

         saved.saveAsPEM(out);

         out.close();

         } catch(IOException ex){

         System.err.println("Could not save trusted sites list");

         }        

         return true; 

     case CERT_SESSIONACCEPT:

         CertificateManager.getCertificateManager()

         .getTrustedSites().addCertificate(certs[0]);

         return true;

     case CERT_REJECT:

         return false;

     default:

         return false;

     }
 

In cases where the user selects 'Accept forever', the trusted sites stored on disk are loaded into a new ServerCertificateList, and the accepted certificate is added to this new list. The list is then written back to disk.

In addition, the certificate is added to the ordinary trusted sites list. In the case of the user selecting 'Accept for this session', this is the only thing that is done.

Adding a Signlist

A similar feature to the trusted sites list can be added for client certificates by using the signlist of the certificate manager. This is a ClientCertificateList that contains certificates that will be used to return a signature without asking the user first, which is helpful when several connections to the same server is made.

    if(cert != null){                        

         return cm.getClientCertificates().getCertificateChain(cert);

     }    

     else{

         return null;

     }
 

The following block of code adds the certificate to the signlist if the 'Always sign' button was pressed in the user dialog.

    if(cert != null){

         if(selector.useForever()){

         PrivateKey priv = cm.supplyPrivateKey(cert);

         X509Certificate[] chain 

             = cm.getClientCertificates().getCertificateChain(cert);

         cm.getSignList().addCertificate(chain,priv);

         }

         return cm.getClientCertificates().getCertificateChain(cert);

     }    

     else{

         return null;

     }
 
Encrypted Private Keys

A private key is a sensitive piece of information, since it can potentially reveal the identity of the user. It is therefore desirable to encrypt private keys. This can be done by initializing the factory for private keys with a key. Until now the private key factory was not specified, and was passed null to the certificate parsing method. In this case the client certificate parser just uses its own private key factory. To be able to encrypt the private key part of the packages, a PKCS8PrivateKeyFactory must be instantiated with a key string and passed to the restoreListFromPEM( ) call in ClientCertificateList.

The code for initializing the client certificates is the following:

try{

    java.io.InputStream in = new

        FileInputStream(CLIENT_CERTIFICATE_FILE_NAME);

     cm.setClientCertificates(ClientCertificateList.restoreListFromPEM

        (in,null));

     in.close();

     }

     catch(IOException ex){

         System.err.println("Could not load client certificates");

     }
 

To be able to encrypt the private keys, it should be replaced with the following piece of code:

    //Install the list of client/personal certificates.

     PasswordDialog passwdDialog = new PasswordDialog(new Frame());

     byte[] passwd = passwdDialog.askForPassword();

     PKCS8PrivateKeyFactory pkcs8fac = new PKCS8PrivateKeyFactory(passwd);

     try{

         java.io.InputStream in = new

            FileInputStream(CLIENT_CERTIFICATE_FILE_NAME);

        cm.setClientCertificates(ClientCertificateList.restoreListFromPEM

            (in,null));

         in.close();

     }

     catch(IOException ex){

         System.err.println("Could not load client certificates");

     }
 

All the private keys in the list must be signed with the same key. The PasswordDialog class is a simple class that asks for a password before the certificates are loaded. Here the code asks for a password at startup. In an actual application, the loading of client certificates could be delayed until a server actually asks for a certificate, and then ask for password.

The class PasswordDialog is an AWT dialog class, and the code is listed below.

class PasswordDialog extends Dialog implements ActionListener{

private static final int BUFFER = 7;

     

     java.awt.TextField textField = null;

     java.awt.Button okButton = null;

     java.awt.Button rejectButton = null;

 

private byte[] passwd = null;

 

     PasswordDialog(Frame owner) {

         super(owner);

     this.setModal(true);

     createSelector(owner);

     }

            

PasswordDialog(Frame owner, boolean modal) {

         super(owner, modal);

     createSelector(owner);

     }

             

PasswordDialog(Frame owner, String title) {

         super(owner, title);

     this.setModal(true);

     createSelector(owner);

     }

            

PasswordDialog(Frame owner, String title, boolean modal) {

     super(owner, title, modal);

     createSelector(owner);

     }

     

private void createSelector(Frame owner){

     this.setLayout(new GridLayout(3,1));

     this.add(new Label("Type master password for your personal certificates"));

     

     textField = new TextField(40);

     textField.setEchoChar('*');

     this.add(textField);

     

     //the button panel

     Panel buttonPanel = new Panel();

     okButton = new Button("OK");

     buttonPanel.add(okButton);

     rejectButton =  new Button("Cancel");

     buttonPanel.add(rejectButton);

     this.add(buttonPanel);

     

     okButton.addActionListener(this);

     rejectButton.addActionListener(this);

}

 

public void actionPerformed(ActionEvent ev){

     if(ev.getSource()==okButton){

         passwd = textField.getText().getBytes();

     }

     else if(ev.getSource()==rejectButton){

         passwd = "".getBytes();

     }

     setVisible(false);

}

 

     

public byte[] askForPassword(){

     

     pack();

     setVisible(true);

     return passwd;

}

}
 


Copyright 2005. ICEsoft Technologies, Inc.
http://www.icesoft.com

TOC PREV NEXT INDEX