这一篇聊一聊 LTPA 这个协议 , 这个算是一个很少见的协议 , 专属于 IBM , 我们只是简单的说说它...
ltpa 全称 Lightweight Third-Party Authentication , 即轻量级第三方认证 . 这个协议看起来很复杂 , 其实使用的时候会感觉很简单 , 用法和 JWT 很类似.
LTPA 是 一项 IBM 协议 ,用于 在 WebSphere®Application Server 中提供基于 cookie 或二进制安全性令牌的认证机制 ,其支持 单点登录 SSO .
整个流程中包括多个服务器 , 例如WebSphere® 和 DataPower®。要对其中一个或多个服务器实现单点登录解决方案,您可以将 WebSEAL 配置为支持 LTPA 认证 .
目的 : LTPA 令牌认证的目的是将 LTPA 令牌从第一个 Web Service(其认证生成客户机)流动到下游 Web Service , 简单点说就是 IDP Server 生成令牌 , 下发到下游服务 .
Cookie 作用 : 具有有效 LTPA cookie 的用户可以访问与第一个服务器属于同一身份验证域的服务器,并将自动进行身份验证。
Cookie 本身包含有关已经验证的用户、用户要验证的领域(例如 LDAP 服务器)和时间戳的信息。所有这些信息之二用共享的3DES 密钥加密,并由公/私密密钥对签名。这一切都很好,直到您试图执行一些故障排除,并意识到无法查看这些 cookie 的内部。
宏观流程: User ---> ISAM SSO (WebSeal) - LTPA goes here ----> backend server WAS
宏观解释 :
流程详情:
LTPA 协议是基于 WebSphere 实现的 !
WebSphere 是什么 : WebSphere 是一个IBM 产品 , 它支持在一个因特网域中的一组web 服务器间使用单一登录的认证策略 ,通过密码术 可支持分布式环境的安全性 ,web 用户只需对 WebSphere Application Server 或 Domino 服务器认证一次 ,认证将会通过服务器进行共享.
**PS: 和联合认证有相同的思想 .. **
一个通过有效的LTPA Cookie能够在同一个认证域中所有服务器被自动认证。此Cookie中包含认证信息和时间戳。这些信息通过共享的3DES Key进行了bis 加密。使用公共密钥/私有密钥进行签名。
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=
LTPA (Version 1): www.ibm.com/websphere/a…
LTPA2: www.ibm.com/websphere/a…
LtpaToken LtpaToken 用于与 WebSphere Application Server 的前发行版进行互操作。此令牌仅包含认证身份属性。 LtpaToken 针对 WebSphere Application Server V5.1.0.2 之前的发行版(对于 z/OS®)或 V5.1.1(对于分布式系统)生成。
LtpaToken2 LtpaToken2 包含更强的加密功能,并且您能够向令牌添加多个属性。此令牌包含认证身份和其他信息(例如,属性)。属性用于联系原始登录服务器和唯一高速缓存密钥。如果在确定唯一性时要考虑除身份以外的其他内容,还将使用属性来查找主题。
注意 : 为了允许运行不同版本WebSphere Application Server的服务器之间的互操作性,默认情况下,在将绑定配置为期望LTPA2令牌时,Version 7.0及更高版本的JAX-WS web服务安全运行时可以成功地使用LTPA Version 1令牌。但是,您可以将JAX-WS运行时的绑定配置为只接受LTPA2令牌。有关更多信息,请参阅有关身份验证生成器或使用者令牌设置的文档。
Cookie 加密方式
LTPA cookie 在 DESede/ECB/PKCS5Padding 模式下使用3DES 密钥进行加密。真正的密钥也是在 DESede/ECB/PKCS5Padding 模式下使用3DES 加密的,其中使用用0X0最多24字节填充的所提供密码的 SHA 散列。要解密实际令牌,可以获取密码,生成一个3DES 密钥,解密加密密钥,然后解密 cookie 数据。还有一个公钥/私钥对用于对 cookie 进行签名。
Cookie 的元素 @ my.oschina.net/psuyun/blog…
在与 Domino 做 SSO 的时候,会使用 LTPA Token的认证方式,本文描述它的生成原理,通过它我们可以自己编码生成身份认证的 cookie,实现 SSO。
首先,这个 cookie 由以下部分组成
接下来分别说明各部分的具体内容:
当然不能将密钥直接发送给浏览器,所以将上述部分合并起来(如上图),计算 SHA-1 校验和
然后用 SHA-1 校验和替换掉 Domino LTPA 密钥,最后再将内容通过 Base64 编码,形成最终的 cookie 发送给浏览器。这样如果 cookie 中的任何内容被修改,校验和就不对了,达到了防篡改的效果。所以最终LTPA Cookie所得到的值为以下公式组成:
[token] = BASE64([header][creation time][expiration time][username][SHA-1 hash])
Header: LtpaToken 版本(长度4),Domino的固定为[0x00][0x01][0x02][0x03]
Creation time: 创建时间戳(长度8),格式为Unix time比如[2010-03-12 00:21:49]为4B99189D
expiration time:过期时间戳(长度8) 同上
username: 用户名(长度不定)
SHA-1 hash:SHA-1校验和(长度20)
- 由前面所说的密钥和其余的Token资料合并而成,合成公式如下
- [SHA-1 hash] = SHA-1([header][creation time][expiration time][username][shared secret])
转载自 @ www.cnblogs.com/cmt/p/14580…
解析方法
// LTPA 3DES 密钥
String ltpa3DESKey = "7dH4i81YepbVe+gF9XVUzE4C1Ca5g6A4Q69OFobJV9g=";
// LTPA 密钥密码
String ltpaPassword = "Passw0rd";
try {
// 获得加密key
byte[] secretKey = getSecretKey(ltpa3DESKey, ltpaPassword);
// 使用加密key解密ltpa Cookie
String ltpaPlaintext = new String(decryptLtpaToken(tokenCipher,
secretKey));
displayTokenData(ltpaPlaintext);
} catch (Exception e) {
System.out.println("Caught inner: " + e);
}
//获得安全Key
private static byte[] getSecretKey(String ltpa3DESKey, String password)
throws Exception {
// 使用SHA获得key密码的hash值
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(password.getBytes());
byte[] hash3DES = new byte[24];
System.arraycopy(md.digest(), 0, hash3DES, 0, 20);
// 使用0替换后4个字节
Arrays.fill(hash3DES, 20, 24, (byte) 0);
// BASE64解码 ltpa3DESKey
byte[] decode3DES = Base64.decodeBase64(ltpa3DESKey.getBytes());
// 使用key密码hash值解密已Base64解码的ltpa3DESKey
return decrypt(decode3DES, hash3DES);
}
//解密LtpaToken
public static byte[] decryptLtpaToken(String encryptedLtpaToken, byte[] key)
throws Exception {
// Base64解码LTPAToken
final byte[] ltpaByteArray = Base64.decodeBase64(encryptedLtpaToken
.getBytes());
// 使用key解密已Base64解码的LTPAToken
return decrypt(ltpaByteArray, key);
}
// DESede/ECB/PKC5Padding解方法
public static byte[] decrypt(byte[] ciphertext, byte[] key)
throws Exception {
final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
final KeySpec keySpec = new DESedeKeySpec(key);
final Key secretKey = SecretKeyFactory.getInstance("TripleDES")
.generateSecret(keySpec);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(ciphertext);
}
生成 LTPA Token
/**
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…...
通过F5 BIG-IP创建Domino LTPAToken
when RULE_INIT {
set cookie_name "LtpaToken" # Don't change this
set ltpa_version "\x00\x01\x02\x03" # Don't change this
set ltpa_secret "b64encodedsecretkey" # Set this to the LTPA secrey key from your Lotus Domino LTPA configuration
set ltpa_timeout "1800" # Set this to the timeout value from your Lotus Domino LTPA configuration
}
when HTTP_REQUEST {
#
# Do your usual F5 HTTP authentication here
#
# Initial values
set creation_time_temp [clock seconds]
set creation_time [format %X $creation_time_temp]
set expr_time_temp [expr { $creation_time_temp + $::ltpa_timeout}]
set expr_time [format %X $expr_time_temp]
set username [HTTP::username]
set ltpa_secret_decode [b64decode $::ltpa_secret]
# First part of token
set cookie_data_raw {}
append cookie_data_raw $::ltpa_version
append cookie_data_raw $creation_time
append cookie_data_raw $expr_time
append cookie_data_raw $username
append cookie_data_raw $ltpa_secret_decode
# SHA1 of first part of token
set sha_cookie_raw [sha1 $cookie_data_raw]
# Final not yet encoded token
set ltpa_token_raw {}
append ltpa_token_raw $::ltpa_version
append ltpa_token_raw $creation_time
append ltpa_token_raw $expr_time
append ltpa_token_raw $username
append ltpa_token_raw $sha_cookie_raw
# Final Base64 encoded token
set ltpa_token_final [b64encode $ltpa_token_raw]
# Insert the cookie
HTTP::cookie insert name $::cookie_name value $ltpa_token_final
}
# Remove Authorization HTTP header to avoid using basic authentication
if { [HTTP::header exists "Authorization"] } {
HTTP::header remove "Authorization"
}
}
LTPA 是一个企业痕迹很重的协议 ,它基本上归属于 IBM , 这就意味着使用如果有困难 , 文档不一定能解决 , 而请求服务支持可不是一个简单的事情 .
当然 ,在使用中 , ltpa cookie 也可以被当成一种 JWT Token 的一种生成方式 , 将其放在Cookie 中 ,再基于 SSO 认证 , 这不算一个 LTPA 体系 , 仅仅是使用了 LTPA 的 Cookie 生成方式 .
阅读量:198
点赞量:0
收藏量:0