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

@ -265,6 +265,7 @@ package/linux/Makefile
package/linux/CASA_auth_token_svc.spec
server/Makefile
server/Svc/Makefile
server/Svc/external/Makefile
server/Svc/src/Makefile
server/Svc/src/com/Makefile
server/Svc/src/com/novell/Makefile

View File

@ -20,7 +20,7 @@
#######################################################################
SUBDIRS = src
DIST_SUBDIRS = src
DIST_SUBDIRS = src external
EXTRA_DIST = authtoken.settings \
identoken.settings \
@ -34,6 +34,7 @@ ROOT = ../..
LIBDIR = $(ROOT)/$(LIB)
IDENT_ABSTRACTION_DIR = /usr/share/java/identity-abstraction
AXIS_JARS_DIR = external
JAVAROOT = .
JAVAC= javac
@ -50,6 +51,8 @@ JAVAFILES = src/com/novell/casa/authtoksvc/ProtoDefs.java \
src/com/novell/casa/authtoksvc/AuthTokenConfig.java \
src/com/novell/casa/authtoksvc/EnabledSvcsConfig.java \
src/com/novell/casa/authtoksvc/AuthMechanism.java \
src/com/novell/casa/authtoksvc/WSSecurity.java \
src/com/novell/casa/authtoksvc/SessionToken.java \
src/com/novell/casa/authtoksvc/Authenticate.java \
src/com/novell/casa/authtoksvc/RpcMethod.java \
src/com/novell/casa/authtoksvc/Rpc.java \
@ -66,8 +69,7 @@ JAVAFILES = src/com/novell/casa/authtoksvc/ProtoDefs.java \
src/com/novell/casa/authtoksvc/GetAuthTokReqMsg.java \
src/com/novell/casa/authtoksvc/GetAuthTokRespMsg.java \
src/com/novell/casa/authtoksvc/Krb5Authenticate.java \
src/com/novell/casa/authtoksvc/PwdAuthenticate.java \
src/com/novell/casa/authtoksvc/SessionToken.java
src/com/novell/casa/authtoksvc/PwdAuthenticate.java
BUILDDIR = build
@ -77,8 +79,12 @@ AUTHTOKEN_FILES = -C $(BUILDDIR)/webapp/WEB-INF/classes com
CLASSES = $(addprefix $(BUILDDIR)/, $(JAVAFILES:%.java=%.class))
#AXIS_LIBS = $(AXIS_JARS_DIR)/axis.jar:$(AXIS_JARS_DIR)/axis-ant.jar:$(AXIS_JARS_DIR)/commons-discovery-0.2.jar:$(AXIS_JARS_DIR)/commons-logging-1.0.4.jar:$(AXIS_JARS_DIR)/commons-logging-api.jar:$(AXIS_JARS_DIR)/jaxrpc.jar:$(AXIS_JARS_DIR)/log4j-1.2.8.jar:$(AXIS_JARS_DIR)/saaj.jar:$(AXIS_JARS_DIR)/wsdl4j-1.5.1.jar:$(AXIS_JARS_DIR)/wss4j-1.5.0.jar:$(AXIS_JARS_DIR)/xalan.jar:$(AXIS_JARS_DIR)/xercesImpl.jar:$(AXIS_JARS_DIR)/xml-apis.jar:$(AXIS_JARS_DIR)/xmlsec-1.2.1.jar
AXIS_LIBS = $(AXIS_JARS_DIR)/axis.jar:$(AXIS_JARS_DIR)/saaj.jar:$(AXIS_JARS_DIR)/wss4j-1.5.0.jar:$(AXIS_JARS_DIR)/xmlsec-1.2.1.jar
#AXIS_LIBS = $(AXIS_JARS_DIR)/wss4j-1.5.0.jar
LIBS = /usr/share/java/servletapi5.jar
CLASSPATH = $(IDENT_ABSTRACTION_DIR)/identity-abstraction.jar:$(LIBS)
CLASSPATH = $(AXIS_LIBS):$(IDENT_ABSTRACTION_DIR)/identity-abstraction.jar:$(LIBS)
CUR_DIR := $(shell pwd)
@ -98,6 +104,7 @@ $(BUILDDIR)/$(WEBAPP): $(BUILDDIR) $(CLASSES)
cp src/com/novell/casa/authtoksvc/Pwd_mechanism.settings $(BUILDDIR)/webapp/WEB-INF/conf/installed_auth_mechanisms/PwdAuthenticate/mechanism.settings
cp $(IDENT_ABSTRACTION_DIR)/*.jar $(BUILDDIR)/webapp/WEB-INF/lib/
rm $(BUILDDIR)/webapp/WEB-INF/lib/identity-abstraction.jar
cp $(AXIS_JARS_DIR)/*.jar $(BUILDDIR)/webapp/WEB-INF/lib/
ls $(BUILDDIR)/webapp/WEB-INF/lib/
jar cvf $(BUILDDIR)/$(WEBAPP) -C $(BUILDDIR)/webapp .
cp $(BUILDDIR)/$(WEBAPP) $(LIBDIR)/java/

View File

@ -69,6 +69,8 @@ Thhe following is an example svc.settings file:
<IAConfigFile>/home/jluciani/jakarta-tomcat-5.0.28/webapps/CasaAuthTokenSvc/WEB-INF/conf/iaRealms.xml</IAConfigFile>
<ReconfigureInterval>60</ReconfigureInterval>
<startSearchContext>o=novell</startSearchContext>
<KeyStoreUser>privKey<KeyStoreUser>
<KeyStorePwd>foobar<KeyStorePwd>
</settings>
Note the following about the sample svc.settings file:
@ -102,6 +104,12 @@ Note the following about the sample svc.settings file:
from the root of the tree. This setting or an equivalent setting will be moved
to the identity abstraction configuration file where it belongs. Once this is done,
the setting will no longer be recognized within the svc.settings file.
- The KeyStoreUses setting specifies the user's alias name in the keystore that identifies
the private key that is to be used to sign tokens.
- The KeyStorePwd setting specifies the password of the user specified by KeyStoreUser to get
the private signing key from the keystore.
CONFIGURING SERVICES TO CONSUME CASA AUTHENTICATION TOKENS

View File

@ -0,0 +1,52 @@
#######################################################################
#
# Copyright (C) 2006 Novell, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This program 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free
# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Author: Juan Carlos Luciani <jluciani@novell.com>
#
#######################################################################
SUBDIRS =
DIST_SUBDIRS =
CFILES =
EXTRA_DIST = axis.jar \
axis-ant.jar \
commons-discovery-0.2.jar \
commons-logging-1.0.4.jar \
commons-logging-api.jar \
jaxrpc.jar \
log4j.properties \
log4j-1.2.8.jar \
README \
saaj.jar \
wsdl4j-1.5.1.jar \
wss4j-1.5.0.jar \
xalan.jar \
xercesImpl.jar \
xml-apis.jar \
xmlsec-1.2.1.jar
.PHONY: package package-clean package-install package-uninstall
package package-clean package-install package-uninstall:
$(MAKE) -C $(TARGET_OS) $@
maintainer-clean-local:
rm -f Makefile.in

View File

@ -0,0 +1,25 @@
The following describes the source of the files present in this folder.
axis-1_4 ----> axis-ant.jar
axis-1_4 ----> axis.jar
axis-1_4 ----> commons-discovery-0.2.jar
axis-1_4 ----> commons-logging-1.0.4.jar
xml-security-1_2_1 ----> commons-logging-api.jar
axis-1_4 ----> jaxrpc.jar
axis-1_4 ----> log4j-1.2.8.jar
axis-1_4 ----> log4j.properties
axis-1_4 ----> saaj.jar
axis-1_4 ----> wsdl4j-1.5.1.jar
wss4j-1.5 ----> wss4j-1.5.0.jar
xml-security-1_2_1 ----> xalan.jar
xml-security-1_2_1 ----> xercesImpl.jar
xml-security-1_2_1 ----> xml-apis.jar
xml-security-1_2_1 ----> xmlsec-1.2.1.jar
xml-security-1_2_1 - URL: http://xml.apache.org/security/dist/java-library/ - File: xml-security-bin-1_2_1.zip
axis-1_4 - URL: http://www.apache.org/dyn/closer.cgi/ws/axis/1_4 - File: axis-bin-1_4.tar.gz
wss4j-1.5 - URL: http://www.apache.org/dyn/dyn/closer.cgi/ws/wss4j/ - File: wss4j-bin-1.5.0.zip

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,20 @@
# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=INFO, CONSOLE
#log4j.rootCategory=INFO, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=- %m%n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.Threshold=INFO
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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