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\:/,如: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 密钥

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

04// LTPA 密钥密码

05String ltpaPassword ="Passw0rd";

06try {

07// 获得加密key

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

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

10String ltpaPlaintext =new String(decryptLtpaToken(tokenCipher,

11secretKey));

12displayTokenData(ltpaPlaintext);

13}catch (Exception e) {

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

15}

16…

17//获得安全Key

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

19throws Exception {

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

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

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

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

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

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

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

27// BASE64解码 ltpa3DESKey

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

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

30return decrypt(decode3DES, hash3DES);

31}

32//解密LtpaToken

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

34throws Exception {

35// Base64解码LTPAToken

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

37.getBytes());

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

39return decrypt(ltpaByteArray, key);

40}

41// DESede/ECB/PKC5Padding解方法

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

43throws Exception {

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

45final KeySpec keySpec =new DESedeKeySpec(key);

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

47.generateSecret(keySpec);

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

49return 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 解析

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

02…...

03final String CHARSET ="Cp850";

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

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

06ByteArrayInputStream stream =new ByteArrayInputStream(ltpa);

07int usernameLength = ltpa.length –40;

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

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

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

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

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

13// 读取LTPAToken版本号

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

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

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

17// 读取开始时间

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

19// 读取到期时间

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

21// 读取Domino用户DN

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

23// 读取SHA校验和

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

25// 转换用户名

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

27try {

28InputStreamReader isr =new InputStreamReader(

29new ByteArrayInputStream(username),

30CHARSET);

31isr.read(characters);

32}catch (Exception e) {

33}

34// 获得Domino用户DN

35String dn =new String(characters);

36// 获得创建时间

37Date creationDate =new Date(

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

39// 获得到期时间

40Date expiresDate =new Date(

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

42…...

43// 创建LTPA Token

44ByteArrayOutputStream ostream =new ByteArrayOutputStream();

45try {

46// LTPA Token版本号

47ostream.write(header);

48// 创建时间

49ostream.write(creation);

50// 过期时间

51ostream.write(expires);

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

53ostream.write(username);

54// Domino LTPA 密钥

55ostream.write(dominoSecret);

56ostream.close();

57}catch (IOException e) {

58throw new RuntimeException(e);

59}

60// 进行 SHA-1 校验和

61MessageDigest md;

62try {

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

64md.reset();

65}catch (NoSuchAlgorithmException e) {

66throw new RuntimeException(e);

67}

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

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

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

3.3.2 生成

01/**

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

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*/

16public static String createLtpaToken(String username,

17GregorianCalendar creationTime,int durationMinutes,

18String ltpaSecretStr)throws NoSuchAlgorithmException {

19// Base64解码ltpaSecretStr

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

21// 用户名字节数组

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

23byte[] workingBuffer =new byte[preUserDataLength

24+ usernameArray.length + ltpaSecret.length];

25

26// 设置ltpaToken版本至workingBuffer

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

28ltpaTokenVersion.length);

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

30GregorianCalendar expirationDate = (GregorianCalendar) creationTime

31.clone();

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

33

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

35String hex = dateStringFiller

36+ Integer.toHexString(

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

38.toUpperCase();

39// 设置创建时间至workingBuffer

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

41- dateStringLength, workingBuffer, creationDatePosition,

42dateStringLength);

43

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

45hex = dateStringFiller

46+ Integer.toHexString(

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

48.toUpperCase();

49// 设置过期时间至workingBuffer

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

51- dateStringLength, workingBuffer, expirationDatePosition,

52dateStringLength);

53

54// 设置用户全称至workingBuffer

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

56usernameArray.length);

57

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

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

60+ usernameArray.length, ltpaSecret.length);

61// 创建Hash字符串

62byte[] hash = createHash(workingBuffer);

63

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

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

66+ hashLength];

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

68+ usernameArray.length);

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

70+ usernameArray.length, hashLength);

71// 返回已Base64编码的outputBuffer

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

73}

74…...

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

F5 iRule代码如下:

when RULE_INIT {

01set cookie_name"LtpaToken"           # 不更改

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

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

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

05}

06

07when HTTP_REQUEST {

08#

09# Do your usual F5 HTTP authentication here

10#

11# Initial values

12set creation_time_temp [clock seconds]

13set creation_time [format %X $creation_time_temp]

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

15set expr_time [format %X $expr_time_temp]

16set username [HTTP::username]

17set ltpa_secret_decode [b64decode $::ltpa_secret]

18# First part of token

19set cookie_data_raw {}

20append cookie_data_raw $::ltpa_version

21append cookie_data_raw $creation_time

22append cookie_data_raw $expr_time

23append cookie_data_raw $username

24append cookie_data_raw $ltpa_secret_decode

25# SHA1 of first part of token

26set sha_cookie_raw [sha1 $cookie_data_raw]

27# Final not yet encoded token

28set ltpa_token_raw {}

29append ltpa_token_raw $::ltpa_version

30append ltpa_token_raw $creation_time

31append ltpa_token_raw $expr_time

32append ltpa_token_raw $username

33append ltpa_token_raw $sha_cookie_raw

34# Final Base64 encoded token

35set ltpa_token_final [b64encode $ltpa_token_raw]

36# Insert the cookie

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

38}

39# Remove Authorization HTTP header to avoid using basic authentication

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

41HTTP::header remove"Authorization"

42}

43}

相关链接:

java ltpa_LTPA Cookie原理相关推荐

  1. java对cookie的操作

    原文:http://www.cnblogs.com/muzongyan/archive/2010/08/30/1812552.html java对cookie的操作比较简单,主要介绍下建立cookie ...

  2. java lock的原理,Java中Lock原理探究

    在对于lock锁的使用上,很多人只是掌握了最基础的方法,但是对实现的过程不是很清楚.这里我们对lock锁功能的实现进行分析,以ReentrantLock为例,分析它的锁类型,并对相关的调用方法进行展示 ...

  3. Java并发编程原理与实战六:主线程等待子线程解决方案

    Java并发编程原理与实战六:主线程等待子线程解决方案 参考文章: (1)Java并发编程原理与实战六:主线程等待子线程解决方案 (2)https://www.cnblogs.com/pony1223 ...

  4. Java虚拟机工作原理详解

    原文地址:http://blog.csdn.net/bingduanlbd/article/details/8363734 一.类加载器 首先来看一下java程序的执行过程. 从这个框图很容易大体上了 ...

  5. java对cookie的操作_java对cookie的操作

    Java对cookie的操作比较简单,主要介绍下建立cookie和读取cookie,以及如何设定cookie的生命周期和cookie的路径问题. 建立一个无生命周期的cookie,即随着浏览器的关闭即 ...

  6. 10分钟看懂, Java NIO 底层原理

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:硬刚一周,3W字总结,一年的经验告诉你如何准备校招! 个人原创100W+访问量博客:点击前往,查看更多 写在前面 ...

  7. java底层原理书籍_不愧是阿里p8大佬!终于把Java 虚拟机底层原理讲清楚了,请签收...

    概述 JVM 的内存模型和 JVM 的垃圾回收机制一直是 Java 业内从业者绕不开的话题(实际调优.面试)JVM是java中很重要的一块知识,也是面试常问的问题之一,直至今天,仍然还有许多面试者在被 ...

  8. 基础的java增删改查,Java基础系列(基础):Java使用Cookie增删改查操作!

    什么是Cookie? Cookie是由W3C组织提出,最早由NetScape社区发展的一种机制. Cookie是存储于访问者的计算机中的变量.每当同一台计算机通过浏览器请求某个页面时,就会发送这个co ...

  9. Java volatile关键字原理解剖

    Java volatile关键字原理解剖 文章目录 Java volatile关键字原理解剖 参考文章 前置知识 CPU缓存模型 CPU缓存行 并发编程基本概念 Java锁概念 volatile关键字 ...

最新文章

  1. 传感器可以让智能手机测量生命体征
  2. 调用java_UiPath如何调用Java
  3. NR 5G EN-DC架构下的5G无线承载
  4. 二次函数的求根公式的几何原理
  5. 云栖大会展出两款一体机,搭载新一代无影融合架构
  6. 两边填上相同的数_二年级必考题,在括号里填上相同的数~
  7. 一般算术表达式转换成后缀式
  8. bzoj 5281: [Usaco2018 Open]Talent Show【dp】
  9. Visual Studio + VAssistX常用快捷键收藏
  10. 基于51单片机ADC0808自动数字电压表仿真数码管显示
  11. java设置input隐藏,控制input输入框提示信息显示和隐藏的方法
  12. Cortex-A 架构
  13. html怎样设置图片的圆角矩形,css怎么画圆角矩形?
  14. POJ 1606 Jugs
  15. 计算机网络通过IP地址计算子网掩码
  16. 如何实现自动化按图片搜索淘宝商品(拍立淘)功能?拍立淘API接口item_search_img
  17. LeetCode-1264. 页面推荐(中等)
  18. AcWing 第69场周赛
  19. 【三国演义】——诸葛亮
  20. CSS的作用与各种样式

热门文章

  1. Java内存溢出和内存泄露后怎么解决
  2. python 四种单例模式
  3. Yii2 获取URL的一些方法
  4. 三步搞定 opencv 初始环境设定
  5. 如何在XSLT里调用C#的代码
  6. iPhone开发环境搭建For PC
  7. 手机被偷后如何让小偷不能用
  8. Cortex-M家族发展史,简述Cortex-M0~M4的各个优势
  9. 巨亏的旷视科技,是AI独角兽还是物联网企业?
  10. 计算机视觉开源库OpenCV之利用开操作(Opening Operation)修复受损照片方法