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

Encrypting/decrypting with DES/CBC/PKCS5Padding

Look at the other questions for more information about the CBC mode and PKCS5Padding. Essentially, a CBC needs a initialization vector (IV) that is basically the seed for encrypting the first block of plaintext. A byte array is filled up with random bytes. You need the same initialization vector for decrypting. You can transmit or save this IV along with your encrypted message. The secret symmetric encryption/decryption key in our case is “password”.

Main.java:

import javax.crypto.spec.*;
import java.security.*;
import javax.crypto.*;
 
public class Main
{
   static IvParameterSpec iv;
 
   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 = encrypt(toEncrypt, "password");
 
      System.out.println("Decrypting...");
      String decrypted = decrypt(encrypted, "password");
    
      System.out.println("Decrypted text: " + decrypted);
   } 
 
   public static byte[] encrypt(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("DES");
      kg.init(sr);
      SecretKey sk = kg.generateKey();
 
      // create an instance of cipher
      Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
 
      // generate an initialization vector (IV)
      SecureRandom secureRandom = new SecureRandom();
      byte[] ivspec = new byte[cipher.getBlockSize()];
      secureRandom.nextBytes(ivspec);
      iv = new IvParameterSpec(ivspec);
 
      // initialize the cipher with the key and IV
      cipher.init(Cipher.ENCRYPT_MODE, sk, iv);
 
      // enctypt!
      byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());
 
      return encrypted;
   }
 
   public static String decrypt(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("DES");
      kg.init(sr);
      SecretKey sk = kg.generateKey();
 
      // do the decryption with that key
      Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
      cipher.init(Cipher.DECRYPT_MODE, sk, iv);
      byte[] decrypted = cipher.doFinal(toDecrypt);
 
      return new String(decrypted);
   }
}

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

Performing declarative authorization with JAAS

Authorization

Authorization is about allowing or denying access to resources to a particular subject (a user, a group, a company, …). When a subject is authenticated, it is augmented with one or more principals that identify the subject for one or more resources, for example a social security number for one resource or a role of an administrator for another. A subject can also have credentials associated with them, any Java objects that contains security-related information about the subject, for example a certificate or a password.

To go ahead with this example, first read the authentication example.

In the following example, the authentication example will be augmented with a section that is only executed when it is permitted to do so by a particular principal, in our example “johndoe”. The following policy file will grant permissions to the principal “johndoe” to read and write to the file “c:\testfile”. A policy file specifies what permissions are allowed to code coming from a specified code source and executed by a specified principal.

accesscontrol.policy:

/** User-Based Access Control Policy **/

grant Principal MyPrincipal "johndoe" {
   permission java.io.FilePermission "c:/testfile", "read,write";
};

We also have another policy file that grants permissions to read and write System properties (needed by the Swing DialogCallbackHandler), to create a LoginContext (necessary for authentication), to execute a doAsPrivileged method (necessary for executing sensitive code that requires principal permissions) and to modify principals (necessary when we add a principal to the subject). jaasmain.policy:

grant {
   permission java.util.PropertyPermission "*", "read, write";
   permission javax.security.auth.AuthPermission 
                    "createLoginContext";
   permission javax.security.auth.AuthPermission "doAsPrivileged";
   permission javax.security.auth.AuthPermission "modifyPrincipals";
};

jaas.config:

MyAuthenticationComponents {
   UsernamePasswordLoginModule required;
};

Our module that encapsulates code to do authentication has not changed from the authentication example.

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;
      }
   }
}

Our passwd “database” textfile has not changed from the authentication example.

passwd:

johndoe,sdefujm
janedoe,yuymndee

Our MyPrincipal class has also not changed from the authentication example.

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;
   }
}

The code that is to be executed based on user authentication must be inside the run method of a class that implements java.security.PrivilegedAction.

WriteFileAction.java:

import java.security.PrivilegedAction;
import java.io.*;
 
public class WriteFileAction implements PrivilegedAction {
   public Object run() {
 
      try {
         BufferedWriter bw = new BufferedWriter(new FileWriter("c:\testfile"));
         bw.write("the shorter you live, the longer you're dead");
         bw.close();
         System.out.println("c:\testfile successfully written!");
      }
      catch(IOException e) {
         System.out.println(e);
      }
 
      return null;
   }
}

We want this code to be executed only when a specified principal is running it (“johndoe” as specified in the policy file). We enforce this by calling this code indirectly through the method doAs or doAsPrivileged. The difference between the two is described here.

Main.java:

import com.sun.security.auth.callback.*;
import javax.security.auth.login.*;
import javax.security.auth.*;
import java.security.*;
import java.util.*;
import java.io.*;
 
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);
 
         PrivilegedAction action = new WriteFileAction();
         subject.doAsPrivileged(subject, action, null);
  
         loginContext.logout();
      }
      catch(LoginException e) {
         System.out.println("Unauthorized user!");
      } 
 
      // stop AWT thread (DialogCallbackHandler)
      System.exit(0);
   }
}

To run the code, you need to specify the policy files (or change the default java.policy one):

   c:jdk1.4binjava -Djava.security.auth.policy=accesscontrol.policy
                      -Djava.security.policy=jaasmain.policy
                      -Djava.security.auth.login.config==jaas.config
                      -Djava.security.manager
                      Main

Running the code with username=”johndoe”, password=”sdefujm” results in:

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

c:testfile successfully written!
LoginModule logout()

Running the code with username=”janedoe”, password=”yuymndee” (another authenticated user, but not authorized) results in:

LoginModule initialize()
LoginModule login()
LoginModule commit()
Subject:
	Principal: janedoe
Exception in thread "main" java.security.AccessControlException: access denied
java.io.FilePermission c:testfile write)
        at java.security.AccessControlContext.checkPermission(AccessControlCont
xt.java:273)
        at java.security.AccessController.checkPermission(AccessController.java
400)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:545)
        at java.lang.SecurityManager.checkWrite(SecurityManager.java:978)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:103)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:69)
        at java.io.FileWriter.<init>(FileWriter.java:44)
        at WriteFileAction.run(WriteFileAction.java:8)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAsPrivileged(Subject.java:436)
        at Main.main(Main.java:21)

Notice that “janedoe” is correctly authenticated, but not authorized to run the privileged code as that principal is not specified in the policy file accesscontrol.policy.

What is non-repudiation?

Non-repuditation means the ability to prove that a transaction originated from a particular party. So that party cannot deny that he performed a certain transaction. A receiver cannot deny that he received a certain message from a sender, and a sender cannot deny that he sent a message to the receiver.

Encrypting/decrypting with Triple-DES

Triple-DES or DESede is an improvement over DES (Data Encryption Standard). It uses three DES keys k1, k2 and k3. A message is encrypted with k1 first, then decrypted with k2 and encrypted again with k3 (DESencryptiondecryptionencryption). This increases security as the key length effectively increases from 56 to 112 or 168 (two or three keys may be used in DESede). As a programmer you don’t have to worry about managing several keys, they are all encoded into one key.

The DESede key size is 128 or 192 bit and block size 64 bit.

Eventually, DES and Triple-DES will be replaced by the Advanced Encryption Standard (AES).

Main.java:

import javax.crypto.spec.*;
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 = encrypt(toEncrypt, "password");
 
      System.out.println("Decrypting...");
      String decrypted = decrypt(encrypted, "password");
    
      System.out.println("Decrypted text: " + decrypted);
   } 
 
   public static byte[] encrypt(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("DESede");
      kg.init(sr);
      SecretKey sk = kg.generateKey();
 
      // create an instance of cipher
      Cipher cipher = Cipher.getInstance("DESede");
 
      // initialize the cipher with the key 
      cipher.init(Cipher.ENCRYPT_MODE, sk);
 
      // enctypt!
      byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());
 
      return encrypted;
   }
 
   public static String decrypt(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("DESede");
      kg.init(sr);
      SecretKey sk = kg.generateKey();
 
      // do the decryption with that key
      Cipher cipher = Cipher.getInstance("DESede");
      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 programmatic authorization with JAAS

Authorization

Authorization is about allowing or denying access to resources to a particular subject (a user, a group, a company, …). When a subject is authenticated, it is augmented with one or more principals that identify the subject for one or more resources, for example a social security number for one resource or a role of an administrator for another. A subject can also have credentials associated with them, any Java objects that contains security-related information about the subject, for example a certificate or a password.

To go ahead with this example, first read the authentication example.

In the following example, the authentication example will be augmented with a section that is only executed when it is permitted to do so by a particular principal, in our example “johndoe”. As opposed to specifying the principals and permissions in a policy file (see How do I use authorization with JAAS (declarative)) it is done programmatically.


We also have another policy file that grants permissions to read and write System properties (needed by the Swing DialogCallbackHandler), to create a LoginContext (necessary for authentication), to execute a doAsPrivileged method (necessary for executing sensitive code that requires principal permissions) and to modify principals (necessary when we add a principal to the subject). jaasmain.policy:

grant {
   permission java.util.PropertyPermission "*", "read, write";
   permission javax.security.auth.AuthPermission 
                    "createLoginContext.Main";
   permission javax.security.auth.AuthPermission "doAsPrivileged";
   permission javax.security.auth.AuthPermission "modifyPrincipals";
};

Our module that encapsulates code to do authentication has not changed from the authentication example.

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;
      }
   }
}

Our passwd “database” textfile has not changed from the authentication example.

passwd:

johndoe,sdefujm
janedoe,yuymndee

Our MyPrincipal class has also not changed from the authentication example.

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;
   }
}

The code that is to be executed based on user authentication must be inside the run method of a class that implements java.security.PrivilegedAction.

WriteFileAction.java:

import java.security.PrivilegedAction;
import java.io.*;
 
public class WriteFileAction implements PrivilegedAction {
   public Object run() {
 
      try {
         BufferedWriter bw = new BufferedWriter(new FileWriter("c:\testfile"));
         bw.write("the shorter you live, the longer you're dead");
         bw.close();
         System.out.println("c:\testfile successfully written!");
      }
      catch(IOException e) {
         System.out.println(e);
      }
 
      return null;
   }
}

We want this code to be executed only when a specified principal is running it (“johndoe” as specified in the policy file). We enforce this by calling this code indirectly through the method doAs or doAsPrivileged. The difference between the two is described here.

Main.java:

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

To run the code, you need to specify the policy files (or change the default java.policy one):

   c:jdk1.4binjava -Djava.security.auth.policy=accesscontrol.policy
                      -Djava.security.policy=jaasmain.policy
                      -Djava.security.auth.login.config==jaasmain.config
                      -Djava.security.manager
                      Main

Running the code with username=”johndoe”, password=”sdefujm” results in:

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

c:testfile successfully written!
LoginModule logout()

Running the code with username=”janedoe”, password=”yuymndee” (another authenticated user, but not authorized) results in:

LoginModule initialize()
LoginModule login()
LoginModule commit()
Subject:
	Principal: janedoe
Exception in thread "main" java.security.AccessControlException: access denied
java.io.FilePermission c:testfile write)
        at java.security.AccessControlContext.checkPermission(AccessControlCont
xt.java:273)
        at java.security.AccessController.checkPermission(AccessController.java
400)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:545)
        at java.lang.SecurityManager.checkWrite(SecurityManager.java:978)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:103)
        at java.io.FileOutputStream.<init>(FileOutputStream.java:69)
        at java.io.FileWriter.<init>(FileWriter.java:44)
        at WriteFileAction.run(WriteFileAction.java:8)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAsPrivileged(Subject.java:436)
        at Main.main(Main.java:21)

Notice that “janedoe” is correctly authenticated, but not authorized to run the privileged code as that principal is not specified in the policy file accesscontrol.policy.

Using cryptography in Java

To use cryptography in Java, you will need to download the Java Cryptography
Extension (JCE) API and the reference implementation by Sun (only available for
US and Canada due to export restrictions) or another implementation by a
so-called cryptography service provider.

Steps in installing/configuring the JCE1.2:

  1. For US/CAN users: download JCE 1.2 from java.sun.com
  2. Unzip the file (eg. to c:java) and add jce1_2-do.jar to your classpath:
    set classpath=%classpath%;c:javajce1.2libjce1_2-do.jar
  3. Register the SunJCE provider:
    Statically: edit the security properties file, located in libsecurityjava.security
    and add the following line to it:
    security.provider.n=com.sun.crypto.provider.SunJCE
    where n is the preference order (you could have other providers!)
    Dynamically: you can also add the provider at runtime:
     
            Security.addProvider(new com.sun.crypto.provider.SunJCE());
         

    Make sure jce1_2-do.jar is added to your classpath, in order for this to compile.

Steps in installing/configuring another service provider, eg. Cryptix:

  1. Cryptix is available from servers located around the world: download Cryptix_bin_3-1-1.zip (or a newer version) from www.cryptix.org
  2. Unzip the file (eg. to c:javacryptix) and add the installed jar files (in C:javacryptixclasses) to your classpath: set classpath=%classpath%;c:javacryptixclassesSPT_0-1-1.jar; …
  3. Register the Cryptix provider:
    Statically: edit the security properties file, located in libsecurityjava.security
    and add the following line to it:
    security.provider.n=cryptix.provider.Cryptix
    where n is the preference order (you could have other providers!)
    Cryptix provides a program that does this for you. Assuming you have changed your classpath, you can invoke
    the following installation script:
     
            java cryptix.provider.Install
         

    Dynamically: you can also add the provider at runtime:

     
            Security.addProvider(new cryptix.provider.Cryptix());
         

Encrypting/decrypting with the IDEA algorithm

IDEA stands for International Data Encryption Algorithm. It is a patented, commercial algorithm that is fairly new (1990). It is faster than DES and considered more secure. It is used in PGP and SSL.

The IDEA key size is 128 bit and block size 64 bit.

Main.java:

import javax.crypto.spec.*;
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 = encrypt(toEncrypt, "password");
 
      System.out.println("Decrypting...");
      String decrypted = decrypt(encrypted, "password");
    
      System.out.println("Decrypted text: " + decrypted);
   } 
 
   public static byte[] encrypt(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("IDEA");
      kg.init(sr);
      SecretKey sk = kg.generateKey();
 
      // create an instance of cipher
      Cipher cipher = Cipher.getInstance("IDEA");
 
      // initialize the cipher with the key 
      cipher.init(Cipher.ENCRYPT_MODE, sk);
 
      // enctypt!
      byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());
 
      return encrypted;
   }
 
   public static String decrypt(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("IDEA");
      kg.init(sr);
      SecretKey sk = kg.generateKey();
 
      // do the decryption with that key
      Cipher cipher = Cipher.getInstance("IDEA");
      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)