Security and Mobile Code tutorial

 

 

[Week 8 Aims] Aims of the tutorial
  1. To examine the security features provided in Java.
  2. To provide hands-on experience of how to use security policies and policy files.
  3. To illustrate how to add security features to RMI applciations.

 

 

Introduction

As discussed in the lecture stream, security is a major concern in modern distributed systems particularly with the increasingly open nature of the Internet, coupled with emerging capabilities such as mobile code. Java is fairly unique in the programming language domain by making security an integral part of the language design. This practical session will examine the nature of security support in Java.

 

For further information on Java security and ongoing developments in this fast evolving area, please refer to the links found in: Java security links.

 

Java security actually covers a series of diverse areas, including authentication, access control (authorisation), confidentiality, containment, etc. We look at the key features below.

 

 

Security in Java

Overview

 

Java's approach to security is based on the concept of the sandbox. The sandbox is actually a set of cooperating system components that together offer the desired level of security. Effectively, the sandbox restricts the scope of executing code; Java programs execute in the protected environment of the sandbox and do not have the capability to cause damage outside this sandbox.

 

In more detail, the sandbox consists of 3 key components:

  • the class loader
  • the byte-code verifier
  • the security manager

We examine each in turn below.

 

 

The Class Loader

 

The class loader in Java provides the capability to fetch code from a remote machine. Crucially, however, this loader enforces a strict security model, providing a first line of defence against attack. The class loader achieves this by enforcing a namespace hierarchy. A Java namespace is divided into local and remote classes, with remote classes again being divided according to the level of trust in the source. This is depicted below:

 

Security classes

 

In addition, untrusted components are restricted in their access to the system, e.g. no access to local files. The namespace effectively creates the outer walls of the sandbox.

 

 

The Byte-Code Verifier

 

The role of this component is to validate all untrusted components before they are entitled to execute in a given namespace. The verifier ensures the imported code adheres to the rules of the language. Checks carried out include:

 

  • the code is formatted correctly
  • stacks will not overflow or underflow during execution
  • private data is protected
  • etc.

 

The verifier executes transparently when code is downloaded into the sandbox environment.

 

 

The Security Manager

 

The security manager is the most important component in the sandbox; the role of the security manager is to perform run-time checks on potentially dangerous operations:

 

  • file I/O
  • network access
  • creation on new class loaders
  • etc.

 

The manager reserves the right to veto any such operations. You will exploit the features of the security manager in the exercises below.

 

Note that, when using RMI over a network, the use of a security manager is compulsory.

 

 

Summary

 

The following diagram illustrates how the various components of the sandbox fit into the Java development process:

Java Sandbox

 

Other Security Features

The JavaSecurity API The java.security package contains a number of additional classes for constructing secure programs. Security functions supported include:

  • access control lists
  • digital signatures
  • key management
  • message digests

 

We do not however look at these aspects in this practical. Instead, the interested reader is referred to the Java documentation.

 

 

Secure Sockets.

 

It is also important to be able to set up secure communications in Java. This is supported by the RMI socket factory. This allows the RMI transport layer to use a different protocol from the default TCP, provided by java.net.Socket. Therefore, your client and server can communicate across sockets that can encrypt your data.

 

Secure sockets For more information read the following links:

Creating a custom RMI Socket Factory

Example RMI socket factory code

 

 

Week 8 Tasks

8.1 Using the security manager

All the systems you have implemented so far have executed within the same directory, to avoid dealing with security issues. However, in a distributed environment it is unlikely that users will execute the client from the same directory as the server. Therefore, this task illustrates how to run an application across different directories and machines.

 

Follow the steps described to make the Shares Service execute across different directories:

 

 

[NO Demo!]

  1. Create two new directories on your H: drive. e.g. H:/RMI/Client/ & H:/RMI/Server.
  2. Copy the following files to the Server folder (or their equivalents):
    • shares.java
    • sharesimpl.java
    • sharesserver.java
  3. Compile these files and create the stub files.
  4. Copy the following files to the Client folder (or their equivalents):
    • shares.java
    • sharesclient.java
  5. Compile the two files.
  6. Execute the system, with the following commands at separate command prompts:
    • H:/RMI/Server>rmiregistry
    • H:/RMI/Server>java sharesserver
    • H:/RMI/Client>java sharesclient

 

You will receive an error message - "RemoteException java.rmi.UnmarshalException: error unmarshalling return; nested exception is: java.lang.ClassNotFoundException: sharesimpl_Stub (no security manager: RMI class loader disabled)".

 

 

The error message means that the client was unable to find and download the stub class from its current location, because no security manager was in place to control the access. There are two ways to solve this problem: (1) copy the class file to the client directory, or (2) add a security manager to your code. The second is the preferred solution in a distributed environment and therefore, will be demonstrated next.

 

 

[NO Demo!]

  1. Add a security manager to your client code, using the following code in the main method:

    System.setSecurityManager(new java.rmi.RMISecurityManager());

    N.b. Alternatively you can add a secuirty manager at the command prompt using:

    >java -Djava.security.manager sharesclient

  2. You now need to specify a policy file to inform the security manager which permission to grant. A sample policy file is provided: client.policy. You also need to specify the codebase of the required stub file. These two tasks are accomplished with the following command:

    H:/RMI/Client>java -Djava.security.policy==client.policy
    -Djava.rmi.server.codebase=file:/H:\RMI\Server\/ sharesclient

 

 

 

Activation To find out more about using the security manager and policy files:
1. Security, Overview of Security in JDK 1.2
2. Policy Files in JDK 1.2

 

 

8.2 Authentication

Presently, it is possible for any client to call any of the methods available from the Share service. Your task is to ensure that only authentic users are allowed to add new shares to the remote service. To do this you must add a simple authentication protocol that is utilised when a client attempts to add a share. You may implement any authentication protocol that you feel is suitable, however, the following pseudocode is given as a sample (N.b. it is not a completely secure method and should not be treated as such).

 

Pseudocode for simple authentication

 

Additions to the server side:

  1. Add a data structure to the remote shares object that stores a list of authentic user identities and their corresponding private encryption keys.
  2. Add a new method to the shares object that delivers an authentication "ticket", for example:

    String getTicket(ID){

    // Test to see if the ID exists - throw exception if doesn't
    if (listOfUsers contains(ID)) throw new RemoteException("User does not exist");
    //retrieve Private key of user
    Key = ListofUsers(ID).key
    // Encrypt the sent Authentication code with the local server key (hint: reuse - caesar)
    TempTicket = Encrypt(ID, Server_Key);
    // Encrypt the encrypted message with the user's private key and return it to them
    return Encrypt(TempTicket, Key);

    }

     

  3. Extend the AddNewShare method to take an extra parameter (ValidTicket) and then test within the method that the ticket is valid before completing the new share operation:

    void AddNewShare(Name, Price, Code, Ticket){

    // Decrypt ticket
    String User = Decrypt(Ticket, Server_Key);
    //The decrypted ticket should hold an ID - test to see if it is an authentic user
    if (ListOfUsers doesn't contain User) throw new RemoteException("Unauthorised User");
    // Otherwise Add new share
    ....

    }

 

Additions to the client side:

  1. In the client, before calling the AddNewShare method a ticket must be obtained. To do this:
    • Call the getTicket method with the clients identity;
    • Decrypt the returned string with the client's private key to obtain the ticket;
    • Call the AddNewSharemethod with the obtained ticket.

Valid HTML 4.01!