CPAN423 Enterprise Java Programming

Lecture #6:Security

Security is one of the most important aspects of the Internet and web application development. The following issues must be addressed in perform a secure and successful transaction:

  • Privacy: ensuring that data cannot be accessed by unauthorized parties.
  • Integrity: Make sure that data is exchanged without altering or compromising.
  • Authentication: Prove the identity of the sender and receiver of information over the internet.
    Authorization: Allows users to access certain resources.
  • Nonrepudiation: Prove legally that a message has been sent or received.

In this lecture we will focus on authentication, authorization, and Secure Socket Layer. Also we will cover password based encryption.

Authentication and Authorization with Resin Web Server:

Resin can be configured to authenticate the identity of its users and securing resources so that only authorized users can access them.

The XmlAuthenticator class in Resin provides you with a username/password combination to protect your application. For example, assume that we have to provide a password protection to the web application cpan423. We need to add the following configuration to web.xml inside cpan423:

<!-- XmlAuthentication -->

<authenticator>

<type>com.caucho.http.security.XmlAuthenticator</type>

<init>

<user>muthana:ali:user</user>

<user>john:smith:user</user>

</init>

</authenticator>

<login-config auth-method='basic'/>

<security-constraint url-pattern='/*' role-name='user'/>

The element security-constaint is used for authorization by limiting access to resources on the server to certain user(s)

Instead of putting the user name/password/role combinations are hard coded in the configuration file, we can put them in a separate XML file:

<!-- XmlAuthentication in a file -->

<authenticator>

<type>com.caucho.http.security.XmlAuthenticator</type>

<init>

<path>WEB-INF\password.xml</path>

<password-digest>none</password-digest>

</init>

</authenticator>

<login-config auth-method='basic'/>

<security-constraint url-pattern='/*' role-name='user'/>

Then password.xml can have the following content:

<authenticator>

<user name="muthana" password="ali" role="user" />

<user name="john" password="smith" role="user" />

</authenticator>

The above configurations will cause the server to display a login popup dialog for users trying to access the cpan423 site. Instead of the popup dialog, we can use a custom form to authenticate the users:

<!-- XmlAuthentication using used defined form -->

<authenticator>

<type>com.caucho.http.security.XmlAuthenticator</type>

<init>

<path>WEB-INF\password.xml</path>

<password-digest>none</password-digest>

<logout-on-session-timeout>true </logout-on-session-timeout>

</init>

</authenticator>

<login-config auth-method='form'>

<form-login-config form-login-page='/login.html'

form-error-page='/error.html'/>

</login-config>

<security-constraint url-pattern='/*' role-name='user'/>

login.html should be defined as follows:

<form action='j_security_check' method='POST'>

<table>

<tr<td>User:<td<input name='j_username'>

<tr<td>Password:<td<input name='j_password'>

<tr<td colspan=2>

<input type="hidden" name="j_uri" value="browsers.jsp"/>

<tr<td<input type=submit>

</table>

</form>

Putting the user login in an XML file may not be preferred by some developer. Resin provides the JdbcAUthenticator class to retrieve the user name/password/role from a database table.

To use the JdbcAuthenticator, first we have to create the following table under Oracle database server on MUNRO:

create table login (username varchar(250) primary key, password varchar(250),cookie varchar(250), role varchar(250));

Then we should add the following to web.xml:

<database jndi-name="jdbc/test">

<driver type="oracle.jdbc.driver.OracleDriver">

<url>jdbc:oracle:thin:@munro.humber.ca:1521:msit</url>

<user>user</user>

<password>password</password>

</driver>

</database>

<!-- Resin-specific JdbcAuthenticator -->

<authenticator type='com.caucho.server.security.JdbcAuthenticator'>

<init>

<data-source>jdbc/test</data-source>

<password-query>

SELECT password FROM login WHERE username=?

</password-query>

<cookie-auth-query>

SELECT username FROM login WHERE cookie=?

</cookie-auth-query>

<cookie-auth-update>

UPDATE login SET cookie=? WHERE username=?

</cookie-auth-update>

<role-query>

SELECT role FROM login WHERE username=?

</role-query>

</init>

</authenticator>

<login-config auth-method="basic">

<realm-name>resin</realm-name>

</login-config>

<security-constraint url-pattern='/*' role-name='user'/>

The passwords in the previous configuration are stored in text format. We can encrypt the password before we store it in the database table. Resin also provides us with the class com.cuacho.http.security.PasswordDigest to generate an encrypted password. Following is a Servlet that can be used to generate encrypted password using this class:

import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*;

import java.sql.*;

import javax.naming.*;

import javax.sql.*;

import com.caucho.http.security.PasswordDigest;

public class addUser extends HttpServlet

{

DataSource ds;

Connection con;

Statement stmt;

public void init() throws ServletException

{

try

{

Context ic = (Context) new InitialContext();

ds = (DataSource) ic.lookup("java:comp/env/jdbc/test");

}

catch (Exception e)

{

}

}

public void doPost (HttpServletRequest req, HttpServletResponse res)

throws ServletException, IOException

{

PrintWriter out = res.getWriter();

try

{

stmt = con.createStatement();

String id = req.getParameter("ID");

String password = req.getParameter("password");

String role = req.getParameter("role");

PasswordDigest pd=new PasswordDigest();

pd.setAlgorithm("MD5");

pd.setFormat("base64");

pd.setRealm("resin");

String encPassword=pd.getPasswordDigest(id,password);

String sql= "insert into login values (' "+id +"', '"+encPassword+"',null,'"+role+"')" ;

int rsOut = stmt.executeUpdate(sql);

}

catch (Exception e)

{

out.println("Exception" + e );

}

}

public void destroy()

{

try

{

con.close();

}

catch(SQLException sle)

{}

}

}

And following is an HTML form called addUser.html used to invoke the above servlet:

<form action=' method='POST'>

<table>

<tr<td>User ID:</td<td<input name='ID'</td</tr>

<tr<td>User Password:</td<td<input name='password'</td</tr>

<tr<td>User Role:</td<td<input name='role'</td</tr>

<tr<td colspan=2>

<tr<td<input type=submit>\

</table>

</form>

You need to add the following to web.xml:

<database jndi-name="jdbc/test">

<driver type="oracle.jdbc.driver.OracleDriver">

<url>jdbc:oracle:thin:@munro.humber.ca:1521:msit</url>

<user>user</user>

<password>password</password>

</driver>

</database>

<!-- Resin-specific JdbcAuthenticator -->

<authenticator type='com.caucho.server.security.JdbcAuthenticator'>

<init>

<data-source>jdbc/test</data-source>

<password-query>

SELECT password FROM login WHERE username=?

</password-query>

<cookie-auth-query>

SELECT username FROM login WHERE cookie=?

</cookie-auth-query>

<cookie-auth-update>

UPDATE login SET cookie=? WHERE username=?

</cookie-auth-update>

<role-query>

SELECT role FROM login WHERE username=?

</role-query>

</init>

</authenticator>

<login-config auth-method="digest">

<realm-name>resin</realm-name>

</login-config>

<security-constraint url-pattern='/*' role-name='user'/>

<servlet>

<servlet-name>addUser</servlet-name>

<servlet-class>addUser</servlet-class>

</servlet>

<servlet-mapping>

<url-pattern>/add</url-pattern>

<servlet-name>addUser</servlet-name>

</servlet-mapping>

This approach will provide the user with a popup dialog to login. We can use the following configuration to provide a custom login form:

<database jndi-name="jdbc/test">

<driver type="oracle.jdbc.driver.OracleDriver">

<url>jdbc:oracle:thin:@munro.humber.ca:1521:msit</url>

<user>user</user>

<password>password</password>

</driver>

</database>

<!-- Resin-specific JdbcAuthenticator with user defined form -->

<authenticator type='com.caucho.server.security.JdbcAuthenticator'>

<init>

<data-source>jdbc/test</data-source>

<password-query>

SELECT password FROM login WHERE username=?

</password-query>

<cookie-auth-query>

SELECT username FROM login WHERE cookie=?

</cookie-auth-query>

<cookie-auth-update>

UPDATE login SET cookie=? WHERE username=?

</cookie-auth-update>

<role-query>

SELECT role FROM login WHERE username=?

</role-query>

</init>

</authenticator>

<login-config auth-method='form'>

<form-login-config form-login-page='/login.html'

form-error-page='/error.html'/>

</login-config>

<security-constraint url-pattern='/*' role-name='user'/>

<servlet>

<servlet-name>addUser</servlet-name>

<servlet-class>addUser</servlet-class>

</servlet>

<servlet-mapping>

<url-pattern>/add</url-pattern>

<servlet-name>addUser</servlet-name>

</servlet-mapping>

Where login.html and error.html are defined earlier.

Password Based Encryption:

Java Cryptography Extension (JCE) provides many security utilities and supports many encryption algorithms. In this section we will focus on Password based Encryption. This kind of encryption utilizes an array of bytes called a slat and an integer number to randomize the generated key(s).

Following is a servlet used to add an encrypted password to a database table under Oracle database server on MUNRO:

import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*;

import java.sql.*;

import javax.naming.*;

import javax.sql.*;

import java.security.*;

import java.security.spec.*;

import com.sun.crypto.provider.SunJCE;

import javax.crypto.*;

import javax.crypto.spec.*;

/*

This example uses password based encryption provided by the

java Cryptography extension

*/

public class addENCUser extends HttpServlet

{

DataSource ds;

Connection con;

Statement stmt;

/*

PBE implementations will mix in a random number, known as a salt, to create the key.

salt must be 8 bytes long

*/

private static final byte[] salt =

{ ( byte )0xf5, ( byte )0x33, ( byte )0x01, ( byte )0x2a,

( byte )0xb2, ( byte )0xcc, ( byte )0xe4, ( byte )0x7f

};

// iteration count

private int iterationCount = 100;

public void init() throws ServletException

{

//Add a provider to the next position available.

Security.addProvider( new SunJCE() );

try

{

Context ic = (Context) new InitialContext();

ds = (DataSource) ic.lookup("java:comp/env/jdbc/test");

}

catch (Exception e)

{

}

}

public void doPost (HttpServletRequest req, HttpServletResponse res)

throws ServletException, IOException

{

PrintWriter out = res.getWriter();

try

{

// get a connection from the DataSource

con=ds.getConnection();

stmt = con.createStatement();

// get the ID and password from the client

String userID = req.getParameter("ID");

String userPassword = req.getParameter("password");

// a user defined encryption password

String passEnc="cpan423";

Cipher cipher = null;

// create a user-chosen password that can be used with

// password-based encryption (PBE).

PBEKeySpec keySpec =

new PBEKeySpec( passEnc.toCharArray() );

// create a secret key factory object

SecretKeyFactory keyFactory =

SecretKeyFactory.getInstance( "PBEWithMD5AndDES" );

// create a s secret key for encryption

SecretKey secretKey = keyFactory.generateSecret( keySpec );

// specifies parameters used with password based encryption

PBEParameterSpec parameterSpec =

new PBEParameterSpec( salt, iterationCount );

// obtain cipher instance reference

cipher = Cipher.getInstance( "PBEWithMD5AndDES" );

// initialize cipher in encrypt mode

cipher.init( Cipher.ENCRYPT_MODE, secretKey,

parameterSpec );

// create array of bytes

byte[] outputArray = null;

outputArray = userPassword.getBytes( "ISO-8859-1" );

ByteArrayOutputStream test=new ByteArrayOutputStream();

/* Xreate CipherOutputStream to stream the password to

The ByteAeeayOutputStream test

*/

CipherOutputStream outC =

new CipherOutputStream( test, cipher );

outC.write( outputArray );

outC.flush();

outC.close();

//store the encrypted password in a string

String str=test.toString();

// insert a new record to the database with the encrypted password

String sql= "insert into myusers values ("+userID +", '"+str+"')" ;

int rsOut = stmt.executeUpdate(sql);

out.println("Record was inserted successfully");

}

catch (Exception e)

{

out.println("Exception" + e );

}

}

public void destroy()

{

try

{

con.close();

}

catch(SQLException sle)

{}

}

}

Where myuser table is created using the following statement:

create table myusers (idnumber primary key,password varchar(250))

And following is an HTML form called addUserENC.html used to pass the user ID and password to the servet:

<form action=' method='POST'>

<table>

<tr<td>User ID:</td<td<input name='ID'</td</tr>

<tr<td>User Password:</td<td<input name='password'</td</tr>

<tr<td colspan=2>

<tr<td<input type=submit>

</table>

</form>

Remember that we need to add the following to the web.xml:

<database jndi-name="jdbc/test">

<driver type="oracle.jdbc.driver.OracleDriver">

<url>jdbc:oracle:thin:@munro.humber.ca:1521:msit</url>

<user>user</user>

<password>password</password>

</driver>

</database>

<servlet>

<servlet-name>addUserENC</servlet-name>

<servlet-class>addENCUser</servlet-class>

</servlet>

<servlet-mapping>

<url-pattern>/addUserENC</url-pattern>

<servlet-name>addUserENC</servlet-name>

</servlet-mapping>

If you have the database configuration tag already added to the web.xml, you do not have to repeat it.

The following is servlet to check the user login against the user ID and the encrypted password stored under Oracle database server:

import javax.servlet.*;

import javax.servlet.http.*;

import java.io.*;

import java.sql.*;

import javax.naming.*;

import javax.sql.*;

import java.util.*;

import java.security.*;

import java.security.spec.*;

import com.sun.crypto.provider.SunJCE;

import javax.crypto.*;

import javax.crypto.spec.*;

/*

This example uses password based decryption provided by the

java Cryptography extension

*/

public class loginENC extends HttpServlet

{

DataSource ds;

Connection con;

Statement stmt;

/*

PBE implementations will mix in a random number, known as a salt, to create the key.

salt must be 8 bytes long

*/

private static final byte[] salt =

{ ( byte )0xf5, ( byte )0x33, ( byte )0x01, ( byte )0x2a,

( byte )0xb2, ( byte )0xcc, ( byte )0xe4, ( byte )0x7f

};

// iteration count

private int iterationCount = 100;

public void init() throws ServletException

{

//Add a provider to the next position available.

Security.addProvider( new SunJCE() );

try

{

Context ic = (Context) new InitialContext();

ds = (DataSource) ic.lookup("java:comp/env/jdbc/test");

}

catch (Exception e)

{

}

}

public void doPost (HttpServletRequest req, HttpServletResponse res)

throws ServletException, IOException

{

PrintWriter out = res.getWriter();

try

{

// get a connection

con=ds.getConnection();

stmt = con.createStatement();

// get the user ID /password from the client

String userID = req.getParameter("ID");

String userPassword = req.getParameter("password");

// a user defined encryption password

String passEnc="cpan423";

// query the database

String sql="select * from myusers where id= "+userID;

ResultSet rs=stmt.executeQuery(sql);

String str=null;

while (rs.next())

str=rs.getString(2);

// check the result

if (str !=null)

{

Vector fileBytes = new Vector();

// create secret key

Cipher cipher = null;

// create a user-chosen password that can be used with

// password-based encryption (PBE).

PBEKeySpec keySpec =

new PBEKeySpec( passEnc.toCharArray() );

// create a secret key factory object

SecretKeyFactory keyFactory =

SecretKeyFactory.getInstance( "PBEWithMD5AndDES" );

// generate secret key for encryption

SecretKey secretKey = keyFactory.generateSecret( keySpec );

// specifies parameters used with password based encryption

PBEParameterSpec parameterSpec =

new PBEParameterSpec( salt, iterationCount );

// obtain cipher instance reference.

cipher = Cipher.getInstance( "PBEWithMD5AndDES" );

// initialize cipher in decrypt mode

cipher.init( Cipher.DECRYPT_MODE, secretKey,

parameterSpec );

// creat a ByteArrayInputStream object

ByteArrayInputStream testIN=new ByteArrayInputStream(str.getBytes());

/*

create a CipherInputStream to decrypt the password to the ByteArrayInputStream object

*/

CipherInputStream in =

new CipherInputStream( testIN, cipher );

byte contents = ( byte ) in.read();

while ( contents != -1 )

{

fileBytes.add( new Byte( contents ) );

contents = ( byte ) in.read();

}

in.close();

byte[] decryptedText = new byte[ fileBytes.size() ];

for ( int i = 0; i < fileBytes.size(); i++ )

{

decryptedText[ i ] =

( ( Byte )fileBytes.elementAt( i ) ).byteValue();

}

// convert the decrypted password to a string

String passwordDEC=new String( decryptedText );

// compare the password

if (userPassword.equals(passwordDEC))

{ out.println("login successful");

}

else

{

out.println("login unsuccessful");

}

}

else

out.println("No match found");

}

catch (Exception e)

{

out.println("Exception" + e );

}

}

public void destroy()

{

try

{

con.close();

}

catch(SQLException sle)

{}

}

}

And following is an HTML form called loginENC.html used by the user to login:

<form action=' method='POST'>

<table>

<tr<td>User ID:</td<td<input name='ID'</td</tr>

<tr<td>User Password:</td<td<input name='password'</td</tr>

<tr<td<input type=submit>

</table>

</form>

The following must be added to the web.xml to configure our servlet:

<database jndi-name="jdbc/test">

<driver type="oracle.jdbc.driver.OracleDriver">

<url>jdbc:oracle:thin:@munro.humber.ca:1521:msit</url>

<user>user</user>

<password>password</password>

</driver>

</database>

<servlet>

<servlet-name>loginENC</servlet-name>

<servlet-class>loginENC</servlet-class>

</servlet>

<servlet-mapping>

<url-pattern> /loginENC</url-pattern>

<servlet-name>loginENC</servlet-name>

</servlet-mapping>

Secured Socket Layer and Resin Web Server:

Secured Sockets Layer is a protocol that enables encrypted, authenticated communications across the Internet. It was developed by Netscape Communications to secure communications over the internet. SSL encrypts the communication between browser and Web server so only the intended parties can read certain data.

To configure SSL with resin, perform the following:

1-Use Java’s Secure Socket Extension tool keytool to build a test server certificate as shown below:

The keytool is available as part of J2sdk1.4 or later versions.

2-Copy the file server.keystore from j2sdk1.4.2/bin to a folder called key under resin’s main folder.

3-Add the following to the resin configuration file:

<http port="8443">

<jsse-ssl>

<key-store-file>keys/server.keystore</key-store-file>

<password>password</password>

</jsse-ssl>

</http>

4- Restart Resin and type the following in the browser’s URL:

1