Request::Request(const X509_Certificate& issuer_cert, const X509_Certificate& subject_cert) : m_issuer(issuer_cert), m_certid(m_issuer, BigInt::decode(subject_cert.serial_number())) { if(subject_cert.issuer_dn() != issuer_cert.subject_dn()) throw Invalid_Argument("Invalid cert pair to OCSP::Request (mismatched issuer,subject args?)"); }
bool X509_Certificate::operator<(const X509_Certificate& other) const { /* If signature values are not equal, sort by lexicographic ordering of that */ if(this->signature() != other.signature()) { return (this->signature() < other.signature()); } // Then compare the signed contents return this->signed_body() < other.signed_body(); }
bool Certificate_Store_In_SQL::remove_cert(const X509_Certificate& cert) { if(!find_cert(cert.subject_dn(),cert.subject_key_id())) return false; auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "certificates WHERE fingerprint == ?1"); stmt->bind(1,cert.fingerprint("SHA-256")); stmt->spin(); return true; }
Response online_check(const X509_Certificate& issuer, const X509_Certificate& subject, Certificate_Store* trusted_roots, std::chrono::milliseconds timeout) { if(subject.issuer_dn() != issuer.subject_dn()) throw Invalid_Argument("Invalid cert pair to OCSP::online_check (mismatched issuer,subject args?)"); return online_check(issuer, BigInt::decode(subject.serial_number()), subject.ocsp_responder(), trusted_roots, timeout); }
CertID::CertID(const X509_Certificate& issuer, const X509_Certificate& subject) { /* In practice it seems some responders, including, notably, ocsp.verisign.com, will reject anything but SHA-1 here */ std::unique_ptr<HashFunction> hash(HashFunction::create("SHA-160")); m_hash_id = AlgorithmIdentifier(hash->name(), AlgorithmIdentifier::USE_NULL_PARAM); m_issuer_key_hash = unlock(hash->process(issuer.subject_public_key_bitstring())); m_issuer_dn_hash = unlock(hash->process(subject.raw_issuer_dn())); m_subject_serial = BigInt::decode(subject.serial_number()); }
/* * Create a CRL_Entry */ CRL_Entry::CRL_Entry(const X509_Certificate& cert, CRL_Code why) : throw_on_unknown_critical(false) { serial = cert.serial_number(); time = X509_Time(system_time()); reason = why; }
Certificate_Status_Code Response::verify_signature(const X509_Certificate& issuer) const { if (m_responses.empty()) return m_dummy_response_status; try { std::unique_ptr<Public_Key> pub_key(issuer.subject_public_key()); const std::vector<std::string> sig_info = split_on(OIDS::lookup(m_sig_algo.get_oid()), '/'); if(sig_info.size() != 2 || sig_info[0] != pub_key->algo_name()) return Certificate_Status_Code::OCSP_RESPONSE_INVALID; std::string padding = sig_info[1]; Signature_Format format = (pub_key->message_parts() >= 2) ? DER_SEQUENCE : IEEE_1363; PK_Verifier verifier(*pub_key, padding, format); if(verifier.verify_message(ASN1::put_in_sequence(m_tbs_bits), m_signature)) return Certificate_Status_Code::OCSP_SIGNATURE_OK; else return Certificate_Status_Code::OCSP_SIGNATURE_ERROR; } catch(Exception&) { return Certificate_Status_Code::OCSP_SIGNATURE_ERROR; } }
bool Certificate_Store_In_SQL::insert_key(const X509_Certificate& cert, const Private_Key& key) { insert_cert(cert); if(find_key(cert)) return false; auto pkcs8 = PKCS8::BER_encode(key, m_rng, m_password); auto fpr = key.fingerprint("SHA-256"); auto stmt1 = m_database->new_statement( "INSERT OR REPLACE INTO " + m_prefix + "keys ( fingerprint, key ) VALUES ( ?1, ?2 )"); stmt1->bind(1,fpr); stmt1->bind(2,pkcs8.data(),pkcs8.size()); stmt1->spin(); auto stmt2 = m_database->new_statement( "UPDATE " + m_prefix + "certificates SET priv_fingerprint = ?1 WHERE fingerprint == ?2"); stmt2->bind(1,fpr); stmt2->bind(2,cert.fingerprint("SHA-256")); stmt2->spin(); return true; }
void Certificate_Store_In_SQL::affirm_cert(const X509_Certificate& cert) { auto stmt = m_database->new_statement("DELETE FROM " + m_prefix + "revoked WHERE fingerprint == ?1"); stmt->bind(1,cert.fingerprint("SHA-256")); stmt->spin(); }
/* * Search based on the contents of a DN entry */ bool DN_Check::match(const X509_Certificate& cert) const { std::vector<std::string> info = cert.subject_info(dn_entry); for(u32bit j = 0; j != info.size(); ++j) if(compare(info[j], looking_for)) return true; return false; }
std::shared_ptr<const X509_CRL> Certificate_Store_In_Memory::find_crl_for(const X509_Certificate& subject) const { const std::vector<uint8_t>& key_id = subject.authority_key_id(); for(const auto& c : m_crls) { // Only compare key ids if set in both call and in the CRL if(key_id.size()) { std::vector<uint8_t> akid = c->authority_key_id(); if(akid.size() && akid != key_id) // no match continue; } if(c->issuer_dn() == subject.issuer_dn()) return c; } return {}; }
/* * Create a CRL_Entry */ CRL_Entry::CRL_Entry(const X509_Certificate& cert, CRL_Code why) { m_data.reset(new CRL_Entry_Data); m_data->m_serial = cert.serial_number(); m_data->m_time = X509_Time(std::chrono::system_clock::now()); m_data->m_reason = why; if(why != UNSPECIFIED) { m_data->m_extensions.add(new Cert_Extension::CRL_ReasonCode(why)); } }
bool Certificate_Store_In_SQL::insert_cert(const X509_Certificate& cert) { if(find_cert(cert.subject_dn(),cert.subject_key_id())) return false; DER_Encoder enc; auto stmt = m_database->new_statement("INSERT OR REPLACE INTO " + m_prefix + "certificates (\ fingerprint, \ subject_dn, \ key_id, \ priv_fingerprint, \ certificate \ ) VALUES ( ?1, ?2, ?3, ?4, ?5 )"); stmt->bind(1,cert.fingerprint("SHA-256")); cert.subject_dn().encode_into(enc); stmt->bind(2,enc.get_contents_unlocked()); stmt->bind(3,cert.subject_key_id()); stmt->bind(4,std::vector<uint8_t>()); enc = DER_Encoder(); cert.encode_into(enc); stmt->bind(5,enc.get_contents_unlocked()); stmt->spin(); return true; }
std::shared_ptr<const X509_CRL> Certificate_Store_In_SQL::find_crl_for(const X509_Certificate& subject) const { auto all_crls = generate_crls(); for(auto crl: all_crls) { if(!crl.get_revoked().empty() && crl.issuer_dn() == subject.issuer_dn()) return std::shared_ptr<X509_CRL>(new X509_CRL(crl)); } return std::shared_ptr<X509_CRL>(); }
std::vector<byte> CertID::extract_key_bitstr(const X509_Certificate& cert) const { const auto key_bits = cert.subject_public_key_bits(); AlgorithmIdentifier public_key_algid; std::vector<byte> public_key_bitstr; BER_Decoder(key_bits) .decode(public_key_algid) .decode(public_key_bitstr, BIT_STRING); return public_key_bitstr; }
bool CertID::is_id_for(const X509_Certificate& issuer, const X509_Certificate& subject) const { try { if(BigInt::decode(subject.serial_number()) != m_subject_serial) return false; std::unique_ptr<HashFunction> hash(HashFunction::create(OIDS::lookup(m_hash_id.oid))); if(m_issuer_dn_hash != unlock(hash->process(subject.raw_issuer_dn()))) return false; if(m_issuer_key_hash != unlock(hash->process(issuer.subject_public_key_bitstring()))) return false; } catch(...) { return false; } return true; }
/** * Check if this particular certificate is listed in the CRL */ bool X509_CRL::is_revoked(const X509_Certificate& cert) const { /* If the cert wasn't issued by the CRL issuer, it's possible the cert is revoked, but not by this CRL. Maybe throw an exception instead? */ if(cert.issuer_dn() != issuer_dn()) return false; std::vector<uint8_t> crl_akid = authority_key_id(); std::vector<uint8_t> cert_akid = cert.authority_key_id(); if(!crl_akid.empty() && !cert_akid.empty()) { if(crl_akid != cert_akid) return false; } std::vector<uint8_t> cert_serial = cert.serial_number(); bool is_revoked = false; // FIXME would be nice to avoid a linear scan here - maybe sort the entries? for(const CRL_Entry& entry : get_revoked()) { if(cert_serial == entry.serial_number()) { if(entry.reason_code() == REMOVE_FROM_CRL) is_revoked = false; else is_revoked = true; } } return is_revoked; }
/* * Verify a Certificate Verify message */ bool Certificate_Verify::verify(const X509_Certificate& cert, const Handshake_State& state, const Policy& policy) const { std::unique_ptr<Public_Key> key(cert.subject_public_key()); policy.check_peer_key_acceptable(*key); std::pair<std::string, Signature_Format> format = state.parse_sig_format(*key.get(), m_hash_algo, m_sig_algo, true, policy); PK_Verifier verifier(*key, format.first, format.second); return verifier.verify_message(state.hash().get_contents(), m_signature); }
// Private key handling std::shared_ptr<const Private_Key> Certificate_Store_In_SQL::find_key(const X509_Certificate& cert) const { auto stmt = m_database->new_statement("SELECT key FROM " + m_prefix + "keys " "JOIN " + m_prefix + "certificates ON " + m_prefix + "keys.fingerprint == " + m_prefix + "certificates.priv_fingerprint " "WHERE " + m_prefix + "certificates.fingerprint == ?1"); stmt->bind(1,cert.fingerprint("SHA-256")); std::shared_ptr<const Private_Key> key; while(stmt->step()) { auto blob = stmt->get_blob(0); DataSource_Memory src(blob.first,blob.second); key.reset(PKCS8::load_key(src, m_rng, m_password)); } return key; }
void save_pair(const std::string& name, const std::string& password, const X509_Certificate& cert, const Private_Key& key, RandomNumberGenerator& rng) { std::string cert_fsname = name + "_cert.pem"; std::string key_fsname = name + "_key.pem"; std::ofstream cert_out(cert_fsname.c_str()); cert_out << cert.PEM_encode() << "\n"; cert_out.close(); std::ofstream key_out(key_fsname.c_str()); if(password != "") key_out << PKCS8::PEM_encode(key, rng, password); else key_out << PKCS8::PEM_encode(key); key_out.close(); }
/* * Verify a Certificate Verify message */ bool Certificate_Verify::verify(const X509_Certificate& cert, const Handshake_State& state, const Policy& policy) const { std::unique_ptr<Public_Key> key(cert.subject_public_key()); policy.check_peer_key_acceptable(*key); std::pair<std::string, Signature_Format> format = state.parse_sig_format(*key.get(), m_scheme, true, policy); const bool signature_valid = state.callbacks().tls_verify_message(*key, format.first, format.second, state.hash().get_contents(), m_signature); #if defined(BOTAN_UNSAFE_FUZZER_MODE) return true; #else return signature_valid; #endif }
// Revocation void Certificate_Store_In_SQL::revoke_cert(const X509_Certificate& cert, CRL_Code code, const X509_Time& time) { insert_cert(cert); auto stmt1 = m_database->new_statement( "INSERT OR REPLACE INTO " + m_prefix + "revoked ( fingerprint, reason, time ) VALUES ( ?1, ?2, ?3 )"); stmt1->bind(1,cert.fingerprint("SHA-256")); stmt1->bind(2,code); if(time.time_is_set()) { DER_Encoder der; time.encode_into(der); stmt1->bind(3,der.get_contents_unlocked()); } else { stmt1->bind(3,-1); } stmt1->spin(); }
Response online_check(const X509_Certificate& issuer, const X509_Certificate& subject, const Certificate_Store* trusted_roots) { const std::string responder_url = subject.ocsp_responder(); if(responder_url == "") throw std::runtime_error("No OCSP responder specified"); OCSP::Request req(issuer, subject); auto http = HTTP::POST_sync(responder_url, "application/ocsp-request", req.BER_encode()); http.throw_unless_ok(); // Check the MIME type? OCSP::Response response(*trusted_roots, http.body()); return response; }
/** * Verify a Certificate Verify message */ bool Certificate_Verify::verify(const X509_Certificate& cert, HandshakeHash& hash) { // FIXME: duplicate of Server_Key_Exchange::verify std::auto_ptr<Public_Key> key(cert.subject_public_key()); std::string padding = ""; Signature_Format format = IEEE_1363; if(key->algo_name() == "RSA") padding = "EMSA3(TLS.Digest.0)"; else if(key->algo_name() == "DSA") { padding == "EMSA1(SHA-1)"; format = DER_SEQUENCE; } else throw Invalid_Argument(key->algo_name() + " is invalid/unknown for TLS signatures"); PK_Verifier verifier(*key, padding, format); return verifier.verify_message(hash.final(), signature); }
/* * Compare two certificates for equality */ bool X509_Certificate::operator==(const X509_Certificate& other) const { return (this->signature() == other.signature() && this->signature_algorithm() == other.signature_algorithm() && this->signed_body() == other.signed_body()); }
/* * Match by issuer and serial number */ bool IandS_Match::match(const X509_Certificate& cert) const { if(cert.serial_number() != serial) return false; return (cert.issuer_dn() == issuer); }
GeneralName::MatchResult GeneralName::matches(const X509_Certificate& cert) const { std::vector<std::string> nam; std::function<bool(const GeneralName*,const std::string&)> match_fn; if(type() == "DNS") { match_fn = std::mem_fn(&GeneralName::matches_dns); nam = cert.subject_info("DNS"); if(nam.empty()) { nam = cert.subject_info("CN"); } } else if(type() == "DN") { match_fn = std::mem_fn(&GeneralName::matches_dn); std::stringstream ss; ss << cert.subject_dn(); nam.push_back(ss.str()); } else if(type() == "IP") { match_fn = std::mem_fn(&GeneralName::matches_ip); nam = cert.subject_info("IP"); } else { return MatchResult::UnknownType; } if(nam.empty()) { return MatchResult::NotFound; } bool some = false; bool all = true; for(const std::string& n: nam) { bool m = match_fn(this,n); some |= m; all &= m; } if(all) { return MatchResult::All; } else if(some) { return MatchResult::Some; } else { return MatchResult::None; } }
/* * Match by subject key identifier */ bool SKID_Match::match(const X509_Certificate& cert) const { return (cert.subject_key_id() == skid); }