Commit 929a0aa4 authored by eroman's avatar eroman Committed by Commit bot

Add error details to TBSCertificate parsing function and tests.

BUG=634443

Review-Url: https://codereview.chromium.org/2341943002
Cr-Commit-Position: refs/heads/master@{#419363}
parent bc94e6ac
...@@ -30,6 +30,9 @@ DEFINE_CERT_ERROR_ID( ...@@ -30,6 +30,9 @@ DEFINE_CERT_ERROR_ID(
DEFINE_CERT_ERROR_ID(kSignatureValueNotBitString, DEFINE_CERT_ERROR_ID(kSignatureValueNotBitString,
"Couldn't read Certificate.signatureValue as BIT STRING"); "Couldn't read Certificate.signatureValue as BIT STRING");
DEFINE_CERT_ERROR_ID(kUnconsumedDataInsideTbsCertificateSequence,
"Unconsumed data inside TBSCertificate");
// Returns true if |input| is a SEQUENCE and nothing else. // Returns true if |input| is a SEQUENCE and nothing else.
WARN_UNUSED_RESULT bool IsSequenceTLV(const der::Input& input) { WARN_UNUSED_RESULT bool IsSequenceTLV(const der::Input& input) {
der::Parser parser(input); der::Parser parser(input);
...@@ -258,7 +261,16 @@ bool ParseCertificate(const der::Input& certificate_tlv, ...@@ -258,7 +261,16 @@ bool ParseCertificate(const der::Input& certificate_tlv,
// } // }
bool ParseTbsCertificate(const der::Input& tbs_tlv, bool ParseTbsCertificate(const der::Input& tbs_tlv,
const ParseCertificateOptions& options, const ParseCertificateOptions& options,
ParsedTbsCertificate* out) { ParsedTbsCertificate* out,
CertErrors* errors) {
// The rest of this function assumes that |errors| is non-null.
if (!errors) {
CertErrors unused_errors;
return ParseTbsCertificate(tbs_tlv, options, out, &unused_errors);
}
// TODO(crbug.com/634443): Add useful error information to |errors|.
der::Parser parser(tbs_tlv); der::Parser parser(tbs_tlv);
// Certificate ::= SEQUENCE { // Certificate ::= SEQUENCE {
...@@ -374,8 +386,10 @@ bool ParseTbsCertificate(const der::Input& tbs_tlv, ...@@ -374,8 +386,10 @@ bool ParseTbsCertificate(const der::Input& tbs_tlv,
// However because only v1, v2, and v3 certificates are supported by the // However because only v1, v2, and v3 certificates are supported by the
// parsing, there shouldn't be any subsequent data in those versions, so // parsing, there shouldn't be any subsequent data in those versions, so
// reject. // reject.
if (tbs_parser.HasMore()) if (tbs_parser.HasMore()) {
errors->AddError(kUnconsumedDataInsideTbsCertificateSequence);
return false; return false;
}
// By definition the input was a single TBSCertificate, so there shouldn't be // By definition the input was a single TBSCertificate, so there shouldn't be
// unconsumed data. // unconsumed data.
......
...@@ -102,6 +102,9 @@ NET_EXPORT bool ParseCertificate(const der::Input& certificate_tlv, ...@@ -102,6 +102,9 @@ NET_EXPORT bool ParseCertificate(const der::Input& certificate_tlv,
// on success and sets the results in |out|. Certain invalid inputs may // on success and sets the results in |out|. Certain invalid inputs may
// be accepted based on the provided |options|. // be accepted based on the provided |options|.
// //
// If |errors| was non-null then any warnings/errors that occur during parsing
// are added to it.
//
// Note that on success |out| aliases data from the input |tbs_tlv|. // Note that on success |out| aliases data from the input |tbs_tlv|.
// Hence the fields of the ParsedTbsCertificate are only valid as long as // Hence the fields of the ParsedTbsCertificate are only valid as long as
// |tbs_tlv| remains valid. // |tbs_tlv| remains valid.
...@@ -129,8 +132,8 @@ NET_EXPORT bool ParseCertificate(const der::Input& certificate_tlv, ...@@ -129,8 +132,8 @@ NET_EXPORT bool ParseCertificate(const der::Input& certificate_tlv,
// } // }
NET_EXPORT bool ParseTbsCertificate(const der::Input& tbs_tlv, NET_EXPORT bool ParseTbsCertificate(const der::Input& tbs_tlv,
const ParseCertificateOptions& options, const ParseCertificateOptions& options,
ParsedTbsCertificate* out) ParsedTbsCertificate* out,
WARN_UNUSED_RESULT; CertErrors* errors) WARN_UNUSED_RESULT;
// Represents a "Version" from RFC 5280: // Represents a "Version" from RFC 5280:
// Version ::= INTEGER { v1(0), v2(1), v3(2) } // Version ::= INTEGER { v1(0), v2(1), v3(2) }
......
...@@ -41,7 +41,7 @@ void ParseCertificateForFuzzer(const der::Input& in) { ...@@ -41,7 +41,7 @@ void ParseCertificateForFuzzer(const der::Input& in) {
SignatureAlgorithm::CreateFromDer(signature_algorithm_tlv)); SignatureAlgorithm::CreateFromDer(signature_algorithm_tlv));
ParsedTbsCertificate tbs; ParsedTbsCertificate tbs;
if (!ParseTbsCertificate(tbs_certificate_tlv, {}, &tbs)) if (!ParseTbsCertificate(tbs_certificate_tlv, {}, &tbs, &errors))
return; return;
RDNSequence subject; RDNSequence subject;
......
...@@ -122,7 +122,10 @@ TEST(ParseCertificateTest, AlgorithmNotSequence) { ...@@ -122,7 +122,10 @@ TEST(ParseCertificateTest, AlgorithmNotSequence) {
// Loads tbsCertificate data and expectations from the PEM file |file_name|. // Loads tbsCertificate data and expectations from the PEM file |file_name|.
// Verifies that parsing the TBSCertificate succeeds, and each parsed field // Verifies that parsing the TBSCertificate succeeds, and each parsed field
// matches the expectations. // matches the expectations.
void EnsureParsingTbsSucceeds(const std::string& file_name, //
// TODO(eroman): Get rid of the |expected_version| parameter -- this should be
// encoded in the test expectations file.
void RunTbsCertificateTestGivenVersion(const std::string& file_name,
CertificateVersion expected_version) { CertificateVersion expected_version) {
std::string data; std::string data;
std::string expected_serial_number; std::string expected_serial_number;
...@@ -135,26 +138,39 @@ void EnsureParsingTbsSucceeds(const std::string& file_name, ...@@ -135,26 +138,39 @@ void EnsureParsingTbsSucceeds(const std::string& file_name,
std::string expected_issuer_unique_id; std::string expected_issuer_unique_id;
std::string expected_subject_unique_id; std::string expected_subject_unique_id;
std::string expected_extensions; std::string expected_extensions;
std::string expected_errors;
// Read the certificate data and test expectations from a single PEM file. // Read the certificate data and test expectations from a single PEM file.
const PemBlockMapping mappings[] = { const PemBlockMapping mappings[] = {
{"TBS CERTIFICATE", &data}, {"TBS CERTIFICATE", &data},
{"SIGNATURE ALGORITHM", &expected_signature_algorithm}, {"SIGNATURE ALGORITHM", &expected_signature_algorithm, true},
{"SERIAL NUMBER", &expected_serial_number}, {"SERIAL NUMBER", &expected_serial_number, true},
{"ISSUER", &expected_issuer}, {"ISSUER", &expected_issuer, true},
{"VALIDITY NOTBEFORE", &expected_validity_not_before}, {"VALIDITY NOTBEFORE", &expected_validity_not_before, true},
{"VALIDITY NOTAFTER", &expected_validity_not_after}, {"VALIDITY NOTAFTER", &expected_validity_not_after, true},
{"SUBJECT", &expected_subject}, {"SUBJECT", &expected_subject, true},
{"SPKI", &expected_spki}, {"SPKI", &expected_spki, true},
{"ISSUER UNIQUE ID", &expected_issuer_unique_id, true}, {"ISSUER UNIQUE ID", &expected_issuer_unique_id, true},
{"SUBJECT UNIQUE ID", &expected_subject_unique_id, true}, {"SUBJECT UNIQUE ID", &expected_subject_unique_id, true},
{"EXTENSIONS", &expected_extensions, true}, {"EXTENSIONS", &expected_extensions, true},
{"ERRORS", &expected_errors, true},
}; };
ASSERT_TRUE(ReadTestDataFromPemFile(GetFilePath(file_name), mappings)); std::string test_file_path = GetFilePath(file_name);
ASSERT_TRUE(ReadTestDataFromPemFile(test_file_path, mappings));
bool expected_result = !expected_spki.empty();
// Parsing the TBSCertificate should succeed.
ParsedTbsCertificate parsed; ParsedTbsCertificate parsed;
ASSERT_TRUE(ParseTbsCertificate(der::Input(&data), {}, &parsed)); CertErrors errors;
bool actual_result =
ParseTbsCertificate(der::Input(&data), {}, &parsed, &errors);
EXPECT_EQ(expected_result, actual_result);
EXPECT_EQ(expected_errors, errors.ToDebugString()) << "Test file: "
<< test_file_path;
if (!expected_result || !actual_result)
return;
// Ensure that the ParsedTbsCertificate matches expectations. // Ensure that the ParsedTbsCertificate matches expectations.
EXPECT_EQ(expected_version, parsed.version); EXPECT_EQ(expected_version, parsed.version);
...@@ -184,30 +200,18 @@ void EnsureParsingTbsSucceeds(const std::string& file_name, ...@@ -184,30 +200,18 @@ void EnsureParsingTbsSucceeds(const std::string& file_name,
EXPECT_EQ(!expected_extensions.empty(), parsed.has_extensions); EXPECT_EQ(!expected_extensions.empty(), parsed.has_extensions);
} }
// Loads certificate data from the PEM file |file_name| and verifies that the void RunTbsCertificateTest(const std::string& file_name) {
// Certificate parsing succeed, however the TBSCertificate parsing fails. RunTbsCertificateTestGivenVersion(file_name, CertificateVersion::V3);
void EnsureParsingTbsFails(const std::string& file_name) {
std::string data;
const PemBlockMapping mappings[] = {
{"TBS CERTIFICATE", &data},
};
ASSERT_TRUE(ReadTestDataFromPemFile(GetFilePath(file_name), mappings));
// Parsing the TBSCertificate should fail.
ParsedTbsCertificate parsed;
ASSERT_FALSE(ParseTbsCertificate(der::Input(&data), {}, &parsed));
} }
// Tests parsing a TBSCertificate for v3 that contains no optional fields. // Tests parsing a TBSCertificate for v3 that contains no optional fields.
TEST(ParseTbsCertificateTest, Version3NoOptionals) { TEST(ParseTbsCertificateTest, Version3NoOptionals) {
EnsureParsingTbsSucceeds("tbs_v3_no_optionals.pem", CertificateVersion::V3); RunTbsCertificateTest("tbs_v3_no_optionals.pem");
} }
// Tests parsing a TBSCertificate for v3 that contains extensions. // Tests parsing a TBSCertificate for v3 that contains extensions.
TEST(ParseTbsCertificateTest, Version3WithExtensions) { TEST(ParseTbsCertificateTest, Version3WithExtensions) {
EnsureParsingTbsSucceeds("tbs_v3_extensions.pem", CertificateVersion::V3); RunTbsCertificateTest("tbs_v3_extensions.pem");
} }
// Tests parsing a TBSCertificate for v3 that contains no optional fields, and // Tests parsing a TBSCertificate for v3 that contains no optional fields, and
...@@ -216,115 +220,111 @@ TEST(ParseTbsCertificateTest, Version3WithExtensions) { ...@@ -216,115 +220,111 @@ TEST(ParseTbsCertificateTest, Version3WithExtensions) {
// CAs are not supposed to include negative serial numbers, however RFC 5280 // CAs are not supposed to include negative serial numbers, however RFC 5280
// expects consumers to deal with it anyway). // expects consumers to deal with it anyway).
TEST(ParseTbsCertificateTest, NegativeSerialNumber) { TEST(ParseTbsCertificateTest, NegativeSerialNumber) {
EnsureParsingTbsSucceeds("tbs_negative_serial_number.pem", RunTbsCertificateTest("tbs_negative_serial_number.pem");
CertificateVersion::V3);
} }
// Tests parsing a TBSCertificate with a serial number that is 21 octets long // Tests parsing a TBSCertificate with a serial number that is 21 octets long
// (and the first byte is 0). // (and the first byte is 0).
TEST(ParseTbCertificateTest, SerialNumber21OctetsLeading0) { TEST(ParseTbCertificateTest, SerialNumber21OctetsLeading0) {
EnsureParsingTbsFails("tbs_serial_number_21_octets_leading_0.pem"); RunTbsCertificateTest("tbs_serial_number_21_octets_leading_0.pem");
} }
// Tests parsing a TBSCertificate with a serial number that is 26 octets long // Tests parsing a TBSCertificate with a serial number that is 26 octets long
// (and does not contain a leading 0). // (and does not contain a leading 0).
TEST(ParseTbsCertificateTest, SerialNumber26Octets) { TEST(ParseTbsCertificateTest, SerialNumber26Octets) {
EnsureParsingTbsFails("tbs_serial_number_26_octets.pem"); RunTbsCertificateTest("tbs_serial_number_26_octets.pem");
} }
// Tests parsing a TBSCertificate which lacks a version number (causing it to // Tests parsing a TBSCertificate which lacks a version number (causing it to
// default to v1). // default to v1).
TEST(ParseTbsCertificateTest, Version1) { TEST(ParseTbsCertificateTest, Version1) {
EnsureParsingTbsSucceeds("tbs_v1.pem", CertificateVersion::V1); RunTbsCertificateTestGivenVersion("tbs_v1.pem", CertificateVersion::V1);
} }
// The version was set to v1 explicitly rather than omitting the version field. // The version was set to v1 explicitly rather than omitting the version field.
TEST(ParseTbsCertificateTest, ExplicitVersion1) { TEST(ParseTbsCertificateTest, ExplicitVersion1) {
EnsureParsingTbsFails("tbs_explicit_v1.pem"); RunTbsCertificateTest("tbs_explicit_v1.pem");
} }
// Extensions are not defined in version 1. // Extensions are not defined in version 1.
TEST(ParseTbsCertificateTest, Version1WithExtensions) { TEST(ParseTbsCertificateTest, Version1WithExtensions) {
EnsureParsingTbsFails("tbs_v1_extensions.pem"); RunTbsCertificateTest("tbs_v1_extensions.pem");
} }
// Extensions are not defined in version 2. // Extensions are not defined in version 2.
TEST(ParseTbsCertificateTest, Version2WithExtensions) { TEST(ParseTbsCertificateTest, Version2WithExtensions) {
EnsureParsingTbsFails("tbs_v2_extensions.pem"); RunTbsCertificateTest("tbs_v2_extensions.pem");
} }
// A boring version 2 certificate with none of the optional fields. // A boring version 2 certificate with none of the optional fields.
TEST(ParseTbsCertificateTest, Version2NoOptionals) { TEST(ParseTbsCertificateTest, Version2NoOptionals) {
EnsureParsingTbsSucceeds("tbs_v2_no_optionals.pem", CertificateVersion::V2); RunTbsCertificateTestGivenVersion("tbs_v2_no_optionals.pem",
CertificateVersion::V2);
} }
// A version 2 certificate with an issuer unique ID field. // A version 2 certificate with an issuer unique ID field.
TEST(ParseTbsCertificateTest, Version2IssuerUniqueId) { TEST(ParseTbsCertificateTest, Version2IssuerUniqueId) {
EnsureParsingTbsSucceeds("tbs_v2_issuer_unique_id.pem", RunTbsCertificateTestGivenVersion("tbs_v2_issuer_unique_id.pem",
CertificateVersion::V2); CertificateVersion::V2);
} }
// A version 2 certificate with both a issuer and subject unique ID field. // A version 2 certificate with both a issuer and subject unique ID field.
TEST(ParseTbsCertificateTest, Version2IssuerAndSubjectUniqueId) { TEST(ParseTbsCertificateTest, Version2IssuerAndSubjectUniqueId) {
EnsureParsingTbsSucceeds("tbs_v2_issuer_and_subject_unique_id.pem", RunTbsCertificateTestGivenVersion("tbs_v2_issuer_and_subject_unique_id.pem",
CertificateVersion::V2); CertificateVersion::V2);
} }
// A version 3 certificate with all of the optional fields (issuer unique id, // A version 3 certificate with all of the optional fields (issuer unique id,
// subject unique id, and extensions). // subject unique id, and extensions).
TEST(ParseTbsCertificateTest, Version3AllOptionals) { TEST(ParseTbsCertificateTest, Version3AllOptionals) {
EnsureParsingTbsSucceeds("tbs_v3_all_optionals.pem", CertificateVersion::V3); RunTbsCertificateTest("tbs_v3_all_optionals.pem");
} }
// The version was set to v4, which is unrecognized. // The version was set to v4, which is unrecognized.
TEST(ParseTbsCertificateTest, Version4) { TEST(ParseTbsCertificateTest, Version4) {
EnsureParsingTbsFails("tbs_v4.pem"); RunTbsCertificateTest("tbs_v4.pem");
} }
// Tests that extraneous data after extensions in a v3 is rejected. // Tests that extraneous data after extensions in a v3 is rejected.
TEST(ParseTbsCertificateTest, Version3DataAfterExtensions) { TEST(ParseTbsCertificateTest, Version3DataAfterExtensions) {
EnsureParsingTbsFails("tbs_v3_data_after_extensions.pem"); RunTbsCertificateTest("tbs_v3_data_after_extensions.pem");
} }
// Tests using a real-world certificate (whereas the other tests are fabricated // Tests using a real-world certificate (whereas the other tests are fabricated
// (and in fact invalid) data. // (and in fact invalid) data.
TEST(ParseTbsCertificateTest, Version3Real) { TEST(ParseTbsCertificateTest, Version3Real) {
EnsureParsingTbsSucceeds("tbs_v3_real.pem", CertificateVersion::V3); RunTbsCertificateTest("tbs_v3_real.pem");
} }
// Parses a TBSCertificate whose "validity" field expresses both notBefore // Parses a TBSCertificate whose "validity" field expresses both notBefore
// and notAfter using UTCTime. // and notAfter using UTCTime.
TEST(ParseTbsCertificateTest, ValidityBothUtcTime) { TEST(ParseTbsCertificateTest, ValidityBothUtcTime) {
EnsureParsingTbsSucceeds("tbs_validity_both_utc_time.pem", RunTbsCertificateTest("tbs_validity_both_utc_time.pem");
CertificateVersion::V3);
} }
// Parses a TBSCertificate whose "validity" field expresses both notBefore // Parses a TBSCertificate whose "validity" field expresses both notBefore
// and notAfter using GeneralizedTime. // and notAfter using GeneralizedTime.
TEST(ParseTbsCertificateTest, ValidityBothGeneralizedTime) { TEST(ParseTbsCertificateTest, ValidityBothGeneralizedTime) {
EnsureParsingTbsSucceeds("tbs_validity_both_generalized_time.pem", RunTbsCertificateTest("tbs_validity_both_generalized_time.pem");
CertificateVersion::V3);
} }
// Parses a TBSCertificate whose "validity" field expresses notBefore using // Parses a TBSCertificate whose "validity" field expresses notBefore using
// UTCTime and notAfter using GeneralizedTime. // UTCTime and notAfter using GeneralizedTime.
TEST(ParseTbsCertificateTest, ValidityUTCTimeAndGeneralizedTime) { TEST(ParseTbsCertificateTest, ValidityUTCTimeAndGeneralizedTime) {
EnsureParsingTbsSucceeds("tbs_validity_utc_time_and_generalized_time.pem", RunTbsCertificateTest("tbs_validity_utc_time_and_generalized_time.pem");
CertificateVersion::V3);
} }
// Parses a TBSCertificate whose validity" field expresses notBefore using // Parses a TBSCertificate whose validity" field expresses notBefore using
// GeneralizedTime and notAfter using UTCTime. Also of interest, notBefore > // GeneralizedTime and notAfter using UTCTime. Also of interest, notBefore >
// notAfter. Parsing will succeed, however no time can satisfy this constraint. // notAfter. Parsing will succeed, however no time can satisfy this constraint.
TEST(ParseTbsCertificateTest, ValidityGeneralizedTimeAndUTCTime) { TEST(ParseTbsCertificateTest, ValidityGeneralizedTimeAndUTCTime) {
EnsureParsingTbsSucceeds("tbs_validity_generalized_time_and_utc_time.pem", RunTbsCertificateTest("tbs_validity_generalized_time_and_utc_time.pem");
CertificateVersion::V3);
} }
// Parses a TBSCertificate whose "validity" field does not strictly follow // Parses a TBSCertificate whose "validity" field does not strictly follow
// the DER rules (and fails to be parsed). // the DER rules (and fails to be parsed).
TEST(ParseTbsCertificateTest, ValidityRelaxed) { TEST(ParseTbsCertificateTest, ValidityRelaxed) {
EnsureParsingTbsFails("tbs_validity_relaxed.pem"); RunTbsCertificateTest("tbs_validity_relaxed.pem");
} }
// Reads a PEM file containing a block "EXTENSION". This input will be // Reads a PEM file containing a block "EXTENSION". This input will be
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/sha1.h" #include "base/sha1.h"
#include "crypto/sha2.h" #include "crypto/sha2.h"
#include "net/cert/internal/cert_errors.h"
#include "net/cert/internal/parse_ocsp.h" #include "net/cert/internal/parse_ocsp.h"
#include "net/der/encode_values.h" #include "net/der/encode_values.h"
...@@ -500,10 +501,13 @@ bool GetOCSPCertStatus(const OCSPResponseData& response_data, ...@@ -500,10 +501,13 @@ bool GetOCSPCertStatus(const OCSPResponseData& response_data,
out->status = OCSPRevocationStatus::GOOD; out->status = OCSPRevocationStatus::GOOD;
ParsedTbsCertificate tbs_cert; ParsedTbsCertificate tbs_cert;
if (!ParseTbsCertificate(cert_tbs_certificate_tlv, {}, &tbs_cert)) // TODO(crbug.com/634443): Propagate the errors.
CertErrors errors;
if (!ParseTbsCertificate(cert_tbs_certificate_tlv, {}, &tbs_cert, &errors))
return false; return false;
ParsedTbsCertificate issuer_tbs_cert; ParsedTbsCertificate issuer_tbs_cert;
if (!ParseTbsCertificate(issuer_tbs_certificate_tlv, {}, &issuer_tbs_cert)) if (!ParseTbsCertificate(issuer_tbs_certificate_tlv, {}, &issuer_tbs_cert,
&errors))
return false; return false;
bool found = false; bool found = false;
......
...@@ -98,8 +98,8 @@ scoped_refptr<ParsedCertificate> ParsedCertificate::CreateInternal( ...@@ -98,8 +98,8 @@ scoped_refptr<ParsedCertificate> ParsedCertificate::CreateInternal(
return nullptr; return nullptr;
} }
if (!ParseTbsCertificate(result->tbs_certificate_tlv_, options, if (!ParseTbsCertificate(result->tbs_certificate_tlv_, options, &result->tbs_,
&result->tbs_)) { errors)) {
return nullptr; return nullptr;
} }
......
...@@ -133,7 +133,7 @@ bool ParseCertificateSandboxed(const base::StringPiece& certificate, ...@@ -133,7 +133,7 @@ bool ParseCertificateSandboxed(const base::StringPiece& certificate,
ParsedTbsCertificate parsed_tbs_cert; ParsedTbsCertificate parsed_tbs_cert;
if (!ParseTbsCertificate(tbs_cert, ParseCertificateOptions(), if (!ParseTbsCertificate(tbs_cert, ParseCertificateOptions(),
&parsed_tbs_cert)) &parsed_tbs_cert, nullptr))
return false; return false;
if (!GetCommonName(parsed_tbs_cert.subject_tlv, subject)) if (!GetCommonName(parsed_tbs_cert.subject_tlv, subject))
......
...@@ -26,3 +26,9 @@ $ openssl asn1parse -i < [TBS CERTIFICATE] ...@@ -26,3 +26,9 @@ $ openssl asn1parse -i < [TBS CERTIFICATE]
MEWgAwIBAgIBATADBAEBMAMEAQUwHhcNMTIxMDE4MDMxMjAwWhcNMTMxMDE4MTQ1OTU5WjADBAG MEWgAwIBAgIBATADBAEBMAMEAQUwHhcNMTIxMDE4MDMxMjAwWhcNMTMxMDE4MTQ1OTU5WjADBAG
DMAMEAfOjBTADBAHdBQA= DMAMEAfOjBTADBAHdBQA=
-----END TBS CERTIFICATE----- -----END TBS CERTIFICATE-----
[Error] Unconsumed data inside TBSCertificate
-----BEGIN ERRORS-----
W0Vycm9yXSBVbmNvbnN1bWVkIGRhdGEgaW5zaWRlIFRCU0NlcnRpZmljYXRlCg==
-----END ERRORS-----
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment