xref: /aosp_15_r20/external/cronet/net/tools/transport_security_state_generator/cert_util.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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