普通文本  |  412行  |  14.11 KB

// Copyright (c) 2011 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 "chrome/common/net/x509_certificate_model.h"

#include <cert.h>
#include <cms.h>
#include <hasht.h>
#include <keyhi.h>  // SECKEY_DestroyPrivateKey
#include <keythi.h>  // SECKEYPrivateKey
#include <pk11pub.h>  // PK11_FindKeyByAnyCert
#include <seccomon.h>  // SECItem
#include <sechash.h>

#include "base/logging.h"
#include "base/string_number_conversions.h"
#include "crypto/nss_util.h"
#include "net/base/x509_certificate.h"
#include "chrome/third_party/mozilla_security_manager/nsNSSCertHelper.h"
#include "chrome/third_party/mozilla_security_manager/nsNSSCertificate.h"
#include "chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.h"

namespace psm = mozilla_security_manager;

namespace {

// Convert a char* return value from NSS into a std::string and free the NSS
// memory.  If the arg is NULL, an empty string will be returned instead.
std::string Stringize(char* nss_text, const std::string& alternative_text) {
  if (!nss_text)
    return alternative_text;

  std::string s = nss_text;
  PORT_Free(nss_text);
  return s;
}

// Hash a certificate using the given algorithm, return the result as a
// colon-seperated hex string.  The len specified is the number of bytes
// required for storing the raw fingerprint.
// (It's a bit redundant that the caller needs to specify len in addition to the
// algorithm, but given the limited uses, not worth fixing.)
std::string HashCert(CERTCertificate* cert, HASH_HashType algorithm, int len) {
  unsigned char fingerprint[HASH_LENGTH_MAX];

  DCHECK(NULL != cert->derCert.data);
  DCHECK_NE(0U, cert->derCert.len);
  DCHECK_LE(len, HASH_LENGTH_MAX);
  memset(fingerprint, 0, len);
  SECStatus rv = HASH_HashBuf(algorithm, fingerprint, cert->derCert.data,
                              cert->derCert.len);
  DCHECK_EQ(rv, SECSuccess);
  return x509_certificate_model::ProcessRawBytes(fingerprint, len);
}

std::string ProcessSecAlgorithmInternal(SECAlgorithmID* algorithm_id) {
  return psm::GetOIDText(&algorithm_id->algorithm);
}

std::string ProcessExtension(
    const std::string& critical_label,
    const std::string& non_critical_label,
    CERTCertExtension* extension) {
  std::string criticality =
      extension->critical.data && extension->critical.data[0] ?
          critical_label : non_critical_label;
  return criticality + "\n" +
      psm::ProcessExtensionData(SECOID_FindOIDTag(&extension->id),
                                &extension->value);
}

////////////////////////////////////////////////////////////////////////////////
// NSS certificate export functions.

class FreeNSSCMSMessage {
 public:
  inline void operator()(NSSCMSMessage* x) const {
    NSS_CMSMessage_Destroy(x);
  }
};
typedef scoped_ptr_malloc<NSSCMSMessage, FreeNSSCMSMessage>
    ScopedNSSCMSMessage;

class FreeNSSCMSSignedData {
 public:
  inline void operator()(NSSCMSSignedData* x) const {
    NSS_CMSSignedData_Destroy(x);
  }
};
typedef scoped_ptr_malloc<NSSCMSSignedData, FreeNSSCMSSignedData>
    ScopedNSSCMSSignedData;

}  // namespace

namespace x509_certificate_model {

using net::X509Certificate;
using std::string;

string GetCertNameOrNickname(X509Certificate::OSCertHandle cert_handle) {
  string name = ProcessIDN(Stringize(CERT_GetCommonName(&cert_handle->subject),
                                     ""));
  if (!name.empty())
    return name;
  return GetNickname(cert_handle);
}

string GetNickname(X509Certificate::OSCertHandle cert_handle) {
  string name;
  if (cert_handle->nickname) {
    name = cert_handle->nickname;
    // Hack copied from mozilla: Cut off text before first :, which seems to
    // just be the token name.
    size_t colon_pos = name.find(':');
    if (colon_pos != string::npos)
      name = name.substr(colon_pos + 1);
  }
  return name;
}

string GetTokenName(X509Certificate::OSCertHandle cert_handle) {
  return psm::GetCertTokenName(cert_handle);
}

string GetVersion(X509Certificate::OSCertHandle cert_handle) {
  unsigned long version = ULONG_MAX;
  if (SEC_ASN1DecodeInteger(&cert_handle->version, &version) == SECSuccess &&
      version != ULONG_MAX)
    return base::UintToString(version + 1);
  return "";
}

net::CertType GetType(X509Certificate::OSCertHandle cert_handle) {
    return psm::GetCertType(cert_handle);
}

string GetEmailAddress(X509Certificate::OSCertHandle cert_handle) {
  if (cert_handle->emailAddr)
    return cert_handle->emailAddr;
  return "";
}

void GetUsageStrings(X509Certificate::OSCertHandle cert_handle,
                     std::vector<string>* usages) {
  psm::GetCertUsageStrings(cert_handle, usages);
}

string GetKeyUsageString(X509Certificate::OSCertHandle cert_handle) {
  SECItem key_usage;
  key_usage.data = NULL;
  string key_usage_str;
  if (CERT_FindKeyUsageExtension(cert_handle, &key_usage) == SECSuccess) {
    key_usage_str = psm::ProcessKeyUsageBitString(&key_usage, ',');
    PORT_Free(key_usage.data);
  }
  return key_usage_str;
}

string GetSerialNumberHexified(X509Certificate::OSCertHandle cert_handle,
                               const string& alternative_text) {
  return Stringize(CERT_Hexify(&cert_handle->serialNumber, true),
                   alternative_text);
}

string GetIssuerCommonName(X509Certificate::OSCertHandle cert_handle,
                           const string& alternative_text) {
  return Stringize(CERT_GetCommonName(&cert_handle->issuer), alternative_text);
}

string GetIssuerOrgName(X509Certificate::OSCertHandle cert_handle,
                        const string& alternative_text) {
  return Stringize(CERT_GetOrgName(&cert_handle->issuer), alternative_text);
}

string GetIssuerOrgUnitName(X509Certificate::OSCertHandle cert_handle,
                            const string& alternative_text) {
  return Stringize(CERT_GetOrgUnitName(&cert_handle->issuer), alternative_text);
}

string GetSubjectOrgName(X509Certificate::OSCertHandle cert_handle,
                         const string& alternative_text) {
  return Stringize(CERT_GetOrgName(&cert_handle->subject), alternative_text);
}

string GetSubjectOrgUnitName(X509Certificate::OSCertHandle cert_handle,
                             const string& alternative_text) {
  return Stringize(CERT_GetOrgUnitName(&cert_handle->subject),
                   alternative_text);
}

string GetSubjectCommonName(X509Certificate::OSCertHandle cert_handle,
                            const string& alternative_text) {
  return Stringize(CERT_GetCommonName(&cert_handle->subject), alternative_text);
}

bool GetTimes(X509Certificate::OSCertHandle cert_handle,
              base::Time* issued, base::Time* expires) {
  PRTime pr_issued, pr_expires;
  if (CERT_GetCertTimes(cert_handle, &pr_issued, &pr_expires) == SECSuccess) {
    *issued = crypto::PRTimeToBaseTime(pr_issued);
    *expires = crypto::PRTimeToBaseTime(pr_expires);
    return true;
  }
  return false;
}

string GetTitle(X509Certificate::OSCertHandle cert_handle) {
  return psm::GetCertTitle(cert_handle);
}

string GetIssuerName(X509Certificate::OSCertHandle cert_handle) {
  return psm::ProcessName(&cert_handle->issuer);
}

string GetSubjectName(X509Certificate::OSCertHandle cert_handle) {
  return psm::ProcessName(&cert_handle->subject);
}

void GetEmailAddresses(X509Certificate::OSCertHandle cert_handle,
                       std::vector<string>* email_addresses) {
  for (const char* addr = CERT_GetFirstEmailAddress(cert_handle);
       addr; addr = CERT_GetNextEmailAddress(cert_handle, addr)) {
    // The first email addr (from Subject) may be duplicated in Subject
    // Alternative Name, so check subsequent addresses are not equal to the
    // first one before adding to the list.
    if (!email_addresses->size() || (*email_addresses)[0] != addr)
      email_addresses->push_back(addr);
  }
}

void GetNicknameStringsFromCertList(
    const std::vector<scoped_refptr<X509Certificate> >& certs,
    const string& cert_expired,
    const string& cert_not_yet_valid,
    std::vector<string>* nick_names) {
  CERTCertList* cert_list = CERT_NewCertList();
  for (size_t i = 0; i < certs.size(); ++i) {
    CERT_AddCertToListTail(
        cert_list,
        CERT_DupCertificate(certs[i]->os_cert_handle()));
  }
  // Would like to use CERT_GetCertNicknameWithValidity on each cert
  // individually instead of having to build a CERTCertList for this, but that
  // function is not exported.
  CERTCertNicknames* cert_nicknames = CERT_NicknameStringsFromCertList(
      cert_list,
      const_cast<char*>(cert_expired.c_str()),
      const_cast<char*>(cert_not_yet_valid.c_str()));
  DCHECK_EQ(cert_nicknames->numnicknames,
            static_cast<int>(certs.size()));

  for (int i = 0; i < cert_nicknames->numnicknames; ++i)
    nick_names->push_back(cert_nicknames->nicknames[i]);

  CERT_FreeNicknames(cert_nicknames);
  CERT_DestroyCertList(cert_list);
}

// For background see this discussion on dev-tech-crypto.lists.mozilla.org:
// http://web.archiveorange.com/archive/v/6JJW7E40sypfZGtbkzxX
//
// NOTE: This function relies on the convention that the same PKCS#11 ID
// is shared between a certificate and its associated private and public
// keys.  I tried to implement this with PK11_GetLowLevelKeyIDForCert(),
// but that always returns NULL on Chrome OS for me.
std::string GetPkcs11Id(net::X509Certificate::OSCertHandle cert_handle) {
  std::string pkcs11_id;
  SECKEYPrivateKey *priv_key = PK11_FindKeyByAnyCert(cert_handle,
                                                     NULL /* wincx */);
  if (priv_key) {
    // Get the CKA_ID attribute for a key.
    SECItem* sec_item = PK11_GetLowLevelKeyIDForPrivateKey(priv_key);
    if (sec_item) {
      pkcs11_id = base::HexEncode(sec_item->data, sec_item->len);
      SECITEM_FreeItem(sec_item, PR_TRUE);
    }
    SECKEY_DestroyPrivateKey(priv_key);
  }
  return pkcs11_id;
}

void GetExtensions(
    const string& critical_label,
    const string& non_critical_label,
    X509Certificate::OSCertHandle cert_handle,
    Extensions* extensions) {
  if (cert_handle->extensions) {
    for (size_t i = 0; cert_handle->extensions[i] != NULL; ++i) {
      Extension extension;
      extension.name = psm::GetOIDText(&cert_handle->extensions[i]->id);
      extension.value = ProcessExtension(
          critical_label, non_critical_label, cert_handle->extensions[i]);
      extensions->push_back(extension);
    }
  }
}

string HashCertSHA256(X509Certificate::OSCertHandle cert_handle) {
  return HashCert(cert_handle, HASH_AlgSHA256, SHA256_LENGTH);
}

string HashCertSHA1(X509Certificate::OSCertHandle cert_handle) {
  return HashCert(cert_handle, HASH_AlgSHA1, SHA1_LENGTH);
}

void GetCertChainFromCert(X509Certificate::OSCertHandle cert_handle,
                          X509Certificate::OSCertHandles* cert_handles) {
  CERTCertList* cert_list =
      CERT_GetCertChainFromCert(cert_handle, PR_Now(), certUsageSSLServer);
  CERTCertListNode* node;
  for (node = CERT_LIST_HEAD(cert_list);
       !CERT_LIST_END(node, cert_list);
       node = CERT_LIST_NEXT(node)) {
    cert_handles->push_back(CERT_DupCertificate(node->cert));
  }
  CERT_DestroyCertList(cert_list);
}

void DestroyCertChain(X509Certificate::OSCertHandles* cert_handles) {
  for (X509Certificate::OSCertHandles::iterator i(cert_handles->begin());
       i != cert_handles->end(); ++i)
    CERT_DestroyCertificate(*i);
  cert_handles->clear();
}

string GetDerString(X509Certificate::OSCertHandle cert_handle) {
  return string(reinterpret_cast<const char*>(cert_handle->derCert.data),
                cert_handle->derCert.len);
}

string GetCMSString(const X509Certificate::OSCertHandles& cert_chain,
                    size_t start, size_t end) {
  ScopedPRArenaPool arena(PORT_NewArena(1024));
  CHECK(arena.get());

  ScopedNSSCMSMessage message(NSS_CMSMessage_Create(arena.get()));
  CHECK(message.get());

  // First, create SignedData with the certificate only (no chain).
  ScopedNSSCMSSignedData signed_data(NSS_CMSSignedData_CreateCertsOnly(
      message.get(), cert_chain[start], PR_FALSE));
  if (!signed_data.get()) {
    LOG(ERROR) << "NSS_CMSSignedData_Create failed";
    return "";
  }
  // Add the rest of the chain (if any).
  for (size_t i = start + 1; i < end; ++i) {
    if (NSS_CMSSignedData_AddCertificate(signed_data.get(), cert_chain[i]) !=
        SECSuccess) {
      LOG(ERROR) << "NSS_CMSSignedData_AddCertificate failed on " << i;
      return "";
    }
  }

  NSSCMSContentInfo *cinfo = NSS_CMSMessage_GetContentInfo(message.get());
  if (NSS_CMSContentInfo_SetContent_SignedData(
      message.get(), cinfo, signed_data.get()) == SECSuccess) {
    ignore_result(signed_data.release());
  } else {
    LOG(ERROR) << "NSS_CMSMessage_GetContentInfo failed";
    return "";
  }

  SECItem cert_p7 = { siBuffer, NULL, 0 };
  NSSCMSEncoderContext *ecx = NSS_CMSEncoder_Start(message.get(), NULL, NULL,
                                                   &cert_p7, arena.get(), NULL,
                                                   NULL, NULL, NULL, NULL,
                                                   NULL);
  if (!ecx) {
    LOG(ERROR) << "NSS_CMSEncoder_Start failed";
    return "";
  }

  if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
    LOG(ERROR) << "NSS_CMSEncoder_Finish failed";
    return "";
  }

  return string(reinterpret_cast<const char*>(cert_p7.data), cert_p7.len);
}

string ProcessSecAlgorithmSignature(X509Certificate::OSCertHandle cert_handle) {
  return ProcessSecAlgorithmInternal(&cert_handle->signature);
}

string ProcessSecAlgorithmSubjectPublicKey(
    X509Certificate::OSCertHandle cert_handle) {
  return ProcessSecAlgorithmInternal(
      &cert_handle->subjectPublicKeyInfo.algorithm);
}

string ProcessSecAlgorithmSignatureWrap(
    X509Certificate::OSCertHandle cert_handle) {
  return ProcessSecAlgorithmInternal(
      &cert_handle->signatureWrap.signatureAlgorithm);
}

string ProcessSubjectPublicKeyInfo(X509Certificate::OSCertHandle cert_handle) {
  return psm::ProcessSubjectPublicKeyInfo(&cert_handle->subjectPublicKeyInfo);
}

string ProcessRawBitsSignatureWrap(X509Certificate::OSCertHandle cert_handle) {
  return ProcessRawBits(cert_handle->signatureWrap.signature.data,
                        cert_handle->signatureWrap.signature.len);
}

void RegisterDynamicOids() {
}

}  // namespace x509_certificate_model