1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/common/net/x509_certificate_model.h"
10 #include <keyhi.h> // SECKEY_DestroyPrivateKey
11 #include <keythi.h> // SECKEYPrivateKey
12 #include <pk11pub.h> // PK11_FindKeyByAnyCert
13 #include <seccomon.h> // SECItem
16 #include "base/logging.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h"
19 #include "chrome/third_party/mozilla_security_manager/nsNSSCertificate.h"
20 #include "chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.h"
21 #include "crypto/nss_util.h"
22 #include "crypto/scoped_nss_types.h"
23 #include "net/cert/x509_certificate.h"
25 namespace psm
= mozilla_security_manager
;
29 // Convert a char* return value from NSS into a std::string and free the NSS
30 // memory. If the arg is NULL, an empty string will be returned instead.
31 std::string
Stringize(char* nss_text
, const std::string
& alternative_text
) {
33 return alternative_text
;
35 std::string s
= nss_text
;
40 // Hash a certificate using the given algorithm, return the result as a
41 // colon-seperated hex string. The len specified is the number of bytes
42 // required for storing the raw fingerprint.
43 // (It's a bit redundant that the caller needs to specify len in addition to the
44 // algorithm, but given the limited uses, not worth fixing.)
45 std::string
HashCert(CERTCertificate
* cert
, HASH_HashType algorithm
, int len
) {
46 unsigned char fingerprint
[HASH_LENGTH_MAX
];
48 DCHECK(NULL
!= cert
->derCert
.data
);
49 DCHECK_NE(0U, cert
->derCert
.len
);
50 DCHECK_LE(len
, HASH_LENGTH_MAX
);
51 memset(fingerprint
, 0, len
);
52 SECStatus rv
= HASH_HashBuf(algorithm
, fingerprint
, cert
->derCert
.data
,
54 DCHECK_EQ(rv
, SECSuccess
);
55 return x509_certificate_model::ProcessRawBytes(fingerprint
, len
);
58 std::string
ProcessSecAlgorithmInternal(SECAlgorithmID
* algorithm_id
) {
59 return psm::GetOIDText(&algorithm_id
->algorithm
);
62 std::string
ProcessExtension(
63 const std::string
& critical_label
,
64 const std::string
& non_critical_label
,
65 CERTCertExtension
* extension
) {
66 std::string criticality
=
67 extension
->critical
.data
&& extension
->critical
.data
[0] ?
68 critical_label
: non_critical_label
;
69 return criticality
+ "\n" +
70 psm::ProcessExtensionData(SECOID_FindOIDTag(&extension
->id
),
74 ////////////////////////////////////////////////////////////////////////////////
75 // NSS certificate export functions.
77 class FreeNSSCMSMessage
{
79 inline void operator()(NSSCMSMessage
* x
) const {
80 NSS_CMSMessage_Destroy(x
);
83 typedef scoped_ptr_malloc
<NSSCMSMessage
, FreeNSSCMSMessage
>
86 class FreeNSSCMSSignedData
{
88 inline void operator()(NSSCMSSignedData
* x
) const {
89 NSS_CMSSignedData_Destroy(x
);
92 typedef scoped_ptr_malloc
<NSSCMSSignedData
, FreeNSSCMSSignedData
>
93 ScopedNSSCMSSignedData
;
97 namespace x509_certificate_model
{
99 using net::X509Certificate
;
102 string
GetCertNameOrNickname(X509Certificate::OSCertHandle cert_handle
) {
103 string name
= ProcessIDN(
104 Stringize(CERT_GetCommonName(&cert_handle
->subject
), std::string()));
107 return GetNickname(cert_handle
);
110 string
GetNickname(X509Certificate::OSCertHandle cert_handle
) {
112 if (cert_handle
->nickname
) {
113 name
= cert_handle
->nickname
;
114 // Hack copied from mozilla: Cut off text before first :, which seems to
115 // just be the token name.
116 size_t colon_pos
= name
.find(':');
117 if (colon_pos
!= string::npos
)
118 name
= name
.substr(colon_pos
+ 1);
123 string
GetTokenName(X509Certificate::OSCertHandle cert_handle
) {
124 return psm::GetCertTokenName(cert_handle
);
127 string
GetVersion(X509Certificate::OSCertHandle cert_handle
) {
128 // If the version field is omitted from the certificate, the default
130 unsigned long version
= 0;
131 if (cert_handle
->version
.len
== 0 ||
132 SEC_ASN1DecodeInteger(&cert_handle
->version
, &version
) == SECSuccess
) {
133 return base::UintToString(version
+ 1);
135 return std::string();
138 net::CertType
GetType(X509Certificate::OSCertHandle cert_handle
) {
139 return psm::GetCertType(cert_handle
);
142 string
GetEmailAddress(X509Certificate::OSCertHandle cert_handle
) {
143 if (cert_handle
->emailAddr
)
144 return cert_handle
->emailAddr
;
145 return std::string();
148 void GetUsageStrings(X509Certificate::OSCertHandle cert_handle
,
149 std::vector
<string
>* usages
) {
150 psm::GetCertUsageStrings(cert_handle
, usages
);
153 string
GetKeyUsageString(X509Certificate::OSCertHandle cert_handle
) {
155 key_usage
.data
= NULL
;
156 string key_usage_str
;
157 if (CERT_FindKeyUsageExtension(cert_handle
, &key_usage
) == SECSuccess
) {
158 key_usage_str
= psm::ProcessKeyUsageBitString(&key_usage
, ',');
159 PORT_Free(key_usage
.data
);
161 return key_usage_str
;
164 string
GetSerialNumberHexified(X509Certificate::OSCertHandle cert_handle
,
165 const string
& alternative_text
) {
166 return Stringize(CERT_Hexify(&cert_handle
->serialNumber
, true),
170 string
GetIssuerCommonName(X509Certificate::OSCertHandle cert_handle
,
171 const string
& alternative_text
) {
172 return Stringize(CERT_GetCommonName(&cert_handle
->issuer
), alternative_text
);
175 string
GetIssuerOrgName(X509Certificate::OSCertHandle cert_handle
,
176 const string
& alternative_text
) {
177 return Stringize(CERT_GetOrgName(&cert_handle
->issuer
), alternative_text
);
180 string
GetIssuerOrgUnitName(X509Certificate::OSCertHandle cert_handle
,
181 const string
& alternative_text
) {
182 return Stringize(CERT_GetOrgUnitName(&cert_handle
->issuer
), alternative_text
);
185 string
GetSubjectOrgName(X509Certificate::OSCertHandle cert_handle
,
186 const string
& alternative_text
) {
187 return Stringize(CERT_GetOrgName(&cert_handle
->subject
), alternative_text
);
190 string
GetSubjectOrgUnitName(X509Certificate::OSCertHandle cert_handle
,
191 const string
& alternative_text
) {
192 return Stringize(CERT_GetOrgUnitName(&cert_handle
->subject
),
196 string
GetSubjectCommonName(X509Certificate::OSCertHandle cert_handle
,
197 const string
& alternative_text
) {
198 return Stringize(CERT_GetCommonName(&cert_handle
->subject
), alternative_text
);
201 bool GetTimes(X509Certificate::OSCertHandle cert_handle
,
202 base::Time
* issued
, base::Time
* expires
) {
203 PRTime pr_issued
, pr_expires
;
204 if (CERT_GetCertTimes(cert_handle
, &pr_issued
, &pr_expires
) == SECSuccess
) {
205 *issued
= crypto::PRTimeToBaseTime(pr_issued
);
206 *expires
= crypto::PRTimeToBaseTime(pr_expires
);
212 string
GetTitle(X509Certificate::OSCertHandle cert_handle
) {
213 return psm::GetCertTitle(cert_handle
);
216 string
GetIssuerName(X509Certificate::OSCertHandle cert_handle
) {
217 return psm::ProcessName(&cert_handle
->issuer
);
220 string
GetSubjectName(X509Certificate::OSCertHandle cert_handle
) {
221 return psm::ProcessName(&cert_handle
->subject
);
224 void GetEmailAddresses(X509Certificate::OSCertHandle cert_handle
,
225 std::vector
<string
>* email_addresses
) {
226 for (const char* addr
= CERT_GetFirstEmailAddress(cert_handle
);
227 addr
; addr
= CERT_GetNextEmailAddress(cert_handle
, addr
)) {
228 // The first email addr (from Subject) may be duplicated in Subject
229 // Alternative Name, so check subsequent addresses are not equal to the
230 // first one before adding to the list.
231 if (!email_addresses
->size() || (*email_addresses
)[0] != addr
)
232 email_addresses
->push_back(addr
);
236 void GetNicknameStringsFromCertList(
237 const std::vector
<scoped_refptr
<X509Certificate
> >& certs
,
238 const string
& cert_expired
,
239 const string
& cert_not_yet_valid
,
240 std::vector
<string
>* nick_names
) {
241 CERTCertList
* cert_list
= CERT_NewCertList();
242 for (size_t i
= 0; i
< certs
.size(); ++i
) {
243 CERT_AddCertToListTail(
245 CERT_DupCertificate(certs
[i
]->os_cert_handle()));
247 // Would like to use CERT_GetCertNicknameWithValidity on each cert
248 // individually instead of having to build a CERTCertList for this, but that
249 // function is not exported.
250 CERTCertNicknames
* cert_nicknames
= CERT_NicknameStringsFromCertList(
252 const_cast<char*>(cert_expired
.c_str()),
253 const_cast<char*>(cert_not_yet_valid
.c_str()));
254 DCHECK_EQ(cert_nicknames
->numnicknames
,
255 static_cast<int>(certs
.size()));
257 for (int i
= 0; i
< cert_nicknames
->numnicknames
; ++i
)
258 nick_names
->push_back(cert_nicknames
->nicknames
[i
]);
260 CERT_FreeNicknames(cert_nicknames
);
261 CERT_DestroyCertList(cert_list
);
264 // For background see this discussion on dev-tech-crypto.lists.mozilla.org:
265 // http://web.archiveorange.com/archive/v/6JJW7E40sypfZGtbkzxX
267 // NOTE: This function relies on the convention that the same PKCS#11 ID
268 // is shared between a certificate and its associated private and public
269 // keys. I tried to implement this with PK11_GetLowLevelKeyIDForCert(),
270 // but that always returns NULL on Chrome OS for me.
271 std::string
GetPkcs11Id(net::X509Certificate::OSCertHandle cert_handle
) {
272 std::string pkcs11_id
;
273 SECKEYPrivateKey
*priv_key
= PK11_FindKeyByAnyCert(cert_handle
,
276 // Get the CKA_ID attribute for a key.
277 SECItem
* sec_item
= PK11_GetLowLevelKeyIDForPrivateKey(priv_key
);
279 pkcs11_id
= base::HexEncode(sec_item
->data
, sec_item
->len
);
280 SECITEM_FreeItem(sec_item
, PR_TRUE
);
282 SECKEY_DestroyPrivateKey(priv_key
);
288 const string
& critical_label
,
289 const string
& non_critical_label
,
290 X509Certificate::OSCertHandle cert_handle
,
291 Extensions
* extensions
) {
292 if (cert_handle
->extensions
) {
293 for (size_t i
= 0; cert_handle
->extensions
[i
] != NULL
; ++i
) {
295 extension
.name
= psm::GetOIDText(&cert_handle
->extensions
[i
]->id
);
296 extension
.value
= ProcessExtension(
297 critical_label
, non_critical_label
, cert_handle
->extensions
[i
]);
298 extensions
->push_back(extension
);
303 string
HashCertSHA256(X509Certificate::OSCertHandle cert_handle
) {
304 return HashCert(cert_handle
, HASH_AlgSHA256
, SHA256_LENGTH
);
307 string
HashCertSHA1(X509Certificate::OSCertHandle cert_handle
) {
308 return HashCert(cert_handle
, HASH_AlgSHA1
, SHA1_LENGTH
);
311 void GetCertChainFromCert(X509Certificate::OSCertHandle cert_handle
,
312 X509Certificate::OSCertHandles
* cert_handles
) {
313 CERTCertList
* cert_list
=
314 CERT_GetCertChainFromCert(cert_handle
, PR_Now(), certUsageSSLServer
);
315 CERTCertListNode
* node
;
316 for (node
= CERT_LIST_HEAD(cert_list
);
317 !CERT_LIST_END(node
, cert_list
);
318 node
= CERT_LIST_NEXT(node
)) {
319 cert_handles
->push_back(CERT_DupCertificate(node
->cert
));
321 CERT_DestroyCertList(cert_list
);
324 void DestroyCertChain(X509Certificate::OSCertHandles
* cert_handles
) {
325 for (X509Certificate::OSCertHandles::iterator
i(cert_handles
->begin());
326 i
!= cert_handles
->end(); ++i
)
327 CERT_DestroyCertificate(*i
);
328 cert_handles
->clear();
331 string
GetDerString(X509Certificate::OSCertHandle cert_handle
) {
332 return string(reinterpret_cast<const char*>(cert_handle
->derCert
.data
),
333 cert_handle
->derCert
.len
);
336 string
GetCMSString(const X509Certificate::OSCertHandles
& cert_chain
,
337 size_t start
, size_t end
) {
338 crypto::ScopedPLArenaPool
arena(PORT_NewArena(1024));
341 ScopedNSSCMSMessage
message(NSS_CMSMessage_Create(arena
.get()));
342 DCHECK(message
.get());
344 // First, create SignedData with the certificate only (no chain).
345 ScopedNSSCMSSignedData
signed_data(NSS_CMSSignedData_CreateCertsOnly(
346 message
.get(), cert_chain
[start
], PR_FALSE
));
347 if (!signed_data
.get()) {
348 DLOG(ERROR
) << "NSS_CMSSignedData_Create failed";
349 return std::string();
351 // Add the rest of the chain (if any).
352 for (size_t i
= start
+ 1; i
< end
; ++i
) {
353 if (NSS_CMSSignedData_AddCertificate(signed_data
.get(), cert_chain
[i
]) !=
355 DLOG(ERROR
) << "NSS_CMSSignedData_AddCertificate failed on " << i
;
356 return std::string();
360 NSSCMSContentInfo
*cinfo
= NSS_CMSMessage_GetContentInfo(message
.get());
361 if (NSS_CMSContentInfo_SetContent_SignedData(
362 message
.get(), cinfo
, signed_data
.get()) == SECSuccess
) {
363 ignore_result(signed_data
.release());
365 DLOG(ERROR
) << "NSS_CMSMessage_GetContentInfo failed";
366 return std::string();
369 SECItem cert_p7
= { siBuffer
, NULL
, 0 };
370 NSSCMSEncoderContext
*ecx
= NSS_CMSEncoder_Start(message
.get(), NULL
, NULL
,
371 &cert_p7
, arena
.get(), NULL
,
372 NULL
, NULL
, NULL
, NULL
,
375 DLOG(ERROR
) << "NSS_CMSEncoder_Start failed";
376 return std::string();
379 if (NSS_CMSEncoder_Finish(ecx
) != SECSuccess
) {
380 DLOG(ERROR
) << "NSS_CMSEncoder_Finish failed";
381 return std::string();
384 return string(reinterpret_cast<const char*>(cert_p7
.data
), cert_p7
.len
);
387 string
ProcessSecAlgorithmSignature(X509Certificate::OSCertHandle cert_handle
) {
388 return ProcessSecAlgorithmInternal(&cert_handle
->signature
);
391 string
ProcessSecAlgorithmSubjectPublicKey(
392 X509Certificate::OSCertHandle cert_handle
) {
393 return ProcessSecAlgorithmInternal(
394 &cert_handle
->subjectPublicKeyInfo
.algorithm
);
397 string
ProcessSecAlgorithmSignatureWrap(
398 X509Certificate::OSCertHandle cert_handle
) {
399 return ProcessSecAlgorithmInternal(
400 &cert_handle
->signatureWrap
.signatureAlgorithm
);
403 string
ProcessSubjectPublicKeyInfo(X509Certificate::OSCertHandle cert_handle
) {
404 return psm::ProcessSubjectPublicKeyInfo(&cert_handle
->subjectPublicKeyInfo
);
407 string
ProcessRawBitsSignatureWrap(X509Certificate::OSCertHandle cert_handle
) {
408 return ProcessRawBits(cert_handle
->signatureWrap
.signature
.data
,
409 cert_handle
->signatureWrap
.signature
.len
);
412 void RegisterDynamicOids() {
413 psm::RegisterDynamicOids();
416 } // namespace x509_certificate_model