bug 1058812 - (3/3) mozilla::pkix: test handling unsupported signature algorithms r=briansmith
authorDavid Keeler <dkeeler@mozilla.com>
Wed, 08 Oct 2014 09:48:15 -0700
changeset 209464 3ae75c8b8f640c99186f04e7ffbacd4450b57e4b
parent 209463 a4356f6fb7e79422f862bd6066dcc938cc588164
child 209465 50a379edc315b4e83463967538fd8a3c7e107d5a
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersbriansmith
bugs1058812
milestone35.0a1
bug 1058812 - (3/3) mozilla::pkix: test handling unsupported signature algorithms r=briansmith
security/pkix/test/gtest/moz.build
security/pkix/test/gtest/pkixcert_signature_algorithm_tests.cpp
security/pkix/test/gtest/pkixder_pki_types_tests.cpp
security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
security/pkix/test/lib/pkixtestnss.cpp
security/pkix/test/lib/pkixtestutil.cpp
security/pkix/test/lib/pkixtestutil.h
--- a/security/pkix/test/gtest/moz.build
+++ b/security/pkix/test/gtest/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 SOURCES += [
     'pkixbuild_tests.cpp',
     'pkixcert_extension_tests.cpp',
+    'pkixcert_signature_algorithm_tests.cpp',
     'pkixcheck_CheckKeyUsage_tests.cpp',
     'pkixcheck_CheckValidity_tests.cpp',
 
     # The naming conventions are described in ./README.txt.
 
     'pkixder_input_tests.cpp',
     'pkixder_pki_types_tests.cpp',
     'pkixder_universal_types_tests.cpp',
new file mode 100644
--- /dev/null
+++ b/security/pkix/test/gtest/pkixcert_signature_algorithm_tests.cpp
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+#include "pkix/pkix.h"
+#include "pkix/pkixnss.h"
+#include "pkixgtest.h"
+#include "pkixtestutil.h"
+
+using namespace mozilla::pkix;
+using namespace mozilla::pkix::test;
+
+static ByteString
+CreateCert(const char* issuerCN,
+           const char* subjectCN,
+           EndEntityOrCA endEntityOrCA,
+           const ByteString& signatureAlgorithm,
+           /*optional*/ TestKeyPair* issuerKey,
+           /*out*/ ScopedTestKeyPair& subjectKey,
+           /*out*/ ByteString& subjectDER)
+{
+  static long serialNumberValue = 0;
+  ++serialNumberValue;
+  ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
+  EXPECT_FALSE(ENCODING_FAILED(serialNumber));
+
+  ByteString issuerDER(CNToDERName(issuerCN));
+  EXPECT_FALSE(ENCODING_FAILED(issuerDER));
+  subjectDER = CNToDERName(subjectCN);
+  EXPECT_FALSE(ENCODING_FAILED(subjectDER));
+
+  ByteString extensions[2];
+  if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
+    extensions[0] =
+      CreateEncodedBasicConstraints(true, nullptr,
+                                    ExtensionCriticality::Critical);
+    EXPECT_FALSE(ENCODING_FAILED(extensions[0]));
+  }
+
+  ByteString certDER(CreateEncodedCertificate(v3, signatureAlgorithm,
+                                              serialNumber,
+                                              issuerDER, oneDayBeforeNow,
+                                              oneDayAfterNow, subjectDER,
+                                              extensions, issuerKey,
+                                              signatureAlgorithm, subjectKey));
+  EXPECT_FALSE(ENCODING_FAILED(certDER));
+  return certDER;
+}
+
+class AlgorithmTestsTrustDomain : public TrustDomain
+{
+public:
+  AlgorithmTestsTrustDomain(const ByteString& rootDER,
+                            const ByteString& rootSubjectDER,
+               /*optional*/ const ByteString& intDER,
+               /*optional*/ const ByteString& intSubjectDER)
+    : rootDER(rootDER)
+    , rootSubjectDER(rootSubjectDER)
+    , intDER(intDER)
+    , intSubjectDER(intSubjectDER)
+  {
+  }
+
+private:
+  virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
+                              Input candidateCert,
+                              /*out*/ TrustLevel& trustLevel)
+  {
+    if (InputEqualsByteString(candidateCert, rootDER)) {
+      trustLevel = TrustLevel::TrustAnchor;
+    } else {
+      trustLevel = TrustLevel::InheritsTrust;
+    }
+    return Success;
+  }
+
+  virtual Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker,
+                            Time)
+  {
+    ByteString* issuerDER = nullptr;
+    if (InputEqualsByteString(encodedIssuerName, rootSubjectDER)) {
+      issuerDER = &rootDER;
+    } else if (InputEqualsByteString(encodedIssuerName, intSubjectDER)) {
+      issuerDER = &intDER;
+    } else {
+      // FindIssuer just returns success if it can't find a potential issuer.
+      return Success;
+    }
+    Input issuerCert;
+    Result rv = issuerCert.Init(issuerDER->data(), issuerDER->length());
+    if (rv != Success) {
+      return rv;
+    }
+    bool keepGoing;
+    return checker.Check(issuerCert, nullptr, keepGoing);
+  }
+
+  virtual Result CheckRevocation(EndEntityOrCA, const CertID&, Time,
+                                 const Input*, const Input*)
+  {
+    return Success;
+  }
+
+  virtual Result IsChainValid(const DERArray&, Time)
+  {
+    return Success;
+  }
+
+  virtual Result VerifySignedData(const SignedDataWithSignature& signedData,
+                                  Input subjectPublicKeyInfo)
+  {
+    EXPECT_NE(SignatureAlgorithm::unsupported_algorithm, signedData.algorithm);
+    return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo,
+                                             nullptr);
+  }
+
+  virtual Result DigestBuf(Input, uint8_t*, size_t)
+  {
+    ADD_FAILURE();
+    return Result::FATAL_ERROR_LIBRARY_FAILURE;
+  }
+
+  virtual Result CheckPublicKey(Input subjectPublicKeyInfo)
+  {
+    return TestCheckPublicKey(subjectPublicKeyInfo);
+  }
+
+  ByteString rootDER;
+  ByteString rootSubjectDER;
+  ByteString intDER;
+  ByteString intSubjectDER;
+};
+
+static const ByteString NO_INTERMEDIATE; // empty
+
+struct ChainValidity
+{
+  // In general, a certificate is generated for each of these.  However, if
+  // optionalIntermediateSignatureAlgorithm is NO_INTERMEDIATE, then only 2
+  // certificates are generated.
+  // The certificate generated for the given rootSignatureAlgorithm is the
+  // trust anchor.
+  ByteString endEntitySignatureAlgorithm;
+  ByteString optionalIntermediateSignatureAlgorithm;
+  ByteString rootSignatureAlgorithm;
+  bool isValid;
+};
+
+static const ChainValidity CHAIN_VALIDITY[] =
+{
+  // The trust anchor may have a signature with an unsupported signature
+  // algorithm.
+  { sha256WithRSAEncryption,
+    NO_INTERMEDIATE,
+    md5WithRSAEncryption,
+    true
+  },
+  { sha256WithRSAEncryption,
+    NO_INTERMEDIATE,
+    md2WithRSAEncryption,
+    true
+  },
+
+  // Certificates that are not trust anchors must not have a signature with an
+  // unsupported signature algorithm.
+  { md5WithRSAEncryption,
+    NO_INTERMEDIATE,
+    sha256WithRSAEncryption,
+    false
+  },
+  { md2WithRSAEncryption,
+    NO_INTERMEDIATE,
+    sha256WithRSAEncryption,
+    false
+  },
+  { md2WithRSAEncryption,
+    NO_INTERMEDIATE,
+    md5WithRSAEncryption,
+    false
+  },
+  { sha256WithRSAEncryption,
+    md5WithRSAEncryption,
+    sha256WithRSAEncryption,
+    false
+  },
+  { sha256WithRSAEncryption,
+    md2WithRSAEncryption,
+    sha256WithRSAEncryption,
+    false
+  },
+  { sha256WithRSAEncryption,
+    md2WithRSAEncryption,
+    md5WithRSAEncryption,
+    false
+  },
+};
+
+class pkixcert_IsValidChainForAlgorithm
+  : public ::testing::Test
+  , public ::testing::WithParamInterface<ChainValidity>
+{
+};
+
+TEST_P(pkixcert_IsValidChainForAlgorithm, IsValidChainForAlgorithm)
+{
+  const ChainValidity& chainValidity(GetParam());
+  const char* rootCN = "CN=Root";
+  ScopedTestKeyPair rootKey;
+  ByteString rootSubjectDER;
+  ByteString rootEncoded(
+    CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA,
+               chainValidity.rootSignatureAlgorithm,
+               nullptr, rootKey, rootSubjectDER));
+  EXPECT_FALSE(ENCODING_FAILED(rootEncoded));
+  EXPECT_FALSE(ENCODING_FAILED(rootSubjectDER));
+
+  const char* issuerCN = rootCN;
+  TestKeyPair* issuerKey = rootKey.get();
+
+  const char* intermediateCN = "CN=Intermediate";
+  ScopedTestKeyPair intermediateKey;
+  ByteString intermediateSubjectDER;
+  ByteString intermediateEncoded;
+  if (chainValidity.optionalIntermediateSignatureAlgorithm != NO_INTERMEDIATE) {
+    intermediateEncoded =
+      CreateCert(rootCN, intermediateCN, EndEntityOrCA::MustBeCA,
+                 chainValidity.optionalIntermediateSignatureAlgorithm,
+                 rootKey.get(), intermediateKey, intermediateSubjectDER);
+    EXPECT_FALSE(ENCODING_FAILED(intermediateEncoded));
+    EXPECT_FALSE(ENCODING_FAILED(intermediateSubjectDER));
+    issuerCN = intermediateCN;
+    issuerKey = intermediateKey.get();
+  }
+
+  AlgorithmTestsTrustDomain trustDomain(rootEncoded, rootSubjectDER,
+                                        intermediateEncoded,
+                                        intermediateSubjectDER);
+
+  const char* endEntityCN = "CN=End Entity";
+  ScopedTestKeyPair endEntityKey;
+  ByteString endEntitySubjectDER;
+  ByteString endEntityEncoded(
+    CreateCert(issuerCN, endEntityCN, EndEntityOrCA::MustBeEndEntity,
+               chainValidity.endEntitySignatureAlgorithm,
+               issuerKey, endEntityKey, endEntitySubjectDER));
+  EXPECT_FALSE(ENCODING_FAILED(endEntityEncoded));
+  EXPECT_FALSE(ENCODING_FAILED(endEntitySubjectDER));
+
+  Input endEntity;
+  ASSERT_EQ(Success, endEntity.Init(endEntityEncoded.data(),
+                                    endEntityEncoded.length()));
+  Result expectedResult = chainValidity.isValid
+                        ? Success
+                        : Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+  ASSERT_EQ(expectedResult,
+            BuildCertChain(trustDomain, endEntity, Now(),
+                           EndEntityOrCA::MustBeEndEntity,
+                           KeyUsage::noParticularKeyUsageRequired,
+                           KeyPurposeId::id_kp_serverAuth,
+                           CertPolicyId::anyPolicy, nullptr));
+}
+
+INSTANTIATE_TEST_CASE_P(pkixcert_IsValidChainForAlgorithm,
+                        pkixcert_IsValidChainForAlgorithm,
+                        testing::ValuesIn(CHAIN_VALIDITY));
--- a/security/pkix/test/gtest/pkixder_pki_types_tests.cpp
+++ b/security/pkix/test/gtest/pkixder_pki_types_tests.cpp
@@ -411,29 +411,29 @@ TEST_F(pkixder_SignatureAlgorithmIdentif
   static const uint8_t DER[] = {
     0x30, 0x0b, 0x06, 0x09,
     0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04
   };
   Input input(DER);
   Reader reader(input);
 
   SignatureAlgorithm alg;
-  ASSERT_EQ(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED,
-            SignatureAlgorithmIdentifier(reader, alg));
+  ASSERT_EQ(Success, SignatureAlgorithmIdentifier(reader, alg));
+  ASSERT_EQ(SignatureAlgorithm::unsupported_algorithm, alg);
 }
 
 TEST_F(pkixder_SignatureAlgorithmIdentifier, Invalid_SignatureAlgorithm_SHA256)
 {
   // The OID identifies id-sha256 (2.16.840.1.101.3.4.2.1). It is invalid
   // because SHA-256 is not a signature algorithm.
   static const uint8_t DER[] = {
     0x30, 0x0b, 0x06, 0x09,
     0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
   };
   Input input(DER);
   Reader reader(input);
 
   SignatureAlgorithm alg;
-  ASSERT_EQ(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED,
-            SignatureAlgorithmIdentifier(reader, alg));
+  ASSERT_EQ(Success, SignatureAlgorithmIdentifier(reader, alg));
+  ASSERT_EQ(SignatureAlgorithm::unsupported_algorithm, alg);
 }
 
 } // unnamed namespace
--- a/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
+++ b/security/pkix/test/gtest/pkixocsp_VerifyEncodedOCSPResponse.cpp
@@ -234,31 +234,33 @@ public:
   static void SetUpTestCase()
   {
     pkixocsp_VerifyEncodedResponse::SetUpTestCase();
   }
 
   ByteString CreateEncodedOCSPSuccessfulResponse(
                     OCSPResponseContext::CertStatus certStatus,
                     const CertID& certID,
-                    /*optional*/ const char* signerName,
+       /*optional*/ const char* signerName,
                     const TestKeyPair& signerKeyPair,
                     time_t producedAt, time_t thisUpdate,
-                    /*optional*/ const time_t* nextUpdate,
-                    /*optional*/ const ByteString* certs = nullptr)
+       /*optional*/ const time_t* nextUpdate,
+                    const ByteString& signatureAlgorithm,
+       /*optional*/ const ByteString* certs = nullptr)
   {
     OCSPResponseContext context(certID, producedAt);
     if (signerName) {
       context.signerNameDER = CNToDERName(signerName);
       EXPECT_FALSE(ENCODING_FAILED(context.signerNameDER));
     }
     context.signerKeyPair = signerKeyPair.Clone();
     EXPECT_TRUE(context.signerKeyPair);
     context.responseStatus = OCSPResponseContext::successful;
     context.producedAt = producedAt;
+    context.signatureAlgorithm = signatureAlgorithm;
     context.certs = certs;
 
     context.certStatus = certStatus;
     context.thisUpdate = thisUpdate;
     context.nextUpdate = nextUpdate ? *nextUpdate : 0;
     context.includeNextUpdate = nextUpdate != nullptr;
 
     return CreateEncodedOCSPResponse(context);
@@ -266,17 +268,18 @@ public:
 };
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byKey)
 {
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID, byKey,
                          *rootKeyPair, oneDayBeforeNow,
-                         oneDayBeforeNow, &oneDayAfterNow));
+                         oneDayBeforeNow, &oneDayAfterNow,
+                         sha256WithRSAEncryption));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID,
                                       Now(), END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
@@ -284,17 +287,18 @@ TEST_F(pkixocsp_VerifyEncodedResponse_su
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byName)
 {
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID, rootName,
                          *rootKeyPair, oneDayBeforeNow,
-                         oneDayBeforeNow, &oneDayAfterNow));
+                         oneDayBeforeNow, &oneDayAfterNow,
+                         sha256WithRSAEncryption));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
@@ -302,17 +306,18 @@ TEST_F(pkixocsp_VerifyEncodedResponse_su
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, good_byKey_without_nextUpdate)
 {
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID, byKey,
                          *rootKeyPair, oneDayBeforeNow,
-                         oneDayBeforeNow, nullptr));
+                         oneDayBeforeNow, nullptr,
+                         sha256WithRSAEncryption));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
@@ -320,17 +325,18 @@ TEST_F(pkixocsp_VerifyEncodedResponse_su
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, revoked)
 {
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::revoked, *endEntityCertID, byKey,
                          *rootKeyPair, oneDayBeforeNow,
-                         oneDayBeforeNow, &oneDayAfterNow));
+                         oneDayBeforeNow, &oneDayAfterNow,
+                         sha256WithRSAEncryption));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
@@ -338,28 +344,49 @@ TEST_F(pkixocsp_VerifyEncodedResponse_su
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_successful, unknown)
 {
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::unknown, *endEntityCertID, byKey,
                          *rootKeyPair, oneDayBeforeNow,
-                         oneDayBeforeNow, &oneDayAfterNow));
+                         oneDayBeforeNow, &oneDayAfterNow,
+                         sha256WithRSAEncryption));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_UNKNOWN_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
+TEST_F(pkixocsp_VerifyEncodedResponse_successful,
+       good_unsupportedSignatureAlgorithm)
+{
+  ByteString responseString(
+               CreateEncodedOCSPSuccessfulResponse(
+                         OCSPResponseContext::good, *endEntityCertID, byKey,
+                         *rootKeyPair, oneDayBeforeNow,
+                         oneDayBeforeNow, &oneDayAfterNow,
+                         md5WithRSAEncryption));
+  Input response;
+  ASSERT_EQ(Success,
+            response.Init(responseString.data(), responseString.length()));
+  bool expired;
+  ASSERT_EQ(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID,
+                                      Now(), END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      response, expired));
+  ASSERT_FALSE(expired);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // indirect responses (signed by a delegated OCSP responder cert)
 
 class pkixocsp_VerifyEncodedResponse_DelegatedResponder
   : public pkixocsp_VerifyEncodedResponse_successful
 {
 protected:
   // certSubjectName should be unique for each call. This way, we avoid any
@@ -367,40 +394,45 @@ protected:
   // we generate a new keypair on each call. Either one of these should be
   // sufficient to avoid issues with the NSS cache, but we do both to be
   // cautious.
   //
   // signerName should be byKey to use the byKey ResponderID construction, or
   // another value (usually equal to certSubjectName) to use the byName
   // ResponderID construction.
   //
+  // certSignatureAlgorithm specifies the signature algorithm that the
+  // certificate will be signed with, not the OCSP response.
+  //
   // If signerEKU is omitted, then the certificate will have the
   // id-kp-OCSPSigning EKU. If signerEKU is SEC_OID_UNKNOWN then it will not
   // have any EKU extension. Otherwise, the certificate will have the given
   // EKU.
   ByteString CreateEncodedIndirectOCSPSuccessfulResponse(
                const char* certSubjectName,
                OCSPResponseContext::CertStatus certStatus,
                const char* signerName,
+               const ByteString& certSignatureAlgorithm,
                /*optional*/ const Input* signerEKUDER = &OCSPSigningEKUDER,
                /*optional, out*/ ByteString* signerDEROut = nullptr)
   {
     assert(certSubjectName);
 
     const ByteString extensions[] = {
       signerEKUDER
         ? CreateEncodedEKUExtension(*signerEKUDER,
                                     ExtensionCriticality::NotCritical)
         : ByteString(),
       ByteString()
     };
     ScopedTestKeyPair signerKeyPair;
     ByteString signerDER(CreateEncodedCertificate(
                            ++rootIssuedCount, rootName,
                            oneDayBeforeNow, oneDayAfterNow, certSubjectName,
+                           certSignatureAlgorithm,
                            signerEKUDER ? extensions : nullptr,
                            rootKeyPair.get(), signerKeyPair));
     EXPECT_FALSE(ENCODING_FAILED(signerDER));
     if (signerDEROut) {
       *signerDEROut = signerDER;
     }
 
     ByteString signerNameDER;
@@ -408,24 +440,27 @@ protected:
       signerNameDER = CNToDERName(signerName);
       EXPECT_FALSE(ENCODING_FAILED(signerNameDER));
     }
     ByteString certs[] = { signerDER, ByteString() };
     return CreateEncodedOCSPSuccessfulResponse(certStatus, *endEntityCertID,
                                                signerName, *signerKeyPair,
                                                oneDayBeforeNow,
                                                oneDayBeforeNow,
-                                               &oneDayAfterNow, certs);
+                                               &oneDayAfterNow,
+                                               sha256WithRSAEncryption,
+                                               certs);
   }
 
   static ByteString CreateEncodedCertificate(uint32_t serialNumber,
                                              const char* issuer,
                                              time_t notBefore,
                                              time_t notAfter,
                                              const char* subject,
+                                             const ByteString& signatureAlg,
                                 /*optional*/ const ByteString* extensions,
                                 /*optional*/ TestKeyPair* signerKeyPair,
                                      /*out*/ ScopedTestKeyPair& keyPair)
   {
     ByteString serialNumberDER(CreateEncodedSerialNumber(serialNumber));
     if (ENCODING_FAILED(serialNumberDER)) {
       return ByteString();
     }
@@ -434,53 +469,53 @@ protected:
       return ByteString();
     }
     ByteString subjectDER(CNToDERName(subject));
     if (ENCODING_FAILED(subjectDER)) {
       return ByteString();
     }
     return ::mozilla::pkix::test::CreateEncodedCertificate(
                                     v3,
-                                    sha256WithRSAEncryption,
+                                    signatureAlg,
                                     serialNumberDER, issuerDER, notBefore,
                                     notAfter, subjectDER, extensions,
                                     signerKeyPair,
-                                    sha256WithRSAEncryption,
+                                    signatureAlg,
                                     keyPair);
   }
 
   static const Input OCSPSigningEKUDER;
 };
 
 /*static*/ const Input pkixocsp_VerifyEncodedResponse_DelegatedResponder::
   OCSPSigningEKUDER(tlv_id_kp_OCSPSigning);
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_byKey)
 {
   ByteString responseString(
                CreateEncodedIndirectOCSPSuccessfulResponse(
                          "good_indirect_byKey", OCSPResponseContext::good,
-                         byKey));
+                         byKey, sha256WithRSAEncryption));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_byName)
 {
   ByteString responseString(
                CreateEncodedIndirectOCSPSuccessfulResponse(
                          "good_indirect_byName", OCSPResponseContext::good,
-                         "good_indirect_byName"));
+                         "good_indirect_byName", sha256WithRSAEncryption));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Success,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
@@ -492,17 +527,18 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
 {
   ScopedTestKeyPair missingSignerKeyPair(GenerateKeyPair());
   ASSERT_TRUE(missingSignerKeyPair);
 
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID, byKey,
                          *missingSignerKeyPair, oneDayBeforeNow,
-                         oneDayBeforeNow, nullptr));
+                         oneDayBeforeNow, nullptr,
+                         sha256WithRSAEncryption));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
@@ -513,17 +549,18 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
        good_byName_missing_signer)
 {
   ScopedTestKeyPair missingSignerKeyPair(GenerateKeyPair());
   ASSERT_TRUE(missingSignerKeyPair);
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
                          "missing", *missingSignerKeyPair,
-                         oneDayBeforeNow, oneDayBeforeNow, nullptr));
+                         oneDayBeforeNow, oneDayBeforeNow, nullptr,
+                         sha256WithRSAEncryption));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
@@ -540,26 +577,27 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
     ByteString()
   };
 
   ScopedTestKeyPair signerKeyPair;
   ByteString signerDER(CreateEncodedCertificate(
                           ++rootIssuedCount, rootName,
                           now - (10 * Time::ONE_DAY_IN_SECONDS),
                           now - (2 * Time::ONE_DAY_IN_SECONDS),
-                          signerName, extensions, rootKeyPair.get(),
-                          signerKeyPair));
+                          signerName, sha256WithRSAEncryption, extensions,
+                          rootKeyPair.get(), signerKeyPair));
   ASSERT_FALSE(ENCODING_FAILED(signerDER));
 
   ByteString certs[] = { signerDER, ByteString() };
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
                          signerName, *signerKeyPair, oneDayBeforeNow,
-                         oneDayBeforeNow, &oneDayAfterNow, certs));
+                         oneDayBeforeNow, &oneDayAfterNow,
+                         sha256WithRSAEncryption, certs));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
@@ -575,43 +613,45 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
     ByteString()
   };
 
   ScopedTestKeyPair signerKeyPair;
   ByteString signerDER(CreateEncodedCertificate(
                          ++rootIssuedCount, rootName,
                          now + (2 * Time::ONE_DAY_IN_SECONDS),
                          now + (10 * Time::ONE_DAY_IN_SECONDS),
-                         signerName, extensions, rootKeyPair.get(),
-                         signerKeyPair));
+                         signerName, sha256WithRSAEncryption, extensions,
+                         rootKeyPair.get(), signerKeyPair));
   ASSERT_FALSE(ENCODING_FAILED(signerDER));
 
   ByteString certs[] = { signerDER, ByteString() };
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
                          signerName, *signerKeyPair, oneDayBeforeNow,
-                         oneDayBeforeNow, &oneDayAfterNow, certs));
+                         oneDayBeforeNow, &oneDayAfterNow,
+                         sha256WithRSAEncryption, certs));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_no_eku)
 {
   ByteString responseString(
                CreateEncodedIndirectOCSPSuccessfulResponse(
                          "good_indirect_wrong_eku",
-                         OCSPResponseContext::good, byKey, nullptr));
+                         OCSPResponseContext::good, byKey,
+                         sha256WithRSAEncryption, nullptr));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
@@ -621,17 +661,18 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
 static const Input serverAuthEKUDER(tlv_id_kp_serverAuth);
 
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder,
        good_indirect_wrong_eku)
 {
   ByteString responseString(
                CreateEncodedIndirectOCSPSuccessfulResponse(
                         "good_indirect_wrong_eku",
-                        OCSPResponseContext::good, byKey, &serverAuthEKUDER));
+                        OCSPResponseContext::good, byKey,
+                        sha256WithRSAEncryption, &serverAuthEKUDER));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
@@ -639,17 +680,18 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
 }
 
 // Test that signature of OCSP response signer cert is verified
 TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder, good_tampered_eku)
 {
   ByteString tamperedResponse(
                CreateEncodedIndirectOCSPSuccessfulResponse(
                          "good_indirect_tampered_eku",
-                         OCSPResponseContext::good, byKey, &serverAuthEKUDER));
+                         OCSPResponseContext::good, byKey,
+                         sha256WithRSAEncryption, &serverAuthEKUDER));
   ASSERT_EQ(Success,
             TamperOnce(tamperedResponse,
                        ByteString(tlv_id_kp_serverAuth,
                                   sizeof(tlv_id_kp_serverAuth)),
                        ByteString(tlv_id_kp_OCSPSigning,
                                   sizeof(tlv_id_kp_OCSPSigning))));
   Input tamperedResponseInput;
   ASSERT_EQ(Success, tamperedResponseInput.Init(tamperedResponse.data(),
@@ -675,27 +717,28 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   const ByteString extensions[] = {
     CreateEncodedEKUExtension(OCSPSigningEKUDER,
                               ExtensionCriticality::NotCritical),
     ByteString()
   };
   ScopedTestKeyPair signerKeyPair;
   ByteString signerDER(CreateEncodedCertificate(
                          1, subCAName, oneDayBeforeNow, oneDayAfterNow,
-                         signerName, extensions, unknownKeyPair.get(),
-                         signerKeyPair));
+                         signerName, sha256WithRSAEncryption, extensions,
+                         unknownKeyPair.get(), signerKeyPair));
   ASSERT_FALSE(ENCODING_FAILED(signerDER));
 
   // OCSP response signed by that delegated responder
   ByteString certs[] = { signerDER, ByteString() };
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
                          signerName, *signerKeyPair, oneDayBeforeNow,
-                         oneDayBeforeNow, &oneDayAfterNow, certs));
+                         oneDayBeforeNow, &oneDayAfterNow,
+                         sha256WithRSAEncryption, certs));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
@@ -715,41 +758,42 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   const ByteString subCAExtensions[] = {
     CreateEncodedBasicConstraints(true, 0, ExtensionCriticality::NotCritical),
     ByteString()
   };
   ScopedTestKeyPair subCAKeyPair;
   ByteString subCADER(CreateEncodedCertificate(
                         ++rootIssuedCount, rootName,
                         oneDayBeforeNow, oneDayAfterNow,
-                        subCAName, subCAExtensions, rootKeyPair.get(),
-                        subCAKeyPair));
+                        subCAName, sha256WithRSAEncryption,
+                        subCAExtensions, rootKeyPair.get(), subCAKeyPair));
   ASSERT_FALSE(ENCODING_FAILED(subCADER));
 
   // Delegated responder cert signed by that sub-CA
   const ByteString extensions[] = {
     CreateEncodedEKUExtension(OCSPSigningEKUDER,
                               ExtensionCriticality::NotCritical),
     ByteString(),
   };
   ScopedTestKeyPair signerKeyPair;
   ByteString signerDER(CreateEncodedCertificate(
                          1, subCAName, oneDayBeforeNow, oneDayAfterNow,
-                         signerName, extensions, subCAKeyPair.get(),
-                         signerKeyPair));
+                         signerName, sha256WithRSAEncryption, extensions,
+                         subCAKeyPair.get(), signerKeyPair));
   ASSERT_FALSE(ENCODING_FAILED(signerDER));
 
   // OCSP response signed by the delegated responder issued by the sub-CA
   // that is trying to impersonate the root.
   ByteString certs[] = { subCADER, signerDER, ByteString() };
   ByteString responseString(
                CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
                          signerName, *signerKeyPair, oneDayBeforeNow,
-                         oneDayBeforeNow, &oneDayAfterNow, certs));
+                         oneDayBeforeNow, &oneDayAfterNow,
+                         sha256WithRSAEncryption, certs));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
@@ -768,64 +812,89 @@ TEST_F(pkixocsp_VerifyEncodedResponse_De
   // sub-CA of root (root is the direct issuer of endEntity)
   const ByteString subCAExtensions[] = {
     CreateEncodedBasicConstraints(true, 0, ExtensionCriticality::NotCritical),
     ByteString()
   };
   ScopedTestKeyPair subCAKeyPair;
   ByteString subCADER(CreateEncodedCertificate(++rootIssuedCount, rootName,
                                                oneDayBeforeNow, oneDayAfterNow,
-                                               subCAName, subCAExtensions,
+                                               subCAName,
+                                               sha256WithRSAEncryption,
+                                               subCAExtensions,
                                                rootKeyPair.get(),
                                                subCAKeyPair));
   ASSERT_FALSE(ENCODING_FAILED(subCADER));
 
   // Delegated responder cert signed by that sub-CA
   const ByteString extensions[] = {
     CreateEncodedEKUExtension(OCSPSigningEKUDER,
                               ExtensionCriticality::NotCritical),
     ByteString()
   };
   ScopedTestKeyPair signerKeyPair;
   ByteString signerDER(CreateEncodedCertificate(
                          1, subCAName, oneDayBeforeNow, oneDayAfterNow,
-                         signerName, extensions, subCAKeyPair.get(),
-                         signerKeyPair));
+                         signerName, sha256WithRSAEncryption, extensions,
+                         subCAKeyPair.get(), signerKeyPair));
   ASSERT_FALSE(ENCODING_FAILED(signerDER));
 
   // OCSP response signed by the delegated responder issued by the sub-CA
   // that is trying to impersonate the root.
   ByteString certs[] = { signerDER, subCADER, ByteString() };
   ByteString responseString(
                  CreateEncodedOCSPSuccessfulResponse(
                          OCSPResponseContext::good, *endEntityCertID,
                          signerName, *signerKeyPair, oneDayBeforeNow,
-                         oneDayBeforeNow, &oneDayAfterNow, certs));
+                         oneDayBeforeNow, &oneDayAfterNow,
+                         sha256WithRSAEncryption, certs));
   Input response;
   ASSERT_EQ(Success,
             response.Init(responseString.data(), responseString.length()));
   bool expired;
   ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
             VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
                                       END_ENTITY_MAX_LIFETIME_IN_DAYS,
                                       response, expired));
   ASSERT_FALSE(expired);
 }
 
+TEST_F(pkixocsp_VerifyEncodedResponse_DelegatedResponder,
+       good_unsupportedSignatureAlgorithmOnResponder)
+{
+  // Note that the algorithm ID (md5WithRSAEncryption) identifies the signature
+  // algorithm that will be used to sign the certificate that issues the OCSP
+  // responses, not the responses themselves.
+  ByteString responseString(
+               CreateEncodedIndirectOCSPSuccessfulResponse(
+                         "good_indirect_unsupportedSignatureAlgorithm",
+                         OCSPResponseContext::good, byKey,
+                         md5WithRSAEncryption));
+  Input response;
+  ASSERT_EQ(Success,
+            response.Init(responseString.data(), responseString.length()));
+  bool expired;
+  ASSERT_EQ(Result::ERROR_OCSP_INVALID_SIGNING_CERT,
+            VerifyEncodedOCSPResponse(trustDomain, *endEntityCertID, Now(),
+                                      END_ENTITY_MAX_LIFETIME_IN_DAYS,
+                                      response, expired));
+}
+
 class pkixocsp_VerifyEncodedResponse_GetCertTrust
   : public pkixocsp_VerifyEncodedResponse_DelegatedResponder {
 public:
   void SetUp()
   {
     pkixocsp_VerifyEncodedResponse_DelegatedResponder::SetUp();
 
     responseString =
         CreateEncodedIndirectOCSPSuccessfulResponse(
           "OCSPGetCertTrustTest Signer", OCSPResponseContext::good,
-          byKey, &OCSPSigningEKUDER, &signerCertDER);
+          byKey, sha256WithRSAEncryption, &OCSPSigningEKUDER,
+          &signerCertDER);
     if (ENCODING_FAILED(responseString)) {
       abort();
     }
     if (response.Init(responseString.data(), responseString.length())
           != Success) {
       abort();
     }
     if (signerCertDER.length() == 0) {
--- a/security/pkix/test/lib/pkixtestnss.cpp
+++ b/security/pkix/test/lib/pkixtestnss.cpp
@@ -26,16 +26,17 @@
 
 #include <limits>
 
 #include "cryptohi.h"
 #include "keyhi.h"
 #include "nss.h"
 #include "pk11pub.h"
 #include "pkix/pkixnss.h"
+#include "pkixder.h"
 #include "secerr.h"
 #include "secitem.h"
 
 namespace mozilla { namespace pkix { namespace test {
 
 namespace {
 
 typedef ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey>
@@ -71,20 +72,38 @@ public:
     , privateKey(privateKey)
   {
   }
 
   virtual Result SignData(const ByteString& tbs,
                           const ByteString& signatureAlgorithm,
                           /*out*/ ByteString& signature) const
   {
-    SECOidTag signatureAlgorithmOidTag;
-    if (signatureAlgorithm == sha256WithRSAEncryption) {
-      signatureAlgorithmOidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION;
-    } else {
+    // signatureAlgorithm is of the form SEQUENCE { OID { <OID bytes> } },
+    // whereas SECOID_GetAlgorithmTag wants just the OID bytes, so we have to
+    // unwrap it here. As long as signatureAlgorithm is short enough, we don't
+    // have to do full DER decoding here.
+    // Also, this is just for testing purposes - there shouldn't be any
+    // untrusted input given to this function. If we make a mistake, we only
+    // have ourselves to blame.
+    if (signatureAlgorithm.length() > 127 ||
+        signatureAlgorithm.length() < 4 ||
+        signatureAlgorithm.data()[0] != der::SEQUENCE ||
+        signatureAlgorithm.data()[2] != der::OIDTag) {
+      return Result::FATAL_ERROR_INVALID_ARGS;
+    }
+    SECAlgorithmID signatureAlgorithmID;
+    signatureAlgorithmID.algorithm.data =
+      const_cast<unsigned char*>(signatureAlgorithm.data() + 4);
+    signatureAlgorithmID.algorithm.len = signatureAlgorithm.length() - 4;
+    signatureAlgorithmID.parameters.data = nullptr;
+    signatureAlgorithmID.parameters.len = 0;
+    SECOidTag signatureAlgorithmOidTag =
+      SECOID_GetAlgorithmTag(&signatureAlgorithmID);
+    if (signatureAlgorithmOidTag == SEC_OID_UNKNOWN) {
       return Result::FATAL_ERROR_INVALID_ARGS;
     }
 
     SECItem signatureItem;
     if (SEC_SignData(&signatureItem, tbs.data(), tbs.length(),
                      privateKey.get(), signatureAlgorithmOidTag)
           != SECSuccess) {
       return MapPRErrorCodeToResult(PR_GetError());
--- a/security/pkix/test/lib/pkixtestutil.cpp
+++ b/security/pkix/test/lib/pkixtestutil.cpp
@@ -32,23 +32,16 @@
 
 #include "pkixder.h"
 #include "pkixutil.h"
 
 using namespace std;
 
 namespace mozilla { namespace pkix { namespace test {
 
-// python DottedOIDToCode.py --alg sha256WithRSAEncryption 1.2.840.113549.1.1.11
-static const uint8_t alg_sha256WithRSAEncryption[] = {
-  0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b
-};
-const ByteString sha256WithRSAEncryption(alg_sha256WithRSAEncryption,
-  MOZILLA_PKIX_ARRAY_LENGTH(alg_sha256WithRSAEncryption));
-
 namespace {
 
 inline void
 fclose_void(FILE* file) {
   (void) fclose(file);
 }
 
 typedef mozilla::pkix::ScopedPtr<FILE, fclose_void> ScopedFILE;
@@ -72,16 +65,29 @@ OpenFile(const string& dir, const string
 #else
   file = fopen(path.c_str(), mode.c_str());
 #endif
   return file.release();
 }
 
 } // unnamed namespace
 
+bool
+InputEqualsByteString(Input input, const ByteString& bs)
+{
+  Input bsInput;
+  if (bsInput.Init(bs.data(), bs.length()) != Success) {
+    // Init can only fail if it is given a bad pointer or if the input is too
+    // long, which won't ever happen. Plus, if it does, it is ok to call abort
+    // since this is only test code.
+    abort();
+  }
+  return InputsAreEqual(input, bsInput);
+}
+
 Result
 TamperOnce(/*in/out*/ ByteString& item, const ByteString& from,
            const ByteString& to)
 {
   if (from.length() < 8) {
     return Result::FATAL_ERROR_INVALID_ARGS;
   }
   if (from.length() != to.length()) {
@@ -125,16 +131,17 @@ TLV(uint8_t tag, const ByteString& value
 
 OCSPResponseContext::OCSPResponseContext(const CertID& certID, time_t time)
   : certID(certID)
   , responseStatus(successful)
   , skipResponseBytes(false)
   , producedAt(time)
   , extensions(nullptr)
   , includeEmptyExtensions(false)
+  , signatureAlgorithm(sha256WithRSAEncryption)
   , badSignature(false)
   , certs(nullptr)
 
   , certStatus(good)
   , revocationTime(0)
   , thisUpdate(time)
   , nextUpdate(time + 10)
   , includeNextUpdate(true)
@@ -753,17 +760,17 @@ BasicOCSPResponse(OCSPResponseContext& c
 {
   ByteString tbsResponseData(ResponseData(context));
   if (ENCODING_FAILED(tbsResponseData)) {
     return ByteString();
   }
 
   // TODO(bug 980538): certs
   return SignedData(tbsResponseData, context.signerKeyPair.get(),
-                    sha256WithRSAEncryption,
+                    context.signatureAlgorithm,
                     context.badSignature, context.certs);
 }
 
 // Extension ::= SEQUENCE {
 //   id               OBJECT IDENTIFIER,
 //   critical         BOOLEAN DEFAULT FALSE
 //   value            OCTET STRING
 // }
--- a/security/pkix/test/lib/pkixtestutil.h
+++ b/security/pkix/test/lib/pkixtestutil.h
@@ -69,23 +69,46 @@ static const uint8_t tlv_id_kp_OCSPSigni
   0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09
 };
 
 // python DottedOIDToCode.py --tlv id-kp-serverAuth 1.3.6.1.5.5.7.3.1
 static const uint8_t tlv_id_kp_serverAuth[] = {
   0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01
 };
 
-extern const ByteString sha256WithRSAEncryption;
+// python DottedOIDToCode.py --alg sha256WithRSAEncryption 1.2.840.113549.1.1.11
+const uint8_t alg_sha256WithRSAEncryption[] = {
+  0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b
+};
+
+const ByteString sha256WithRSAEncryption(alg_sha256WithRSAEncryption,
+  MOZILLA_PKIX_ARRAY_LENGTH(alg_sha256WithRSAEncryption));
+
+// python DottedOIDToCode.py --alg md5WithRSAEncryption 1.2.840.113549.1.1.4
+const uint8_t alg_md5WithRSAEncryption[] = {
+  0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04
+};
+
+const ByteString md5WithRSAEncryption(alg_md5WithRSAEncryption,
+  MOZILLA_PKIX_ARRAY_LENGTH(alg_md5WithRSAEncryption));
+
+// python DottedOIDToCode.py --alg md2WithRSAEncryption 1.2.840.113549.1.1.2
+const uint8_t alg_md2WithRSAEncryption[] = {
+  0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x02
+};
+
+const ByteString md2WithRSAEncryption(alg_md2WithRSAEncryption,
+  MOZILLA_PKIX_ARRAY_LENGTH(alg_md2WithRSAEncryption));
 
 // e.g. YMDHMS(2016, 12, 31, 1, 23, 45) => 2016-12-31:01:23:45 (GMT)
 mozilla::pkix::Time YMDHMS(int16_t year, int16_t month, int16_t day,
                            int16_t hour, int16_t minutes, int16_t seconds);
 
 ByteString CNToDERName(const char* cn);
+bool InputEqualsByteString(Input input, const ByteString& bs);
 
 class TestKeyPair
 {
 public:
   virtual ~TestKeyPair() { }
 
   // The DER encoding of the entire SubjectPublicKeyInfo structure. This is
   // what is encoded in certificates.
@@ -216,16 +239,17 @@ public:
 
   std::time_t producedAt;
 
   OCSPResponseExtension* extensions;
   bool includeEmptyExtensions; // If true, include the extension wrapper
                                // regardless of if there are any actual
                                // extensions.
   ScopedTestKeyPair signerKeyPair;
+  ByteString signatureAlgorithm; // DER encoding of signature algorithm to use.
   bool badSignature; // If true, alter the signature to fail verification
   const ByteString* certs; // optional; array terminated by an empty string
 
   // The following fields are on a per-SingleResponse basis. In the future we
   // may support including multiple SingleResponses per response.
   enum CertStatus {
     good = 0,
     revoked = 1,