如何在TypeScript中解析Base64格式的时间戳令牌并提取生成时间(等效于Java+BouncyCastle实现)
如何在TypeScript中解析Base64格式的时间戳令牌并提取生成时间(等效于Java+BouncyCastle实现)
我之前也碰到过和你一模一样的需求——把Base64的时间戳令牌(TSA返回的TimeStampToken)解析出来提取生成时间,刚好可以用你提到的pkijs或jsrsasign实现,完全对应你Java+BouncyCastle的逻辑。下面分别给你两种库的完整示例:
方法一:使用pkijs(TypeScript推荐)
pkijs对ASN.1结构的处理更贴合现代TypeScript项目的类型友好性,你之前没找到正确入口是因为需要先通过fromBER函数解析字节流,再转换成对应类实例。具体步骤如下:
1. 安装依赖
npm install pkijs @peculiar/asn1-schema
2. 完整TypeScript代码
import { fromBER, SignedData, TSTInfo } from "pkijs"; // 替换为你的Base64格式时间戳令牌 const base64TimestampToken = "YOUR_BASE64_TIMESTAMP_TOKEN_HERE"; // 步骤1:将Base64解码为Uint8Array(字节数组) const rawTokenBytes = Uint8Array.from(atob(base64TimestampToken), char => char.charCodeAt(0)); // 步骤2:解析BER编码的字节流为ASN.1对象 const asn1ParseResult = fromBER(rawTokenBytes); if (asn1ParseResult.offset === -1) { throw new Error("解析时间戳令牌失败:无效的BER编码"); } // 步骤3:转换为CMS SignedData实例(TimeStampToken本质就是CMS SignedData结构) const signedData = new SignedData({ schema: asn1ParseResult.result }); // 步骤4:提取并验证封装的TSTInfo内容 const encapContent = signedData.encapContentInfo; // 验证内容类型是否为TSTInfo(可选但推荐,避免解析错误的令牌) const TST_INFO_OID = "1.2.840.113549.1.9.16.1.4"; if (encapContent.eContentType !== TST_INFO_OID) { throw new Error("不是有效的TimeStampToken:内容类型不匹配"); } // 步骤5:解析TSTInfo并提取生成时间 const tstInfoAsn1 = fromBER(encapContent.eContent.valueBlock.valueHex); const tstInfo = new TSTInfo({ schema: tstInfoAsn1.result }); // 最终得到生成时间(Date对象,直接可用) const genTime = tstInfo.genTime; console.log("时间戳生成时间:", genTime);
方法二:使用jsrsasign
jsrsasign的API更偏向底层ASN.1十六进制处理,需要先把Base64转成十六进制再解析,适合已经在项目中使用该库做签名的场景:
1. 安装依赖
npm install jsrsasign
2. 完整TypeScript代码
import * as jsrsasign from "jsrsasign"; // 替换为你的Base64格式时间戳令牌 const base64TimestampToken = "YOUR_BASE64_TIMESTAMP_TOKEN_HERE"; // 步骤1:将Base64转换为十六进制字符串(jsrsasign偏好十六进制输入) const hexToken = jsrsasign.hexfromb64(base64TimestampToken); // 步骤2:解析CMS SignedData结构 const signedData = new jsrsasign.KJUR.asn1.cms.SignedData({ hex: hexToken }); // 步骤3:验证内容类型并提取TSTInfo的十六进制内容 const TST_INFO_OID = "1.2.840.113549.1.9.16.1.4"; if (signedData.getEncapContentType() !== TST_INFO_OID) { throw new Error("不是有效的TimeStampToken:内容类型不匹配"); } const tstInfoHex = signedData.getEncapContentHex(); // 步骤4:解析TSTInfo并提取生成时间 // TSTInfo的ASN.1结构:SEQUENCE { version, policy, messageImprint, serialNumber, genTime, ... } // genTime是第4个元素(ASN.1索引从0开始,对应列表索引3) const tstInfoAsn1 = new jsrsasign.ASN1HEX(tstInfoHex); const genTimeHex = tstInfoAsn1.getVbyList([3]); // 将GeneralizedTime类型的十六进制转换为Date对象 const genTime = jsrsasign.ASN1HEX.getDateFromGeneralizedTimeHex(genTimeHex); console.log("时间戳生成时间:", genTime);
补充说明
- 两种方法都完全复刻了你Java代码的逻辑:Base64解码 → 解析CMS SignedData → 提取TSTInfo → 获取生成时间
- pkijs的类型提示更完善,适合TypeScript项目长期维护;jsrsasign更适合已经在项目中使用该库做数字签名的场景
- 验证内容类型OID的步骤是可选的,但能有效避免解析非时间戳令牌导致的错误
内容来源于stack exchange




