static VALUE ossl_x509stctx_get_chain(VALUE self) { X509_STORE_CTX *ctx; STACK_OF(X509) *chain; X509 *x509; int i, num; VALUE ary; GetX509StCtx(self, ctx); if((chain = X509_STORE_CTX_get_chain(ctx)) == NULL){ return Qnil; } if((num = sk_X509_num(chain)) < 0){ OSSL_Debug("certs in chain < 0???"); return rb_ary_new(); } ary = rb_ary_new2(num); for(i = 0; i < num; i++) { x509 = sk_X509_value(chain, i); rb_ary_push(ary, ossl_x509_new(x509)); } return ary; }
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { int i, j; /* * Preverify checks the platform's certificate store; don't * allow any chain that doesn't already validate according to * that. */ if (!preverify_ok) return 0; /* check each certificate in the chain against our built-in pinlist. */ STACK_OF(X509) *chain = X509_STORE_CTX_get_chain(ctx); if (!chain) die("No certificate chain available"); bool found = false; for (i=0; i < sk_X509_num(chain); i++) { _cleanup_free_ char *spki_hash = NULL; spki_hash = hash_subject_pubkey_info(sk_X509_value(chain, i)); if (!spki_hash) continue; for (j=0; j < (int) ARRAY_SIZE(PK_PINS); j++) { if (strcmp(PK_PINS[j], spki_hash) == 0) { found = true; break; } } } return found; }
json::value get_cert_chain_information(boost::asio::ssl::verify_context &verifyCtx) { X509_STORE_CTX *storeContext = verifyCtx.native_handle(); STACK_OF(X509) *certStack = X509_STORE_CTX_get_chain(storeContext); const int numCerts = sk_X509_num(certStack); if (numCerts < 0) { return {}; } json::value certChainInformation; for (int index = 0; index < numCerts; ++index) { X509 *cert = sk_X509_value(certStack, index); json::value certInformation; certInformation[U("Issuer")] = json::value::string(get_issuer_from_cert(cert)); certInformation[U("Subject")] = json::value::string(get_subject_from_cert(cert)); certInformation[U("FingerPrint")] = json::value::string(get_fingerprint_from_cert(cert)); utility::stringstream_t countInfo; countInfo << "Certificate: " << index; certChainInformation[countInfo.str()] = certInformation; } return certChainInformation; }
bool ssl_options_t::has_fingerprint(boost::asio::ssl::verify_context &ctx) const { // can we check the certificate against a list of fingerprints? if (!fingerprints_.empty()) { X509_STORE_CTX *sctx = ctx.native_handle(); if (!sctx) { MERROR("Error getting verify_context handle"); return false; } X509* cert = nullptr; const STACK_OF(X509)* chain = X509_STORE_CTX_get_chain(sctx); if (!chain || sk_X509_num(chain) < 1 || !(cert = sk_X509_value(chain, 0))) { MERROR("No certificate found in verify_context"); return false; } // buffer for the certificate digest and the size of the result std::vector<uint8_t> digest(EVP_MAX_MD_SIZE); unsigned int size{ 0 }; // create the digest from the certificate if (!X509_digest(cert, EVP_sha256(), digest.data(), &size)) { MERROR("Failed to create certificate fingerprint"); return false; } // strip unnecessary bytes from the digest digest.resize(size); return std::binary_search(fingerprints_.begin(), fingerprints_.end(), digest); }
/* find the issuer certificate without lookups */ NOEXPORT X509 *get_current_issuer(X509_STORE_CTX *callback_ctx) { STACK_OF(X509) *chain; int depth; chain=X509_STORE_CTX_get_chain(callback_ctx); depth=X509_STORE_CTX_get_error_depth(callback_ctx); if(depth<sk_X509_num(chain)-1) /* not the root CA cert */ ++depth; /* index of the issuer cert */ return sk_X509_value(chain, depth); }
bool verify_cert_chain_platform_specific(boost::asio::ssl::verify_context &verifyCtx, const std::string &hostName) { X509_STORE_CTX *storeContext = verifyCtx.native_handle(); int currentDepth = X509_STORE_CTX_get_error_depth(storeContext); if (currentDepth != 0) { return true; } STACK_OF(X509) *certStack = X509_STORE_CTX_get_chain(storeContext); const int numCerts = sk_X509_num(certStack); if (numCerts < 0) { return false; } std::vector<std::string> certChain; certChain.reserve(numCerts); for (int i = 0; i < numCerts; ++i) { X509 *cert = sk_X509_value(certStack, i); // Encode into DER format into raw memory. int len = i2d_X509(cert, nullptr); if (len < 0) { return false; } std::string certData; certData.resize(len); unsigned char * buffer = reinterpret_cast<unsigned char *>(&certData[0]); len = i2d_X509(cert, &buffer); if (len < 0) { return false; } certChain.push_back(std::move(certData)); } auto verify_result = verify_X509_cert_chain(certChain, hostName); // The Windows Crypto APIs don't do host name checks, use Boost's implementation. #if defined(_WIN32) if (verify_result) { boost::asio::ssl::rfc2818_verification rfc2818(hostName); verify_result = rfc2818(verify_result, verifyCtx); } #endif return verify_result; }
std::vector<std::string> get_cert_chain_public_keys(boost::asio::ssl::verify_context &verifyCtx) { std::vector<std::string> certChain; X509_STORE_CTX *storeContext = verifyCtx.native_handle(); STACK_OF(X509) *certStack = X509_STORE_CTX_get_chain(storeContext); const int numCerts = sk_X509_num(certStack); if (numCerts < 0) { return certChain; } certChain.reserve(numCerts); for (int i = 0; i < numCerts; ++i) { X509 *cert = sk_X509_value(certStack, i); certChain.push_back(get_public_key_from_cert(cert)); } return certChain; }
/*============================================================================ * OpcUa_P_OpenSSL_PKI_ValidateCertificate *===========================================================================*/ OpcUa_StatusCode OpcUa_P_OpenSSL_PKI_ValidateCertificate( OpcUa_PKIProvider* a_pProvider, OpcUa_ByteString* a_pCertificate, OpcUa_Void* a_pCertificateStore, OpcUa_Int* a_pValidationCode /* Validation return codes from OpenSSL */ ) { OpcUa_P_OpenSSL_CertificateStore_Config* pCertificateStoreCfg; const unsigned char* p; X509* pX509Certificate = OpcUa_Null; STACK_OF(X509)* pX509Chain = OpcUa_Null; X509_STORE_CTX* verify_ctx = OpcUa_Null; /* holds data used during verification process */ char CertFile[MAX_PATH]; struct dirent **dirlist = NULL; int numCertificates = 0, i; OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "PKI_ValidateCertificate"); OpcUa_ReturnErrorIfArgumentNull(a_pProvider); OpcUa_ReturnErrorIfArgumentNull(a_pProvider->Handle); OpcUa_ReturnErrorIfArgumentNull(a_pCertificate); OpcUa_ReturnErrorIfArgumentNull(a_pCertificateStore); OpcUa_ReturnErrorIfArgumentNull(a_pValidationCode); pCertificateStoreCfg = (OpcUa_P_OpenSSL_CertificateStore_Config*)a_pProvider->Handle; /* convert DER encoded bytestring certificate to openssl X509 certificate */ p = a_pCertificate->Data; if(!(pX509Certificate = d2i_X509((X509**)OpcUa_Null, &p, a_pCertificate->Length))) { OpcUa_GotoErrorWithStatus(OpcUa_Bad); } while(p < a_pCertificate->Data + a_pCertificate->Length) { X509* pX509AddCertificate; if(!(pX509AddCertificate = d2i_X509((X509**)OpcUa_Null, &p, a_pCertificate->Data + a_pCertificate->Length - p))) { OpcUa_GotoErrorWithStatus(OpcUa_Bad); } if(pX509Chain == NULL) { pX509Chain = sk_X509_new_null(); OpcUa_GotoErrorIfAllocFailed(pX509Chain); } if(!sk_X509_push(pX509Chain, pX509AddCertificate)) { X509_free(pX509AddCertificate); OpcUa_GotoErrorWithStatus(OpcUa_Bad); } } /* create verification context and initialize it */ if(!(verify_ctx = X509_STORE_CTX_new())) { OpcUa_GotoErrorWithStatus(OpcUa_Bad); } #if (OPENSSL_VERSION_NUMBER > 0x00907000L) if(X509_STORE_CTX_init(verify_ctx, (X509_STORE*)a_pCertificateStore, pX509Certificate, pX509Chain) != 1) { OpcUa_GotoErrorWithStatus(OpcUa_Bad); } #else X509_STORE_CTX_init(verify_ctx, (X509_STORE*)a_pCertificateStore, pX509Certificate, pX509Chain); #endif if(X509_STORE_CTX_set_app_data(verify_ctx, pCertificateStoreCfg) != 1) { OpcUa_GotoErrorWithStatus(OpcUa_Bad); } if((pCertificateStoreCfg->Flags & OPCUA_P_PKI_OPENSSL_CHECK_REVOCATION_ALL) == OPCUA_P_PKI_OPENSSL_CHECK_REVOCATION_ALL_EXCEPT_SELF_SIGNED && !verify_ctx->check_issued(verify_ctx, pX509Certificate, pX509Certificate)) { /* set the flags of the store so that CRLs are consulted */ X509_STORE_CTX_set_flags(verify_ctx, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); } /* verify the certificate */ *a_pValidationCode = X509_V_OK; if(X509_verify_cert(verify_ctx) <= 0) { *a_pValidationCode = verify_ctx->error; switch(verify_ctx->error) { case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_CRL_NOT_YET_VALID: case X509_V_ERR_CRL_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: { uStatus = OpcUa_BadCertificateTimeInvalid; break; } case X509_V_ERR_CERT_REVOKED: { uStatus = OpcUa_BadCertificateRevoked; break; } case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: { uStatus = OpcUa_BadCertificateUntrusted; break; } case X509_V_ERR_CERT_SIGNATURE_FAILURE: { uStatus = OpcUa_BadSecurityChecksFailed; break; } default: { uStatus = OpcUa_BadCertificateInvalid; } } OpcUa_GotoErrorIfBad(uStatus); } if(pCertificateStoreCfg->Flags & OPCUA_P_PKI_OPENSSL_REQUIRE_CHAIN_CERTIFICATE_IN_TRUST_LIST) { FILE* pCertificateFile; X509* pTrustCert; STACK_OF(X509)* chain; int trusted, n; chain = X509_STORE_CTX_get_chain(verify_ctx); trusted = 0; if(pCertificateStoreCfg->CertificateTrustListLocation == NULL || pCertificateStoreCfg->CertificateTrustListLocation[0] == '\0') { uStatus = OpcUa_Bad; OpcUa_GotoErrorIfBad(uStatus); } numCertificates = scandir(pCertificateStoreCfg->CertificateTrustListLocation, &dirlist, certificate_filter_der, alphasort); for (i=0; i<numCertificates; i++) { uStatus = OpcUa_P_OpenSSL_BuildFullPath(pCertificateStoreCfg->CertificateTrustListLocation, dirlist[i]->d_name, MAX_PATH, CertFile); OpcUa_GotoErrorIfBad(uStatus); /* read DER certificates */ pCertificateFile = fopen(CertFile, "r"); if(pCertificateFile == OpcUa_Null) { continue; /* ignore access errors */ } pTrustCert = d2i_X509_fp(pCertificateFile, (X509**)OpcUa_Null); fclose(pCertificateFile); if(pTrustCert == OpcUa_Null) { continue; /* ignore parse errors */ } for(n = 0; n < sk_X509_num(chain); n++) { if (X509_cmp(sk_X509_value(chain, n), pTrustCert) == 0) break; } X509_free(pTrustCert); if(n < sk_X509_num(chain)) { trusted = 1; break; } } for (i=0; i<numCertificates; i++) { free(dirlist[i]); } free(dirlist); dirlist = NULL; if(!trusted) { uStatus = OpcUa_BadCertificateUntrusted; OpcUa_GotoErrorIfBad(uStatus); } } X509_STORE_CTX_free(verify_ctx); X509_free(pX509Certificate); if(pX509Chain != OpcUa_Null) { sk_X509_pop_free(pX509Chain, X509_free); } OpcUa_ReturnStatusCode; OpcUa_BeginErrorHandling; if(dirlist != NULL) { for (i=0; i<numCertificates; i++) { free(dirlist[i]); } free(dirlist); } if(verify_ctx != OpcUa_Null) { X509_STORE_CTX_free(verify_ctx); } if(pX509Certificate != OpcUa_Null) { X509_free(pX509Certificate); } if(pX509Chain != OpcUa_Null) { sk_X509_pop_free(pX509Chain, X509_free); } OpcUa_FinishErrorHandling; }
STACK_OF(X509) * X509_STORE_CTX_get0_chain(X509_STORE_CTX* ctx) { return X509_STORE_CTX_get_chain(ctx); }
// take certificate hashes, check their validity and output json that // will indicate which certificate were used for verification, whatever // the chain was trusted and if all certificates needed for verification // (with the exception of root CA) were present in hashes int process_chain(const char **cert_hashes) { int ret; int rc; // return code from function char *f_name; X509 *cert; X509 *x509; X509_STORE *store; X509_STORE_CTX *csc; STACK_OF(X509) *ustack; STACK_OF(X509) *vstack; // load certificates to temp structures // first the end entity cert // (EE cert needs to be passed separately to OpenSSL verification context) f_name = hash_to_filename(cert_hashes[0]); if (f_name == NULL) return 1; cert = load_cert(f_name); free(f_name); if (cert == NULL) { printf("can't load certificate!\n"); return 1; } // then the intermediate certificates ustack = sk_X509_new_null(); for (int i=1; cert_hashes[i]!=NULL; i++) { //printf(".\n"); f_name = hash_to_filename(cert_hashes[i]); if (f_name == NULL) { // file not found continue; } x509 = load_cert(f_name); if (x509 == NULL) { // loading cert failed continue; } sk_X509_push(ustack, x509); free(f_name); } // first try with just trusted certificates store = SSL_CTX_get_cert_store(trusted_only); if (store == NULL) { fprintf(stderr, "store init failed\n"); return 1; } X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST); csc = X509_STORE_CTX_new(); ret = X509_STORE_CTX_init(csc, store, cert, ustack); if (ret != 1) { return 1; } ret = X509_verify_cert(csc); if (ret != 1) { // printf("%s\n", X509_verify_cert_error_string(csc->error)); } else { // chain is complete, output certificate hashes printf("{\"chain\":\"complete\",\"certificates\":["); vstack = X509_STORE_CTX_get_chain(csc); for(int i=0; i<sk_X509_num(vstack); i++) { X509 *c = sk_X509_value(vstack, i); const EVP_MD *digest; unsigned char md[EVP_MAX_MD_SIZE]; int n; digest = EVP_get_digestbyname("sha256"); X509_digest(c, digest, md, &n); printf("\""); for(int i=0; i<n; i++) { printf("%02x", md[i]); } printf("\""); if (i+1 < sk_X509_num(vstack)) { printf(","); } } printf("]}"); X509_STORE_CTX_free(csc); sk_X509_pop_free(ustack, X509_free); X509_free(cert); return 0; } X509_STORE_CTX_free(csc); // validation failed with just the trust anchors, retry with all // known intermediate certificates store = SSL_CTX_get_cert_store(all_CAs); if (store == NULL) { fprintf(stderr, "store init failed\n"); return 1; } X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST); csc = X509_STORE_CTX_new(); ret = X509_STORE_CTX_init(csc, store, cert, ustack); if (ret != 1) { return 1; } ret = X509_verify_cert(csc); if (ret != 1) { // certificate untrusted printf("{\"chain\":\"untrusted\"}"); } else { // chain successfully verified using all certificates, // print all the certs used to verify it printf("{\"chain\":\"incomplete\",\"certificates\":["); vstack = X509_STORE_CTX_get_chain(csc); for(int i=0; i<sk_X509_num(vstack); i++) { X509 *c = sk_X509_value(vstack, i); const EVP_MD *digest; unsigned char md[EVP_MAX_MD_SIZE]; int n; digest = EVP_get_digestbyname("sha256"); X509_digest(c, digest, md, &n); printf("\""); for(int i=0; i<n; i++) { printf("%02x", md[i]); } printf("\""); if (i+1 < sk_X509_num(vstack)) { printf(","); } } printf("]}"); } X509_STORE_CTX_free(csc); sk_X509_pop_free(ustack, X509_free); X509_free(cert); return 0; }