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

380 Views Asked by At

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',
  },
);
2

There are 2 best solutions below

0
Namachi On BEST ANSWER

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',
  },
);
1
Aliif On

The error message you're encountering, "Object's schema was not verified against input data for OCSPResponse," suggests that there is a mismatch between the expected schema of the OCSP response and the actual data received. This usually means that the received data does not conform to the expected ASN.1 schema.

Here are some steps to help you troubleshoot and potentially resolve this issue:

  1. Verify the Response Content Type: Ensure that the content type of the OCSP response received from the server matches the expected content type. In your code, you set the 'Content-Type' header to 'application/ocsp-request' when making the OCSP request. Make sure that the server responds with the correct 'Content-Type' header, which should be 'application/ocsp-response' for OCSP responses.

    Example:

    headers: {
      'Content-Type': 'application/ocsp-response',
    },
    
  2. Check OCSP Response Encoding: OCSP responses are DER encoded. Make sure that the response you receive is DER encoded and not corrupted. If it's not DER encoded, you may need to handle the decoding differently.

    Example:

    const ocspResponseBuffer = Buffer.from(response.data, 'binary');
    
  3. Ensure Response Data is Complete: Verify that you receive a complete OCSP response from the server. It should include all the necessary components, such as the response status, responseBytes, and response data. Incomplete responses can cause schema verification issues.

  4. Check for Possible Network Errors: Ensure that there are no network-related issues causing data corruption during transmission. You can add error handling to check for network errors, such as timeouts or connection issues.

    Example:

    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
    }
    
  5. Verify ASN.1 Schema: Ensure that the ASN.1 schema you are using to decode the OCSP response matches the structure of the response received from the server. You can refer to the PKI.js documentation to confirm the expected structure.

  6. Logging and Debugging: Add logging statements at various stages of your code to inspect the data and identify where the mismatch between the schema and data might be occurring. This can help pinpoint the issue.

By systematically checking these points and debugging your code, you should be able to identify the source of the error and make the necessary corrections to successfully decode the OCSP response and determine the certificate status.