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)

What is a SecurityManager?

The SecurityManager contains a number of methods that check whether a certain operation is permitted, eg. checkRead, checkWrite, checkPropertyAccess. For example, when you are instantiate an object of the class FileInputStream to open a file, the security manager will be consulted to see if you allowed to do so:

   public FileInputStream(String name) throws FileNotFoundException {
      SecurityManager security = System.getSecurityManager();
      if (security != null) {
         security.checkRead(name);
      }
      fd = new FileDescriptor();
      open(name);
   }

For applications, no security manager is installed and hence the checkRead in the FileInputStream constructor will not be executed and the file is successfully opened. The following program shows that no security manager is installed and thus will output null:

 
public class Main
{
   public static void main(String []args) {
      SecurityManager security = System.getSecurityManager();
      System.out.println(security);
   }
}

To make sure your program is using a default security manager, use the switch -Djava.security.manager when your run your program or do it programmatically by calling System.setSecurityManager.

c:> java Main
null
 
c:> java -Djava.security.manager Main
java.lang.SecurityManager@7032dd38

Look at the output of the following program with and without a security manager:

public class Main
{
   public static void main(String []args) {
      System.out.println(System.getProperty("java.version"));
      System.out.println(System.getProperty("test"));
   }
}

The program tries to output the System properties “java.version” and “test” (a dummy one).

C:> java Main
1.2.2
null

C:> java -Djava.security.manager Main
1.2.2
Exception in thread "main" java.security.AccessControlException: access denied
java.util.PropertyPermission test read)
        at java.security.AccessControlContext.checkPermission(AccessControlCont
xt.java:195)
        at java.security.AccessController.checkPermission(AccessController.java
403)
        at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
        at java.lang.SecurityManager.checkPropertyAccess(SecurityManager.java:1
43)
        at java.lang.System.getProperty(System.java:539)
        at Main.main(Main.java:5)

Notice that without a security manager installed, it runs as we wish. With a default security manager installed, it correctly returns the System property “java.version” but throws an AccessControlException when trying to read the property “test”. Why is that?

First, let’s look at the getProperty method in the System.java source file:

   public static String getProperty(String key) {
      if (security != null) {
         security.checkPropertyAccess(key);
      }
      return props.getProperty(key);
   }

It checks for a security manager and if one exists, it uses checkPropertyAccess to determine whether it is allowed to read the property key that is passed in as an argument. Permissions are granted through policy configuration files. There is a system-wide policy file and a single user policy file. The system-wide policy file is located at JAVA_HOME/lib/security/java.policy and the user policy file can be found at USER_HOME/.java.policy (in my case, I found it in C:WINDOWS). The locations of these two policy files are specified in the file JAVA_HOME/lib/security/java.security (in my case, I found it in C:jdk1.2.2jrelibsecurity). When starting up, the system policy is loaded first and the user policy is added to it. If neither of these policy files are present, a built-in policy is used, which is the same as the sandbox policy.

Now, if you look at a part of the system wide policy file java.policy:

      . . .
 
	// allows anyone to listen on un-privileged ports
	permission java.net.SocketPermission "localhost:1024-", "listen";

	// "standard" properies that can be read by anyone

	permission java.util.PropertyPermission "java.version", "read";
	permission java.util.PropertyPermission "java.vendor", "read";
	permission java.util.PropertyPermission "java.vendor.url", "read";
   
      . . .

To find out how these policy files are structured, look at the document Permissions in the Java 2 SDK or Permissions and Security Policy.

Notice that a permission is granted to read the System property “java.version”. That’s why we can read in this property even with a security manager installed. No permission has been given to read the property “test”, so this results in an Exception.

You can make the JVM use additional policy configuration files with a command-line argument:

   java -Djava.security.manager -Djava.security.policy=mypolicyfile Main

For example, we could add an extra permission to read the property “test”.

mypolicyfile:

grant {
   permission java.util.PropertyPermission "test", "read";
};

result:

C:>java -Djava.security.manager -Djava.security.policy=mypolicyfile Main
1.2.2
null

mypolicyfile can be either made with a simple text editor or with the graphical JDK tool policytool.

If you want the JVM use only your policy file and not the system-wide nor the user policy, use a double equal sign:

C:>java -Djava.security.manager -Djava.security.policy==mypolicyfile Main

This would result in an exception when trying to read the system property “java.version”.

The policy file can be highly customized. It should be structured as follows:

   grant signedBy "signer_name", codeBase "URL", 
      principal principal_class_name "principal_name",
      principal principal_class_name "principal_name",
      ... {
      permission permission_class_name "target_name", "action", 
         signedBy "signer_name";
      permission permission_class_name "target_name", "action", 
         signedBy "signer_name";
      ...
   };

For more information on policy file syntax look at the document Default Policy Implementation and Policy File Syntax

( Note: the principal entry is useful for assigning permission based on who is running the code as opposed to where the code is coming from or who signed it, look at the JAAS category for more information. )

For example, to only grant permissions to our Main class located in C: we would write:

grant codeBase "file:C:/" {
   permission java.util.PropertyPermission "test", "read";
};

Running our Main in C:test would result in an AccessControlException: Access Denied.

Encrypting/decrypting using RC2

RC2 stands for Rivest’s Code 2. More information can be found on the RSA Security site.

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("RC2");
      kg.init(sr);
      SecretKey sk = kg.generateKey();
 
      // create an instance of cipher
      Cipher cipher = Cipher.getInstance("RC2");
 
      // 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("RC2");
      kg.init(sr);
      SecretKey sk = kg.generateKey();
 
      // do the decryption with that key
      Cipher cipher = Cipher.getInstance("RC2");
      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)