std::shared_ptr<const X509_Certificate> Certificate_Store_MacOS::find_cert_by_pubkey_sha1(const std::vector<uint8_t>& key_hash) const { if(key_hash.size() != 20) { throw Invalid_Argument("Certificate_Store_MacOS::find_cert_by_pubkey_sha1 invalid hash"); } scoped_CFType<CFDataRef> key_hash_cfdata(createCFDataView(key_hash)); check_notnull(key_hash_cfdata, "create key hash search object"); scoped_CFType<CFArrayRef> result(m_impl->search( { {kSecAttrPublicKeyHash, key_hash_cfdata.get()}, })); if(!result) { return nullptr; // no certificate found } const auto count = CFArrayGetCount(result.get()); BOTAN_ASSERT(count > 0, "certificate result list contains an object"); // `count` might be greater than 1, but we'll just select the first match auto cfCert = to_SecCertificateRef(CFArrayGetValueAtIndex(result.get(), 0)); return readCertificate(cfCert); }
std::vector<X509_DN> Certificate_Store_MacOS::all_subjects() const { scoped_CFType<CFArrayRef> result(m_impl->search()); if(!result) { return {}; // not a single certificate found in the keychain } const auto count = CFArrayGetCount(result.get()); BOTAN_ASSERT(count > 0, "subject result list contains data"); std::vector<X509_DN> output; output.reserve(count); for(unsigned int i = 0; i < count; ++i) { // Note: Apple's API provides SecCertificateCopyNormalizedSubjectSequence // which would have saved us from reading a Botan::X509_Certificate, // however, this function applies the same DN "normalization" as // stated above. auto cfCert = to_SecCertificateRef(CFArrayGetValueAtIndex(result.get(), i)); auto cert = readCertificate(cfCert); output.emplace_back(cert->subject_dn()); } return output; }
std::vector<std::shared_ptr<const X509_Certificate>> Certificate_Store_MacOS::find_all_certs( const X509_DN& subject_dn, const std::vector<uint8_t>& key_id) const { std::vector<uint8_t> dn_data; DER_Encoder encoder(dn_data); normalize(subject_dn).encode_into(encoder); scoped_CFType<CFDataRef> dn_cfdata(createCFDataView(dn_data)); check_notnull(dn_cfdata, "create DN search object"); Certificate_Store_MacOS_Impl::Query query_params( { {kSecAttrSubject, dn_cfdata.get()} }); scoped_CFType<CFDataRef> keyid_cfdata(createCFDataView(key_id)); check_notnull(keyid_cfdata, "create key ID search object"); if(!key_id.empty()) { query_params.push_back({kSecAttrSubjectKeyID, keyid_cfdata.get()}); } scoped_CFType<CFArrayRef> result(m_impl->search(std::move(query_params))); if(!result) { return {}; // no certificates found } const auto count = CFArrayGetCount(result.get()); BOTAN_ASSERT(count > 0, "certificate result list contains data"); std::vector<std::shared_ptr<const X509_Certificate>> output; output.reserve(count); for(unsigned int i = 0; i < count; ++i) { auto cfCert = to_SecCertificateRef(CFArrayGetValueAtIndex(result.get(), i)); output.emplace_back(readCertificate(cfCert)); } return output; }
bool isValid() { static bool hasValidated = false; static bool validates = false; if ( hasValidated ) return validates; Environment *env = Environment::Instance(); if ( env == NULL ) { cerr << "FATAL ERROR: No environment available" << endl; return false; } hasValidated = true; string licenseDir = env->configDir() + "/key"; string licenseFile = licenseDir + "/License"; string licenseKeyfile = licenseDir + "/License.key"; string licenseSignature = licenseDir + "/License.signed"; boost::filesystem::path path = SC_FS_PATH(env->shareDir()) / SC_FS_PATH("licenses") / SC_FS_PATH("seiscomp3.crt"); if ( !Seiscomp::Util::fileExists(path.string().c_str()) ) { path = SC_FS_PATH(env->configDir()) / SC_FS_PATH("licenses") / SC_FS_PATH("seiscomp3.crt"); if ( !Seiscomp::Util::fileExists(path.string()) ) { path = SC_FS_PATH(env->configDir()) / SC_FS_PATH("key") / SC_FS_PATH("License.crt"); } } X509 *x509 = readCertificate(path.string()); if ( x509 ) { ASN1_TIME* notAfter = X509_get_notAfter(x509), * notBefore = X509_get_notBefore(x509); time_t ptime = time(NULL); int res = X509_cmp_time(notBefore, &ptime); if ( res == 0 || res > 0 ) { X509_free(x509); cerr << "FATAL ERROR: License has expired: " << path.string() << endl; return false; } res = X509_cmp_time(notAfter, &ptime); if ( res == 0 || res < 0 ) { X509_free(x509); cerr << "FATAL ERROR: License has expired: " << path.string() << endl; return false; } OpenSSL_add_all_algorithms(); OpenSSL_add_all_ciphers(); OpenSSL_add_all_digests(); EVP_PKEY* pkey=X509_get_pubkey(x509); if ( !pkey ) { X509_free(x509); EVP_cleanup(); cerr << "FATAL ERROR: License verification has failed: " << path.string() << endl; return false; } res = X509_verify(x509, pkey); if ( res != 1 ) { X509_free(x509); EVP_PKEY_free(pkey); EVP_cleanup(); cerr << "FATAL ERROR: License verification has failed: " << path.string() << endl; return false; } char *buf; if ( readNID(&buf, x509, NID_netscape_comment) ) { licenseText = buf; delete buf; } EVP_PKEY_free(pkey); X509_free(x509); EVP_cleanup(); return true; } // Read license file MD5_CTX ctx; MD5_Init(&ctx); unsigned char digest[MD5_DIGEST_LENGTH]; char data[64]; size_t len; ifstream f; try { f.open(licenseFile.c_str(), ios_base::in); } catch ( std::exception &e ) { cerr << "FATAL ERROR: Failed to open license file: " << licenseFile << endl; validates = false; return false; } if ( !f.good() ) { cerr << "FATAL ERROR: Failed to open license file: " << licenseFile << endl; validates = false; return false; } licenseText.clear(); try { while ( (len = f.rdbuf()->sgetn(data, sizeof(data))) > 0 ) { licenseText.append(data, len); MD5_Update(&ctx, data, len); } } catch ( ... ) { cerr << "FATAL ERROR: Invalid license file: " << licenseFile << endl; f.close(); validates = false; return false; } f.close(); MD5_Final(digest, &ctx); int strength = 0; RSA *publicKey = readKey(licenseKeyfile.c_str(), PUBLIC, 1024, 8192, strength); if ( publicKey == NULL ) { cerr << "FATAL ERROR: Invalid key file: " << licenseKeyfile << endl; validates = false; return false; } BIO *bio_file = NULL, *b64_file; b64_file = BIO_new(BIO_f_base64()); bio_file = BIO_new_file(licenseSignature.c_str(), "r"); bio_file = BIO_push(b64_file, bio_file); int sigLength = strength / 8; unsigned char *signature = new unsigned char[sigLength]; sigLength = BIO_read(bio_file, signature, sigLength); BIO_free_all(bio_file); if ( sigLength <= 0 ) { delete [] signature; cerr << "FATAL ERROR: Empty signature" << endl; validates = false; return false; } validates = RSA_verify(NID_md5, digest, MD5_DIGEST_LENGTH, signature, sigLength, publicKey); delete [] signature; /* if ( validates ) { cerr << "-----BEGIN LICENSE-----" << endl; cerr << licenseText << endl; cerr << "-----END LICENSE-----" << endl << endl; } */ return validates; }