// 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/cert_verify_proc_android.h" #include #include #include "base/logging.h" #include "base/sha1.h" #include "base/strings/string_piece.h" #include "crypto/sha2.h" #include "net/android/cert_verify_result_android.h" #include "net/android/network_library.h" #include "net/base/net_errors.h" #include "net/cert/asn1_util.h" #include "net/cert/cert_status_flags.h" #include "net/cert/cert_verify_result.h" #include "net/cert/x509_certificate.h" namespace net { namespace { // Returns true if the certificate verification call was successful (regardless // of its result), i.e. if |verify_result| was set. Otherwise returns false. bool VerifyFromAndroidTrustManager(const std::vector& cert_bytes, const std::string& hostname, CertVerifyResult* verify_result) { android::CertVerifyStatusAndroid status; std::vector verified_chain; // TODO(joth): Fetch the authentication type from SSL rather than hardcode. android::VerifyX509CertChain(cert_bytes, "RSA", hostname, &status, &verify_result->is_issued_by_known_root, &verified_chain); switch (status) { case android::VERIFY_FAILED: return false; case android::VERIFY_OK: break; case android::VERIFY_NO_TRUSTED_ROOT: verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID; break; case android::VERIFY_EXPIRED: case android::VERIFY_NOT_YET_VALID: verify_result->cert_status |= CERT_STATUS_DATE_INVALID; break; case android::VERIFY_UNABLE_TO_PARSE: verify_result->cert_status |= CERT_STATUS_INVALID; break; case android::VERIFY_INCORRECT_KEY_USAGE: verify_result->cert_status |= CERT_STATUS_INVALID; break; default: NOTREACHED(); verify_result->cert_status |= CERT_STATUS_INVALID; break; } // Save the verified chain. if (!verified_chain.empty()) { std::vector verified_chain_pieces(verified_chain.size()); for (size_t i = 0; i < verified_chain.size(); i++) { verified_chain_pieces[i] = base::StringPiece(verified_chain[i]); } scoped_refptr verified_cert = X509Certificate::CreateFromDERCertChain(verified_chain_pieces); if (verified_cert) verify_result->verified_cert = verified_cert; } // Extract the public key hashes. for (size_t i = 0; i < verified_chain.size(); i++) { base::StringPiece spki_bytes; if (!asn1::ExtractSPKIFromDERCert(verified_chain[i], &spki_bytes)) continue; HashValue sha1(HASH_VALUE_SHA1); base::SHA1HashBytes(reinterpret_cast(spki_bytes.data()), spki_bytes.size(), sha1.data()); verify_result->public_key_hashes.push_back(sha1); HashValue sha256(HASH_VALUE_SHA256); crypto::SHA256HashString(spki_bytes, sha256.data(), crypto::kSHA256Length); verify_result->public_key_hashes.push_back(sha256); } return true; } bool GetChainDEREncodedBytes(X509Certificate* cert, std::vector* chain_bytes) { X509Certificate::OSCertHandle cert_handle = cert->os_cert_handle(); X509Certificate::OSCertHandles cert_handles = cert->GetIntermediateCertificates(); // Make sure the peer's own cert is the first in the chain, if it's not // already there. if (cert_handles.empty() || cert_handles[0] != cert_handle) cert_handles.insert(cert_handles.begin(), cert_handle); chain_bytes->reserve(cert_handles.size()); for (X509Certificate::OSCertHandles::const_iterator it = cert_handles.begin(); it != cert_handles.end(); ++it) { std::string cert_bytes; if(!X509Certificate::GetDEREncoded(*it, &cert_bytes)) return false; chain_bytes->push_back(cert_bytes); } return true; } } // namespace CertVerifyProcAndroid::CertVerifyProcAndroid() {} CertVerifyProcAndroid::~CertVerifyProcAndroid() {} bool CertVerifyProcAndroid::SupportsAdditionalTrustAnchors() const { return false; } int CertVerifyProcAndroid::VerifyInternal( X509Certificate* cert, const std::string& hostname, int flags, CRLSet* crl_set, const CertificateList& additional_trust_anchors, CertVerifyResult* verify_result) { if (!cert->VerifyNameMatch(hostname, &verify_result->common_name_fallback_used)) { verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID; } std::vector cert_bytes; if (!GetChainDEREncodedBytes(cert, &cert_bytes)) return ERR_CERT_INVALID; if (!VerifyFromAndroidTrustManager(cert_bytes, hostname, verify_result)) { NOTREACHED(); return ERR_FAILED; } if (IsCertStatusError(verify_result->cert_status)) return MapCertStatusToNetError(verify_result->cert_status); return OK; } } // namespace net