Implemented securing Authentication and Session Tokens using

WS-Security.
This commit is contained in:
Juan Carlos Luciani
2006-08-18 17:48:34 +00:00
parent 2ec99203a2
commit b06912c332
29 changed files with 711 additions and 652 deletions

View File

@@ -256,6 +256,11 @@ public class AuthReqMsg
m_state = AWAITING_REALM_ELEMENT_END;
break;
case AWAITING_REALM_ELEMENT_END:
// Consume the data
m_authReqMsg.m_realm = m_authReqMsg.m_realm.concat(new String(ch, start, length));
break;
case AWAITING_MECH_DATA:
// Consume the data
m_authReqMsg.m_authMechanism = new String(ch, start, length);
@@ -264,6 +269,11 @@ public class AuthReqMsg
m_state = AWAITING_MECH_ELEMENT_END;
break;
case AWAITING_MECH_ELEMENT_END:
// Consume the data
m_authReqMsg.m_authMechanism = m_authReqMsg.m_authMechanism.concat(new String(ch, start, length));
break;
case AWAITING_AUTH_MECH_TOKEN_DATA:
// Consume the data
m_authReqMsg.m_authMechToken = new String(ch, start, length);
@@ -272,6 +282,11 @@ public class AuthReqMsg
m_state = AWAITING_AUTH_MECH_TOKEN_ELEMENT_END;
break;
case AWAITING_AUTH_MECH_TOKEN_ELEMENT_END:
// Consume the data
m_authReqMsg.m_authMechToken = m_authReqMsg.m_authMechToken.concat(new String(ch, start, length));
break;
default:
// Do nothing
break;

View File

@@ -26,20 +26,26 @@ package com.novell.casa.authtoksvc;
import java.io.ByteArrayInputStream;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.client.AxisClient;
import org.apache.axis.configuration.NullProvider;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.axis.message.SOAPBody;
import org.apache.axis.message.MessageElement;
import javax.xml.namespace.QName;
import java.io.*;
/*
* AuthToken Class.
*
* This class constructs authentication tokens that clients can present
* to services for authentication. The format of the authentication token
* is as follows:
*
* <?xml version="1.0" encoding="ISO-8859-1"?>
* to services for authentication. The authentication token consists of
* a SOAP message secured with WSSecurity with the appropriate elements signed
* and with a timestamp. The body of the SOAP message is as follows:
*
* <auth_token>
* <signature>signature value</signature>
* <lifetime>lifetime value</lifetime>
@@ -51,280 +57,23 @@ public class AuthToken
{
private String m_token;
private String m_lifetime;
private String m_lifetimeShorter;
private String m_identityTokenType;
private StringBuffer m_identityToken;
private String m_signature;
private String m_lifetime = "";
private String m_lifetimeShorter = "";
private String m_identityTokenType = null;
private String m_identityToken = null;
/*
* Class for handling parsing events.
*/
private class SAXHandler extends org.xml.sax.helpers.DefaultHandler
{
private final static int AWAITING_ROOT_ELEMENT_START = 0;
private final static int AWAITING_ROOT_ELEMENT_END = 1;
private final static int AWAITING_SIGNATURE_ELEMENT_START = 2;
private final static int AWAITING_SIGNATURE_ELEMENT_END = 3;
private final static int AWAITING_SIGNATURE_DATA = 4;
private final static int AWAITING_LIFETIME_ELEMENT_START = 5;
private final static int AWAITING_LIFETIME_ELEMENT_END = 6;
private final static int AWAITING_LIFETIME_DATA = 7;
private final static int AWAITING_IDENT_TOKEN_ELEMENT_START = 8;
private final static int AWAITING_IDENT_TOKEN_ELEMENT_END = 9;
private final static int AWAITING_IDENT_TOKEN_DATA = 10;
private final static int AWAITING_TYPE_ELEMENT_START = 11;
private final static int AWAITING_TYPE_ELEMENT_END = 12;
private final static int AWAITING_TYPE_DATA = 13;
private final static int DONE_PARSING = 14;
static final String authTokenSoapMsg =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<SOAP-ENV:Envelope" +
" xmlns:SOAP-ENV=\"http://www.w3.org/2003/05/soap-envelope\"\n" +
" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
" <SOAP-ENV:Body>" +
" <auth_token><ident_token><type></type></ident_token></auth_token>" +
" </SOAP-ENV:Body>" +
"</SOAP-ENV:Envelope>";
private AuthToken m_AuthToken;
private int m_state;
/*
* Constructor
*/
public SAXHandler (AuthToken AuthToken)
{
super();
// Initialize our members
m_AuthToken = AuthToken;
m_state = AWAITING_ROOT_ELEMENT_START;
}
/*
* endDocument() implementation.
*/
public void endDocument () throws SAXException
{
// Verify that we obtained all of the required elements
if (m_state != DONE_PARSING)
{
System.err.println("AuthToken SAXHandler.endDocument()- Missing element");
throw new SAXException("Missing element");
}
}
/*
* startElement() implementation.
*/
public void startElement (String uri, String name, String qName, org.xml.sax.Attributes atts) throws SAXException
{
// Proceed based on our state
switch (m_state)
{
case AWAITING_ROOT_ELEMENT_START:
// Verify that we are processing the expected tag
if (ProtoDefs.authTokenElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_SIGNATURE_ELEMENT_START;
}
else
{
System.err.println("AuthToken SAXHandler.startElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_SIGNATURE_ELEMENT_START:
// Verify that we are processing the expected tag
if (ProtoDefs.signatureElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_SIGNATURE_DATA;
}
else
{
System.err.println("AuthToken SAXHandler.startElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_LIFETIME_ELEMENT_START:
// Verify that we are processing the expected tag
if (ProtoDefs.lifetimeElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_LIFETIME_DATA;
}
else
{
System.err.println("AuthToken SAXHandler.startElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_IDENT_TOKEN_ELEMENT_START:
// Verify that we are processing the expected tag
if (ProtoDefs.identTokenElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_TYPE_ELEMENT_START;
}
else
{
System.err.println("AuthToken SAXHandler.startElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_TYPE_ELEMENT_START:
// Verify that we are processing the expected tag
if (ProtoDefs.typeElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_TYPE_DATA;
}
else
{
System.err.println("AuthToken SAXHandler.startElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
default:
System.err.println("AuthToken SAXHandler.startElement()- State error");
throw new SAXException("State error");
}
}
/*
* endElement() immplementation.
*/
public void endElement (String uri, String name, String qName) throws SAXException
{
// Proceed based on our state
switch (m_state)
{
case AWAITING_ROOT_ELEMENT_END:
// Verify that we are processing the expected tag
if (ProtoDefs.authTokenElementName.equals(qName))
{
// Advance to the next state
m_state = DONE_PARSING;
}
else
{
System.err.println("AuthToken SAXHandler.endElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_SIGNATURE_ELEMENT_END:
// Verify that we are processing the expected tag
if (ProtoDefs.signatureElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_LIFETIME_ELEMENT_START;
}
else
{
System.err.println("AuthToken SAXHandler.endElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_LIFETIME_ELEMENT_END:
// Verify that we are processing the expected tag
if (ProtoDefs.lifetimeElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_IDENT_TOKEN_ELEMENT_START;
}
else
{
System.err.println("AuthToken SAXHandler.endElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_TYPE_ELEMENT_END:
// Verify that we are processing the expected tag
if (ProtoDefs.typeElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_IDENT_TOKEN_DATA;
}
else
{
System.err.println("AuthToken SAXHandler.endElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_IDENT_TOKEN_ELEMENT_END:
// Verify that we are processing the expected tag
if (ProtoDefs.identTokenElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_ROOT_ELEMENT_END;
}
else
{
System.err.println("AuthToken SAXHandler.endElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
default:
System.err.println("AuthToken SAXHandler.startElement()- State error");
throw new SAXException("State error");
}
}
/*
* character() implementation.
*/
public void characters (char ch[], int start, int length) throws SAXException
{
// Proceed based on our state
switch (m_state)
{
case AWAITING_SIGNATURE_DATA:
// Consume the data
m_AuthToken.m_signature = new String(ch, start, length);
// Advance to the next state
m_state = AWAITING_SIGNATURE_ELEMENT_END;
break;
case AWAITING_LIFETIME_DATA:
// Consume the data
m_AuthToken.m_lifetime = new String(ch, start, length);
// Advance to the next state
m_state = AWAITING_LIFETIME_ELEMENT_END;
break;
case AWAITING_TYPE_DATA:
// Consume the data
m_AuthToken.m_identityTokenType = new String(ch, start, length);
// Advance to the next state
m_state = AWAITING_TYPE_ELEMENT_END;
break;
case AWAITING_IDENT_TOKEN_DATA:
case AWAITING_IDENT_TOKEN_ELEMENT_END:
// Consume the data
m_AuthToken.m_identityToken.append(ch, start, length);
// Advance to the next state
m_state = AWAITING_IDENT_TOKEN_ELEMENT_END;
break;
default:
// Do nothing
break;
}
}
}
static final private MessageContext axisMsgContext = new MessageContext(new AxisClient(new NullProvider()));
/*
* Constructor.
@@ -354,37 +103,28 @@ public class AuthToken
targetHost,
svcConfig);
m_identityToken = new StringBuffer();
m_identityToken.append(identityToken.getEncodedToken());
m_identityToken = identityToken.getEncodedToken();
m_identityTokenType = identityToken.getProviderType();
m_lifetime = authTokenConfig.getSetting(AuthTokenConfig.TokenLifetime);
m_lifetimeShorter = authTokenConfig.getSetting(AuthTokenConfig.LifetimeShorter);
// Generate a signature
// tbd - Over identToken, identToken type, and lifetime data.
m_signature = "tbd";
// Create AuthTokenMessage
Message authTokenMessage = getMessage(identityToken.getEncodedToken(),
identityToken.getProviderType(),
Integer.valueOf(m_lifetime).intValue(),
svcConfig);
// Get a StringBuffer to help us with the construction of the token
StringBuffer sb = new StringBuffer();
// Start building the message
sb.append(ProtoDefs.xmlDeclaration + "\r\n");
sb.append("<" + ProtoDefs.authTokenElementName + ">" + "\r\n");
sb.append("<" + ProtoDefs.signatureElementName + ">" + m_signature + "</" + ProtoDefs.signatureElementName + ">" + "\r\n");
sb.append("<" + ProtoDefs.lifetimeElementName + ">" + m_lifetime + "</" + ProtoDefs.lifetimeElementName + ">" + "\r\n");
sb.append("<" + ProtoDefs.identTokenElementName + ">"
+ "<" + ProtoDefs.typeElementName + ">" + m_identityTokenType + "</" + ProtoDefs.typeElementName + ">"
+ m_identityToken + "</" + ProtoDefs.identTokenElementName + ">" + "\r\n");
sb.append("</" + ProtoDefs.authTokenElementName + ">" + "\r\n");
// Save the token
m_token = sb.toString();
// Now save the message as a string
OutputStream outStream = new ByteArrayOutputStream();
authTokenMessage.writeTo(outStream);
m_token = outStream.toString();
outStream.close();
}
catch (Exception e)
{
// tbd
System.err.println("AuthToken()- Exception: " + e.toString());
System.err.println("AuthToken()- Exception: " + e.toString());
}
}
else
@@ -402,33 +142,102 @@ public class AuthToken
// Decode the token string
m_token = Base64Coder.decode(token);
// Instantiate string buffer for the identity token
m_identityToken = new StringBuffer();
// Now instantiate a SOAP message with the string
InputStream inStream = new ByteArrayInputStream(m_token.getBytes());
Message message = new Message(inStream);
// Get access to the SOAP Envelope
SOAPEnvelope envelope = message.getSOAPEnvelope();
// Verify the message
if (WSSecurity.verifyMessage(envelope))
{
// Message verification succeded, now obtain the identity token
// and its type from the message body.
SOAPBody body = (SOAPBody) envelope.getBody();
QName authTokenElementName = new QName("auth_token");
MessageElement authTokenElement = body.getChildElement(authTokenElementName);
QName identTokenElementName = new QName("ident_token");
MessageElement identTokenElement = authTokenElement.getChildElement(identTokenElementName);
if (identTokenElement != null)
{
QName identTokenTypeElementName = new QName("type");
MessageElement identTokenTypeElement = identTokenElement.getChildElement(identTokenTypeElementName);
if (identTokenTypeElement != null)
{
m_identityToken = identTokenElement.getChildNodes().item(1).getNodeValue();
m_identityTokenType = identTokenTypeElement.getValue();
}
}
if (m_identityToken == null || m_identityTokenType == null)
{
System.out.println("AuthToken()- Required data missing from authentication token");
throw new Exception("Error: Required data missing from Authentication Token");
}
}
else
{
// Message verification failed
System.err.println("AuthToken()- Invalid Authentication Token");
throw new Exception("Invalid Authentication Token");
}
}
/**
* Get AuthToken SOAP Message
*
* @param identityToken String containing the identity token that should be part of the message
* @param identityTokenType String containing the identity token type
* @param lifetime Lifetime that should be specified in the message timestamp (seconds)
* @param svcConfig Service configuratio object
* @return <code>Message<code> AuthToken message, null if the method fails.
*/
private Message getMessage(String identityToken,
String identityTokenType,
int lifetime,
SvcConfig svcConfig)
{
Message secureMessage;
// Now parse the token into its elements
try
{
// Parse the AuthToken
XMLReader xr = XMLReaderFactory.createXMLReader();
SAXHandler handler = new SAXHandler(this);
xr.setContentHandler(handler);
xr.setErrorHandler(handler);
// Build SOAP Message with an identity token in the body
//
// First create a message and obtain its body
InputStream inStream = new ByteArrayInputStream(authTokenSoapMsg.getBytes());
Message message = new Message(inStream);
message.setMessageContext(axisMsgContext);
SOAPBody body = (SOAPBody) message.getSOAPBody();
ByteArrayInputStream inStream = new ByteArrayInputStream(m_token.getBytes());
InputSource source = new InputSource(inStream);
xr.parse(source);
// Get access to the auth_token element
QName authTokenElementName = new QName("auth_token");
MessageElement authTokenElement = body.getChildElement(authTokenElementName);
// Verify the signature
// tbd
// Get access to the ident_token element and set its value
QName identTokenElementName = new QName("ident_token");
MessageElement identTokenElement = authTokenElement.getChildElement(identTokenElementName);
identTokenElement.addTextNode(identityToken);
// Verify that the token has not expired
// tbd
// Get access to the identity token type element element and set its value
QName identTokenTypeElementName = new QName("type");
MessageElement identTokenTypeElement = identTokenElement.getChildElement(identTokenTypeElementName);
identTokenTypeElement.setValue(identityTokenType);
// Now we need to secure the SOAP message that we created, we are doing to
// do so by adding a timestamp and signing the timestamp as well as the body.
// To do this we are going to leverage WS-Security.
secureMessage = WSSecurity.secureSOAPEnvelope(message.getSOAPEnvelope(),
lifetime,
svcConfig);
}
catch (SAXException e)
catch (Exception e)
{
System.err.println("AuthToken()- Parse exception: " + e.toString());
throw new Exception("Protocol error");
System.out.println("AuthToken.getMessage() - Exception caught building message, error: " + e.getMessage());
secureMessage = null;
}
return secureMessage;
}
/*
@@ -441,11 +250,20 @@ public class AuthToken
/*
* Returns the lifetime of the token.
*
* Note: It is only valid to execute this procedure if its called on an object
* instantiated via the constructor which takes a lifetime parameter.
*/
public String getLifetime()
public String getLifetime() throws Exception
{
// tbd - Convert to tokenLifetime and lifetimeShorter to ints, substractand then convert result to string
return "60";
// Throw exeption if the lifetime parameter is not set
if (m_lifetime.length() == 0)
{
System.out.println("AuthToken.getLifetime() - Called when lifetime is not set");
throw new Exception("Error: Called getLifetime while not set");
}
return Integer.toString(Integer.valueOf(m_lifetime).intValue() - Integer.valueOf(m_lifetimeShorter).intValue());
}
/*
@@ -453,7 +271,7 @@ public class AuthToken
*/
public String getIdentityToken()
{
return m_identityToken.toString();
return m_identityToken;
}
/*

View File

@@ -281,16 +281,16 @@ public class Authenticate implements RpcMethod
// An identity was resolved, get a SessionToken for it.
SessionToken sessionToken = new SessionToken(identId,
authReqMsg.getRealm(),
m_svcConfig.getSetting(SvcConfig.SessionTokenLifetime));
m_svcConfig.getSetting(SvcConfig.SessionTokenLifetime),
m_svcConfig);
// Write out the response
String respLifetime = Integer.toString(Integer.valueOf(m_svcConfig.getSetting(SvcConfig.SessionTokenLifetime)).intValue()
- Integer.valueOf(m_svcConfig.getSetting(SvcConfig.LifetimeShorter)).intValue());
AuthRespMsg authRespMsg = new AuthRespMsg(ProtoDefs.httpOkStatusMsg,
ProtoDefs.httpOkStatusCode,
sessionToken.toString(),
m_svcConfig.getSetting(SvcConfig.SessionTokenLifetime));
// tbd - Convert to ints, perform calculation, and then convert result to string
//m_svcConfig.getSetting(SvcConfig.SessionTokenLifetime)
//- m_svcConfig.getSetting(SvcConfig.LifetimeShorter));
respLifetime);
out.println(authRespMsg.toString());
}
else

View File

@@ -102,7 +102,6 @@ public class GetAuthPolicyReqMsg
// Proceed based on our state
switch (m_state)
{
case AWAITING_ROOT_ELEMENT_START:
// Verify that we are processing the expected tag
if (ProtoDefs.getAuthPolicyRequestElementName.equals(qName))
@@ -160,7 +159,6 @@ public class GetAuthPolicyReqMsg
// Proceed based on our state
switch (m_state)
{
case AWAITING_ROOT_ELEMENT_END:
// Verify that we are processing the expected tag
if (ProtoDefs.getAuthPolicyRequestElementName.equals(qName))
@@ -217,7 +215,6 @@ public class GetAuthPolicyReqMsg
// Proceed based on our state
switch (m_state)
{
case AWAITING_SERVICE_DATA:
// Consume the data
m_GetAuthPolicyReqMsg.m_serviceName = new String(ch, start, length);
@@ -226,6 +223,11 @@ public class GetAuthPolicyReqMsg
m_state = AWAITING_SERVICE_ELEMENT_END;
break;
case AWAITING_SERVICE_ELEMENT_END:
// Consume the data
m_GetAuthPolicyReqMsg.m_serviceName = m_GetAuthPolicyReqMsg.m_serviceName.concat(new String(ch, start, length));
break;
case AWAITING_HOST_DATA:
// Consume the data
m_GetAuthPolicyReqMsg.m_hostName = new String(ch, start, length);
@@ -234,6 +236,11 @@ public class GetAuthPolicyReqMsg
m_state = AWAITING_HOST_ELEMENT_END;
break;
case AWAITING_HOST_ELEMENT_END:
// Consume the data
m_GetAuthPolicyReqMsg.m_hostName = m_GetAuthPolicyReqMsg.m_hostName.concat(new String(ch, start, length));
break;
default:
// Do nothing
break;

View File

@@ -107,7 +107,6 @@ public class GetAuthTokReqMsg
// Proceed based on our state
switch (m_state)
{
case AWAITING_ROOT_ELEMENT_START:
// Verify that we are processing the expected tag
if (ProtoDefs.getAuthTokRequestElementName.equals(qName))
@@ -179,7 +178,6 @@ public class GetAuthTokReqMsg
// Proceed based on our state
switch (m_state)
{
case AWAITING_ROOT_ELEMENT_END:
// Verify that we are processing the expected tag
if (ProtoDefs.getAuthTokRequestElementName.equals(qName))
@@ -250,7 +248,6 @@ public class GetAuthTokReqMsg
// Proceed based on our state
switch (m_state)
{
case AWAITING_SERVICE_DATA:
// Consume the data
m_GetAuthTokReqMsg.m_serviceName = new String(ch, start, length);
@@ -259,6 +256,11 @@ public class GetAuthTokReqMsg
m_state = AWAITING_SERVICE_ELEMENT_END;
break;
case AWAITING_SERVICE_ELEMENT_END:
// Consume the data
m_GetAuthTokReqMsg.m_serviceName = m_GetAuthTokReqMsg.m_serviceName.concat(new String(ch, start, length));
break;
case AWAITING_HOST_DATA:
// Consume the data
m_GetAuthTokReqMsg.m_hostName = new String(ch, start, length);
@@ -267,6 +269,11 @@ public class GetAuthTokReqMsg
m_state = AWAITING_HOST_ELEMENT_END;
break;
case AWAITING_HOST_ELEMENT_END:
// Consume the data
m_GetAuthTokReqMsg.m_hostName = m_GetAuthTokReqMsg.m_hostName.concat(new String(ch, start, length));
break;
case AWAITING_SESSION_TOKEN_DATA:
// Consume the data
m_GetAuthTokReqMsg.m_sessionToken = new String(ch, start, length);
@@ -275,6 +282,11 @@ public class GetAuthTokReqMsg
m_state = AWAITING_SESSION_TOKEN_ELEMENT_END;
break;
case AWAITING_SESSION_TOKEN_ELEMENT_END:
// Consume the data
m_GetAuthTokReqMsg.m_sessionToken = m_GetAuthTokReqMsg.m_sessionToken.concat(new String(ch, start, length));
break;
default:
// Do nothing
break;

View File

@@ -48,7 +48,8 @@ JAVAFILES = ProtoDefs.java \
GetAuthTokRespMsg.java \
Krb5Authenticate.java \
PwdAuthenticate.java \
SessionToken.java
SessionToken.java \
WSSecurity.java
EXTRA_DIST = $(JAVAFILES) \
Krb5_mechanism.settings \

View File

@@ -26,20 +26,26 @@ package com.novell.casa.authtoksvc;
import java.io.ByteArrayInputStream;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.client.AxisClient;
import org.apache.axis.configuration.NullProvider;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.axis.message.SOAPBody;
import org.apache.axis.message.MessageElement;
import javax.xml.namespace.QName;
import java.io.*;
/*
* SessionToken class.
*
* This class constructs session tokens that Casa clients can present to
* the Casa server to prove that an entity has been authenticated to
* a particular realm. The format of the session token is as follows:
*
* <?xml version="1.0" encoding="ISO-8859-1"?>
* This class constructs sessions tokens that clients can present to an ATS
* to prove that an entity has been authenticated to a particular realm.
* The session token consists of a SOAP message secured with WSSecurity
* with the appropriate elements signed and with a timestamp. The body of
* the SOAP message is as follows:
*
* <session_token>
* <signature>signature value</signature>
* <lifetime>lifetime value</lifetime>
@@ -51,308 +57,47 @@ import org.xml.sax.helpers.XMLReaderFactory;
public class SessionToken
{
private String m_id;
private String m_realm;
private String m_lifetime;
private String m_signature;
private String m_id = null;
private String m_realm = null;
private String m_token;
/*
* Class for handling parsing events.
*/
private class SAXHandler extends org.xml.sax.helpers.DefaultHandler
{
private final static int AWAITING_ROOT_ELEMENT_START = 0;
private final static int AWAITING_ROOT_ELEMENT_END = 1;
private final static int AWAITING_SIGNATURE_ELEMENT_START = 2;
private final static int AWAITING_SIGNATURE_ELEMENT_END = 3;
private final static int AWAITING_SIGNATURE_DATA = 4;
private final static int AWAITING_LIFETIME_ELEMENT_START = 5;
private final static int AWAITING_LIFETIME_ELEMENT_END = 6;
private final static int AWAITING_LIFETIME_DATA = 7;
private final static int AWAITING_REALM_ELEMENT_START = 8;
private final static int AWAITING_REALM_ELEMENT_END = 9;
private final static int AWAITING_REALM_DATA = 10;
private final static int AWAITING_IDENT_ID_ELEMENT_START = 11;
private final static int AWAITING_IDENT_ID_ELEMENT_END = 12;
private final static int AWAITING_IDENT_ID_DATA = 13;
private final static int DONE_PARSING = 14;
static final String sessionTokenSoapMsg =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<SOAP-ENV:Envelope" +
" xmlns:SOAP-ENV=\"http://www.w3.org/2003/05/soap-envelope\"\n" +
" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n" +
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" +
" <SOAP-ENV:Body>" +
" <session_token><realm></realm><ident_id></ident_id></session_token>" +
" </SOAP-ENV:Body>" +
"</SOAP-ENV:Envelope>";
private SessionToken m_SessionToken;
private int m_state;
static final private MessageContext axisMsgContext = new MessageContext(new AxisClient(new NullProvider()));
/*
* Constructor
*/
public SAXHandler (SessionToken SessionToken)
{
super();
// Initialize our members
m_SessionToken = SessionToken;
m_state = AWAITING_ROOT_ELEMENT_START;
}
/*
* endDocument() implementation.
*/
public void endDocument () throws SAXException
{
// Verify that we obtained all of the required elements
if (m_state != DONE_PARSING)
{
System.err.println("SessionToken SAXHandler.endDocument()- Missing element");
throw new SAXException("Missing element");
}
}
/*
* startElement() implementation.
*/
public void startElement (String uri, String name, String qName, org.xml.sax.Attributes atts) throws SAXException
{
// Proceed based on our state
switch (m_state)
{
case AWAITING_ROOT_ELEMENT_START:
// Verify that we are processing the expected tag
if (ProtoDefs.sessionTokenElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_SIGNATURE_ELEMENT_START;
}
else
{
System.err.println("SessionToken SAXHandler.startElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_SIGNATURE_ELEMENT_START:
// Verify that we are processing the expected tag
if (ProtoDefs.signatureElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_SIGNATURE_DATA;
}
else
{
System.err.println("SessionToken SAXHandler.startElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_LIFETIME_ELEMENT_START:
// Verify that we are processing the expected tag
if (ProtoDefs.lifetimeElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_LIFETIME_DATA;
}
else
{
System.err.println("SessionToken SAXHandler.startElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_REALM_ELEMENT_START:
// Verify that we are processing the expected tag
if (ProtoDefs.realmElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_REALM_DATA;
}
else
{
System.err.println("SessionToken SAXHandler.startElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_IDENT_ID_ELEMENT_START:
// Verify that we are processing the expected tag
if (ProtoDefs.identIdElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_IDENT_ID_DATA;
}
else
{
System.err.println("SessionToken SAXHandler.startElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
default:
System.err.println("SessionToken SAXHandler.startElement()- State error");
throw new SAXException("State error");
}
}
/*
* endElement() immplementation.
*/
public void endElement (String uri, String name, String qName) throws SAXException
{
// Proceed based on our state
switch (m_state)
{
case AWAITING_ROOT_ELEMENT_END:
// Verify that we are processing the expected tag
if (ProtoDefs.sessionTokenElementName.equals(qName))
{
// Advance to the next state
m_state = DONE_PARSING;
}
else
{
System.err.println("SessionToken SAXHandler.endElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_SIGNATURE_ELEMENT_END:
// Verify that we are processing the expected tag
if (ProtoDefs.signatureElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_LIFETIME_ELEMENT_START;
}
else
{
System.err.println("SessionToken SAXHandler.endElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_LIFETIME_ELEMENT_END:
// Verify that we are processing the expected tag
if (ProtoDefs.lifetimeElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_REALM_ELEMENT_START;
}
else
{
System.err.println("SessionToken SAXHandler.endElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_REALM_ELEMENT_END:
// Verify that we are processing the expected tag
if (ProtoDefs.realmElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_IDENT_ID_ELEMENT_START;
}
else
{
System.err.println("SessionToken SAXHandler.endElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
case AWAITING_IDENT_ID_ELEMENT_END:
// Verify that we are processing the expected tag
if (ProtoDefs.identIdElementName.equals(qName))
{
// Advance to the next state
m_state = AWAITING_ROOT_ELEMENT_END;
}
else
{
System.err.println("SessionToken SAXHandler.endElement()- Un-expected element");
throw new SAXException("Un-expected element");
}
break;
default:
System.err.println("SessionToken SAXHandler.startElement()- State error");
throw new SAXException("State error");
}
}
/*
* character() implementation.
*/
public void characters (char ch[], int start, int length) throws SAXException
{
// Proceed based on our state
switch (m_state)
{
case AWAITING_SIGNATURE_DATA:
// Consume the data
m_SessionToken.m_signature = new String(ch, start, length);
// Advance to the next state
m_state = AWAITING_SIGNATURE_ELEMENT_END;
break;
case AWAITING_LIFETIME_DATA:
// Consume the data
m_SessionToken.m_lifetime = new String(ch, start, length);
// Advance to the next state
m_state = AWAITING_LIFETIME_ELEMENT_END;
break;
case AWAITING_REALM_DATA:
// Consume the data
m_SessionToken.m_realm = new String(ch, start, length);
// Advance to the next state
m_state = AWAITING_REALM_ELEMENT_END;
break;
case AWAITING_IDENT_ID_DATA:
// Consume the data
m_SessionToken.m_id = new String(ch, start, length);
// Advance to the next state
m_state = AWAITING_IDENT_ID_ELEMENT_END;
break;
default:
// Do nothing
break;
}
}
}
/*
* Constructor
*/
public SessionToken(String id, String realm, String lifetime) throws Exception
public SessionToken(String id,
String realm,
String lifetime,
SvcConfig svcConfig) throws Exception
{
// Save copies of the input parameters
m_id = id;
m_realm = realm;
m_lifetime = lifetime;
// Generate a signature
// tbd - Over id, realm, and lifetime data.
m_signature = "tbd";
// Create SessionTokenMessage
Message sessionTokenMessage = getMessage(realm,
id,
Integer.valueOf(lifetime).intValue(),
svcConfig);
// Get a StringBuffer to help us with the construction of the token
StringBuffer sb = new StringBuffer();
// Start building the message
sb.append(ProtoDefs.xmlDeclaration + "\r\n");
sb.append("<" + ProtoDefs.sessionTokenElementName + ">" + "\r\n");
sb.append("<" + ProtoDefs.signatureElementName + ">" + m_signature + "</" + ProtoDefs.signatureElementName + ">" + "\r\n");
sb.append("<" + ProtoDefs.lifetimeElementName + ">" + m_lifetime + "</" + ProtoDefs.lifetimeElementName + ">" + "\r\n");
sb.append("<" + ProtoDefs.realmElementName + ">" + m_realm + "</" + ProtoDefs.realmElementName + ">" + "\r\n");
sb.append("<" + ProtoDefs.identIdElementName + ">" + m_id + "</" + ProtoDefs.identIdElementName + ">" + "\r\n");
sb.append("</" + ProtoDefs.sessionTokenElementName + ">" + "\r\n");
// Save the token
m_token = sb.toString();
// Now save the message as a string
OutputStream outStream = new ByteArrayOutputStream();
sessionTokenMessage.writeTo(outStream);
m_token = outStream.toString();
outStream.close();
}
/*
@@ -364,30 +109,102 @@ public class SessionToken
// Decode the token string
m_token = Base64Coder.decode(token);
// Now parse the token into its elements
// Now instantiate a SOAP message with the string
InputStream inStream = new ByteArrayInputStream(m_token.getBytes());
Message message = new Message(inStream);
// Get access to the SOAP Envelope
SOAPEnvelope envelope = message.getSOAPEnvelope();
// Verify the message
if (WSSecurity.verifyMessage(envelope))
{
// Message verification succeded, now obtain the realm and identity id
// from the message body.
SOAPBody body = (SOAPBody) envelope.getBody();
QName sessionTokenElementName = new QName("session_token");
MessageElement sessionTokenElement = body.getChildElement(sessionTokenElementName);
QName realmElementName = new QName("realm");
MessageElement realmElement = sessionTokenElement.getChildElement(realmElementName);
if (realmElement != null)
{
m_realm = realmElement.getChildNodes().item(0).getNodeValue();
}
QName identIdElementName = new QName("ident_id");
MessageElement identIdElement = sessionTokenElement.getChildElement(identIdElementName);
if (identIdElement != null)
{
m_id = identIdElement.getChildNodes().item(0).getNodeValue();
}
if (m_realm == null || m_id == null)
{
System.out.println("SessionToken()- Required data missing from session token");
throw new Exception("Error: Required data missing from session Token");
}
}
else
{
// Message verification failed
System.err.println("SessionToken()- Invalid Session Token");
throw new Exception("Invalid Session Token");
}
}
/**
* Get SessionToken SOAP Message
*
* @param realm String containing the identity token that should be part of the message
* @param identityId String containing the identity token type
* @param lifetime Lifetime that should be specified in the message timestamp (seconds)
* @param svcConfig Service Config object
* @return <code>Message<code> SessionToken message, null if the method fails.
*/
private Message getMessage(String realm,
String identityId,
int lifetime,
SvcConfig svcConfig)
{
Message secureMessage;
try
{
// Parse the SessionToken
XMLReader xr = XMLReaderFactory.createXMLReader();
SAXHandler handler = new SAXHandler(this);
xr.setContentHandler(handler);
xr.setErrorHandler(handler);
// Build SOAP Message with an identity token in the body
//
// First create a message and obtain its body
InputStream inStream = new ByteArrayInputStream(sessionTokenSoapMsg.getBytes());
Message message = new Message(inStream);
message.setMessageContext(axisMsgContext);
SOAPBody body = (SOAPBody) message.getSOAPBody();
ByteArrayInputStream inStream = new ByteArrayInputStream(m_token.getBytes());
InputSource source = new InputSource(inStream);
xr.parse(source);
// Get access to the session_token element
QName sessionTokenElementName = new QName("session_token");
MessageElement sessionTokenElement = body.getChildElement(sessionTokenElementName);
// Verify the signature
// tbd
// Get access to the realm element and set its value
QName realmElementName = new QName("realm");
MessageElement realmElement = sessionTokenElement.getChildElement(realmElementName);
realmElement.addTextNode(realm);
// Verify that the token has not expired
// tbd
// Get access to the ident_id element and set its value
QName identIdElementName = new QName("ident_id");
MessageElement identIdElement = sessionTokenElement.getChildElement(identIdElementName);
identIdElement.addTextNode(identityId);
// Now we need to secure the SOAP message that we created, we are doing to
// do so by adding a timestamp and signing the timestamp as well as the body.
// To do this we are going to leverage WS-Security.
secureMessage = WSSecurity.secureSOAPEnvelope(message.getSOAPEnvelope(),
lifetime,
svcConfig);
}
catch (SAXException e)
catch (Exception e)
{
System.err.println("SessionToken()- Parse exception: " + e.toString());
throw new Exception("Protocol error");
System.out.println("SessionToken.getMessage() - Exception caught building message, error: " + e.getMessage());
secureMessage = null;
}
return secureMessage;
}
/*

View File

@@ -53,6 +53,8 @@ public class SvcConfig
public final static String ConfigFolderPath = "ConfigFolderPath";
public final static String AppRootPath = "AppRootPath";
public final static String ReconfigureInterval = "ReconfigureInterval";
public final static String KeyStoreUser = "KeyStoreUser";
public final static String KeyStorePwd = "KeyStorePwd";
// Default configuration values
public final static String DefaultSessionTokenLifetimeValue = "43200"; // Seconds

View File

@@ -0,0 +1,274 @@
/***********************************************************************
*
* Copyright (C) 2006 Novell, Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail,
* you may find current contact information at www.novell.com.
*
* Author: Juan Carlos Luciani <jluciani@novell.com>
*
***********************************************************************/
package com.novell.casa.authtoksvc;
import java.io.ByteArrayInputStream;
import org.apache.axis.Message;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.ws.security.*;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.components.crypto.CryptoFactory;
import org.apache.ws.security.message.WSSecHeader;
import org.apache.ws.security.message.WSSecSignature;
import org.apache.ws.security.message.WSSecTimestamp;
import org.apache.xml.security.c14n.Canonicalizer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.xml.soap.MessageFactory;
import java.util.Set;
import java.util.Vector;
/*
* WSSecurity Class.
*
* This class provides static methods for securing and verifying SOAP messages. SOAP messages
* are secured by adding a timestamp and signing the appropriate elements using methods and
* headers defined by WS* specifications.
*
*/
public class WSSecurity
{
static final private WSSecurityEngine secEngine = new WSSecurityEngine();
static final private Crypto crypto = CryptoFactory.getInstance();
/**
* Creates a SOAP message from a document.
*
*/
private static Message toSOAPMessage(Document doc) throws Exception
{
Canonicalizer c14n = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS);
byte[] canonicalMessage = c14n.canonicalizeSubtree(doc);
ByteArrayInputStream in = new ByteArrayInputStream(canonicalMessage);
MessageFactory factory = MessageFactory.newInstance();
return (org.apache.axis.Message) factory.createMessage(null, in);
}
/***
* Returns the first element that containes an Id with value
* <code>uri</code> and <code>namespace</code>.
* <p/>
*
* Copyright Note: The code for this function was copied from file
* WSSecurityUtil.java from package org.apache.ws.security.util.
* The Copyright notice on this file is as follows:
*
* Copyright 2003-2006 The Apache Software Foundation, or their licensors, as
* appropriate.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @param startNode Where to start the search
* @param value Value of the Id attribute
* @param namespace Namespace URI of the Id
* @return The found element or <code>null</code>
*/
private static Element findElementById(Node startNode,
String value,
String namespace)
{
// Just return null if startNode is set to null
if (startNode == null)
{
return null;
}
Node startParent = startNode.getParentNode();
Node processedNode;
while (startNode != null)
{
// start node processing at this point
if (startNode.getNodeType() == Node.ELEMENT_NODE)
{
Element se = (Element) startNode;
if (se.hasAttributeNS(namespace, "Id")
&& value.equals(se.getAttributeNS(namespace, "Id")))
{
return se;
}
}
processedNode = startNode;
startNode = startNode.getFirstChild();
// no child, this node is done.
if (startNode == null)
{
// close node processing, get sibling
startNode = processedNode.getNextSibling();
}
// no more siblings, get parent, all children
// of parent are processed.
while (startNode == null)
{
processedNode = processedNode.getParentNode();
if (processedNode == startParent)
{
return null;
}
// close parent node processing (processed node now)
startNode = processedNode.getNextSibling();
}
}
return null;
}
/**
* Verifies SOAP envelope timestamp and signatures.
*
* @param envelope SOAP envelope with timestamp
* @return <code>boolean</code> True if verification succeeds
* @throws Exception on error
*/
public static boolean verifyMessage(SOAPEnvelope envelope) throws Exception
{
boolean msgVerificationStatus = false;
try
{
boolean timeStampProcessed = false;
boolean signatureProcessed = false;
Vector<WSSecurityEngineResult> results;
Document signedDoc = envelope.getAsDocument();
results = secEngine.processSecurityHeader(signedDoc, null, null, crypto);
if (results != null)
{
for (WSSecurityEngineResult result : results)
{
if (result.getAction() == WSConstants.TS)
{
timeStampProcessed = true;
}
else if (result.getAction() == WSConstants.SIGN)
{
// A signature was processed, verify that the signature was over the timestamp
// and the body.
boolean timeStampSigned = false;
boolean bodySigned = false;
Set signedElements = result.getSignedElements();
for (Object signedElement : signedElements)
{
String elementId = (String) signedElement;
Element element = findElementById(signedDoc.getDocumentElement(), elementId, "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
if (element != null)
{
if ("wsu:Timestamp".equalsIgnoreCase(element.getNodeName()))
{
timeStampSigned = true;
}
else if ("SOAP-ENV:Body".equalsIgnoreCase(element.getNodeName()))
{
bodySigned = true;
}
}
}
if (timeStampSigned && bodySigned)
{
signatureProcessed = true;
}
}
}
}
if (timeStampProcessed && signatureProcessed)
{
System.out.println("WSSecurity.verifyMessage() - Validation succeded");
msgVerificationStatus = true;
}
else
{
System.out.println("WSSecurity.verifyMessage() - validation failed");
}
}
catch (WSSecurityException e)
{
System.out.println("WSSecurity.verifyMessage() - Verification failed with error:" + e.getMessage() + " code = " + e.getErrorCode());
}
return msgVerificationStatus;
}
/**
* Add timestamp and sign SOAP message in compliance with WS-Security.
*
* @param envelope String containing a SOAP envelope
* @param timeToLive Value to set the timestamp timeToLive parameter in seconds
* @param svcConfig Service Config object
* @return <code>Message</code> Signed and timestamped SOAP message
* @throws Exception on error
*/
public static Message secureSOAPEnvelope(SOAPEnvelope envelope,
int timeToLive,
SvcConfig svcConfig) throws Exception
{
WSSecSignature signer = new WSSecSignature();
signer.setUserInfo(svcConfig.getSetting(SvcConfig.KeyStoreUser),
svcConfig.getSetting(SvcConfig.KeyStorePwd));
signer.setKeyIdentifierType(WSConstants.X509_KEY_IDENTIFIER); // Include X509 Cert in message
Document doc = envelope.getAsDocument();
WSSecHeader secHeader = new WSSecHeader();
secHeader.insertSecurityHeader(doc);
WSSecTimestamp timeStamper = new WSSecTimestamp();
timeStamper.setTimeToLive(timeToLive);
timeStamper.build(doc, secHeader);
Vector<WSEncryptionPart> parts = new Vector<WSEncryptionPart>();
String soapNamespace = doc.getDocumentElement().getNamespaceURI();
WSEncryptionPart bodyPart = new WSEncryptionPart("Body", soapNamespace, "");
parts.add(bodyPart);
WSEncryptionPart timeStampPart = new WSEncryptionPart(timeStamper.getId());
parts.add(timeStampPart);
signer.setParts(parts);
Document signedDoc = signer.build(doc, crypto, secHeader);
// Convert the signed document into a SOAP message and return it.
return toSOAPMessage(signedDoc);
}
}