鸿 网 互 联 www.68idc.cn

当前位置 : 服务器租用 > 网站安全 > 加密解密 > >

LTPA Cookie原理

来源:互联网 作者:佚名 时间:2015-10-09 06:17
1. 什么是LTPA? Lightweight Third-Party Authentication (LTPA)是IBM Websphere和Domino产品中使用单点登录技术。当服务器配置好LTPA认证方式,用户通过浏览器成功登录后,服务器会自动发送一个session cookie给浏览器;此cookie中包含一个LTPA Token。 ---
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为例:
 
show source01… 
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 解析
show source01    import 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) 
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 生成
show source01    /** 
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 {
 
show source01set 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 
07when 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}
 
相关链接:
http://per.lausten.dk/blog/2009/06/how-to-create-a-ltpa-session-cookie-for-lotus-domino-using-f5.html
http://www-10.lotus.com/ldd/dominowiki.nsf/dx/06162009022019PMWEBPHR.htm
 
网友评论
<