Code example: AuthenticationUserExitHandler API
Retrieve the owner PrivateKey and PublicKey pair for SAML token decryption by invoking the UserExitKeyPair API
Important: A compressed file with user exit code
samples is available in the Members\resources\samples\userexits
installation directory.
AuthenticationInboundService.java
/*
* *****************************************************************************
*
* IBM Confidential
*
* OCO Source Materials
*
* 5725Q72
*
* (C) Copyright IBM Corp. 2013, 2014
*
* The source code for this program is not published or otherwise divested of its trade secrets, irrespective of what
* has been deposited with the U.S. Copyright Office.
*
* The sample contained herein is provided to you "AS IS".
*
* It is furnished by IBM as a simple example and has not been thoroughly tested under all conditions. IBM, therefore,
* cannot guarantee its reliability, serviceability or functionality.
*
* This sample may include the names of individuals, companies, brands and products in order to illustrate concepts as
* completely as possible. All of these names are fictitious and any similarity to the names and addresses used by
* actual persons or business enterprises is entirely coincidental.
*
* ******************************************************************************
*/
package com.ibm.b2b.test.comms.userexits.authentication;
import java.io.ByteArrayInputStream;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import org.apache.commons.lang.Validate;
import org.w3c.dom.Element;
import com.ibm.b2b.userexits.spi.AuthenticationExitException;
import com.ibm.b2b.userexits.spi.AuthenticationUserExitHandler;
import com.ibm.b2b.userexits.spi.BusinessDocExitException;
import com.ibm.b2b.userexits.spi.BusinessDocument;
import com.ibm.b2b.userexits.spi.UserExitKeyPair;
import com.ibm.misc.BASE64Decoder;
/**
* This is just a sample authentication service that can be used as a template to authenticate the certificate inbound
* and then decrypt the SAML token by fetching the key pair for the given alias from MEG keystore.
*
* @author Praveen S Rao
*
*/
public class AuthenticationInboundService implements AuthenticationUserExitHandler {
private static final String SOURCECLASS = AuthenticationInboundService.class.getCanonicalName();
private static final Logger LOGGER = Logger.getLogger( SOURCECLASS );
/*
* (non-Javadoc)
*
* @see com.ibm.b2b.userexits.spi.AuthenticationUserExitHandler#invoke(com.ibm.b2b.userexits.spi.BusinessDocument)
*/
public BusinessDocument invoke( BusinessDocument bdo ) throws AuthenticationExitException {
Validate.notNull( bdo );
final String sourceMethod = "invoke";
if ( LOGGER.isLoggable( Level.FINER ) ) {
LOGGER.entering( SOURCECLASS, sourceMethod, bdo.getTransactionID() );
}
try {
sendCertificateForAuthentication( bdo.getSignCertificate() );
decryptAndVerifySAMLToken( bdo );
} catch ( Exception t ) {
throw new AuthenticationExitException( t );
}
if ( LOGGER.isLoggable( Level.FINER ) ) {
LOGGER.exiting( SOURCECLASS, sourceMethod, bdo.getTransactionID() );
}
return bdo;
}
private void decryptAndVerifySAMLToken( BusinessDocument bdo ) throws BusinessDocExitException {
final String sourceMethod = "decryptAndVerifySAMLToken";
if ( LOGGER.isLoggable( Level.FINER ) ) {
LOGGER.entering( SOURCECLASS, sourceMethod );
}
PrivateKey decryptionKey = null;
Certificate certToBeMatched = null;
try {
// extract SAML token from security element header. Have set actor as ebms here.
Element encryptedSAMLAssertion = bdo.getSecurityElement( new QName(
"urn:oasis:names:tc:SAML:2.0:assertion", "EncryptedAssertion" ), "ebms" );
// get keypair for alias
UserExitKeyPair keyPair = bdo.getOwnerKeyPair( "myalias" );
if ( keyPair == null ) {
throw new BusinessDocExitException( "Decryption keypair could not be found" );
}
BASE64Decoder base64Decoder = new BASE64Decoder();
// get private key encoded data
byte[] privateKeyData = base64Decoder.decodeBuffer( keyPair.getPrivateKey() );
// get keyfactory instance
KeyFactory kf = KeyFactory.getInstance( "RSA" );
// create a pkcs8 key spec
PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec( privateKeyData );
// generate private key from key factory and key spec
decryptionKey = kf.generatePrivate( ks );
// get certificate encoded data
byte[] certData = base64Decoder.decodeBuffer( keyPair.getPublicCert() );
// get certificate factory instance
CertificateFactory cf = CertificateFactory.getInstance( "X509" );
certToBeMatched = cf.generateCertificate( new ByteArrayInputStream( certData ) );
// match recipient info
matchRecipientInfo( encryptedSAMLAssertion, certToBeMatched );
// do SAML token decryption
Element decrytpedSAMLAssertion = doSAMLDecryption( encryptedSAMLAssertion, decryptionKey );
// set the decrypted SAML token into the BDO so that backend can understand
bdo.setSAMLAssertion( decrytpedSAMLAssertion );
} catch ( Exception e ) {
throw new BusinessDocExitException( e );
} finally {
// zeroise key data
if ( decryptionKey != null ) {
Arrays.fill( decryptionKey.getEncoded(), (byte) 0 );
}
}
if ( LOGGER.isLoggable( Level.FINER ) ) {
LOGGER.exiting( SOURCECLASS, sourceMethod );
}
}
private Element doSAMLDecryption( Element encryptedSAMLAssertion, PrivateKey decryptionKey ) {
final String sourceMethod = "doSAMLDecryption";
if ( LOGGER.isLoggable( Level.FINER ) ) {
LOGGER.entering( SOURCECLASS, sourceMethod );
}
// do saml decryption
if ( LOGGER.isLoggable( Level.FINER ) ) {
LOGGER.exiting( SOURCECLASS, sourceMethod );
}
return null;
}
private void matchRecipientInfo( Element encryptedSAMLAssertion, Certificate certToBeMatched )
throws BusinessDocExitException {
final String sourceMethod = "matchRecipientInfo";
if ( LOGGER.isLoggable( Level.FINER ) ) {
LOGGER.entering( SOURCECLASS, sourceMethod );
}
boolean recipientMatch = "1".equals( "1" );
if ( !recipientMatch ) {
throw new BusinessDocExitException( "RecipientInfo not found" );
}
if ( LOGGER.isLoggable( Level.FINER ) ) {
LOGGER.exiting( SOURCECLASS, sourceMethod );
}
}
private void sendCertificateForAuthentication( Certificate signCertificate ) {
final String sourceMethod = "sendCertificateForAuthentication";
if ( LOGGER.isLoggable( Level.FINER ) ) {
LOGGER.entering( SOURCECLASS, sourceMethod );
}
// send certificate to a third party authentication store
if ( LOGGER.isLoggable( Level.FINER ) ) {
LOGGER.exiting( SOURCECLASS, sourceMethod );
}
}
}