blob: 9019625869aacf35bcf3280a4733eeb2bac5ccc6 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/cert/x509_certificate.h"
#include <cert.h>
#include <cryptohi.h>
#include <keyhi.h>
#include <nss.h>
#include <pk11pub.h>
#include <prtime.h>
#include <seccomon.h>
#include <secder.h>
#include <sechash.h>
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/pickle.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "crypto/nss_util.h"
#include "crypto/scoped_nss_types.h"
#include "net/cert/x509_util_nss.h"
namespace net {
void X509Certificate::Initialize() {
x509_util::ParsePrincipal(&cert_handle_->subject, &subject_);
x509_util::ParsePrincipal(&cert_handle_->issuer, &issuer_);
x509_util::ParseDate(&cert_handle_->validity.notBefore, &valid_start_);
x509_util::ParseDate(&cert_handle_->validity.notAfter, &valid_expiry_);
fingerprint_ = CalculateFingerprint(cert_handle_);
ca_fingerprint_ = CalculateCAFingerprint(intermediate_ca_certs_);
serial_number_ = x509_util::ParseSerialNumber(cert_handle_);
}
// static
X509Certificate* X509Certificate::CreateFromBytesWithNickname(
const char* data,
int length,
const char* nickname) {
OSCertHandle cert_handle = CreateOSCertHandleFromBytesWithNickname(data,
length,
nickname);
if (!cert_handle)
return NULL;
X509Certificate* cert = CreateFromHandle(cert_handle, OSCertHandles());
FreeOSCertHandle(cert_handle);
if (nickname)
cert->default_nickname_ = nickname;
return cert;
}
std::string X509Certificate::GetDefaultNickname(CertType type) const {
if (!default_nickname_.empty())
return default_nickname_;
std::string result;
if (type == USER_CERT && cert_handle_->slot) {
// Find the private key for this certificate and see if it has a
// nickname. If there is a private key, and it has a nickname, then
// return that nickname.
SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
cert_handle_->slot,
cert_handle_,
NULL); // wincx
if (private_key) {
char* private_key_nickname = PK11_GetPrivateKeyNickname(private_key);
if (private_key_nickname) {
result = private_key_nickname;
PORT_Free(private_key_nickname);
SECKEY_DestroyPrivateKey(private_key);
return result;
}
SECKEY_DestroyPrivateKey(private_key);
}
}
switch (type) {
case CA_CERT: {
char* nickname = CERT_MakeCANickname(cert_handle_);
result = nickname;
PORT_Free(nickname);
break;
}
case USER_CERT: {
std::string subject_name = subject_.GetDisplayName();
if (subject_name.empty()) {
const char* email = CERT_GetFirstEmailAddress(cert_handle_);
if (email)
subject_name = email;
}
// TODO(gspencer): Internationalize this. It's wrong to assume English
// here.
result = base::StringPrintf("%s's %s ID", subject_name.c_str(),
issuer_.GetDisplayName().c_str());
break;
}
case SERVER_CERT:
result = subject_.GetDisplayName();
break;
case OTHER_CERT:
default:
break;
}
return result;
}
void X509Certificate::GetSubjectAltName(
std::vector<std::string>* dns_names,
std::vector<std::string>* ip_addrs) const {
x509_util::GetSubjectAltName(cert_handle_, dns_names, ip_addrs);
}
bool X509Certificate::IsIssuedByEncoded(
const std::vector<std::string>& valid_issuers) {
// Get certificate chain as scoped list of CERTCertificate objects.
std::vector<CERTCertificate*> cert_chain;
cert_chain.push_back(cert_handle_);
for (size_t n = 0; n < intermediate_ca_certs_.size(); ++n) {
cert_chain.push_back(intermediate_ca_certs_[n]);
}
// Convert encoded issuers to scoped CERTName* list.
std::vector<CERTName*> issuers;
crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
if (!x509_util::GetIssuersFromEncodedList(valid_issuers,
arena.get(),
&issuers)) {
return false;
}
return x509_util::IsCertificateIssuedBy(cert_chain, issuers);
}
// static
bool X509Certificate::GetDEREncoded(X509Certificate::OSCertHandle cert_handle,
std::string* encoded) {
if (!cert_handle || !cert_handle->derCert.len)
return false;
encoded->assign(reinterpret_cast<char*>(cert_handle->derCert.data),
cert_handle->derCert.len);
return true;
}
// static
bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
X509Certificate::OSCertHandle b) {
DCHECK(a && b);
if (a == b)
return true;
return a->derCert.len == b->derCert.len &&
memcmp(a->derCert.data, b->derCert.data, a->derCert.len) == 0;
}
// static
X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes(
const char* data, int length) {
return CreateOSCertHandleFromBytesWithNickname(data, length, NULL);
}
// static
X509Certificate::OSCertHandle
X509Certificate::CreateOSCertHandleFromBytesWithNickname(
const char* data,
int length,
const char* nickname) {
if (length < 0)
return NULL;
crypto::EnsureNSSInit();
if (!NSS_IsInitialized())
return NULL;
SECItem der_cert;
der_cert.data = reinterpret_cast<unsigned char*>(const_cast<char*>(data));
der_cert.len = length;
der_cert.type = siDERCertBuffer;
// Parse into a certificate structure.
return CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der_cert,
const_cast<char*>(nickname),
PR_FALSE, PR_TRUE);
}
// static
X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes(
const char* data,
int length,
Format format) {
return x509_util::CreateOSCertHandlesFromBytes(data, length, format);
}
// static
X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle(
OSCertHandle cert_handle) {
return CERT_DupCertificate(cert_handle);
}
// static
void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) {
CERT_DestroyCertificate(cert_handle);
}
// static
SHA1HashValue X509Certificate::CalculateFingerprint(
OSCertHandle cert) {
SHA1HashValue sha1;
memset(sha1.data, 0, sizeof(sha1.data));
DCHECK(NULL != cert->derCert.data);
DCHECK_NE(0U, cert->derCert.len);
SECStatus rv = HASH_HashBuf(HASH_AlgSHA1, sha1.data,
cert->derCert.data, cert->derCert.len);
DCHECK_EQ(SECSuccess, rv);
return sha1;
}
// static
SHA1HashValue X509Certificate::CalculateCAFingerprint(
const OSCertHandles& intermediates) {
SHA1HashValue sha1;
memset(sha1.data, 0, sizeof(sha1.data));
HASHContext* sha1_ctx = HASH_Create(HASH_AlgSHA1);
if (!sha1_ctx)
return sha1;
HASH_Begin(sha1_ctx);
for (size_t i = 0; i < intermediates.size(); ++i) {
CERTCertificate* ca_cert = intermediates[i];
HASH_Update(sha1_ctx, ca_cert->derCert.data, ca_cert->derCert.len);
}
unsigned int result_len;
HASH_End(sha1_ctx, sha1.data, &result_len, HASH_ResultLenContext(sha1_ctx));
HASH_Destroy(sha1_ctx);
return sha1;
}
// static
X509Certificate::OSCertHandle
X509Certificate::ReadOSCertHandleFromPickle(PickleIterator* pickle_iter) {
return x509_util::ReadOSCertHandleFromPickle(pickle_iter);
}
// static
bool X509Certificate::WriteOSCertHandleToPickle(OSCertHandle cert_handle,
Pickle* pickle) {
return pickle->WriteData(
reinterpret_cast<const char*>(cert_handle->derCert.data),
cert_handle->derCert.len);
}
// static
void X509Certificate::GetPublicKeyInfo(OSCertHandle cert_handle,
size_t* size_bits,
PublicKeyType* type) {
x509_util::GetPublicKeyInfo(cert_handle, size_bits, type);
}
} // namespace net