node.jstypescriptsecuritycertificateocsp

How to Decode and Verify OCSP Response in TypeScript with pkijs for Certificate Validation


Goal

Our objective is to determine whether a certificate is good or if it has been revoked. To achieve this, we make an OCSP request using the pkijs package to facilitate the process, sending details related to a valid certificate such as 'hashAlgorithm,' 'issuerNameHash,' 'issuerKeyHash,' and so on (see code below).

Problem

However, once we successfully obtain the OCSP response, a portion of it appears to be encoded, making it challenging to interpret. The only visible elements are the certificate types, but we are unable to extract additional readable information from this response.

What we tried

We call our function that will determine whether the certificate is valid or revoked, using the following parameters (Hardcoded temporarily, for testing purposes):

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 });
}

Steps from the code above

  1. First of all we fill the request with the help of PKI.js and ASN1.js packages.
  2. Encode the OCSP request before sending it to OCSP API call.
  3. We successfully receive the response with encoded data
  4. Convert encoded OCSP response data to ASN1
  5. Trying to decode and access information to get the certificate status

Error Description

At step 5, an error occurs on the PKI side with the following error message: Object's schema was not verified against input data for OCSPResponse.

Since security with certificates is not our area of expertise at all, and despite our research efforts, we are unable to determine where the error might be and how to correct it.

Thank you in advance for your help.

EDIT #1

Here is the value of reponse.data send by OCSP Service:

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

We don't know what type of encoding it is...

EDIT #2: SOLUTION

I needed to specify that the responseType is an arrayBuffer in Axios method like so:

const response: any = await Axios.post<ArrayBuffer>(ocspRequest.responderURL, encodedOcspReq,
  {
    headers: {
      'Content-Type': 'application/ocsp-request',
    },
    responseType: 'arraybuffer',
  },
);

Solution

  • I needed to specify that the responseType is an arrayBuffer in Axios method like so:

    const response: any = await Axios.post<ArrayBuffer>(ocspRequest.responderURL, encodedOcspReq,
      {
        headers: {
          'Content-Type': 'application/ocsp-request',
        },
        responseType: 'arraybuffer',
      },
    );