java - Lotus Domino C# LtpaToken 类

标签 java c# single-sign-on lotus-domino

各位开发人员,我正在寻找将以下 java 代码转换为 c# 的方法。我已经做了一些转换,如下所示,但我的 LtpaToken 无效。我需要在 Domino 应用程序和 C# Web API 之间创建单点登录。 Notes 通讯簿中的所有用户都将包含在 SQL Server 数据库中,但有些用户不一定包含在通讯簿中。如果 Notes 地址簿中存在用户,则所有用户都将通过 IdentityServer4 登录,将使用 CN、cookieName、cookieDomain 和 Domino Secret 生成 LTPA token 。生成的cookie将被注入(inject)到用户浏览器中。

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;

import javax.servlet.http.Cookie;

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

/**
 * Lightweight Third Party Authentication. Generates and validates ltpa tokens
 * used in Domino single sign on environments. Does not work with WebSphere SSO
 * tokens. You need a properties file named LtpaToken.properties which holds two
 * properties.
 *
 * <pre>
 * ) domino.secret=The base64 encoded secret found in the field LTPA_DominoSecret in the SSO configuration document.
 * ) cookie.domain=The domain you want generated cookies to be from. e.g. '.domain.com' (Note the leading dot)
 * </pre>
 *
 * @author $Author: rkelly $
 * @version $Revision: 1.1 $
 * @created $Date: 2003/04/07 18:22:14 $
 */
public final class LtpaToken {

    private byte[] creation;
    private Date creationDate;
    private byte[] digest;
    private byte[] expires;
    private Date expiresDate;
    private byte[] header;
    private String ltpaToken;
    private byte[] rawToken;
    private byte[] user;
    private String dominoSecret;

    /**
     * Constructor for the LtpaToken object
     *
     * @param token Description of the Parameter
     * @param cookieName
     * @param cookieDomain
     * @param dominoSecret
     */
    public LtpaToken(String token, String cookieName, String cookieDomain, String dominoSecret) {
        init();
        ltpaToken = token;
        this.dominoSecret = dominoSecret;
        byte[] byteArray = ltpaToken.getBytes(); 
        rawToken = Base64.decodeBase64(byteArray);

        user = new byte[(rawToken.length) - 40];
        for (int i = 0; i < 4; i++) {
            header[i] = rawToken[i];
        }
        for (int i = 4; i < 12; i++) {
            creation[i - 4] = rawToken[i];
        }
        for (int i = 12; i < 20; i++) {
            expires[i - 12] = rawToken[i];
        }
        for (int i = 20; i < (rawToken.length - 20); i++) {
            user[i - 20] = rawToken[i];
        }
        for (int i = (rawToken.length - 20); i < rawToken.length; i++) {
            digest[i - (rawToken.length - 20)] = rawToken[i];
        }

        String commonName = new String(user); 
        System.out.println(commonName);    

        creationDate = new Date(Long.parseLong(new String(creation), 16) * 1000);
        expiresDate = new Date(Long.parseLong(new String(expires), 16) * 1000);

        System.out.println(creationDate);
        System.out.println(expiresDate);

    }

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

    public static Cookie newCookie(String sessionToken, String cookieName, String cookieDomain) {
        Cookie cookie = new Cookie(cookieName, sessionToken);

        cookie.setDomain(cookieDomain);
        cookie.setPath("/");
        cookie.setSecure(false);
        cookie.setMaxAge(-1);
        return cookie;
    }

    /**
     * Gets the creationDate attribute of the LtpaToken object
     *
     * @return The creationDate value
     */
    public Date getCreationDate() {
        return creationDate;
    }

    /**
     * Gets the expiresDate attribute of the LtpaToken object
     *
     * @return The expiresDate value
     */
    public Date getExpiresDate() {
        return expiresDate;
    }

    /**
     * Gets the user attribute of the LtpaToken object
     *
     * @return The user value
     */
    public String getUser() {
        return new String(user);
    }

    /**
     * Validates the SHA-1 digest of the token with the Domino secret key.
     *
     * @return Returns true if valid.
     */
    public boolean isValid() {
        boolean validDigest;
        boolean validDateRange;
        byte[] newDigest;
        byte[] bytes = null;
        Date now = new Date();

        MessageDigest md = getDigest();

        bytes = concatenate(bytes, header);

        bytes = concatenate(bytes, creation);

        bytes = concatenate(bytes, expires);

        bytes = concatenate(bytes, user);

        bytes = concatenate(bytes, Base64.decodeBase64(dominoSecret));

        newDigest = md.digest(bytes);

        validDigest = MessageDigest.isEqual(digest, newDigest);

        validDateRange = now.after(creationDate) && now.before(expiresDate);

        return validDateRange && validDigest;
    }


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

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

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

    /**
     * Validates the SHA-1 digest of the token with the Domino secret key.
     *
     * @param ltpaToken Description of the Parameter
     * @param cookieName
     * @param cookieDomain
     * @param serverHostname
     * @param dominoSecret
     * @return The valid value
     */
    public static boolean isValid(String ltpaToken, String cookieName,
            String cookieDomain, String serverHostname, String dominoSecret) {
        LtpaToken ltpa = new LtpaToken(ltpaToken, cookieName, cookieDomain,
                dominoSecret);

        return ltpa.isValid();
    }

    /**
     * Generates a new LtpaToken with 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.
     * @param cookieName
     * @param cookieDomain
     * @param dominoSecret
     * @return The generated token.
     */
    public static LtpaToken generate(String canonicalUser, Date tokenCreation, Date tokenExpires,
            String cookieName, String cookieDomain, String dominoSecret) {

        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.getTimeInMillis() / 1000).toUpperCase().getBytes();
        calendar.setTime(tokenExpires);
        ltpa.expires = Long.toHexString(calendar.getTimeInMillis() / 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.decodeBase64(dominoSecret));
        token = concatenate(token, ltpa.digest);

        String base64encodedToken = Base64.encodeBase64String(token);

        return new LtpaToken(base64encodedToken, cookieName,
                cookieDomain, dominoSecret);
    }

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

    public String getLtpaToken() {
        if (ltpaToken != null) {
            return ltpaToken.trim();
        } else {
            return null;
        }
    }

    public void setLtpaToken(String ltpaToken) {
        this.ltpaToken = ltpaToken;
    }

}

我已经完成了转换,但在涉及 MessageDigest java 类 c# 等效步骤时,我似乎没有得到正确的结果。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Http;
using System.Text;
using System.Security.Cryptography;

namespace Authentication.Services
{
    public class LtpaToken
    {
        private byte[] creation;
        private DateTime creationDate;
        private byte[] digest;
        private byte[] expires;
        private DateTime expiresDate;
        private byte[] header;
        private string ltpaToken;
        private byte[] rawToken;
        private byte[] user;
        private string dominoSecret;

        /* complex code */
        private byte[] oneByte;
        private int blockSize;
        private int digestLength;
        byte[] buffer;
        private int bufOfs;
        long bytesProcessed;

        private int[] W;
        private int[] state;

        public LtpaToken(string token, string cookieName, string cookieDomain, string dominoSecret)
        {

            init();

            ltpaToken = token;
            this.dominoSecret = dominoSecret;
            byte[] DS = Convert.FromBase64String(dominoSecret);
            rawToken = Convert.FromBase64String(ltpaToken);   //Encoding.UTF8.GetString();
            user = new byte[(rawToken.Length) - 40];

            for (int i = 0; i < 4; i++)
            {
                header[i] = rawToken[i];
            }
            for (int i = 4; i < 12; i++)
            {
                creation[i - 4] = rawToken[i];
            }
            for (int i = 12; i < 20; i++)
            {
                expires[i - 12] = rawToken[i];
            }
            for (int i = 20; i < (rawToken.Length - 20); i++)
            {
                user[i - 20] = rawToken[i];
            }
            for (int i = (rawToken.Length - 20); i < rawToken.Length; i++)
            {
                digest[i - (rawToken.Length - 20)] = rawToken[i];
            }

            string sheader = System.Text.Encoding.UTF8.GetString(header);
            string suser = System.Text.Encoding.UTF8.GetString(user);
            string sdigest = System.Text.Encoding.UTF8.GetString(digest);
            string screation = System.Text.Encoding.UTF8.GetString(creation);
            string sexpires = System.Text.Encoding.UTF8.GetString(expires);

            //var epoch = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
            //

            Console.WriteLine(screation);
            long hexcreationdate = long.Parse(screation, System.Globalization.NumberStyles.HexNumber);
            long hexexpirationdate = long.Parse(sexpires, System.Globalization.NumberStyles.HexNumber);
            Console.WriteLine(sexpires);
            //
            creationDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(Convert.ToDouble(hexcreationdate * 1000)).AddHours(2);

            expiresDate = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(Convert.ToDouble(hexexpirationdate * 1000)).AddHours(2);

        }

        private LtpaToken()
        {
            init();
        }


        private void init()
        {
            creation = new byte[8];
            digest = new byte[20];
            expires = new byte[8];
            header = new byte[4];
        }

        public bool isValid(string token, string cookieName, string cookieDomain, string serverHostname, string dominoSecret)
        {
            LtpaToken ltpaToken = new LtpaToken(token, cookieName, cookieDomain, dominoSecret);
            return ltpaToken.isValid();
        }

        public bool isValid()
        {
            bool validDigest = false;
            bool validDateRange = false;
            byte[] newDigest;
            byte[] bytes = null;
            DateTime now = DateTime.Now;

            bytes = concatenate(bytes, header);
            bytes = concatenate(bytes, creation);
            bytes = concatenate(bytes, expires);
            bytes = concatenate(bytes, user);
            bytes = concatenate(bytes, Convert.FromBase64String(dominoSecret));
            newDigest = getSHA1(bytes);
            validDigest = digestIsEqual(newDigest);
            validDateRange = dateIsInRage();
            string result = System.Text.Encoding.UTF8.GetString(user);

            return validDigest & validDateRange;
        }

        private bool digestIsEqual(byte[] newDigest)
        {
            bool v = newDigest.SequenceEqual(digest);

            switch (v)
            {
                case true:
                    return true;

                default:
                    return false;


            }
        }
        public static LtpaToken generate(String canonicalUser, DateTime tokenCreation, DateTime tokenExpires,
                String cookieName, String cookieDomain, String dominoSecret)
        {

            LtpaToken ltpa = new LtpaToken();

            SHA1 md = SHA1.Create();
            ltpa.header = new byte[] { 0, 1, 2, 3 };
            byte[] token = null;

            string strCreation = tokenCreation.ToString("yyyyMMddhhmmss");
            long decValue = Convert.ToInt64(strCreation);
            //Convert to HEX 1245D8F5F7C8
            string screation = decValue.ToString("X");

            string strExpires = tokenCreation.ToString("yyyyMMddhhmmss");
            long expDecValue = Convert.ToInt64(strExpires);
            //Convert to HEX 1245D8F5F7C8
            string sexpires = expDecValue.ToString("X");


            //string screation = tokenCreation.Ticks.ToString("X4");
            //string sexpires = tokenExpires.Ticks.ToString("X4");

            Console.WriteLine(screation);

            ltpa.creation = Encoding.ASCII.GetBytes(screation);
            ltpa.expires = Encoding.ASCII.GetBytes(sexpires);
            ltpa.user = Encoding.ASCII.GetBytes(canonicalUser);

            token = concatenate(token, ltpa.header);
            token = concatenate(token, ltpa.creation);
            token = concatenate(token, ltpa.expires);
            token = concatenate(token, ltpa.user);

            //Console.WriteLine(Convert.ToBase64String(token));
            token = getSHA1(token);
            Console.WriteLine(Convert.ToBase64String(token));
            //md.ComputeHash(token);  

            byte[] dominoBytes = Convert.FromBase64String(dominoSecret);

            ltpa.digest = md.TransformFinalBlock(Convert.FromBase64String(dominoSecret), 0, dominoBytes.Length);

            token = concatenate(token, ltpa.digest);

            String base64encodedToken = Convert.ToBase64String(token, 0, token.Length);

            Console.WriteLine(base64encodedToken);

            return ltpa;
        }

        public static String byte2hex(byte[] b)
        {
            String hs = "";
            String stmp = "";
            for (int n = 0; n < b.Length; n++)
            {
                stmp = (b[n]).ToString("X4");
                if (stmp.Length == 1) hs = hs + "0" + stmp;
                else
                {
                    hs = hs + stmp;
                }
            }
            return hs.ToUpper();
        }


        private bool dateIsInRage()
        {
            if (DateTime.Now > creationDate && DateTime.Now < expiresDate)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        private static byte[] getSHA1(byte[] digest)
        {
            SHA1CryptoServiceProvider SHAObj = new SHA1CryptoServiceProvider();
            //SHAObj.ComputeHash(ASCIIEncoding.ASCII.GetBytes(digesttext));
            SHAObj.ComputeHash(digest);
            byte[] newSHAObj = SHAObj.Hash;
            StringBuilder sb = new StringBuilder();
            foreach (byte b in newSHAObj)
            {
                sb.Append(b.ToString("x2"));
            }
            return newSHAObj;

        }

        private static byte[] concatenate(byte[] a, byte[] b)
        {
            if (a == null)
            {
                return b;
            }
            else
            {
                byte[] bytes = new byte[a.Length + b.Length];

                Array.Copy(a, 0, bytes, 0, a.Length);
                Array.Copy(b, 0, bytes, a.Length, b.Length);
                return bytes;
            }
        }        
    }
}

最佳答案

按照 URL http://www-12.lotus.com/ldd/doc/tools/c/7.0/api70ug.nsf/85255d56004d2bfd85255b1800631684/ceda2cb8df47607f85256c3d005f816d?OpenDocument 中的以下信息成功解决了问题

生成 Domino 样式单点登录 token

  1. 从 Web SSO 配置的 LTPA_DominoSecret 字段读取 BASE-64 编码的 secret 数据。

  2. 从 Web SSO 配置的 LTPA_TokenExpiration 字段中读取过期间隔。

  3. 以 4 个字节的版本控制 header 信息开始。 Ø 版本0为[0x00][0x01][0x02][0x03]

  4. 附加创建时间。 Ø 创建时间表示为距 1970 年 1 月 1 日 12:00 GMT 的偏移量(以秒为单位)。

    • 它被编码为 8 个字符的十六进制字符串。使用带有 %08x 修饰符的 printf()。
  5. 附加到期时间。 Ø 过期时间也表示为距 1970 年 1 月 1 日 12:00 GMT 的偏移量(以秒为单位)。

    • 它被编码为 8 个字符的十六进制字符串。使用带有 %08x 修饰符的 printf()。
  6. 附加用户名。 Ø Username的格式没有限制,但建议使用LMBCS完全标记的规范名称,最大长度为MAXUSERNAME。

  7. 根据已连接的数据加上 20 字节共享 key 生成 SHA-1 哈希(20 字节)。

  8. 在用户名后附加 SHA-1 哈希值。

  9. BASE-64 对最终标记进行编码。

关于java - Lotus Domino C# LtpaToken 类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51607812/

相关文章:

java - 为什么 Jsoup 无法连接某些 URL?

java - android bufferedwriter错误

c# - Windows 10 RTM OSVersion 没有返回我所期望的

security - 了解 Shibboleth 和 SAML

java - 如何动态声明实体?

java - JTable - 使用 PopupMenu 切换列(如 Windows 资源管理器)

c# - 使用 XDocument 和 XElement 如何在 C# 中向 XML 添加 XAttribute 和值

c# socket 多数据包堆栈

biztalk - 在 SSO 中存储可配置值

javascript - SSO 与 Angular6 应用程序的集成