1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/tools/transport_security_state_generator/cert_util.h"
6
7 #include <string>
8 #include <string_view>
9
10 #include "base/base64.h"
11 #include "base/files/file_util.h"
12 #include "base/numerics/clamped_math.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "net/tools/transport_security_state_generator/spki_hash.h"
16 #include "third_party/boringssl/src/include/openssl/crypto.h"
17
18 using net::transport_security_state::SPKIHash;
19
20 namespace {
21
22 static const char kPEMBeginBlock[] = "-----BEGIN %s-----";
23 static const char kPEMEndBlock[] = "-----END %s-----";
24
25 // Tries to extract the BASE64 encoded DER structure from |pem_input| by looking
26 // for the block type in |expected_block_type|. Only attempts the locate the
27 // first matching block. Other blocks are ignored. Returns true on success and
28 // copies the der structure to |*der_output|. Returns false on error.
ParsePEM(std::string_view pem_input,std::string_view expected_block_type,std::string * der_output)29 bool ParsePEM(std::string_view pem_input,
30 std::string_view expected_block_type,
31 std::string* der_output) {
32 const std::string& block_start =
33 base::StringPrintf(kPEMBeginBlock, expected_block_type.data());
34 const std::string& block_end =
35 base::StringPrintf(kPEMEndBlock, expected_block_type.data());
36
37 size_t block_start_pos = pem_input.find(block_start);
38 if (block_start_pos == std::string::npos)
39 return false;
40 size_t base64_start_pos = block_start_pos + block_start.size();
41
42 size_t block_end_pos = pem_input.find(block_end, base64_start_pos);
43 if (block_end_pos == std::string::npos)
44 return false;
45
46 std::string_view base64_encoded =
47 pem_input.substr(base64_start_pos, block_end_pos - base64_start_pos);
48
49 if (!base::Base64Decode(base::CollapseWhitespaceASCII(base64_encoded, true),
50 der_output)) {
51 return false;
52 }
53
54 return true;
55 }
56
57 // Attempts to extract the first entry of type |nid| from |*name|. Returns true
58 // if the field exists and was extracted. Returns false when the field was not
59 // found or the data could not be extracted.
ExtractFieldFromX509Name(X509_NAME * name,int nid,std::string * field)60 bool ExtractFieldFromX509Name(X509_NAME* name, int nid, std::string* field) {
61 int index = X509_NAME_get_index_by_NID(name, nid, -1);
62 if (index == -1) {
63 return false;
64 }
65
66 X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, index);
67 if (!entry) {
68 return false;
69 }
70
71 ASN1_STRING* data = X509_NAME_ENTRY_get_data(entry);
72 if (!data) {
73 return false;
74 }
75
76 uint8_t* buffer = nullptr;
77 size_t length = ASN1_STRING_to_UTF8(&buffer, data);
78 field->assign(reinterpret_cast<const char*>(buffer), length);
79 OPENSSL_free(buffer);
80 return true;
81 }
82
83 } // namespace
84
GetX509CertificateFromPEM(std::string_view pem_data)85 bssl::UniquePtr<X509> GetX509CertificateFromPEM(std::string_view pem_data) {
86 std::string der;
87 if (!ParsePEM(pem_data, "CERTIFICATE", &der)) {
88 return bssl::UniquePtr<X509>();
89 }
90
91 const uint8_t* der_data = reinterpret_cast<const uint8_t*>(der.c_str());
92 return bssl::UniquePtr<X509>(
93 d2i_X509(nullptr, &der_data, base::checked_cast<long>(der.size())));
94 }
95
ExtractSubjectNameFromCertificate(X509 * certificate,std::string * name)96 bool ExtractSubjectNameFromCertificate(X509* certificate, std::string* name) {
97 DCHECK(certificate);
98 X509_NAME* subject = X509_get_subject_name(certificate);
99 if (!subject) {
100 return false;
101 }
102
103 std::string result;
104 // Try extracting the common name first.
105 if (!ExtractFieldFromX509Name(subject, NID_commonName, &result) ||
106 result.empty()) {
107 std::string organization;
108 if (!ExtractFieldFromX509Name(subject, NID_organizationName,
109 &organization)) {
110 return false;
111 }
112
113 std::string organizational_unit;
114 if (!ExtractFieldFromX509Name(subject, NID_organizationalUnitName,
115 &organizational_unit)) {
116 return false;
117 }
118 result = organization + " " + organizational_unit;
119 }
120
121 name->assign(result);
122 return true;
123 }
124
CalculateSPKIHashFromCertificate(X509 * certificate,SPKIHash * out_hash)125 bool CalculateSPKIHashFromCertificate(X509* certificate, SPKIHash* out_hash) {
126 DCHECK(certificate);
127 bssl::UniquePtr<EVP_PKEY> key(X509_get_pubkey(certificate));
128 if (!key) {
129 return false;
130 }
131
132 uint8_t* spki_der;
133 size_t spki_der_len;
134 bssl::ScopedCBB cbb;
135 if (!CBB_init(cbb.get(), 0) ||
136 !EVP_marshal_public_key(cbb.get(), key.get()) ||
137 !CBB_finish(cbb.get(), &spki_der, &spki_der_len)) {
138 return false;
139 }
140
141 out_hash->CalculateFromBytes(spki_der, spki_der_len);
142 OPENSSL_free(spki_der);
143 return true;
144 }
145
CalculateSPKIHashFromKey(std::string_view pem_key,SPKIHash * out_hash)146 bool CalculateSPKIHashFromKey(std::string_view pem_key, SPKIHash* out_hash) {
147 std::string der;
148 bool result = ParsePEM(pem_key, "PUBLIC KEY", &der);
149 if (!result) {
150 return false;
151 }
152
153 out_hash->CalculateFromBytes(reinterpret_cast<const uint8_t*>(der.data()),
154 der.size());
155 return true;
156 }
157