int SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) { HCERTSTORE cs; X509 *cert = NULL; RSA *rsa = NULL, *pub_rsa; CAPI_DATA *cd = calloc(1, sizeof(*cd)); RSA_METHOD *my_rsa_method = calloc(1, sizeof(*my_rsa_method)); if (cd == NULL || my_rsa_method == NULL) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE); goto err; } /* search CURRENT_USER first, then LOCAL_MACHINE */ cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"MY"); if (cs == NULL) { CRYPTOAPIerr(CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE); goto err; } cd->cert_context = find_certificate_in_store(cert_prop, cs); CertCloseStore(cs, 0); if (!cd->cert_context) { cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG, L"MY"); if (cs == NULL) { CRYPTOAPIerr(CRYPTOAPI_F_CERT_OPEN_SYSTEM_STORE); goto err; } cd->cert_context = find_certificate_in_store(cert_prop, cs); CertCloseStore(cs, 0); if (cd->cert_context == NULL) { CRYPTOAPIerr(CRYPTOAPI_F_CERT_FIND_CERTIFICATE_IN_STORE); goto err; } } /* cert_context->pbCertEncoded is the cert X509 DER encoded. */ cert = d2i_X509(NULL, (const unsigned char **) &cd->cert_context->pbCertEncoded, cd->cert_context->cbCertEncoded); if (cert == NULL) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_ASN1_LIB); goto err; } /* set up stuff to use the private key */ if (!CryptAcquireCertificatePrivateKey(cd->cert_context, CRYPT_ACQUIRE_COMPARE_KEY_FLAG, NULL, &cd->crypt_prov, &cd->key_spec, &cd->free_crypt_prov)) { /* if we don't have a smart card reader here, and we try to access a * smart card certificate, we get: * "Error 1223: The operation was canceled by the user." */ CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_ACQUIRE_CERTIFICATE_PRIVATE_KEY); goto err; } /* here we don't need to do CryptGetUserKey() or anything; all necessary key * info is in cd->cert_context, and then, in cd->crypt_prov. */ my_rsa_method->name = "Microsoft CryptoAPI RSA Method"; my_rsa_method->rsa_pub_enc = rsa_pub_enc; my_rsa_method->rsa_pub_dec = rsa_pub_dec; my_rsa_method->rsa_priv_enc = rsa_priv_enc; my_rsa_method->rsa_priv_dec = rsa_priv_dec; /* my_rsa_method->init = init; */ my_rsa_method->finish = finish; my_rsa_method->flags = RSA_METHOD_FLAG_NO_CHECK; my_rsa_method->app_data = (char *) cd; rsa = RSA_new(); if (rsa == NULL) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE); goto err; } /* cert->cert_info->key->pkey is NULL until we call SSL_CTX_use_certificate(), * so we do it here then... */ if (!SSL_CTX_use_certificate(ssl_ctx, cert)) goto err; /* the public key */ pub_rsa = cert->cert_info->key->pkey->pkey.rsa; /* SSL_CTX_use_certificate() increased the reference count in 'cert', so * we decrease it here with X509_free(), or it will never be cleaned up. */ X509_free(cert); cert = NULL; /* I'm not sure about what we have to fill in in the RSA, trying out stuff... */ /* rsa->n indicates the key size */ rsa->n = BN_dup(pub_rsa->n); rsa->flags |= RSA_FLAG_EXT_PKEY; if (!RSA_set_method(rsa, my_rsa_method)) goto err; if (!SSL_CTX_use_RSAPrivateKey(ssl_ctx, rsa)) goto err; /* SSL_CTX_use_RSAPrivateKey() increased the reference count in 'rsa', so * we decrease it here with RSA_free(), or it will never be cleaned up. */ RSA_free(rsa); return 1; err: if (cert) X509_free(cert); if (rsa) RSA_free(rsa); else { if (my_rsa_method) free(my_rsa_method); if (cd) { if (cd->free_crypt_prov && cd->crypt_prov) CryptReleaseContext(cd->crypt_prov, 0); if (cd->cert_context) CertFreeCertificateContext(cd->cert_context); free(cd); } } return 0; }
/* sign arbitrary data */ static int rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) { CAPI_DATA *cd = (CAPI_DATA *) rsa->meth->app_data; HCRYPTHASH hash; DWORD hash_size, len, i; unsigned char *buf; if (cd == NULL) { RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_PASSED_NULL_PARAMETER); return 0; } if (padding != RSA_PKCS1_PADDING) { /* AFAICS, CryptSignHash() *always* uses PKCS1 padding. */ RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); return 0; } /* Unfortunately, there is no "CryptSign()" function in CryptoAPI, that would * be way to straightforward for M$, I guess... So we have to do it this * tricky way instead, by creating a "Hash", and load the already-made hash * from 'from' into it. */ /* For now, we only support NID_md5_sha1 */ if (flen != SSL_SIG_LENGTH) { RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_INVALID_MESSAGE_LENGTH); return 0; } if (!CryptCreateHash(cd->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash)) { CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_CREATE_HASH); return 0; } len = sizeof(hash_size); if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len, 0)) { CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_GET_HASH_PARAM); CryptDestroyHash(hash); return 0; } if ((int) hash_size != flen) { RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, RSA_R_INVALID_MESSAGE_LENGTH); CryptDestroyHash(hash); return 0; } if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) { CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_SET_HASH_PARAM); CryptDestroyHash(hash); return 0; } len = RSA_size(rsa); buf = malloc(len); if (buf == NULL) { RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE); CryptDestroyHash(hash); return 0; } if (!CryptSignHash(hash, cd->key_spec, NULL, 0, buf, &len)) { CRYPTOAPIerr(CRYPTOAPI_F_CRYPT_SIGN_HASH); CryptDestroyHash(hash); free(buf); return 0; } /* and now, we have to reverse the byte-order in the result from CryptSignHash()... */ for (i = 0; i < len; i++) to[i] = buf[len - i - 1]; free(buf); CryptDestroyHash(hash); return len; }
int CryptoAPI_verify_certificate(X509 *x509) { int ret = -1; int len; unsigned char *buf = NULL; PCCERT_CONTEXT pCertContext = NULL; PCCERT_CHAIN_CONTEXT pChainContext = NULL; CERT_ENHKEY_USAGE EnhkeyUsage; CERT_USAGE_MATCH CertUsage; CERT_CHAIN_PARA ChainPara; /* Convert from internal X509 format to DER */ len = i2d_X509(x509, &buf); if (len < 0) { SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_ASN1_LIB); goto err; } #ifdef __MINGW32_VERSION /* MinGW w32api is incomplete when it comes to CryptoAPI, as per version 3.1 * anyway. This is a hack around that problem. */ if (crypt32dll == NULL) { crypt32dll = LoadLibrary("crypt32"); if (crypt32dll == NULL) { CRYPTOAPIerr(CRYPTOAPI_F_LOAD_LIBRARY); goto err; } } if (CertCreateCertificateContext == NULL) { CertCreateCertificateContext = GetProcAddress(crypt32dll, "CertCreateCertificateContext"); if (CertCreateCertificateContext == NULL) { CRYPTOAPIerr(CRYPTOAPI_F_GET_PROC_ADDRESS); goto err; } } #endif /* Create a certificate context based on the above certificate */ pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, buf, len); if (pCertContext == NULL) { CRYPTOAPIerr(CRYPTOAPI_F_CERT_CREATE_CERT_CONTEXT); goto err; } /* Create an empty issuer list */ EnhkeyUsage.cUsageIdentifier = 0; EnhkeyUsage.rgpszUsageIdentifier = NULL; CertUsage.dwType = USAGE_MATCH_TYPE_AND; CertUsage.Usage = EnhkeyUsage; /* Searching and matching criteria to be used when building the chain */ ChainPara.cbSize = sizeof(CERT_CHAIN_PARA); ChainPara.RequestedUsage = CertUsage; /* Get the certificate chain of our certificate */ if (!CertGetCertificateChain(NULL, pCertContext, NULL, NULL, &ChainPara, 0, NULL, &pChainContext)) { CRYPTOAPIerr(CRYPTOAPI_F_CERT_GET_CERT_CHAIN); goto err; } /* return 1 when the certificate is trusted, 0 when it's not; -1 on error */ ret = (pChainContext->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR); err: if (buf) OPENSSL_free(buf); if (pChainContext) CertFreeCertificateChain(pChainContext); if (pCertContext) CertFreeCertificateContext(pCertContext); return ret; }