ltpa token

http://elguji.com/IdeaJam/P/ij.nsf/profile?openform&id=8F34390BD3D698F68625756300335785&view=comments

 

SSO between Lotus Notes Client and Domino HTTP Access (20 Feb 2009)

Now I have found why my LtpaToken not support SSO.

Change the createCookie(..) function from

document.cookie=name+"="+value+"; path=/";

to

document.cookie=name+"="+value+"; path=/; domain=.blabla.bla.net";

The domain is the same as in COOKIE_DOMAIN in the (createLTPAToken) Agent.

SSO between Lotus Notes Client and Domino HTTP Access (05 Feb 2009)

I have make a solution to create an LTPAToken with an Agent.
But the Token is not an SSO Token, so you can only login into one server.

Maybe there is a developer which have a solution to generate an SSO LTPAToken.


Here is the code you need:
Create a Database on the Server where you want to login

In this Database create the following Agents:

Agent: (createLTPAToken)
import lotus.domino.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;

/**
* Creates a document with an LTPAToken
*/
public class JavaAgent extends AgentBase {
private LtpaToken generatedToken;
private String tokenAsString;

public void NotesMain() {

try {
Session session = getSession();
AgentContext agentContext = session.getAgentContext();
Database db = agentContext.getCurrentDatabase();

generatedToken = new LtpaToken();
generatedToken = generatedToken.generate(session.getUserName(), new Date(), new Date());

tokenAsString = generatedToken.toString();

Document doc = db.createDocument();
doc.appendItemValue("Form", "SSOLoginNotesDomino");
doc.appendItemValue("token", tokenAsString);
doc.save();
} catch(Exception e) {
e.printStackTrace();
}
}
}

class LtpaToken {
private byte[] creation;
private Date creationDate;
private byte[] digest;
private byte[] expires;
private Date expiresDate;
private byte[] hash;
private byte[] header;
private Log log;
private String ltpaToken;
private Properties properties = null;
private byte[] rawToken;
private byte[] user;

/**
* Cookie name.
*/
public final static String COOKIE_NAME = "LtpaToken";
private final static String COOKIE_DOMAIN = ".blabla.bla.net";
private final static String DOMINO_SECRET = "WXYzWxWXYZx1yz9wxYZWx+9wxyz=";

/**
* Constructor for the LtpaToken object
*/
public LtpaToken() {
init();
}

/**
* Constructor for the LtpaToken object
*
* @param token Description of the Parameter
*/
public LtpaToken( String token ) {
init();
ltpaToken = token;
rawToken = Base64.decode( token.toCharArray() );
user = new byte[( rawToken.length ) - 40];
for ( int i = 0; i < 4; i++ ) {
header = rawToken;
}
for ( int i = 4; i < 12; i++ ) {
creation[i - 4] = rawToken;
}
for ( int i = 12; i < 20; i++ ) {
expires[i - 12] = rawToken;
}
for ( int i = 20; i < ( rawToken.length - 20 ) ; i++ ) {
user[i - 20] = rawToken;
}
for ( int i = ( rawToken.length - 20 ); i < rawToken.length; i++ ) {
digest[i - ( rawToken.length - 20 )] = rawToken;
}
creationDate = new Date( Long.parseLong( new String( creation ), 16 ) * 1000 );
expiresDate = new Date( Long.parseLong( new String( expires ), 16 ) * 1000 );
}

/**
* Description of the Method
*/
private void init() {
creation = new byte[8];
digest = new byte[20];
expires = new byte[8];
hash = new byte[20];
header = new byte[4];
}

/**
* Creates a new SHA-1 <code>MessageDigest</code> instance.
*
* @return The instance.
*/
private MessageDigest getDigest() {
try {
return MessageDigest.getInstance( "SHA-1" );
} catch ( NoSuchAlgorithmException nsae ) {
}
return null;
}

/**
* Generates a new LtpaToken w ith given parameters.
*
* @param canonicalUser User name in canonical form. e.g. 'CN=Robert Kelly/OU=MIS/O=EBIMED'.
* @param tokenCreation Token creation date.
* @param tokenExpires Token expiration date.
* @return The generated token.
*/
public static LtpaToken generate( String canonicalUser, Date tokenCreation, Date tokenExpires ) {
LtpaToken ltpa = new LtpaToken();

Calendar calendar = Calendar.getInstance();
MessageDigest md = ltpa.getDigest();

ltpa.header = new byte[]{0, 1, 2, 3};
ltpa.user = canonicalUser.getBytes();

byte[] token = null;
calendar.setTime( tokenCreation );

ltpa.creation = Long.toHexString( calendar.getTime().getTime() / 1000 ).toUpperCase().getBytes();
calendar.setTime( tokenExpires );
calendar.add(Calendar.HOUR,8);

ltpa.expires = Long.toHexString( calendar.getTime().getTime() / 1000 ).toUpperCase().getBytes();
ltpa.user = canonicalUser.getBytes();

token = concatenate( token, ltpa.header );
token = concatenate( token, ltpa.creation );
token = concatenate( token, ltpa.expires );
token = concatenate( token, ltpa.user );
md.update( token );
ltpa.digest = md.digest( Base64.decode( DOMINO_SECRET.toCharArray() ) );
token = concatenate( token, ltpa.digest );

return new LtpaToken( new String( Base64.encode( token ) ) );
}

/**
* Helper method to concatenate a byte array.
*
* @param a Byte array a.
* @param b Byte array b.
* @return a + b.
*/
private static byte[] concatenate( byte[] a, byte[] b ) {
if ( a == null ) {
return b;
} else {
byte[] bytes = new byte[a.length + b.length];

System.arraycopy( a, 0, bytes, 0, a.length );
System.arraycopy( b, 0, bytes, a.length, b.length );
return bytes;
}
}

/**
* String representation of LtpaToken object.
*
* @return Returns token String suitable for cookie value.
*/
public String toString() {
return ltpaToken;
}
}

class Base64 {
private static char[] map1 = new char[64];
static {
int i=0;
for (char c='A'; c<='Z'; c++) map1[i++] = c;
for (char c='a'; c<='z'; c++) map1[i++] = c;
for (char c='0'; c<='9'; c++) map1[i++] = c;
map1[i++] = '+'; map1[i++] = '/';
}

// Mapping table from Base64 characters to 6-bit nibbles.
private static byte[] map2 = new byte[128];
static {
for (int i=0; i<map2.length; i++) map2 = -1;
for (int i=0; i<64; i++) map2[map1] = (byte)i;
}

/**

* Encodes a string into Base64 format.
* No blanks or line breaks are inserted.
* @param s a String to be encoded.
* @return A String with the Base64 encoded data.
*/
public static String encodeString (String s) {
return new String(encode(s.getBytes()));
}

/**
* Encodes a byte array into Base64 format.
* No blanks or line breaks are inserted.
* @param in an array containing the data bytes to be encoded.
* @return A character array with the Base64 encoded data.
*/
public static char[] encode (byte[] in) {
return encode(in,in.length);
}

/**
* Encodes a byte array into Base64 format.
* No blanks or line breaks are inserted.
* @param in an array containing the data bytes to be encoded.
* @param iLen number of bytes to process in <code>in</code>.
* @return A character array with the Base64 encoded data.
*/
public static char[] encode (byte[] in, int iLen) {
int oDataLen = (iLen*4+2)/3; // output length without padding
int oLen = ((iLen+2)/3)*4; // output length including padding
char[] out = new char[oLen];
int ip = 0;
int op = 0;
while (ip < iLen) {
int i0 = in[ip++] & 0xff;
int i1 = ip < iLen ? in[ip++] & 0xff : 0;
int i2 = ip < iLen ? in[ip++] & 0xff : 0;
int o0 = i0 >>> 2;
int o1 = ((i0 & 3) << 4) | (i1 >>> 4);
int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6);
int o3 = i2 & 0x3F;
out[op++] = map1[o0];
out[op++] = map1[o1];
out[op] = op < oDataLen ? map1[o2] : '='; op++;
out[op] = op < oDataLen ? map1[o3] : '='; op++;
}
return out;
}

/**
* Decodes a string from Base64 format.
* @param s a Base64 String to be decoded.
* @return A String containing the decoded data.
* @throws IllegalArgumentException if the input is not valid Base64 encoded data.
*/
public static String decodeString (String s) {
return new String(decode(s));
}

/**
* Decodes a byte array from Base64 format.
* @param s a Base64 String to be decoded.
* @return An array containing the decoded data bytes.
* @throws IllegalArgumentException if the input is not valid Base64 encoded data.
*/
public static byte[] decode (String s) {
return decode(s.toCharArray());
}

/**
* Decodes a byte array from Base64 format.
* No blanks or line breaks are allowed within the Base64 encoded data.
* @param in a character array containing the Base64 encoded data.
* @return An array containing the decoded data bytes.
* @throws IllegalArgumentException if the input is not valid Base64 encoded data.
*/
public static byte[] decode (char[] in) {
int iLen = in.length;
if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4.");

while (iLen > 0 && in[iLen-1] == '=') iLen--;

int oLen = (iLen*3) / 4;
byte[] out = new byte[oLen];
int ip = 0;
int op = 0;
while (ip < iLen) {
int i0 = in[ip++];
int i1 = in[ip++];
int i2 = ip < iLen ? in[ip++] : 'A';
int i3 = ip < iLen ? in[ip++] : 'A';
if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127)
throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
int b0 = map2[i0];
int b1 = map2[i1];
int b2 = map2[i2];
int b3 = map2[i3];
if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0)
throw new IllegalArgumentException ("Illegal character in Base64 encoded data.");
int o0 = ( b0 <<2) | (b1>>>4);
int o1 = ((b1 & 0xf)<<4) | (b2>>>2);
int o2 = ((b2 & 3)<<6) | b3;
out[op++] = (byte)o0;
if (op<oLen) out[op++] = (byte)o1;
if (op<oLen) out[op++] = (byte)o2;
}
return out;
}

// Dummy constructor.
private Base64() {}
}

Agent: (openIntranetByID)
Here is the code to open the document

Dim w As New NotesUIWorkspace
Dim s As New NotesSession
Dim db As NotesDatabase
Dim view As NotesView
Dim doc As NotesDocument

Set db = s.CurrentDatabase
Set view = db.GetView("(docByName)")

Print "Intranet will be open .."
Sleep(1)
Print "Intranet will be open ...."
Call view.Refresh()
Set doc = view.GetDocumentByKey(s.UserName, True)
If doc Is Nothing Then
Print "Intranet will be open ....."
Sleep(1)
Print "Intranet will be open ......."
Call view.Refresh()
Set doc = view.GetDocumentByKey(s.UserName, True)

If doc Is Nothing Then
Print "Intranet will be open ........"
Sleep(1)
Print "Intranet will be open .........."
Call view.Refresh()
Set doc = view.GetDocumentByKey(s.UserName, True)
End If
End If

If Not doc Is Nothing Then
Call w.URLOpen( "{ Link } & doc.UniversalID )
Print "Intranet will be open for " & s.CommonUserName & "."
Else
Print "Error while opening Intranet"
End If



In the PostOpen Event of the Database start the Agents (createLTPAToken) and (openIntranetByID), then close the database.

Create a Form SSOLoginNotesDomino in the Database.
In the HTML-Head insert the formula: @SetHTTPHeader("Set-Cookie"; "LtpaToken=" + token)

In the onLoad Event insert
var ltpatoken = getcookie("LtpaToken");
eraseCookie("LtpaToken");
createCookie("LtpaToken", ltpatoken );

document.location.replace("{ Link }


In the JS-Header insert:
function getcookie(c_name) {

if (document.cookie.length>0) {
c_start=document.cookie.indexOf(c_name + "=");

if (c_start!=-1) {
c_start=c_start + c_name.length+1;
c_end=document.cookie.indexOf(";",c_start);

if (c_end==-1) c_end=document.cookie.length;
return unescape(document.cookie.substring(c_start,c_end));
}
}
return "";
}


function eraseCookie(name) {
createCookie(name,"",-1);
}


function createCookie(name,value) {
document.cookie = name+"="+value+"; path=/";
}


Insert a view into the database, called (login)
The first column is sorted and has the formula @Text(@DocumentUniqueID)

Insert a view into the database, called (docByName)
The first columns is sorted and has the formula @Name([Canonicalize];@Author)


Change the COOKIE_DOMAIN and the DOMINO_SECRET values in the (createLTPAToken) for your needs.


Whe you open the database in the lotus notes client:
the agent (createLTPAToken) generates a document with an LTPAToken,
the agent (openIntranetByID) starts the web browser and opens the document with the LTPAToken,
the javascript in the form change the token to root path and redirect to the intranet homepage.


 

http://blogs.nil.com/jeds/2009/04/04/ltpa-token/

 



http://www.dominoexperts.com/articles/Creating-a-session-for-a-user

Since JEDS' blog seems to have vanished from the Internet I'm going to post the source he posted. There has been a couple of requests for it and I happen to hold a copy.

All credits go to JEDS of course! When his site is up we can point the link to the new one!

 

package com.nil.ltpa;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Vector;

import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.Item;
import lotus.domino.NotesError;
import lotus.domino.NotesException;
import lotus.domino.View;

import com.nil.exception.Base64DecodeException;
import com.nil.helpers.HttpUtils;

public class LtpaLibrary {

private static final byte[]    ltpaTokenVersion        = { 0, 1, 2, 3 };
private static final int        dateStringLength        = 8;
private static final String    dateStringFiller        = "00000000";
private static final int        creationDatePosition    = ltpaTokenVersion.length;
private static final int        expirationDatePosition    = creationDatePosition + dateStringLength;
private static final int        preUserDataLength        = ltpaTokenVersion.length + dateStringLength + dateStringLength;
private static final int        hashLength            = 20;

/** This method parses the LtpaToken cookie received from the web browser and returns the information in the <tt>TokenData</tt>
 * class.
 * @param ltpaToken - the cookie (base64 encoded).
 * @param ltpaSecretStr - the contents of the <tt>LTPA_DominoSecret</tt> field from the SSO configuration document.
 * @return The contents of the cookie. If the cookie is invalid (the hash - or some other - test fails), this method returns
 * <tt>null</tt>.
 * @throws NoSuchAlgorithmException
 * @throws Base64DecodeException
 */
public static TokenData parseLtpaToken( String ltpaToken, String ltpaSecretStr ) throws NoSuchAlgorithmException,
        Base64DecodeException {
    byte[] data = HttpUtils.base64Decode( ltpaToken );

    int variableLength = data.length - hashLength;
    /* Compare to 20 to since variableLength must be at least (preUserDataLength + 1) [21] character long:
     * Token version: 4 bytes
     * Token creation: 8 bytes
     * Token expiration: 8 bytes
     * User name: variable length > 0
     */
    if( variableLength <= preUserDataLength ) return null;

    byte[] ltpaSecret = HttpUtils.base64Decode( ltpaSecretStr );

    if( !validateSHA( data, variableLength, ltpaSecret ) ) return null;

    if( !compareBytes( data, 0, ltpaTokenVersion, 0, ltpaTokenVersion.length ) ) return null;

    TokenData ret = new TokenData();
    ret.tokenCreated.setTimeInMillis( (long)Integer.parseInt( getString( data, creationDatePosition, dateStringLength ), 16 ) * 1000 );
    ret.tokenExpiration
            .setTimeInMillis( (long)Integer.parseInt( getString( data, expirationDatePosition, dateStringLength ), 16 ) * 1000 );

    byte[] nameBuffer = new byte[ data.length - ( preUserDataLength + hashLength ) ];
    System.arraycopy( data, preUserDataLength, nameBuffer, 0, variableLength - preUserDataLength );
    ret.username = new String( nameBuffer );

    return ret;
}

private static boolean validateSHA( byte[] ltpaTokenData, int variableLength, byte[] ltpaSecret ) throws NoSuchAlgorithmException {
    MessageDigest sha1 = MessageDigest.getInstance( "SHA-1" );

    byte[] digestData = new byte[ variableLength + ltpaSecret.length ];

    System.arraycopy( ltpaTokenData, 0, digestData, 0, variableLength );
    System.arraycopy( ltpaSecret, 0, digestData, variableLength, ltpaSecret.length );

    byte[] digest = sha1.digest( digestData );

    if( digest.length > ltpaTokenData.length - variableLength ) return false;

    int bytesToCompare = ( digest.length <= ltpaTokenData.length - variableLength ) ? digest.length : ltpaTokenData.length
            - variableLength;

    return compareBytes( digest, 0, ltpaTokenData, variableLength, bytesToCompare );
}

private static boolean compareBytes( byte[] b1, int offset1, byte[] b2, int offset2, int len ) {
    if( len < 0 ) return false;
    // offset must be positive, and the compare chunk must be contained the buffer
    if( ( offset1 < 0 ) || ( offset1 + len > b1.length ) ) return false;
    if( ( offset2 < 0 ) || ( offset2 + len > b2.length ) ) return false;

    for( int i = 0; i < len; i++ )
        if( b1[ offset1 + i ] != b2[ offset2 + i ] ) return false;

    return true;
}

/** Convert bytes from the buffer into a String.
 * @param buffer - the buffer to take the bytes from.
 * @param offset - the offset in the buffer to start at.
 * @param len - the number of bytes to convert into a String.
 * @return - A String representation of specified bytes.
 */
private static String getString( byte[] buffer, int offset, int len ) {
    if( len < 0 ) return "";
    if( ( offset + len ) > buffer.length ) return "";

    byte[] str = new byte[ len ];
    System.arraycopy( buffer, offset, str, 0, len );
    return new String( str );
}

/** Create a valid LTPA token for the specified user. The creation time is <tt>now</tt>.
 * @param username - the user to create the LTPA token for.
 * @param durationMinutes - the duration of the token validity in minutes.
 * @param ltpaSecretStr - the LTPA Domino Secret to use to create the token.
 * @return - base64 encoded LTPA token, ready for the cookie.
 * @throws NoSuchAlgorithmException
 * @throws Base64DecodeException
 */
public static String createLtpaToken( String username, int durationMinutes, String ltpaSecretStr ) throws NoSuchAlgorithmException,
        Base64DecodeException {
    return createLtpaToken( username, new GregorianCalendar(), durationMinutes, ltpaSecretStr );
}

/** Create a valid LTPA token for the specified user.
 * @param username - the user to create the LTPA token for.
 * @param creationTime - the time the token becomes valid.
 * @param durationMinutes - the duration of the token validity in minutes.
 * @param ltpaSecretStr - the LTPA Domino Secret to use to create the token.
 * @return - base64 encoded LTPA token, ready for the cookie.
 * @throws NoSuchAlgorithmException
 * @throws Base64DecodeException
 */
public static String createLtpaToken( String username, GregorianCalendar creationTime, int durationMinutes, String ltpaSecretStr )
        throws NoSuchAlgorithmException, Base64DecodeException {
    // create byte array buffers for both strings
    byte[] ltpaSecret = HttpUtils.base64Decode( ltpaSecretStr );
    byte[] usernameArray = username.getBytes();

    byte[] workingBuffer = new byte[ preUserDataLength + usernameArray.length + ltpaSecret.length ];

    // copy version into workingBuffer
    System.arraycopy( ltpaTokenVersion, 0, workingBuffer, 0, ltpaTokenVersion.length );

    GregorianCalendar expirationDate = (GregorianCalendar)creationTime.clone();
    expirationDate.add( Calendar.MINUTE, durationMinutes );

    // copy creation date into workingBuffer
    String hex = dateStringFiller + Integer.toHexString( (int)( creationTime.getTimeInMillis() / 1000 ) ).toUpperCase();
    System
            .arraycopy( hex.getBytes(), hex.getBytes().length - dateStringLength, workingBuffer, creationDatePosition,
                    dateStringLength );

    // copy expiration date into workingBuffer
    hex = dateStringFiller + Integer.toHexString( (int)( expirationDate.getTimeInMillis() / 1000 ) ).toUpperCase();
    System.arraycopy( hex.getBytes(), hex.getBytes().length - dateStringLength, workingBuffer, expirationDatePosition,
            dateStringLength );

    // copy user name into workingBuffer
    System.arraycopy( usernameArray, 0, workingBuffer, preUserDataLength, usernameArray.length );

    // copy the ltpaSecret into the workingBuffer
    System.arraycopy( ltpaSecret, 0, workingBuffer, preUserDataLength + usernameArray.length, ltpaSecret.length );

    byte[] hash = createHash( workingBuffer );

    // put the public data and the hash into the outputBuffer
    byte[] outputBuffer = new byte[ preUserDataLength + usernameArray.length + hashLength ];
    System.arraycopy( workingBuffer, 0, outputBuffer, 0, preUserDataLength + usernameArray.length );
    System.arraycopy( hash, 0, outputBuffer, preUserDataLength + usernameArray.length, hashLength );

    return HttpUtils.base64Encode( outputBuffer );
}

private static byte[] createHash( byte[] buffer ) throws NoSuchAlgorithmException {
    MessageDigest sha1 = MessageDigest.getInstance( "SHA-1" );
    return sha1.digest( buffer );
}

private static boolean fieldContainsValue( Vector values, Item item ) throws NotesException {
    for( int i = 0; i < values.size(); i++ ) {
        if( item.containsValue( values.get( i ) ) ) return true;
    }
    return false;
}

/** Get the contents of the LTPA_Secret field of the correct SSO configuration document based on the Internet site host name.
 * @param names - the <strong>names.nsf</strong> database.
 * @param siteName - the Internet host name of the site to generate/verify Domino cookie for. 
 * @return A class containing LTPA data, <tt>null</tt> if no matching SSO configuration document was found.
 * @throws NotesException
 * @throws UnknownHostException
 */
public static LtpaData getLtpaSecret( Database names, String siteName ) throws NotesException {
    Vector searchValues = new Vector();
    searchValues.add( siteName );

    try {
        InetAddress addr = InetAddress.getByName( siteName );
        searchValues.add( addr.getHostAddress() );
    } catch( UnknownHostException e ) {
        // do nothing
    }

    View internetSites = names.getView( "($InternetSites)" );
    String ssoQuery = "LtpaToken";

    Vector vRecycle = new Vector( internetSites.getEntryCount() );
    Document webSite = internetSites.getFirstDocument();
    while( webSite != null ) {
        vRecycle.add( webSite );
        if( webSite.getItemValueString( "Form" ).equalsIgnoreCase( "WebSite" )
                && webSite.getItemValueString( "Type" ).equalsIgnoreCase( "WebSite" ) ) {
            // The correct type of document
            if( fieldContainsValue( searchValues, webSite.getFirstItem( "ISiteAdrs" ) ) ) {
                ssoQuery = webSite.getItemValueString( "ISiteOrg" ) + ":" + webSite.getItemValueString( "HTTP_SSOCfg" );
                break;
            }
        }
        webSite = internetSites.getNextDocument( webSite );
    }
    internetSites.recycle( vRecycle ); // recycle all the collected documents
    internetSites.recycle();

    View ssoConfigs = names.getView( "($WebSSOConfigs)" );
    Document ssoConfigDoc = ssoConfigs.getDocumentByKey( ssoQuery );
    ssoConfigs.recycle();

    if( ssoConfigDoc == null )
        throw new NotesException( NotesError.NOTES_ERR_SSOCONFIG, "Site \"" + siteName + "\" SSO config document not found." );

    LtpaData ret = new LtpaData( ssoConfigDoc.getItemValueString( "LTPA_DominoSecret" ), ssoConfigDoc
            .getItemValueInteger( "LTPA_TokenExpiration" ), ssoConfigDoc.getItemValueString( "LTPA_TokenDomain" ) );

    ssoConfigDoc.recycle();

    return ret;
}
}

 

 

 

package com.nil.ltpa;

public class LtpaData {

public String    ltpaSecret;
public String    tokenDomain;
public int    tokenExpiration;

public LtpaData() {
    ltpaSecret = "";
    tokenDomain = "";
    tokenExpiration = 0;
}

public LtpaData( String ltpaSecret, int tokenExpiration, String tokenDomain ) {
    super();
    this.ltpaSecret = ltpaSecret;
    this.tokenExpiration = tokenExpiration;
    this.tokenDomain = tokenDomain;
}

/* (non-Javadoc)
 * @see java.lang.Object#toString()
 */
public String toString() {
    return "LTPAData {Secret=" + ltpaSecret + ", expiration=" + tokenExpiration + ", domain=" + tokenDomain + "}";
}

}

 

package com.nil.ltpa;

import java.util.GregorianCalendar;
import java.util.SimpleTimeZone;

public class TokenData {
public static final SimpleTimeZone    utcTimeZone    = new SimpleTimeZone( 0, "UTC" );

public String                    username;
public GregorianCalendar            tokenCreated;
public GregorianCalendar            tokenExpiration;

public TokenData() {
    username = "";
    tokenCreated = new GregorianCalendar( utcTimeZone );
    tokenCreated.setTimeInMillis( 0 );
    tokenExpiration = new GregorianCalendar( utcTimeZone );
    tokenExpiration.setTimeInMillis( 0 );
}

public String toString() {
    StringBuffer buf = new StringBuffer();

    buf.append( "[ username:" ).append( username ).append( ", tokenCreated: " ).append( tokenCreated.getTime().toString() );
    buf.append( ", tokenExpiration: " ).append( tokenExpiration.getTime().toString() ).append( " ]" );

    return buf.toString();
}
}

 

package com.nil.helpers;

import java.util.Vector;

import com.nil.exception.Base64DecodeException;

public class HttpUtils {

private static final String    base64Chars    = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

public static final String base64Encode( byte[] bytes ) {
    if( bytes == null ) return null;

    StringBuffer ret = new StringBuffer();

    for( int sidx = 0, didx = 0; sidx < bytes.length; sidx += 3, didx += 4 ) {
        ret.append( base64Chars.charAt( ( bytes[ sidx ] >>> 2 ) & 077 ) );
        if( sidx + 1 < bytes.length ) {
            ret.append( base64Chars.charAt( ( bytes[ sidx + 1 ] >>> 4 ) & 017 | ( bytes[ sidx ] << 4 ) & 077 ) );
            if( sidx + 2 < bytes.length )
                ret.append( base64Chars.charAt( ( bytes[ sidx + 2 ] >>> 6 ) & 003 | ( bytes[ sidx + 1 ] << 2 ) & 077 ) );
            else
                ret.append( base64Chars.charAt( ( bytes[ sidx + 1 ] << 2 ) & 077 ) );
            if( sidx + 2 < bytes.length ) ret.append( base64Chars.charAt( bytes[ sidx + 2 ] & 077 ) );
        } else
            ret.append( base64Chars.charAt( ( bytes[ sidx ] << 4 ) & 077 ) );
    }

    int mod = ret.length() % 4;
    for( int i = 0; ( mod > 0 ) && ( i < 4 - mod ); i++ )
        ret.append( '=' );

    return ret.toString();
} // public static final String base64Encode( byte[] bytes )

public static final byte[] base64Decode( String data ) throws Base64DecodeException {
    if( data.length() == 0 ) return new byte[ 0 ];
    Vector dest = new Vector( data.length() );

    // ASCII printable to 0-63 conversion
    int prevBits = 0; // stores the bits left over from the previous step
    int modAdjust = 0; // stores the start of the current line.
    for( int i = 0; i < data.length(); i++ ) {
        char ch = data.charAt( i ); // get the character
        if( ch == '=' ) break; // is it the padding character, no check for correct position
        int mod = ( i - modAdjust ) % 4; // what is the index modulo 4 in the current line
        if( mod == 0 ) {
            // the line can only be broken on modulo 0 (e.g. 72, 76 character per line. MIME specifies 76 as max).
            if( ( ch == '\r' ) || ( ch == '\n' ) ) { // we handle the encoders that use '\n' only as well
                modAdjust = i + 1; // skip the [CR/]LF sequence. The new line probably starts at i + 1;
                continue;
            }
        }
        // if we came to here, there was no special character
        int x = base64Chars.indexOf( ch ); // search for the character in the table
        if( x < 0 ) throw new Base64DecodeException(); // if the character was not found raise an exception
        switch( mod ) {
            case 0:
                prevBits = x << 2; // just store the bits and continue
                break;
            case 1:
                dest.add( new Byte( (byte)( prevBits | x >>> 4 ) ) ); // previous 6 bits OR 2 new ones
                prevBits = ( x & 017 ) << 4; // store 4 bits
                break;
            case 2:
                dest.add( new Byte( (byte)( prevBits | x >>> 2 ) ) ); // previous 4 bits OR 4 new ones
                prevBits = ( x & 003 ) << 6; // store 2 bits
                break;
            case 3:
                dest.add( new Byte( (byte)( prevBits | x ) ) ); // previous 2 bits OR 6 new ones
                break;
        }
    }

    byte[] ret = new byte[ dest.size() ]; // convert the Vector into an array
    for( int i = 0; i < ret.length; i++ )
        ret[ i ] = ( (Byte)dest.get( i ) ).byteValue();

    return ret;
}

public static final boolean isBase64Encoded( String sBase64 ) {
    int len = sBase64.length();
    if( len % 4 != 0 ) return false;
    for( int i = 0; i < len; i++ ) {
        char c = sBase64.charAt( i );
        if( ( c >= 'a' ) && ( c <= 'z' ) ) continue;
        if( ( c >= 'A' ) && ( c <= 'Z' ) ) continue;
        if( ( c >= '0' ) && ( c <= '9' ) ) continue;
        if( ( c == '+' ) || ( c == '/' ) || ( c == '=' ) ) continue;
        return false;
    }
    return true;
}

}

 

package com.nil.exception;

public class Base64DecodeException extends Exception {
/**
 *
 */
private static final long    serialVersionUID    = -5600202677007235761L;

/**
 *
 */
public Base64DecodeException() {
    // Auto-generated constructor stub
}

/**
 * @param argMessage
 */
public Base64DecodeException( String argMessage ) {
    super( argMessage );
}

/**
 * @param argCause
 */
public Base64DecodeException( Throwable argCause ) {
    super( argCause );
}

/**
 * @param argMessage
 * @param argCause
 */
public Base64DecodeException( String argMessage, Throwable argCause ) {
    super( argMessage, argCause );
}

}




For what it's worth, here's my test code that sits on top of the ltpa libraries (debugging bits and all).

import lotus.domino.*;
import com.nil.exception.*;
import com.nil.helpers.*;
import com.nil.ltpa.*;
import java.io.PrintWriter;

public class JavaAgent extends AgentBase {

public String getCookieValue(String cookie, String thisName) {
String prefix = thisName + "=";
String result;
int begin;
int ending;
if (cookie.equals("")) { return ""; }
begin= cookie.indexOf(prefix);
if (begin == 0) {
return "";
}
result = cookie.substring(begin + prefix.length(), cookie.length());
ending = result.indexOf(";") - 1;
if (ending <= -1) return result;
return result.substring(0, ending + 1);
}

public void NotesMain() {

try {
Session session = getSession();
AgentContext agentContext = session.getAgentContext();
PrintWriter pw = getAgentOutput();
try {
Document cDoc = agentContext.getDocumentContext();
Database names;
String token;
LtpaData ltpaData;
TokenData td;
String domain = ".example.com"; //Put your domain here, as listed in the SSO document in the directory
String oldCookie;

oldCookie = getCookieValue(cDoc.getItemValueString("http_cookie"), "LtpaToken");
//pw.println(oldCookie + "<br>");

names = session.getDatabase(session.getServerName(), "names.nsf");
ltpaData = LtpaLibrary.getLtpaSecret( names, domain) ;
pw.println(ltpaData.ltpaSecret + "<br>");

if (oldCookie != "") {
td = LtpaLibrary.parseLtpaToken(oldCookie, ltpaData.ltpaSecret);
if (td == null) {
pw.println ("Cookie not decoded successfully");
} else {
pw.println(td.username + "<br><b>");
pw.println(td.tokenCreated + "<br><b>");
pw.println(td.tokenExpiration + "<br><b>");
pw.println(td.utcTimeZone + "<br><b>");
}
}

token = LtpaLibrary.createLtpaToken( "CN=User Name/O=org", ltpaData.tokenExpiration, ltpaData.ltpaSecret ) ;
pw.println(token + "</b><br>");

pw.println("Set-Cookie:LtpaToken="+token+"; domain="+ltpaData.tokenDomain+"; path=/");
pw.println("Location:http://server.example.org/targetdb.nsf");
} catch (Exception e) {
e.printStackTrace(pw);
}

} catch(Exception e) {
e.printStackTrace();
}
}

}


http://www-10.lotus.com/ldd/dominowiki.nsf/dx/Generating_LTPA_tokens_using_a_Java_servlet

http://wrschneider.blogspot.com/2011/10/quick-and-dirty-sso-with-ltpa.html

http://offbytwo.com/2007/08/21/working-with-ltpa.html

http://openntf.org/XSnippets.nsf/snippet.xsp?id=ltpatoken-generator-for-multi-server-sso-configurations

http://www.openntf.org/projects/pmt.nsf/ProjectLookup/DominoTomcatSSO

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值