1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsNSSCertificate.h"
8 #include "CertVerifier.h"
9 #include "ExtendedValidation.h"
10 #include "NSSCertDBTrustDomain.h"
11 #include "X509CertValidity.h"
13 #include "ipc/IPCMessageUtils.h"
14 #include "mozilla/Assertions.h"
15 #include "mozilla/Base64.h"
16 #include "mozilla/Casting.h"
17 #include "mozilla/NotNull.h"
18 #include "mozilla/Span.h"
19 #include "mozilla/TextUtils.h"
20 #include "mozilla/Unused.h"
21 #include "mozilla/ipc/TransportSecurityInfoUtils.h"
22 #include "mozilla/ipc/IPDLParamTraits.h"
23 #include "mozilla/net/DNS.h"
24 #include "mozpkix/Result.h"
25 #include "mozpkix/pkixnss.h"
26 #include "mozpkix/pkixtypes.h"
27 #include "mozpkix/pkixutil.h"
30 #include "nsIClassInfoImpl.h"
31 #include "nsIObjectInputStream.h"
32 #include "nsIObjectOutputStream.h"
33 #include "nsIX509Cert.h"
34 #include "nsNSSCertHelper.h"
35 #include "nsNSSCertTrust.h"
36 #include "nsPK11TokenDB.h"
37 #include "nsPKCS12Blob.h"
38 #include "nsProxyRelease.h"
39 #include "nsReadableUtils.h"
41 #include "nsThreadUtils.h"
42 #include "nsUnicharUtils.h"
51 # include <winsock.h> // for htonl
54 using namespace mozilla
;
55 using namespace mozilla::psm
;
57 extern LazyLogModule gPIPNSSLog
;
59 NS_IMPL_ISUPPORTS(nsNSSCertificate
, nsIX509Cert
, nsISerializable
, nsIClassInfo
)
61 nsNSSCertificate::nsNSSCertificate() : mCert("nsNSSCertificate::mCert") {}
63 nsNSSCertificate::nsNSSCertificate(CERTCertificate
* cert
)
64 : mCert("nsNSSCertificate::mCert") {
66 mDER
.AppendElements(cert
->derCert
.data
, cert
->derCert
.len
);
67 auto lock
= mCert
.Lock();
68 auto& maybeCert
= lock
.ref();
69 maybeCert
.emplace(UniqueCERTCertificate(CERT_DupCertificate(cert
)));
73 nsNSSCertificate::nsNSSCertificate(nsTArray
<uint8_t>&& der
)
74 : mDER(std::move(der
)), mCert("nsNSSCertificate::mCert") {}
76 UniqueCERTCertificate
nsNSSCertificate::GetOrInstantiateCert() {
77 auto lock
= mCert
.Lock();
78 auto& maybeCert
= lock
.ref();
79 if (maybeCert
.isSome()) {
80 return UniqueCERTCertificate(CERT_DupCertificate((*maybeCert
).get()));
83 if (!EnsureNSSInitializedChromeOrContent()) {
87 SECItem derItem
= {siBuffer
, mDER
.Elements(),
88 static_cast<unsigned int>(mDER
.Length())};
89 UniqueCERTCertificate
cert(CERT_NewTempCertificate(
90 CERT_GetDefaultCertDB(), &derItem
, nullptr, false, true));
94 maybeCert
.emplace(std::move(cert
));
96 return UniqueCERTCertificate(CERT_DupCertificate((*maybeCert
).get()));
99 nsresult
nsNSSCertificate::GetCertType(uint32_t* aCertType
) {
100 UniqueCERTCertificate
cert(GetOrInstantiateCert());
102 return NS_ERROR_FAILURE
;
104 CERTCertTrust certTrust
{0, 0, 0};
105 // If there is no stored trust information, CERT_GetCertTrust will return
106 // SECFailure. This isn't a failure. In this case, all trust bits will remain
108 Unused
<< CERT_GetCertTrust(cert
.get(), &certTrust
);
109 nsNSSCertTrust
trust(&certTrust
);
110 if (cert
->nickname
&& trust
.HasAnyUser()) {
111 *aCertType
= nsIX509Cert::USER_CERT
;
114 if (trust
.HasAnyCA()) {
115 *aCertType
= nsIX509Cert::CA_CERT
;
118 if (trust
.HasPeer(true, false)) {
119 *aCertType
= nsIX509Cert::SERVER_CERT
;
122 if (trust
.HasPeer(false, true) && cert
->emailAddr
) {
123 *aCertType
= nsIX509Cert::EMAIL_CERT
;
126 if (CERT_IsCACert(cert
.get(), nullptr)) {
127 *aCertType
= nsIX509Cert::CA_CERT
;
130 if (cert
->emailAddr
) {
131 *aCertType
= nsIX509Cert::EMAIL_CERT
;
134 *aCertType
= nsIX509Cert::UNKNOWN_CERT
;
139 nsNSSCertificate::GetDbKey(nsACString
& aDbKey
) {
140 static_assert(sizeof(uint64_t) == 8, "type size consistency check");
141 static_assert(sizeof(uint32_t) == 4, "type size consistency check");
143 pkix::Input certInput
;
144 pkix::Result result
= certInput
.Init(mDER
.Elements(), mDER
.Length());
145 if (result
!= pkix::Result::Success
) {
146 return NS_ERROR_INVALID_ARG
;
148 // NB: since we're not building a trust path, the endEntityOrCA parameter is
150 pkix::BackCert
cert(certInput
, pkix::EndEntityOrCA::MustBeEndEntity
, nullptr);
151 result
= cert
.Init();
152 if (result
!= pkix::Result::Success
) {
153 return NS_ERROR_INVALID_ARG
;
156 // The format of the key is the base64 encoding of the following:
157 // 4 bytes: {0, 0, 0, 0} (this was intended to be the module ID, but it was
158 // never implemented)
159 // 4 bytes: {0, 0, 0, 0} (this was intended to be the slot ID, but it was
160 // never implemented)
161 // 4 bytes: <serial number length in big-endian order>
162 // 4 bytes: <DER-encoded issuer distinguished name length in big-endian order>
163 // n bytes: <bytes of serial number>
164 // m bytes: <DER-encoded issuer distinguished name>
166 const char leadingZeroes
[] = {0, 0, 0, 0, 0, 0, 0, 0};
167 buf
.Append(leadingZeroes
, sizeof(leadingZeroes
));
168 uint32_t serialNumberLen
= htonl(cert
.GetSerialNumber().GetLength());
169 buf
.Append(BitwiseCast
<const char*, const uint32_t*>(&serialNumberLen
),
171 uint32_t issuerLen
= htonl(cert
.GetIssuer().GetLength());
172 buf
.Append(BitwiseCast
<const char*, const uint32_t*>(&issuerLen
),
174 buf
.Append(BitwiseCast
<const char*, const unsigned char*>(
175 cert
.GetSerialNumber().UnsafeGetData()),
176 cert
.GetSerialNumber().GetLength());
177 buf
.Append(BitwiseCast
<const char*, const unsigned char*>(
178 cert
.GetIssuer().UnsafeGetData()),
179 cert
.GetIssuer().GetLength());
181 return Base64Encode(buf
, aDbKey
);
185 nsNSSCertificate::GetDisplayName(nsAString
& aDisplayName
) {
186 aDisplayName
.Truncate();
188 UniqueCERTCertificate
cert(GetOrInstantiateCert());
190 return NS_ERROR_FAILURE
;
193 UniquePORTString
commonName(CERT_GetCommonName(&cert
->subject
));
194 UniquePORTString
organizationalUnitName(CERT_GetOrgUnitName(&cert
->subject
));
195 UniquePORTString
organizationName(CERT_GetOrgName(&cert
->subject
));
197 // Only use the nickname for built-in roots where we already have a hard-coded
198 // reasonable display name (unfortunately we have to strip off the leading
199 // slot identifier followed by a ':'). Otherwise, attempt to use the following
201 // - the common name, if present
202 // - an organizational unit name, if present
203 // - an organization name, if present
204 // - the entire subject distinguished name, if non-empty
205 // - an email address, if one can be found
206 // In the unlikely event that none of these fields are present and non-empty
207 // (the subject really shouldn't be empty), an empty string is returned.
208 nsAutoCString builtInRootNickname
;
209 nsAutoCString
fullNickname(cert
->nickname
);
210 static const nsLiteralCString kBuiltinObjectTokenPrefix
=
211 "Builtin Object Token:"_ns
;
212 if (StringBeginsWith(fullNickname
, kBuiltinObjectTokenPrefix
)) {
213 // Substring will gracefully handle the case where index is the last
214 // character in the string (that is, if the nickname is just
215 // "Builtin Object Token:"). In that case, we'll get an empty string.
216 builtInRootNickname
=
217 Substring(fullNickname
, kBuiltinObjectTokenPrefix
.Length());
219 const char* nameOptions
[] = {builtInRootNickname
.get(),
221 organizationalUnitName
.get(),
222 organizationName
.get(),
226 for (auto nameOption
: nameOptions
) {
228 size_t len
= strlen(nameOption
);
230 LossyUTF8ToUTF16(nameOption
, len
, aDisplayName
);
240 nsNSSCertificate::GetEmailAddress(nsAString
& aEmailAddress
) {
241 UniqueCERTCertificate
cert(GetOrInstantiateCert());
243 return NS_ERROR_FAILURE
;
245 if (cert
->emailAddr
) {
246 CopyUTF8toUTF16(MakeStringSpan(cert
->emailAddr
), aEmailAddress
);
248 GetPIPNSSBundleString("CertNoEmailAddress", aEmailAddress
);
254 nsNSSCertificate::GetEmailAddresses(nsTArray
<nsString
>& aAddresses
) {
255 UniqueCERTCertificate
cert(GetOrInstantiateCert());
257 return NS_ERROR_FAILURE
;
260 for (const char* aAddr
= CERT_GetFirstEmailAddress(cert
.get()); aAddr
;
261 aAddr
= CERT_GetNextEmailAddress(cert
.get(), aAddr
)) {
265 aAddresses
.SetCapacity(length
);
267 for (const char* aAddr
= CERT_GetFirstEmailAddress(cert
.get()); aAddr
;
268 aAddr
= CERT_GetNextEmailAddress(cert
.get(), aAddr
)) {
269 CopyASCIItoUTF16(MakeStringSpan(aAddr
), *aAddresses
.AppendElement());
276 nsNSSCertificate::ContainsEmailAddress(const nsAString
& aEmailAddress
,
278 NS_ENSURE_ARG(result
);
281 UniqueCERTCertificate
cert(GetOrInstantiateCert());
283 return NS_ERROR_FAILURE
;
285 for (const char* aAddr
= CERT_GetFirstEmailAddress(cert
.get()); aAddr
;
286 aAddr
= CERT_GetNextEmailAddress(cert
.get(), aAddr
)) {
287 nsAutoString certAddr
;
288 LossyUTF8ToUTF16(aAddr
, strlen(aAddr
), certAddr
);
289 ToLowerCase(certAddr
);
291 nsAutoString
testAddr(aEmailAddress
);
292 ToLowerCase(testAddr
);
294 if (certAddr
== testAddr
) {
304 nsNSSCertificate::GetCommonName(nsAString
& aCommonName
) {
305 aCommonName
.Truncate();
306 UniqueCERTCertificate
cert(GetOrInstantiateCert());
308 return NS_ERROR_FAILURE
;
310 UniquePORTString
commonName(CERT_GetCommonName(&cert
->subject
));
312 LossyUTF8ToUTF16(commonName
.get(), strlen(commonName
.get()), aCommonName
);
318 nsNSSCertificate::GetOrganization(nsAString
& aOrganization
) {
319 aOrganization
.Truncate();
320 UniqueCERTCertificate
cert(GetOrInstantiateCert());
322 return NS_ERROR_FAILURE
;
324 UniquePORTString
organization(CERT_GetOrgName(&cert
->subject
));
326 LossyUTF8ToUTF16(organization
.get(), strlen(organization
.get()),
333 nsNSSCertificate::GetIssuerCommonName(nsAString
& aCommonName
) {
334 aCommonName
.Truncate();
335 UniqueCERTCertificate
cert(GetOrInstantiateCert());
337 return NS_ERROR_FAILURE
;
339 UniquePORTString
commonName(CERT_GetCommonName(&cert
->issuer
));
341 LossyUTF8ToUTF16(commonName
.get(), strlen(commonName
.get()), aCommonName
);
347 nsNSSCertificate::GetIssuerOrganization(nsAString
& aOrganization
) {
348 aOrganization
.Truncate();
349 UniqueCERTCertificate
cert(GetOrInstantiateCert());
351 return NS_ERROR_FAILURE
;
353 UniquePORTString
organization(CERT_GetOrgName(&cert
->issuer
));
355 LossyUTF8ToUTF16(organization
.get(), strlen(organization
.get()),
362 nsNSSCertificate::GetIssuerOrganizationUnit(nsAString
& aOrganizationUnit
) {
363 aOrganizationUnit
.Truncate();
364 UniqueCERTCertificate
cert(GetOrInstantiateCert());
366 return NS_ERROR_FAILURE
;
368 UniquePORTString
organizationUnit(CERT_GetOrgUnitName(&cert
->issuer
));
369 if (organizationUnit
) {
370 LossyUTF8ToUTF16(organizationUnit
.get(), strlen(organizationUnit
.get()),
377 nsNSSCertificate::GetOrganizationalUnit(nsAString
& aOrganizationalUnit
) {
378 aOrganizationalUnit
.Truncate();
379 UniqueCERTCertificate
cert(GetOrInstantiateCert());
381 return NS_ERROR_FAILURE
;
383 UniquePORTString
orgunit(CERT_GetOrgUnitName(&cert
->subject
));
385 LossyUTF8ToUTF16(orgunit
.get(), strlen(orgunit
.get()), aOrganizationalUnit
);
391 nsNSSCertificate::GetSubjectName(nsAString
& _subjectName
) {
392 _subjectName
.Truncate();
393 UniqueCERTCertificate
cert(GetOrInstantiateCert());
395 return NS_ERROR_FAILURE
;
397 if (cert
->subjectName
) {
398 LossyUTF8ToUTF16(cert
->subjectName
, strlen(cert
->subjectName
),
405 nsNSSCertificate::GetIssuerName(nsAString
& _issuerName
) {
406 _issuerName
.Truncate();
407 UniqueCERTCertificate
cert(GetOrInstantiateCert());
409 return NS_ERROR_FAILURE
;
411 if (cert
->issuerName
) {
412 LossyUTF8ToUTF16(cert
->issuerName
, strlen(cert
->issuerName
), _issuerName
);
418 nsNSSCertificate::GetSerialNumber(nsAString
& _serialNumber
) {
419 _serialNumber
.Truncate();
420 UniqueCERTCertificate
cert(GetOrInstantiateCert());
422 return NS_ERROR_FAILURE
;
424 UniquePORTString
tmpstr(
425 CERT_Hexify(&cert
->serialNumber
, true /* use colon delimiters */));
427 _serialNumber
= NS_ConvertASCIItoUTF16(tmpstr
.get());
430 return NS_ERROR_FAILURE
;
433 nsresult
nsNSSCertificate::GetCertificateHash(nsAString
& aFingerprint
,
434 SECOidTag aHashAlg
) {
435 aFingerprint
.Truncate();
437 if (!EnsureNSSInitializedChromeOrContent()) {
438 return NS_ERROR_NOT_AVAILABLE
;
441 nsTArray
<uint8_t> digestArray
;
443 Digest::DigestBuf(aHashAlg
, mDER
.Elements(), mDER
.Length(), digestArray
);
447 SECItem digestItem
= {siBuffer
, digestArray
.Elements(),
448 static_cast<unsigned int>(digestArray
.Length())};
450 UniquePORTString
fpStr(
451 CERT_Hexify(&digestItem
, true /* use colon delimiters */));
453 return NS_ERROR_FAILURE
;
456 aFingerprint
.AssignASCII(fpStr
.get());
461 nsNSSCertificate::GetSha256Fingerprint(nsAString
& aSha256Fingerprint
) {
462 return GetCertificateHash(aSha256Fingerprint
, SEC_OID_SHA256
);
466 nsNSSCertificate::GetSha1Fingerprint(nsAString
& _sha1Fingerprint
) {
467 return GetCertificateHash(_sha1Fingerprint
, SEC_OID_SHA1
);
471 nsNSSCertificate::GetTokenName(nsAString
& aTokenName
) {
472 UniqueCERTCertificate
cert(GetOrInstantiateCert());
474 return NS_ERROR_FAILURE
;
476 UniquePK11SlotInfo
internalSlot(PK11_GetInternalSlot());
478 return NS_ERROR_FAILURE
;
480 nsCOMPtr
<nsIPK11Token
> token(
481 new nsPK11Token(cert
->slot
? cert
->slot
: internalSlot
.get()));
483 nsresult rv
= token
->GetTokenName(tmp
);
487 aTokenName
.Assign(NS_ConvertUTF8toUTF16(tmp
));
492 nsNSSCertificate::GetSha256SubjectPublicKeyInfoDigest(
493 nsACString
& aSha256SPKIDigest
) {
494 aSha256SPKIDigest
.Truncate();
496 if (!EnsureNSSInitializedChromeOrContent()) {
497 return NS_ERROR_NOT_AVAILABLE
;
500 pkix::Input certInput
;
501 pkix::Result result
= certInput
.Init(mDER
.Elements(), mDER
.Length());
502 if (result
!= pkix::Result::Success
) {
503 return NS_ERROR_INVALID_ARG
;
505 // NB: since we're not building a trust path, the endEntityOrCA parameter is
507 pkix::BackCert
cert(certInput
, pkix::EndEntityOrCA::MustBeEndEntity
, nullptr);
508 result
= cert
.Init();
509 if (result
!= pkix::Result::Success
) {
510 return NS_ERROR_INVALID_ARG
;
512 pkix::Input derPublicKey
= cert
.GetSubjectPublicKeyInfo();
513 nsTArray
<uint8_t> digestArray
;
514 nsresult rv
= Digest::DigestBuf(SEC_OID_SHA256
, derPublicKey
.UnsafeGetData(),
515 derPublicKey
.GetLength(), digestArray
);
519 rv
= Base64Encode(nsDependentCSubstring(
520 reinterpret_cast<const char*>(digestArray
.Elements()),
521 digestArray
.Length()),
523 if (NS_WARN_IF(NS_FAILED(rv
))) {
530 nsNSSCertificate::GetRawDER(nsTArray
<uint8_t>& aArray
) {
531 aArray
.SetLength(mDER
.Length());
532 memcpy(aArray
.Elements(), mDER
.Elements(), mDER
.Length());
537 nsNSSCertificate::GetBase64DERString(nsACString
& base64DERString
) {
538 nsDependentCSubstring
derString(
539 reinterpret_cast<const char*>(mDER
.Elements()), mDER
.Length());
540 nsresult rv
= Base64Encode(derString
, base64DERString
);
547 CERTCertificate
* nsNSSCertificate::GetCert() {
548 UniqueCERTCertificate
cert(GetOrInstantiateCert());
549 return cert
.release(); // caller takes ownership
553 nsNSSCertificate::GetValidity(nsIX509CertValidity
** aValidity
) {
554 NS_ENSURE_ARG(aValidity
);
555 pkix::Input certInput
;
556 pkix::Result rv
= certInput
.Init(mDER
.Elements(), mDER
.Length());
557 if (rv
!= pkix::Success
) {
558 return NS_ERROR_FAILURE
;
560 nsCOMPtr
<nsIX509CertValidity
> validity
= new X509CertValidity(certInput
);
561 validity
.forget(aValidity
);
565 // NB: Any updates (except disk-only fields) must be kept in sync with
568 nsNSSCertificate::Write(nsIObjectOutputStream
* aStream
) {
569 // This field used to be the cached EV status, but it is no longer necessary.
570 nsresult rv
= aStream
->Write32(0);
574 rv
= aStream
->Write32(mDER
.Length());
578 return aStream
->WriteBytes(Span(mDER
));
581 // NB: Any updates (except disk-only fields) must be kept in sync with
582 // |DeserializeFromIPC|.
584 nsNSSCertificate::Read(nsIObjectInputStream
* aStream
) {
585 auto lock
= mCert
.Lock();
586 auto& maybeCert
= lock
.ref();
587 if (!mDER
.IsEmpty() || maybeCert
.isSome()) {
588 return NS_ERROR_ALREADY_INITIALIZED
;
591 // This field is no longer used.
592 uint32_t unusedCachedEVStatus
;
593 nsresult rv
= aStream
->Read32(&unusedCachedEVStatus
);
599 rv
= aStream
->Read32(&len
);
604 rv
= aStream
->ReadByteArray(len
, mDER
);
611 void nsNSSCertificate::SerializeToIPC(IPC::MessageWriter
* aWriter
) {
612 bool hasCert
= !mDER
.IsEmpty();
613 WriteParam(aWriter
, hasCert
);
619 WriteParam(aWriter
, mDER
);
622 bool nsNSSCertificate::DeserializeFromIPC(IPC::MessageReader
* aReader
) {
623 auto lock
= mCert
.Lock();
624 auto& maybeCert
= lock
.ref();
625 if (!mDER
.IsEmpty() || maybeCert
.isSome()) {
629 bool hasCert
= false;
630 if (!ReadParam(aReader
, &hasCert
)) {
638 if (!ReadParam(aReader
, &mDER
)) {
645 nsNSSCertificate::GetInterfaces(nsTArray
<nsIID
>& array
) {
651 nsNSSCertificate::GetScriptableHelper(nsIXPCScriptable
** _retval
) {
657 nsNSSCertificate::GetContractID(nsACString
& aContractID
) {
658 aContractID
.SetIsVoid(true);
663 nsNSSCertificate::GetClassDescription(nsACString
& aClassDescription
) {
664 aClassDescription
.SetIsVoid(true);
669 nsNSSCertificate::GetClassID(nsCID
** aClassID
) {
670 *aClassID
= (nsCID
*)moz_xmalloc(sizeof(nsCID
));
671 return GetClassIDNoAlloc(*aClassID
);
675 nsNSSCertificate::GetFlags(uint32_t* aFlags
) {
676 *aFlags
= nsIClassInfo::THREADSAFE
;
681 nsNSSCertificate::GetClassIDNoAlloc(nsCID
* aClassIDNoAlloc
) {
682 static NS_DEFINE_CID(kNSSCertificateCID
, NS_X509CERT_CID
);
684 *aClassIDNoAlloc
= kNSSCertificateCID
;