SChannel/WinSSL: Fix public key pinning

This commit is contained in:
Andrey 2017-10-11 12:38:00 +03:00 committed by moparisthebest
parent 87e505385c
commit 8a878879e9
1 changed files with 23 additions and 130 deletions

View File

@ -129,24 +129,8 @@
* #define failf(x, y, ...) printf(y, __VA_ARGS__) * #define failf(x, y, ...) printf(y, __VA_ARGS__)
*/ */
/* APIs return rsa keys missing the spki header (not DER) */
static const size_t spkiHeaderLength = 24;
static const unsigned char rsa4096SpkiHeader[] = {
0x30, 0x82, 0x02, 0x22, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
0x00, 0x03, 0x82, 0x02, 0x0f, 0x00};
static const unsigned char rsa2048SpkiHeader[] = {
0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
0x00, 0x03, 0x82, 0x01, 0x0f, 0x00};
/* define these here if they aren't already */
#ifndef CALG_SHA_256 #ifndef CALG_SHA_256
#define CALG_SHA_256 0x0000800c # define CALG_SHA_256 0x0000800c
#endif #endif
/* Structs to store Schannel handles */ /* Structs to store Schannel handles */
@ -1710,12 +1694,10 @@ static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex,
struct Curl_easy *data = conn->data; struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex];
CERT_CONTEXT *pCertContextServer = NULL; CERT_CONTEXT *pCertContextServer = NULL;
const CERT_CHAIN_CONTEXT *pChainContext = NULL; const char *x509_der;
HCRYPTPROV hCryptProv = 0; int x509_der_len;
HCRYPTKEY hCertPubKey = NULL; curl_X509certificate x509_parsed;
size_t bloblen, pubkeylen, realpubkeylen; curl_asn1Element *pubkey;
unsigned char *blob = NULL, *pubkey = NULL, *realpubkey = NULL,
*spkiHeader = NULL;
/* Result is returned to caller */ /* Result is returned to caller */
CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
@ -1725,7 +1707,7 @@ static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex,
return CURLE_OK; return CURLE_OK;
do { do {
status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle, status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
SECPKG_ATTR_REMOTE_CERT_CONTEXT, SECPKG_ATTR_REMOTE_CERT_CONTEXT,
&pCertContextServer); &pCertContextServer);
@ -1736,123 +1718,34 @@ static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex,
} }
if(!CryptAcquireContext(&hCryptProv, NULL, NULL, if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) &&
/*PROV_RSA_FULL,*/ (pCertContextServer->cbCertEncoded > 0)))
PROV_RSA_AES, break;
CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
failf(data, "schannel: Failed to acquire crypto context: %s",
Curl_sspi_strerror(conn, GetLastError()));
break; /* failed */
}
/* Get the public key information for the certificate. x509_der = pCertContextServer->pbCertEncoded;
This works for RSA keys, but secp256r1 and secp384r1 gives x509_der_len = pCertContextServer->cbCertEncoded;
this error: memset(&x509_parsed, 0, sizeof x509_parsed);
Unknown error (0x8009310B) - ASN1 bad tag value met. if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
*/ break;
if(!CryptImportPublicKeyInfo(
hCryptProv,
X509_ASN_ENCODING,
&pCertContextServer->pCertInfo->SubjectPublicKeyInfo,
&hCertPubKey)) {
failf(data, "schannel: Failed to import public key info: %s",
Curl_sspi_strerror(conn, GetLastError()));
break; /* failed */
}
/* export to blob */ pubkey = &x509_parsed.subjectPublicKeyInfo;
if(!CryptExportKey(hCertPubKey, 0, PUBLICKEYBLOB, 0, 0, &bloblen)) { if(!pubkey->header || pubkey->end <= pubkey->header) {
failf(data, "schannel: Failed to get public key blob length: %s", failf(data, "SSL: failed retrieving public key from server certificate");
Curl_sspi_strerror(conn, GetLastError()));
break; /* failed */
}
blob = malloc(bloblen);
if(!blob)
break; /* failed */
if(!CryptExportKey(hCertPubKey, 0, PUBLICKEYBLOB, 0, blob, &bloblen)) {
failf(data, "schannel: Failed to export public key to blob: %s",
Curl_sspi_strerror(conn, GetLastError()));
break; /* failed */
}
/* export to der
not sure what second arg should be,
X509_PUBLIC_KEY_INFO -- fails
RSA_CSP_PUBLICKEYBLOB -- not quite der, missing spki header bytes
CNG_RSA_PUBLIC_KEY_BLOB -- fails
PKCS_CONTENT_INFO -- fails
X509_SEQUENCE_OF_ANY - fails
CRYPT_STRING_BASE64HEADER - fails
*/
if(!CryptEncodeObjectEx(X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, blob, 0,
NULL, NULL, &pubkeylen)) {
failf(data, "schannel: Failed to get public key der length: %s",
Curl_sspi_strerror(conn, GetLastError()));
break; break;
} }
pubkey = malloc(pubkeylen); result = Curl_pin_peer_pubkey(data,
if(!pubkey) pinnedpubkey,
break; (const unsigned char *)pubkey->header,
(size_t)(pubkey->end - pubkey->header));
if(!CryptEncodeObjectEx(X509_ASN_ENCODING, RSA_CSP_PUBLICKEYBLOB, blob, 0, if(result) {
NULL, pubkey, &pubkeylen)) { failf(data, "SSL: public key does not match pinned public key!");
failf(data, "schannel: Failed to export public key to der: %s",
Curl_sspi_strerror(conn, GetLastError()));
break;
} }
FILE *fp;
fp=fopen("windows.key", "wb");
fwrite(pubkey, sizeof(pubkey[0]), pubkeylen, fp);
fclose(fp);
switch(pubkeylen) {
case 526:
/* 4096 bit RSA pubkeylen == 526 */
spkiHeader = rsa4096SpkiHeader;
break;
case 270:
/* 2048 bit RSA pubkeylen == 270 */
spkiHeader = rsa2048SpkiHeader;
break;
default:
infof(data, "SSL: unhandled public key length: %d\n", pubkeylen);
continue; /* break from loop */
}
realpubkeylen = pubkeylen + spkiHeaderLength;
realpubkey = malloc(realpubkeylen);
if(!realpubkey)
break;
memcpy(realpubkey, spkiHeader, spkiHeaderLength);
memcpy(realpubkey + spkiHeaderLength, pubkey, pubkeylen);
fp=fopen("windows.real.key", "wb");
fwrite(realpubkey, sizeof(realpubkey[0]), realpubkeylen, fp);
fclose(fp);
/* The one good exit point */
result = Curl_pin_peer_pubkey(data, pinnedpubkey, realpubkey,
realpubkeylen);
} while(0); } while(0);
if(pChainContext)
CertFreeCertificateChain(pChainContext);
if(pCertContextServer) if(pCertContextServer)
CertFreeCertificateContext(pCertContextServer); CertFreeCertificateContext(pCertContextServer);
if(hCryptProv != 0)
CryptReleaseContext(hCryptProv, 0UL);
Curl_safefree(blob);
Curl_safefree(pubkey);
Curl_safefree(realpubkey);
return result; return result;
} }