You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在TypeScript中解析Base64格式的时间戳令牌并提取生成时间(等效于Java+BouncyCastle实现)

如何在TypeScript中解析Base64格式的时间戳令牌并提取生成时间(等效于Java+BouncyCastle实现)

我之前也碰到过和你一模一样的需求——把Base64的时间戳令牌(TSA返回的TimeStampToken)解析出来提取生成时间,刚好可以用你提到的pkijsjsrsasign实现,完全对应你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

火山引擎 最新活动