Search code examples
c#opensslbouncycastleocsp

Correctly handle OCSP requests from C# / Java app (+ BouncyCastle)


I have an application (dotnet core with Bouncy Castle) that generates X509 certificates. Essential part of certificate management is also revoking them. I do support revoking by CRL currently, but there was a requirement from our vendor to implement OCSP as well. And here the problems started to appear.

I've implemented something that looks compatible with the RFC 6960 and with some random Java pieces that are doing something very similar. However I am stuck with the following error that I could not figure out:

OCSP Request Data:
    Version: 1 (0x0)
    Requestor List:
        Certificate ID:
          Hash Algorithm: sha1
          Issuer Name Hash: 5ED580C3E05BAB929E3B5FE31AF1FD2D63DA2B66
          Issuer Key Hash: 0BA5610F200458E9183155354D0956598285F833
          Serial Number: 2D9E89A741BE3BED
    Request Extensions:
        OCSP Nonce:
            0410D596BF4A21EA59007CAD5AE306DCF349
Error querying OCSP responder
27384:error:0D0680A8:asn1 encoding routines:asn1_check_tlen:wrong tag:../openssl-1.1.1c/crypto/asn1/tasn_dec.c:1130:
27384:error:0D06C03A:asn1 encoding routines:asn1_d2i_ex_primitive:nested asn1 error:../openssl-1.1.1c/crypto/asn1/tasn_dec.c:694:
27384:error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error:../openssl-1.1.1c/crypto/asn1/tasn_dec.c:627:Field=responseStatus, Type=OCSP_RESPONSE

The configuration I am following is:

  • #1 root certificate - used to create all client certificates that can be revoked
  • #2 ocsp root certificate - generated separately used to sign ocsp responses

I am testing my code using the following command (from the command line):

$ openssl ocsp -issuer certs/root-ocsp.crt -cert client-cert.crt -text -url http://localhost:12345/certificate/ocsp

To be honest I have no more idea what may be wrong... Basing on the error, I suspect that something is wrong with Asn1 signatures, but after checking few options (including rootCert in chain, etc) result is the same all the time.

The crucial part of the code that generates OCSP response looks like this:

public byte[] GenerateOcsp(OcspReq ocspReq, IList<Certificate> certificates)
{
    //Should be used at all for OCSP?
    var rootCert = _rootCertificateProvider.GetBouncyRootCert("development");
    var ocspCert = _rootCertificateProvider.GetBouncyRootCert("ocsp");

    var respGen = new BasicOcspRespGenerator(ocspCert.Certificate.GetPublicKey());
    var nonceExt = ocspReq.RequestExtensions.GetExtension(OcspObjectIdentifiers.PkixOcspNonce);
    if (nonceExt != null)
        respGen.SetResponseExtensions(new X509Extensions(new[] { OcspObjectIdentifiers.PkixOcspNonce }, new[] { nonceExt }));

    foreach (var req in ocspReq.GetRequestList())
    {
        var serialNo = req.GetCertID().SerialNumber;
        var dbCert = certificates.FirstOrDefault(c => BigInteger.ValueOf(c.SerialNumber).Equals(serialNo));
        if (dbCert == null)
        {
            respGen.AddResponse(req.GetCertID(), new UnknownStatus());
        }
        else if (dbCert.Revocated)
        {
            respGen.AddResponse(req.GetCertID(), new RevokedStatus(dbCert.RevocationDate ?? DateTime.Now, 0));
        }
        else
        {
            respGen.AddResponse(req.GetCertID(), CertificateStatus.Good);
        }
    }

    var random = new SecureRandom();
    var signFactory = new Asn1SignatureFactory("SHA256WithRSA", ocspCert.PrivKey, random);
    var ocspResponse = respGen.Generate(signFactory, new[] { ocspCert.Certificate }, DateTime.UtcNow);
    
    return ocspResponse.GetEncoded();
}

Result is returned through the Controller like this:

  return new FileContentResult(result, "application/ocsp-response");

Any ideas what may be wrong?

Thanks!


Solution

  • To all future people arriving to this question:

    My overall approach in the question was good, the only thing missing was piping a result through an additional response generator.

    So instead of returning ocspResponse.GetEncoded() you should do this and you are done:

    var builder = new OCSPRespGenerator();
    var response = builder.Generate(OCSPRespGenerator.Successful, ocspResponse);
    
    return response.GetEncoded();