提问人:Namachi 提问时间:9/27/2023 最后编辑:Namachi 更新时间:9/27/2023 访问量:156
如何使用 pkijs 在 TypeScript 中解码和验证 OCSP 响应以进行证书验证
How to Decode and Verify OCSP Response in TypeScript with pkijs for Certificate Validation
问:
目标
我们的目标是确定证书是否有效或是否已被吊销。 为了实现这一点,我们使用 pkijs 包发出 OCSP 请求以简化该过程,发送与有效证书相关的详细信息,例如“hashAlgorithm”、“issuerNameHash”、“issuerKeyHash”等(请参阅下面的代码)。
问题
然而,一旦我们成功获得OCSP响应,其中一部分似乎被编码,这使得解释变得具有挑战性。 唯一可见的元素是证书类型,但我们无法从此响应中提取其他可读信息。
我们尝试了什么
我们调用我们的函数,该函数将使用以下参数(临时硬编码,用于测试目的)来确定证书是有效还是已吊销:
await checkCertificateWithOcsp({
hashAlgorithm: 'SHA256',
issuerKeyHash: '7870177724f6234dccf87a8a43c84551533f831257519f90b12bb8eecae0',
issuerNameHash: 'cbe609c06ec9bd944a5d8cf94aee2979d4396fe00f68c6d215e233766514a1',
responderURL: 'https://7kravoouwj.execute-api.eu-west-1.amazonaws.com/test/OCSP-Responder',
serialNumber: '2',
});
import * as asn1js from 'asn1js';
import { AlgorithmIdentifier, CertID, Extension, OCSPRequest, OCSPResponse, Request } from 'pkijs';
import Axios from 'axios';
public static async checkCertificateWithOcsp(ocspRequest: OCSPRequestData) {
// Convert hexadecimal strings into bytes (Uint8Array).
const issuerNameHashBytes = new Uint8Array(ocspRequest.issuerNameHash.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
const issuerKeyHashBytes = new Uint8Array(ocspRequest.issuerKeyHash.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
const serialNumberBytes = new Uint8Array(ocspRequest.serialNumber.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
// 1. Create OCSP Request with PKI.js
const request = new OCSPRequest();
request.tbsRequest.requestList[0] = new Request();
request.tbsRequest.requestExtensions = [
new Extension({
extnID: "1.3.6.1.5.5.7.48.1.2",
critical: false,
extnValue: new asn1js.OctetString().toBER(),
})
];
request.tbsRequest.requestList[0].reqCert = new CertID({
hashAlgorithm: new AlgorithmIdentifier({ algorithmId: "1.3.14.3.2.26" }),
issuerNameHash: new asn1js.OctetString({ valueHex: issuerNameHashBytes }),
issuerKeyHash: new asn1js.OctetString({ valueHex: issuerKeyHashBytes }),
serialNumber: new asn1js.Integer({ valueHex: serialNumberBytes }),
});
// 2. Encode OCSP request
const encodedOcspReq = request.toSchema(true).toBER(false);
// 3. OCSP API Call with Axios
const response: any = await Axios.post<ArrayBuffer>(ocspRequest.responderURL, encodedOcspReq,
{
headers: {
'Content-Type': 'application/ocsp-request',
},
},
);
// 4. Convert response to ASN1
const ocspResponseBuffer = Buffer.from(ocspResponse.data);
const rawOcspResponseBuffer = new Uint8Array(ocspResponseBuffer.buffer);
const asn1 = asn1js.fromBER(rawOcspResponseBuffer.buffer);
// 5. Error occurred in PKI.JS OCSPResponse Class
const decodedOcspResponse = new OCSPResponse({ schema: asn1.result });
}
上述代码中的步骤
- 首先,我们在PKI.js和ASN1.js包的帮助下填充请求。
- 在将 OCSP 请求发送到 OCSP API 调用之前对其进行编码。
- 我们成功接收到带有编码数据的响应
- 将编码的 OCSP 响应数据转换为 ASN1
- 尝试解码和访问信息以获取证书状态
错误描述
在步骤 5 中,PKI 端发生错误,并显示以下错误消息:对象的架构未根据 OCSPResponse 的输入数据进行验证
。
由于证书的安全性根本不是我们的专业领域,尽管我们进行了研究,但我们无法确定错误可能在哪里以及如何纠正它。
提前感谢您的帮助。
编辑#1
以下是 OCSP 服务发送的值:reponse.data
0�{
��t0�p +0�a0�]0�ȡK0I10U V2GRootCA10U
Smartlab10
�&���,dV2G10 UDE20230927144339Z0h0f0O0 +�� �nɽ�J]��J�)y�9o�h���3ve�xpw$�#M��z�C�EQS?�WQ���+�����20230927144339Z0*�H�=���2���S�`���̥��0���oN6N8�'��2ř��=O�l,�>>jA���<~�`f}�%�2���S�`���̥��0���oN6N8�'��2ř��=O�l,�>>jA���<~�`f
我们不知道它是什么类型的编码......
答:
您遇到的错误消息“对象的架构未针对 OCSPResponse 的输入数据进行验证”,这表明 OCSP 响应的预期架构与收到的实际数据不匹配。这通常意味着接收到的数据不符合预期的 ASN.1 架构。
下面是一些步骤,可帮助您排查问题并可能解决此问题:
验证响应内容类型:确保从服务器接收的 OCSP 响应的内容类型与预期的内容类型匹配。在代码中,在发出 OCSP 请求时将“Content-Type”标头设置为“application/ocsp-request”。确保服务器使用正确的“Content-Type”标头进行响应,对于 OCSP 响应,该标头应为“application/ocsp-response”。
例:
headers: { 'Content-Type': 'application/ocsp-response', },
检查OCSP响应编码:OCSP 响应采用 DER 编码。确保您收到的响应是 DER 编码的,并且未损坏。如果它不是 DER 编码的,则可能需要以不同的方式处理解码。
例:
const ocspResponseBuffer = Buffer.from(response.data, 'binary');
确保响应数据完整:验证您是否从服务器收到完整的 OCSP 响应。它应包括所有必要的组件,例如响应状态、responseBytes 和响应数据。不完整的响应可能会导致架构验证问题。
检查可能的网络错误:确保在传输过程中没有导致数据损坏的网络相关问题。您可以添加错误处理来检查网络错误,例如超时或连接问题。
例:
try { const response = await Axios.post<ArrayBuffer>(ocspRequest.responderURL, encodedOcspReq, { headers: { 'Content-Type': 'application/ocsp-request', }, }); // Handle the response here } catch (error) { console.error('Network error:', error); // Handle the error gracefully }
验证ASN.1架构:确保用于解码OCSP响应的ASN.1架构与从服务器接收的响应的结构匹配。您可以参考 PKI.js 文档来确认预期的结构。
日志记录和调试:在代码的各个阶段添加日志记录语句,以检查数据并确定架构和数据之间可能发生不匹配的位置。这有助于查明问题所在。
通过系统地检查这些点并调试代码,您应该能够识别错误的来源并进行必要的更正,以成功解码 OCSP 响应并确定证书状态。
评论
Content-Type
application/ocsp-response
response.data
我需要在 Axios 方法中指定 responseType 是 arrayBuffer,如下所示:
const response: any = await Axios.post<ArrayBuffer>(ocspRequest.responderURL, encodedOcspReq,
{
headers: {
'Content-Type': 'application/ocsp-request',
},
responseType: 'arraybuffer',
},
);
评论