LTPA Cookie原理

1. 什么是LTPA?

Lightweight Third-Party Authentication (LTPA)是IBM Websphere和Domino产品中使用单点登录技术。当服务器配置好LTPA认证方式,用户通过浏览器成功登录后,服务器会自动发送一个session cookie给浏览器;此cookie中包含一个LTPA Token。


2. WebSphere部分

本部分描述适用于已实施WebSphere系列产品应用和Domino平台应用,或WebSphere与Domino之间已完成单点登录。在这样的环境中与构异系统实现单点登录。

一个通过有效的LTPA Cookie能够在同一个认证域中所有服务器被自动认证。此Cookie中包含认证信息和时间戳。这些信息通过共享的3DES Key进行了bis 加密。使用公共密钥/私有密钥进行签名。

LTPA Cookie通过3DES密钥使用DESede/ECB/PKCS5P进行加密。此密钥也是采用DESede/ECB/PKCS5P进行加密,加密后再使用提供的密码再进行SHA Hash,生成24个字节的密钥,再进行Base64编码。

如果你要解析LTPA Token,先得使用Key的密码,生成3DES密钥;再使用3DES密钥解密Token Cookie。也可以使用公共/私有密钥来签名或验证LTPA Cookie。


2.1 WebSphere LTPA 生成原理

首先,这个 cookie 由以下部分组成,以%进行分隔:

  • 用户信息,格式为u:user\:<RealmName>/<UserDN>,如:u:user\:VGOLiveRealm/CN=squallzhong,O=VGOLive Technology
  • 过期时间
  • 签名信息,如:

    u:user\:VGOLiveRealm/CN=squallzhong,O=VGOLive Technology%1301558320666%Cy2CAeru5kEElGj0hrvYsKW2ZVsvvcu6Un573aeX55OO4G3EMYWc0e/ZbqDp1z7MS+dLzniuUH4sYWCMpnKdm7ZGabwmV+WcraBl+y+yzwcl722gHVMOnDZAW7U3jEay9Tk2yG4yXkMWU+617xndpVxke2jtS5wIyVVM3q7UDPw=


2.2 异构系统所需信息

从WebSphere系统中导出ltpa的key文件,使用文本文件打开,如:

com.ibm.websphere.CreationDate=Thu Mar 31 11\:08\:09 GMT+08\:00 2011 com.ibm.websphere.ltpa.version=1.0 com.ibm.websphere.ltpa.3DESKey=7dH4i81YepbVe+gF9XVUzE4C1Ca5g6A4Q69OFobJV9g\= com.ibm.websphere.CreationHost=wasserver com.ibm.websphere.ltpa.PrivateKey=N3bnOE1IbiXNsHXxxemC98iiCnmtw3JUuQvdFjEyh9r2gu+FlQRmG8xp5RBltqc6raI4EgYFhTr+t5/tmRQrFqfNKgvujeJZODeCspohi1V4C0qit7DOoqD9xOOn9Rzdb4PIuJM3ekwuBiZZYTYu7q0TANDygc7VbmwoD3xMPCk5svyvFJ/VshPyg5f7Q+VNM8dlIitU4gK9Qp8VZEqjGoXsYYzYYTQgnwAVtR2GfZtXKlf24EPXSkgUz9j8FwTvcylcKwjS22d6eVjciyAzInnxPqxE2iMRPEFDatHZFox3flsqBswmeDQrAGv8zIiffgP1DLKdjozUyAG+50v97xx7u1RtIrB4B01ik8DuLhw\= com.ibm.websphere.ltpa.Realm=VGOLiveRealm com.ibm.websphere.ltpa.PublicKey=AM04If2+ElGSyVRF0ZEesgvC59vGw8gSIfptjfoXj8iz4C7Ip/KVAu2PDkpQi3LUN/FgVF696tmsegBThks9rmMMHzOix/vGP2721dQZKbD7plOLdWtiY2AYZChsBVkOF26DfiWJ6euxD+a+KNcrfDnu2AXRC/tKncIUJV4LbeJdAQAB

  • 所使用的DNS域,如:vgolive.com
  • 过期时间(分钟),如:30
  • LTPA 3DESKey 密钥及密钥的保护密码
  • Base DN,如:O=VGOLive Technology或DC=vgolive,DC=com

注:

  • 在3DESKey中的反斜杠只是为了在JAVA中可解释等于号,所以正确的3DESKey为7dH4i81YepbVe+gF9XVUzE4C1Ca5g6A4Q69OFobJV9g=
  • 用户名可以通过Base Dn验证字进行拼接,如异构系统中用户名为SquallZhong,相应在Domino中的DN就是CN=SquallZhong,O=DigiWin。此类情况仅限于LDAP中的CN与异构系统中的用户名一致。如果不一致,需要异构系统做用户对应

2.3 实现

Base64解码/编码所需Jar包:
apache-commons-codec-1.3.jar以上

SHA-1的校验使用
java.security.MessageDigest

2.3.1 解析

以下代码为解析从WebSphere或Domino发送过来的LTPAToken Cookie以Java为例:

01

02
        // LTPA 3DES 密钥

03
        String ltpa3DESKey ="7dH4i81YepbVe+gF9XVUzE4C1Ca5g6A4Q69OFobJV9g=";

04
        // LTPA 密钥密码

05
        String ltpaPassword ="Passw0rd";

06
        try {

07
            // 获得加密key

08
            byte[] secretKey = getSecretKey(ltpa3DESKey, ltpaPassword);

09
            // 使用加密key解密ltpa Cookie

10
            String ltpaPlaintext =new String(decryptLtpaToken(tokenCipher,

11
                    secretKey));

12
            displayTokenData(ltpaPlaintext);

13
        }catch (Exception e) {

14
            System.out.println("Caught inner: " + e);

15
        }

16

17
    //获得安全Key

18
    private static byte[] getSecretKey(String ltpa3DESKey, String password)

19
            throws Exception {

20
        // 使用SHA获得key密码的hash值

21
        MessageDigest md = MessageDigest.getInstance("SHA");

22
        md.update(password.getBytes());

23
        byte[] hash3DES =new byte[24];

24
        System.arraycopy(md.digest(),0, hash3DES,0,20);

25
        // 使用0替换后4个字节

26
        Arrays.fill(hash3DES,20,24, (byte)0);

27
        // BASE64解码 ltpa3DESKey

28
        byte[] decode3DES = Base64.decodeBase64(ltpa3DESKey.getBytes());

29
        // 使用key密码hash值解密已Base64解码的ltpa3DESKey

30
        return decrypt(decode3DES, hash3DES);

31
    }

32
    //解密LtpaToken

33
    public static byte[] decryptLtpaToken(String encryptedLtpaToken,byte[] key)

34
            throws Exception {

35
        // Base64解码LTPAToken

36
        final byte[] ltpaByteArray = Base64.decodeBase64(encryptedLtpaToken

37
                .getBytes());

38
        // 使用key解密已Base64解码的LTPAToken

39
        return decrypt(ltpaByteArray, key);

40
    }

41
    // DESede/ECB/PKC5Padding解方法

42
    public static byte[] decrypt(byte[] ciphertext,byte[] key)

43
            throws Exception {

44
        final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");

45
        final KeySpec keySpec =new DESedeKeySpec(key);

46
        final Key secretKey = SecretKeyFactory.getInstance("TripleDES")

47
                .generateSecret(keySpec);

48
        cipher.init(Cipher.DECRYPT_MODE, secretKey);

49
        return cipher.doFinal(ciphertext);

50
    }

51

解析出来的LTPAToken信息以%分隔


2.3.2 生成

Websphere LTPA生成时的签名信息是由用户DN和一些用户其他信息组成字符串,使用私有密钥进行签名,由于不清楚这些信息的组成,故无法产生正确的LTPA。


3. Domino部分

本部分的描述仅适用于单一的Domino平台应用与构异系统实现单点登录。


3.1 Domino LTPA Cookie 生成原理

在与 Domino 做 SSO 的时候,会使用 LTPA Token的认证方式,本文描述它的生成原理,通过它我们可以自己编码生成身份认证的 cookie,实现 SSO。

首先,这个 cookie 由以下部分组成:

  • LTPA token 版本(4字节)
  • 创建时间(8字节)
  • 过期时间(8字节)
  • 用户名(可变长度)
  • Domino LTPA 密钥(20字节)

接下来分别说明各部分的具体内容:

  • LTPA token 版本目前 Domino 只有一种值:0x0001
  • 创建时间为以十六进制方式表示的Unix time,例如:2009-04-09 13:52:42 (GMT +8) = 1239256362 = 49DD8D2A。
  • 过期时间=创建时间 + SSO 配置文档的过期时间(LTPA_TokenExpiration域)
  • 用户名为 Names 中用户文档的FullName域值;如:Squall Zhong/Digiwin
  • Domino LTPA 密钥通过 Base64编码后,保存在 SSO 配置文档的LTPA_DominoSecret域中

当然不能将密钥直接发送给浏览器,所以将上述部分合并起来(如上图),计算 SHA-1 校验和。

然后用 SHA-1 校验和替换掉 Domino LTPA 密钥,最后再将内容通过 Base64 编码,形成最终的 cookie 发送给浏览器(如上图)。这样如果 cookie 中的任何内容被修改,校验和就不对了,达到了防篡改的效果。所以最终LTPA Cookie所得到的值为以下公式组成:

SHA-1=LTPA版本号+创建时间+过期时间+用户名+Domino LTPA 密钥

LTPA Cookie= Base64(LTPA版本号+创建时间+过期时间+用户名+SHA-1)


3.2 异构系统所需信息

  • Domino 所使用的DNS域,如:vgolive.com
  • 过期时间(分钟),如:30
  • Domino LTPA 密钥
  • Domino验证字名称,如:/O=VGOLive Technology

注:用户名可以通过Domino验证字进行拼接,如异构系统中用户名为SquallZhong,相应在Domino中的DN就是CN=SquallZhong/O=VGOLive Technology。此类情况仅限于Domino中的CN与异构系统中的用户名一致。如果不一致,需要异构系统做用户对应


3.3 实现

Base64解码/编码所需Jar包:apache-commons-codec-1.3.jar以上

SHA-1的校验使用
java.security.MessageDigest

转换字符集使用:
Cp850


3.3.1 解析

01
    import org.apache.commons.codec.binary.Base64;

02
…...

03
final String CHARSET ="Cp850";

04
byte[] dominoSecret = Base64.decodeBase64(ltpaDominoSecret.getBytes());

05
byte[] ltpa = Base64.decodeBase64(ltpaToken.getBytes());

06
ByteArrayInputStream stream =new ByteArrayInputStream(ltpa);

07
int usernameLength = ltpa.length –40;

08
byte header[] =new byte[4];

09
byte creation[] =new byte[8];

10
byte expires[] =new byte[8];

11
byte username[] =new byte[usernameLength];

12
byte[] sha =new byte[20];

13
// 读取LTPAToken版本号

14
stream.read(header,0,4);

15
if (header[0] !=0 || header[1] !=1 || header[2] !=2|| header[3] !=3)

16
        throw new IllegalArgumentException("Invalid ltpaToken format");

17
    // 读取开始时间

18
   stream.read(creation,0,8);

19
   // 读取到期时间

20
   stream.read(expires,0,8);

21
   // 读取Domino用户DN

22
   stream.read(username,0, usernameLength);

23
   // 读取SHA校验和

24
   stream.read(sha,0,20);

25
    // 转换用户名

26
   char characters[] =new char[usernameLength];

27
   try {

28
        InputStreamReader isr =new InputStreamReader(

29
            new ByteArrayInputStream(username),

30
            CHARSET);

31
        isr.read(characters);

32
    }catch (Exception e) {

33
    }

34
    // 获得Domino用户DN

35
    String dn =new String(characters);

36
    // 获得创建时间

37
   Date creationDate =new Date(

38
        Long.parseLong(new String(creation),16) *1000);

39
    // 获得到期时间

40
   Date expiresDate =new Date(

41
        Long.parseLong(new String(expires),16) *1000);

42
…...

43
// 创建LTPA Token

44
    ByteArrayOutputStream ostream =new ByteArrayOutputStream();

45
    try {

46
        // LTPA Token版本号

47
        ostream.write(header);

48
        // 创建时间

49
        ostream.write(creation);

50
        // 过期时间

51
        ostream.write(expires);

52
        // Domino用户DN,如CN=SquallZhong/O=DigiWin

53
        ostream.write(username);

54
        // Domino LTPA 密钥

55
        ostream.write(dominoSecret);

56
        ostream.close();

57
    }catch (IOException e) {

58
        throw new RuntimeException(e);

59
    }

60
    // 进行 SHA-1 校验和

61
    MessageDigest md;

62
    try {

63
        md = MessageDigest.getInstance("SHA-1");

64
        md.reset();

65
    }catch (NoSuchAlgorithmException e) {

66
        throw new RuntimeException(e);

67
    }

68
    byte[] digest = md.digest(ostream.toByteArray());

69
    // 完成 SHA-1 校验和,digest长度为20

70
    boolean valid = MessageDigest.isEqual(digest, sha);


3.3.2 生成

01
    /**

02
     * 为指定用户创建有效的LTPA Token.创建时间为<tt>now</tt>.

03
     *

04
     * @param username

05
     *            - 用户名,注:使用用户全称,如:CN=SquallZhong/O=VGOLive Technology

06
     * @param creationTime

07
     *            - 创建时间

08
     * @param durationMinutes

09
     *            - 到期时间,单位:分钟

10
@param ltpaSecretStr

11
     *            - Domino Ltpa 加密字符串

12
     * @return - 返回已Base64编码的Ltpa Cookie.

13
     * @throws NoSuchAlgorithmException

14
     * @throws Base64DecodeException

15
     */

16
    public static String createLtpaToken(String username,

17
            GregorianCalendar creationTime,int durationMinutes,

18
            String ltpaSecretStr)throws NoSuchAlgorithmException {

19
        // Base64解码ltpaSecretStr

20
        byte[] ltpaSecret = Base64.decodeBase64(ltpaSecretStr.getBytes());

21
        // 用户名字节数组

22
        byte[] usernameArray = username.getBytes();

23
        byte[] workingBuffer =new byte[preUserDataLength

24
                + usernameArray.length + ltpaSecret.length];

25

26
        // 设置ltpaToken版本至workingBuffer

27
        System.arraycopy(ltpaTokenVersion,0, workingBuffer,0,

28
                ltpaTokenVersion.length);

29
        // 获得过期时间,过期时间=当前时间+到期时间(分钟)

30
        GregorianCalendar expirationDate = (GregorianCalendar) creationTime

31
                .clone();

32
        expirationDate.add(Calendar.MINUTE, durationMinutes);

33

34
        // 转换创建时间至16进制字符串

35
        String hex = dateStringFiller

36
                + Integer.toHexString(

37
                        (int) (creationTime.getTimeInMillis() /1000))

38
                        .toUpperCase();

39
        // 设置创建时间至workingBuffer

40
        System.arraycopy(hex.getBytes(), hex.getBytes().length

41
                - dateStringLength, workingBuffer, creationDatePosition,

42
                dateStringLength);

43

44
        // 转换过期时间至16进制字符串

45
        hex = dateStringFiller

46
                + Integer.toHexString(

47
                        (int) (expirationDate.getTimeInMillis() /1000))

48
                        .toUpperCase();

49
        // 设置过期时间至workingBuffer

50
        System.arraycopy(hex.getBytes(), hex.getBytes().length

51
                - dateStringLength, workingBuffer, expirationDatePosition,

52
                dateStringLength);

53

54
        // 设置用户全称至workingBuffer

55
        System.arraycopy(usernameArray,0, workingBuffer, preUserDataLength,

56
                usernameArray.length);

57

58
        // 设置已Base64解码ltpaSecret至workingBuffer

59
        System.arraycopy(ltpaSecret,0, workingBuffer, preUserDataLength

60
                + usernameArray.length, ltpaSecret.length);

61
        // 创建Hash字符串

62
        byte[] hash = createHash(workingBuffer);

63

64
        // ltpaToken版本+开始时间(16进制)+到期时间(16进制)+用户全名+SHA-1(ltpaToken版本+开始时间(16进制)+到期时间(16进制)+用户全名)

65
        byte[] outputBuffer =new byte[preUserDataLength + usernameArray.length

66
                + hashLength];

67
        System.arraycopy(workingBuffer,0, outputBuffer,0, preUserDataLength

68
                + usernameArray.length);

69
        System.arraycopy(hash,0, outputBuffer, preUserDataLength

70
                + usernameArray.length, hashLength);

71
        // 返回已Base64编码的outputBuffer

72
        return new String(Base64.encodeBase64(outputBuffer));

73
    }

74
…...


4. 通过F5 BIG-IP创建Domino LTPAToken

F5 iRule代码如下:

when RULE_INIT {

01
set cookie_name"LtpaToken"           # 不更改

02
 set ltpa_version"\x00\x01\x02\x03"   # 不更改

03
 set ltpa_secret"b64encodedsecretkey" # 从Domino SSO文档获得ltpa密钥

04
 set ltpa_timeout"1800"               # 从Domino SSO文档中获得过期时间,单位:秒

05
}

06

07
when HTTP_REQUEST {

08
 #

09
 # Do your usual F5 HTTP authentication here

10
 #

11
 # Initial values

12
 set creation_time_temp [clock seconds]

13
 set creation_time [format %X $creation_time_temp]

14
 set expr_time_temp [expr { $creation_time_temp+ $::ltpa_timeout}]

15
 set expr_time [format %X $expr_time_temp]

16
 set username [HTTP::username]

17
 set ltpa_secret_decode [b64decode $::ltpa_secret]

18
 # First part of token

19
 set cookie_data_raw {}

20
 append cookie_data_raw $::ltpa_version

21
 append cookie_data_raw $creation_time

22
 append cookie_data_raw $expr_time

23
 append cookie_data_raw $username

24
 append cookie_data_raw $ltpa_secret_decode

25
 # SHA1 of first part of token

26
 set sha_cookie_raw [sha1 $cookie_data_raw]

27
 # Final not yet encoded token

28
 set ltpa_token_raw {}

29
 append ltpa_token_raw $::ltpa_version

30
 append ltpa_token_raw $creation_time

31
 append ltpa_token_raw $expr_time

32
 append ltpa_token_raw $username

33
 append ltpa_token_raw $sha_cookie_raw

34
 # Final Base64 encoded token

35
 set ltpa_token_final [b64encode $ltpa_token_raw]

36
 # Insert the cookie

37
 HTTP::cookie insert name $::cookie_name value $ltpa_token_final

38
 }

39
 # Remove Authorization HTTP header to avoid using basic authentication

40
 if { [HTTP::header exists"Authorization"] } {

41
 HTTP::header remove"Authorization"

42
 }

43
}

LTPA Cookie原理

时间: 2024-10-11 06:30:35

LTPA Cookie原理的相关文章

PHP版本--HTTP session cookie原理及应用

PHP 的COOKIE cookie 是一种在远程浏览器端储存数据并以此来跟踪和识别用户的机制.     PHP在http 协议的头信息里发送cookie,因此  setcookie()函数必须在其它信息被输出到浏览器前调用,这和对header()函数的限制类似. ---------------------------------------------------------------------------------------------------------------------

session cookie原理及应用

一.术语session在我的经验里,session这个词被滥用的程度大概仅次于transaction,更加有趣的是transaction与session在某些语境下的含义是相同的. session,中文经常翻译为会话,其本来的含义是指有始有终的一系列动作/消息,比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个session.有时候我们可以看到这样的话“在一个浏览器会话期间,...”,这里的会话一词用的就是其本义,是指从一个浏览器窗口打开到关闭这个期间 ①.最混乱的是“用户(客

Cookie 原理及作用

Cookie的含义是"服务器送给浏览器的甜点",即服务器在响应请求时可以将一些数据以"键-值"对的形式通过响应信息保存在客户端.当浏览器再次访问服务器中相同的应用时,会将之前的Cookie通过请求信息带到服务器端. 下面的Servlet展示了Cookie的功能.          public void doGet(HttpServletRequest request, HttpServletResponse response)                   

学习日常笔记&lt;day11&gt;cookie及session

1.会话管理 1.1会话管理定义 会话管理:管理浏览器客户端和服务端之间的会话过程中产生的会话数据 域对象:实现资源之间的数据共享 request 域对象 context 域对象 1.2.会话技术 Cookie技术:会话数据保存在浏览器客户端中 Session技术:会话数据保存在服务器端 2.Cookie技术 2.1 特点 Session技术:会话数据保存在服务器端 2.2Cookie技术核心 Cookie类:用于存储会话数据 1)构造Cookie对象 Cookie(java.lang.Stri

会话管理(Cookie/Session技术)

什么是会话:用户打开浏览器,点击多个超链接,访问服务器的多个web资源,然后关闭浏览器,整个过程就称为一个会话: 会话过程需要解决的问题:每个用户在使用浏览器与服务器进行会话的过程中,都可能会产生一些数据,这些输入如何来进行保存?比如用户在购物网站浏览的商品记录,用户添加购物车的记录等等这些信息如何进行存储?在程序中会话跟踪是一件非常重要的事情,一个用户的所有请求操作都应该属于同一个会话,而另一个人的所有请求操作应该属于另一个人,二者不能混淆!当想到需要在保存数据时,我们首先肯定会想到使用域对象

cookie session URL重写 与考试

状态管理.Cookie.Session.URL重写 HTTP协议:无状态的连接(每次连接都是新的请求)1.隐藏字段 <input type="hidden" name="session" value=""/> 在响应中进行操作2.Cookie 保存到客户端3.Session 保存到服务器4.用户禁用Cookie:使用URL重写 在URL后加上jsessionid=123; Cookie原理1.数据以"键-值"对的形

Cookie和Session的区别

一.Cookie原理 Cookie就是一个键和一个值构成的,随着服务器端的响应发送给客户端浏览器. 然后客户端浏览器会把Cookie保存起来,当下一次再访问服务器时把Cookie再发送给服务器. 二.session的原理 session底层是依赖Cookie的! 当首次使用session时,服务器端要创建session,session是保存在服务器端,而给客户端的session的id(一个cookie中保存了sessionId). 客户端带走的是sessionId,而数据是保存在session中

servlet中cookie和session操作

1.1 软件中的会话 一次会话: 打开浏览器 -> 访问一些服务器内容 -> 关闭浏览器 登录场景: 打开浏览器 -> 浏览到登陆页面 -> 输入用户名和密码 -> 访问到用户主页(显示用户名) 修改密码(输入原密码) 修改收货地址 ....... 问题:在此处登录会话过程中产生的数据(用户会话数据)如何保存下来呢? 购物场景: 打开浏览器 -> 浏览商品列表  -> 加入购物车(把商品信息保存下来)  -> 关闭浏览器 打开浏览器->  直接进入购物

Cookie知识点小结

问题是什么?有哪些技术?如何解决? 1. Cookie 1)完成回话跟踪的一种机制:采用的是在客户端保存Http状态信息的方案 2)Cookie是在浏览器访问WEB服务器的某个资源时,由WEB服务器在HTTP响应消息头附带传送给浏览器的一个小文本文件. 3)一旦WEB浏览器保存了某个Cookie,那么它在以后每次访问该WEB服务器时,都会在HTTP请求头中将这个Cookie回传给WEB服务器. 4)底层实现原理:WEB服务器通过在HTTP响应消息中增加Set-Cookie响应头字段将Cookie