What is a digital signature?

A digital signature is an extra chunk of data send along with an (encrypted) message which tells the receiver that the message is coming from the one who claims to have sent it. It ensures that the message has not been tampered with by a man-in-the-middle, a person who spies on the communication line and intercepts messages. You also have a proof that the sender is the one who holds the private key of a public-private key pair.

It goes as follows:

   - Two parties, A and B.  A wants to send a message M to B.
  
      +-----+                              +-----+
      |  A  |                              |  B  |
      +-----+                              +-----+
 - holds private key PVT of       - holds public key PUB of
   public-private key pair          public-private key pair
 
Sender:
   1. A calculates a message digest MD on M
   2. A encrypts that message digest MD with his private
      key PVT (A is the only one that has the private key
      that belongs to him!) = digital signature DS 
   3. A sends message M and DS to B
 
Receiver:
   1. B receives the message M and digital signature DS
   2. B decrypts the digital signature DS with the public
      key PUB and gets MD
   3. B calculates a message digest on M = MD2
   4. B compares MD with MD2.  Equality means that the
      sender must have had access to the private key of 
      the PVT-PUB key pair.

But who says this public/private pair belongs to me? I could have identified myself as someone else, generate such a pair and distribute the public key to a sender who believes I am someone else and trick him with this above process claiming to be that someone else…

That’s where digital certificates come in. Check out What is a digital certificate?

What is symmetric cryptography?

In Symmetric Cryptography (also: secret key cryptography), an algorithm is used to scramble the message using a secret key in such a way that it becomes unusable to all except the ones that have access to that secret key. The most widely known symmetric cryptographic algorithm is DES, developed by IBM in the 70ies. It uses a key of 56 bits and operates on chunks of 64 bits at a time. The problem with DES is the short key length, making it possible for someone, that doesn’t have access to the private key, to try all possible keys. DESede provides a significant improvement with the key being 3 times as large.

Symmetric cryptography works very well if encryption and decryption are eventually performed by the same party. But if there are 2 or more parties involved, there is the question of how to exchange the secret key without anyone spying. Look at asymettric cryptography for a solution.

Downloading a secure page (HTTPS) with Java

If you try to get data from an HTTPS-enabled page, you will get a MalformedURLException, as shown in following example:

Main.java:

import java.net.*;
import java.io.*;

public class Main {
   public static void main(String[] args) throws Exception {
      URL url = new URL("https://www.sun.com");
      BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
      String line;

      while ((line = br.readLine()) != null) {
         System.out.println(line);
      }
      br.close();
   }
}

outputs:

C:>java Main
Exception in thread "main" java.net.MalformedURLException: unknown protocol: https
        at java.net.URL.<init>(URL.java:497)
        at java.net.URL.<init>(URL.java:364)
        at java.net.URL.<init>(URL.java:308)
        at Main.main(Main.java, Compiled Code)

This is because Sun has no implementation for the HTTPS protocol in their core libraries. However, they created an reference implementation called Java Secure Socket Extension (JSSE) available as seperate download at http://java.sun.com/products/jsse. Include jcert.jar, jnet.jar and jsse.jar in your classpath and add the following two lines of code to the previous program:

      System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol");
      Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());

Here’s the updated example that works:

import java.security.*;
import java.net.*;
import java.io.*;

public class Main {
   public static void main(String[] args) throws Exception {
      // create a new protocol handler
      System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol");

      // protocol handler uses this security provider
      Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
      URL url = new URL("https://www.sun.com");
      BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
      String line;

      while ((line = br.readLine()) != null) {
         System.out.println(line);
      }

      br.close();
   }
}

What is a digital certificate?

First read the question What is a digital signature? You’ll then understand what the problem is with those signatures. You can’t really proof that the public key that the sender owns to decrypt the message actually belongs to you. That’s where a digital certificate comes in. Those certificates are issued by a certificate authority (CA) which you would have to pay and give lots of information about your identity. You’d also have to send them your public key. The CA uses all this information to generate a certificate and send it back to you. The standard for digital certificates is X.509. It contains information about your identity, your public key and things like a validity period, etc. It’s important that you choose a well-known CA that other people will trust. The CA will have encrypted the certificate with their private key.

When party A now sends a message to party B, it will sign the message with his certificate. Party B will ensure this certificate is correct by decrypting it using the CA’s public key. Now party B has access to your public key and can decrypt the signature to verify the authentication.

BTW, you can also generate a certificate yourself (eg. using the tool keytool) and use that. But people are not likely to accept that certificate as there is no trusted third party involved.

The standard for digital certificates is X.509. This standard allows for certificate chaining. This allows certificates to be put in a hierarchy. Maybe party B doesn’t trust the first CA, but goes up in the hierarchy and finds one that he trusts. For example, an employee of a company might get a certificate from his company who is in turned signed by Thawte (the root CA). Now suppose that employee uses his certificate to try to get the receiver trust him. The receiver isn’t eager to trust the one that issued the certificate, but since that certificate is signed in turn by Thawte, he trust the original certificate.

Cipher modes for symmetric encryption

  • Electronic Code Book Mode (ECB) (http://www.rsasecurity.com/rsalabs/faq/2-1-4-2.html)
    This mode encrypts all plaintext blocks independently. This means that two identical plaintext blocks are encrypted the same way. It’s a very fast mode (encryption can be done in parallel) but it is not as secure because certain patterns can be discovered in the resulting encrypted text. For example, I encrypted the message “aaaaaaaaeeeeeeeeaaaaaaaaaaaaaaaa” with DES/ECB. This is the encrypted text in an array of byte values:
    -74 -36 103 -124 95 62 54 -117
    70 -70 -61 -9 17 95 54 -114
    -74 -36 103 -124 95 62 54 -117
    -74 -36 103 -124 95 62 54 -117
    -88 109 -8 8 -14 -108 19 122 
  • Cipher Block Chaining Mode (CBC) ( http://www.rsasecurity.com/rsalabs/faq/2-1-4-3.html)
    This mode solves the problem of ECB by XOR’ring the result of the encryption of the previous block with the next block before encrypting the next block. It’s slower as it cannot be done in parallel. The method also uses an initialization vector (IV) as a seed. The first plaintext block is XOR’red with that seed. This ensures that two identical plaintext messages are not encrypted identically.
  • Cipher Feedback Mode (CFB) (http://www.rsasecurity.com/rsalabs/faq/2-1-4-4.html)
    This mode is similar to CBC except that it allows for processing smaller increments of plaintext instead of an entire block. It uses a “register” that has the size of a block and initally contains the seed (initialization vector). Everytime x number of bits are to be encrypted, they are XOR’red with the leftmost x bits in the register. The register is shifted left by x bits and the encrypted bits are appended to it. Since you can encrypt bytes at a time, it is useful for transmission of small chunks of data, eg. chat. But incorrectly transmitting one bit would result in a propagation of the error.
  • Output Feedback Mode (OFB) ( http://www.rsasecurity.com/rsalabs/faq/2-1-4-5.html )
    This mode is similar to CFB except that bit errors that occur during transmission are not propagated to affect the decryption of subsequent blocks.

Get started with JAAS

JAAS stands for Java Authentication and Authorization Service. It allows you to grant permission based on who is executing the code. Previous security models already granted permission based on where the code was coming from and who signed it. JAAS has been added to JDK1.4 and the examples here will use that JDK. If you want to run them with JDK version 1.3, download the optional JAAS package here and put jaas.jar in jdk1.3/jre/lib/ext.

JAAS consists of two components: the authentication component and the authorization component. The Authentication component determines who is attempting to run code. The authorization component determines if that entity has the necessary permissions to run that code.

To find out how to authenticate with JAAS, check out JAAS Authentication.
To find out how to allow or deny access to resources, check out JAAS Declarative Authorization and JAAS Programmatic Authorization.

What is a certification authority (CA)?

In assymetric encryption, you have a private key with which you encrypt messages and your receivers have public keys with which they can decrypt them. The receiver wants to ensure that the sender is the one who he claims to be. To build in trust between the sender and receiver, the sender can ask for a certificate to a Certification Authority (CA), after providing this organization with documents that proof his identity. The CA issues to the sender a certificate, containing his public key some identification information and a digital signature created using the private key of the CA. The sender can then use this certificate in communicating messages. The receiver uses the public key of the CA to check on the validity of the public key of the sender. The whole idea is that if the receiver trusts the CA, he trusts all the certificates the CA issues.

Encypting/decrypting a message with the Blowfish algorithm

Blowfish is a free and strong encryption algorithm that uses a key from 32bits to 448bits.

Following code has been tested with the BouncyCastle JCE provider.

Main.java:

import java.security.*;
import javax.crypto.*;
 
public class Main
{
   public static void main(String []args) throws Exception {
      String toEncrypt = "The shorter you live, the longer you're dead";
 
      System.out.println("Encrypting...");
      byte[] encrypted = blowfishEncrypt(toEncrypt, "password");
 
      System.out.println("Decrypting...");
      String decrypted = blowfishDecrypt(encrypted, "password");
    
      System.out.println("Decrypted text: " + decrypted);
   } 
 
   public static byte[] blowfishEncrypt(String toEncrypt, String key) throws Exception {
      // create a binary key from the argument key (seed)
      SecureRandom sr = new SecureRandom(key.getBytes());
      KeyGenerator kg = KeyGenerator.getInstance("Blowfish");
      kg.init(sr);
      SecretKey sk = kg.generateKey();
 
      // do the encryption with that key
      Cipher cipher = Cipher.getInstance("Blowfish");
      cipher.init(Cipher.ENCRYPT_MODE, sk);
      byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());
 
      return encrypted;
   }
 
   public static String blowfishDecrypt(byte[] toDecrypt, String key) throws Exception {
      // create a binary key from the argument key (seed)
      SecureRandom sr = new SecureRandom(key.getBytes());
      KeyGenerator kg = KeyGenerator.getInstance("Blowfish");
      kg.init(sr);
      SecretKey sk = kg.generateKey();
 
      // do the decryption with that key
      Cipher cipher = Cipher.getInstance("Blowfish");
      cipher.init(Cipher.DECRYPT_MODE, sk);
      byte[] decrypted = cipher.doFinal(toDecrypt);
 
      return new String(decrypted);
   }
}

(tested with the BouncyCastle JCE provider, http://www.bouncycastle.org)

Performing authentication with JAAS

Authentication

The authentication component of JAAS can determine who is executing code. It’s against useability to have a user on a system remember different passwords, one for each program where authentication is required. If a user on a system is used to identifying himself by means of a fingerprint device, the ideal situation would be to do the same for your new Java application that requires authentication. JAAS uses a pluggable architecture for authentication called PAM, Pluggable Authentication Module. This allows for integration of existing PAM-enabled authentication mechanisms with your program. So vendors can write authentication modules for different types of login systems. More information about PAM can be found here.

The following example will show how to build an authentication module. The following steps are executed in trying to authenticate a subject:

  1. Instantiate a LoginContext
  2. The LoginContext loads one or more LoginModules described in a Configuration
  3. login() is called on the instantiated LoginContext
  4. The LoginModules are consulted to see if the user can be authenticated and if so, principals and credentials are associated with the subject
  5. If the status of the login is positive, the subject can be retrieved. Otherwise, a LoginException is thrown.

Here is a small example that will ask a user for his username and password and consults a text file to determine if that user has the necessary permissions to continue.

First we create a “database” textfile that contains a list of user entries that can be successfully authenticated in the form username,password.
passwd:

johndoe,sdefujm
janedoe,yuymndee

Then we write our authentication module.

UsernamePasswordLoginModule.java:

import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import javax.security.auth.spi.*;
import javax.security.auth.*;
import java.security.*;
import java.util.*;
import java.io.*;
 
public class UsernamePasswordLoginModule implements LoginModule {
   private Subject subject;
   private CallbackHandler callbackHandler;
 
   private String username;
   private char[] password;
   private boolean loginSucceeded = false;
   private boolean commitSucceeded = false;
  
   private Principal principal;
 
   public void initialize(Subject subject, CallbackHandler callbackHandler, 
                          Map sharedState, Map options) {
      System.out.println("LoginModule initialize()");
      this.subject = subject;
      this.callbackHandler = callbackHandler;
      username = null;
      clearPassword();
      loginSucceeded = false;
      commitSucceeded = false;
   }
 
   public boolean login() throws LoginException {
      System.out.println("LoginModule login()");
      if (callbackHandler == null) {
         throw new LoginException("No CallbackHandler!");
      }
 
      Callback[] callbacks = new Callback[2];
      callbacks[0] = new NameCallback("Username: ");
      callbacks[1] = new PasswordCallback("Password: ", false);
 
      try {
         callbackHandler.handle(callbacks);
         username = ((NameCallback) callbacks[0]).getName();
         char[] temp = ((PasswordCallback) callbacks[1]).getPassword();
         password = new char[temp.length];
         System.arraycopy(temp, 0, password, 0, temp.length);
 
         BufferedReader br = new BufferedReader(new FileReader("passwd"));
         String line;
         while ((line = br.readLine()) != null) {
            int comma = line.indexOf(',');
            String un = line.substring(0, comma);
            String pw = line.substring(comma+1);
 
            if (username.equals(un) && new String(password).equals(pw)) {
               // succeeded!
               loginSucceeded = true;
               return true;
            }
         }
      }
      catch(IOException e) {
         throw new LoginException(e.toString());
      }
      catch(UnsupportedCallbackException e) {
         throw new LoginException(e.toString());
      }
 
      username = null;
      clearPassword();
      loginSucceeded = false;
 
      throw new FailedLoginException("Incorrect Username/Password");
   }
 
   public boolean commit() throws LoginException {
      System.out.println("LoginModule commit()");

      if (loginSucceeded == false) {
         return false;
      }
 
      principal = new MyPrincipal(username);
      if (!(subject.getPrincipals().contains(principal))) {
         subject.getPrincipals().add(principal);
      }
 
      username = null;
      clearPassword();
      commitSucceeded = true;
       
      return true;
   }
  
   public boolean abort() throws LoginException {
      System.out.println("LoginModule abort()");

      if (!loginSucceeded) {
         return false;
      }
      else if (loginSucceeded && commitSucceeded) {
         loginSucceeded = false;
         username = null;
         clearPassword();
         principal = null;
      }
      else {
         logout();
      }
 
      return true;
   }
 
   public boolean logout() throws LoginException {
      System.out.println("LoginModule logout()");

      subject.getPrincipals().remove(principal);
      loginSucceeded = false;
      commitSucceeded = false;
      username = null;
      clearPassword();
      principal = null;
 
      return true;
   }
 
   private void clearPassword() {
      if (password != null) {
         for (int i=0; i<password.length; i++) {
            password[i] = ' ';
         }
         password = null;
      }
   }
}

This implementation of LoginModule contains the code to check whether a user is permitted to log in or not. You can provide multiple login modules, checking several sources for authentication. Our module checks whether a username and password exists in our database textfile passwd. Its task is to determine if a username/password pair (the subject) should be authenticated and if success it should add principals to that subject. When a user is asked to be authenticated, JAAS calls the methods in our LoginModule in a certain order. initialize() will be called with certain state information, most importantly the subject to be authenticated and an instance of CallbackHandler that is used later on to get the login information from the user.

  • login() first creates a set of Callbacks, one for the username and one for the password. After we call handle() on our CallbackHandler, the username/password pair should be filled in in both of these callback objects. In this case (see below), a Swing dialog will pop up and ask the user for his username and password. But it can be anything else to get the user’s info, for example by using the information on the currently logged in user on a Windows NT domain. After we get the username/password information from the user, we check whether they exist in the passwd file.
  • commit() will be called when login was successful. It should fill up the subject with the associated principals and credentials and clean up the state.
  • abort() will be called to abort the authentication procedure when either one of the login() or the commit() method fails.
  • logout() will be called when a user is ready to log out.

    For more information on how to write a LoginModule, check out JAAS LoginModule’s Developer’s Guide.

    Our Main program looks like this:

    Main.java:

    import com.sun.security.auth.callback.*;
    import javax.security.auth.login.*;
    import javax.security.auth.*;
    import java.security.*;
     
    public class Main {
       public static void main(String []args) throws Exception { 
          try {
             LoginContext loginContext = new LoginContext("MyAuthenticationComponents", 
                                                 new DialogCallbackHandler());
     
             // will throw a LoginException if it fails, falls through otherwise
             loginContext.login();
     
             Subject subject = loginContext.getSubject();
             System.out.println(subject);
     
             loginContext.logout();
          }
          catch(LoginException e) {
             System.out.println("Unauthorized user!");
          } 
     
          // stop AWT thread (DialogCallbackHandler)
          System.exit(0);
       }
    }

    You create an instance of LoginContext, a class able to authenticate subjects. You need to pass in a String that determines the LoginModules that will be used in authenticating a subject. In our example, we use MyAuthenticationComponents (look at the jaas.config file below). If no LoginException was thrown, the subject is printed out. Notice that we use the Sun undocumented class DialogCallbackHandler that will pop up a dialogbox to ask the user for a username and password.

    jaas.config:

    MyAuthenticationComponents {
       UsernamePasswordLoginModule required;
    };

    This configuration file specifies the module that should be used to authenticate the user. The required flag specifies that the result of the module must be successful in order for the entire login to succeed. You may specify several login modules with different flags. Other flags are Requisite, Sufficient and Optional. Check out this article for more information on these flags.

    Finally we have a MyPrincipal object that implements the Principal interface and encapsulates a subject’s identify.

    MyPrincipal.java:

    import java.security.*;
    import java.io.*;
     
    public class MyPrincipal implements Principal, Serializable
    {
       private String name;
    
       public MyPrincipal(String name) {
          this.name = name;
       }
      
       public String getName() {
          return name;
       }
      
       public int hashCode() {
          return name.hashCode();
       }
     
       public String toString() {
          return getName();
       }
     
       public boolean equals(Object obj) {
          if (obj == null) {
             return false;
          }
     
          if (!(obj instanceof MyPrincipal)) {
             return false;
          }
     
          MyPrincipal mp = (MyPrincipal) obj;
          if (name.equals(mp.getName())) {
             return true;
          }
     
          return false;
       }
    }

    To run this code, we need to specify the configuration file to be used. You can do that by specifying the System property java.security.auth.login.config at command line:

     c:jdk1.4binjava -Djava.security.auth.login.config==jaas.config Main

    Alternatively, it can be configured in the java.security properties file. Check out this reference for more information.

    Output with username=johndoe, password=sdefujm:

    LoginModule initialize()
    LoginModule login()
    LoginModule commit()
    Subject:
    	Principal: johndoe
    LoginModule logout()

    Output with username=johndoe, password=abcdefg:

    LoginModule initialize()
    LoginModule login()
    LoginModule abort()
    Unauthorized user!

Creating a digital certificate with keytool

Look at the command-line options of keytool.

For example, to create a public/private key pair and store it in the default keystore (in my case C:Windows.keystore) under the alias mykey:

C:> keytool -genkey -keyalg RSA -alias mykey
Enter keystore password:  esuspass
keytool error: Key pair not generated, alias <mykey> already exists

C:>keytool -genkey -keyalg RSA -alias esuskey
Enter keystore password:  esuspass
What is your first and last name?
  [Unknown]:  Joris Van den Bogaert
What is the name of your organizational unit?
  [Unknown]:  Esus Team
What is the name of your organization?
  [Unknown]:  Esus, Inc.
What is the name of your City or Locality?
  [Unknown]:  Meerbeek
What is the name of your State or Province?
  [Unknown]:
What is the two-letter country code for this unit?
  [Unknown]:  BE
Is <CN=Joris Van den Bogaert, OU=Esus Team, O="Esus, Inc.", L=Meerbeek, ST=Unkno
wn, C=BE> correct?
  [no]:  yes