I am trying to get the RSA public key info in WinHTTP. So far I've got the certificate info in CERT_CONTEXT
structure. I can get encryption algorithm and others as follows:
PCCERT_CONTEXT cert;
DWORD certLen = sizeof(PCCERT_CONTEXT);
WinHttpQueryOption(hRequest, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &cert, &certLen);
The encryption algorithm is got by
LPSTR pubKeyAlgo = cert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId;
And we might get the public key as follows:
CRYPT_BIT_BLOB pubKey = cert->pCertInfo->SubjectPublicKeyInfo.PublicKey;
BYTE *p = pKey.pbData;
But as per the documentation, this is an encoded form:
PublicKey
BLOB containing an encoded public key.
So how to get the actual RSA public key parameters like modulus and exponent?
Here's an example of extracting the modulus and exponent of a RSA Public Key using Wincrypt. The example certificate is a SSH client cert from RFC 6187. The certificate can be used for SSH's x509v3-rsa2048-sha256
signature encoding method. The OpenSSL conf file is at the end of the answer, but was adapted from How to create a self-signed certificate with OpenSSL.
RSA_PUBLIC_KEY_XX
is used to cast the blob returned from CryptDecodeObject
to the packed RSA structures the API returns. The name *_XX
was used to avoid colliding with a future Microsoft structure name.
#include <iostream>
#include <iomanip>
#include <sstream>
#include <memory>
#include <string>
#include <vector>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wincrypt.h>
#pragma comment(lib, "kernel32")
#pragma comment(lib, "crypt32")
typedef struct _RSA_PUBLIC_KEY_XX
{
#pragma pack(push, 1)
PUBLICKEYSTRUC PublicKeyStruc;
RSAPUBKEY RsaPubKey;
BYTE RsaModulus[ANYSIZE_ARRAY];
#pragma pack(pop)
} RSA_PUBLIC_KEY_XX, *PRSA_PUBLIC_KEY_XX, *const PCRSA_PUBLIC_KEY_XX;
extern std::string pemCertificate;
void PrintErrorAndThrow(const char* fnName, DWORD dwError);
int main(int argc, char* argv[])
{
DWORD dwSize = 0;
std::vector<BYTE> asnCertificate;
std::vector<BYTE> rsaPublicKey;
BOOL bResult = CryptStringToBinary(
&pemCertificate[0],
static_cast<DWORD>(pemCertificate.size()),
CRYPT_STRING_BASE64HEADER,
NULL, &dwSize,
NULL, NULL);
if (bResult == FALSE)
PrintErrorAndThrow("CryptStringToBinary", GetLastError());
asnCertificate.resize(dwSize);
dwSize = static_cast<DWORD>(asnCertificate.size());
bResult = CryptStringToBinary(
&pemCertificate[0],
static_cast<DWORD>(pemCertificate.size()),
CRYPT_STRING_BASE64HEADER,
&asnCertificate[0], &dwSize,
NULL, NULL);
if (bResult == FALSE)
PrintErrorAndThrow("CryptStringToBinary", GetLastError());
PCCERT_CONTEXT pContext = CertCreateCertificateContext(
X509_ASN_ENCODING,
static_cast<PBYTE>(&asnCertificate[0]),
static_cast<DWORD>(asnCertificate.size()));
if (pContext == NULL)
PrintErrorAndThrow("CertCreateCertificateContext", GetLastError());
PCERT_INFO pInfo = pContext->pCertInfo;
PCERT_PUBLIC_KEY_INFO pSubjectPublicKeyInfo = &pInfo->SubjectPublicKeyInfo;
PCRYPT_BIT_BLOB pPublicKey = &pSubjectPublicKeyInfo->PublicKey;
bResult = CryptDecodeObject(
pContext->dwCertEncodingType,
RSA_CSP_PUBLICKEYBLOB,
pPublicKey->pbData,
pPublicKey->cbData,
0, // flags
NULL, &dwSize);
if (bResult == FALSE)
PrintErrorAndThrow("CryptDecodeObject", GetLastError());
rsaPublicKey.resize(dwSize);
dwSize = static_cast<DWORD>(rsaPublicKey.size());
bResult = CryptDecodeObject(
pContext->dwCertEncodingType,
RSA_CSP_PUBLICKEYBLOB,
pPublicKey->pbData,
pPublicKey->cbData,
0, // flags
&rsaPublicKey[0],
&dwSize);
if (bResult == FALSE)
PrintErrorAndThrow("CryptDecodeObject", GetLastError());
PCRSA_PUBLIC_KEY_XX pRsaPublicKey = reinterpret_cast<PCRSA_PUBLIC_KEY_XX>(&rsaPublicKey[0]);
DWORD dwModulusSize = pRsaPublicKey->RsaPubKey.bitlen/8;
DWORD dwExponent = pRsaPublicKey->RsaPubKey.pubexp;
DWORD dwMagic = pRsaPublicKey->RsaPubKey.magic;
if (dwMagic != 0x31415352 /*RSA1*/)
PrintErrorAndThrow("CryptDecodeObject", ERROR_INVALID_DATA);
// The modulus is little-endian. Iterate in reverse for big-endian.
std::ostringstream oss;
for (size_t i=dwModulusSize; i > 0; --i)
{
oss << std::hex << std::setw(2) << std::setfill('0');
oss << (unsigned int)pRsaPublicKey->RsaModulus[i-1];
if (i > 1) { oss << ":"; }
}
std::cout << "Magic: " << "0x" << std::hex << dwMagic << std::endl;
std::cout << "Exponent: " << std::dec << dwExponent << std::endl;
std::cout << "Modulus: " << oss.str() << std::endl;
if (pContext)
CertFreeCertificateContext(pContext);
return 0;
}
void PrintErrorAndThrow(const char* fnName, DWORD dwError)
{
std::ostringstream oss;
oss << fnName << " failed, error " << dwError << " (";
oss << "0x" << std::hex << dwError << ")" << std::endl;
std::cerr << oss.str() << std::endl;
throw std::runtime_error(oss.str());
}
std::string pemCertificate =
"-----BEGIN CERTIFICATE-----\r\n"
"MIIESjCCAzKgAwIBAgIUb2d597NowQ3H/4UE/qjrqzT+EPkwDQYJKoZIhvcNAQEL\r\n"
"BQAwfDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMREwDwYDVQQHDAhOZXcgWW9y\r\n"
"azEVMBMGA1UECgwMRXhhbXBsZSwgTExDMREwDwYDVQQDDAhKb2huIERvZTEjMCEG\r\n"
"CSqGSIb3DQEJARYUam9obi5kb2VAZXhhbXBsZS5jb20wHhcNMTkwOTE3MTU1OTA1\r\n"
"WhcNMjAwOTE2MTU1OTA1WjB8MQswCQYDVQQGEwJVUzELMAkGA1UECAwCTlkxETAP\r\n"
"BgNVBAcMCE5ldyBZb3JrMRUwEwYDVQQKDAxFeGFtcGxlLCBMTEMxETAPBgNVBAMM\r\n"
"CEpvaG4gRG9lMSMwIQYJKoZIhvcNAQkBFhRqb2huLmRvZUBleGFtcGxlLmNvbTCC\r\n"
"ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOd2VZa5G8clxnT90+1mkCGp\r\n"
"ufPAEXKLwX7THhIOf00NYRpumuhvMBUHFNcH6H17NHM/iauLacXsYIJlHWmTTl1t\r\n"
"0r7fxzVTA4NF8Eedpv+RGXRm3eC/e+Nz/MkdInWZ7qkp6AE9juiEop9rmQPtwM/f\r\n"
"4/LwE2fi+XyhFTwMr2zIUuEVlk1fWaabOVtHweLCWxvstWtomxN4egVY4kB634zM\r\n"
"VywMGRz4YrKzFxSaTYWt5wNoMoPRGeEKFwhBfuXmHIO9QQSxqIfBEoxQfBdppzSN\r\n"
"5qe84Gn9nFSepgmJtw6XLxm31SgMBOuKiwFbFni+oZEnRg9ssy7UYmaAaxcqfikC\r\n"
"AwEAAaOBwzCBwDAdBgNVHQ4EFgQU1nhsQ/gKlSadP9uEUNMj5ezkt7gwHwYDVR0j\r\n"
"BBgwFoAU1nhsQ/gKlSadP9uEUNMj5ezkt7gwCQYDVR0TBAIwADALBgNVHQ8EBAMC\r\n"
"B4AwHwYDVR0RBBgwFoEUam9obi5kb2VAZXhhbXBsZS5jb20wMAYJYIZIAYb4QgEN\r\n"
"BCMWIU9wZW5TU0wgR2VuZXJhdGVkIFNTSCBDZXJ0aWZpY2F0ZTATBgNVHSUEDDAK\r\n"
"BggrBgEFBQcDFTANBgkqhkiG9w0BAQsFAAOCAQEAUkCY+WX55RuqzuuRMt5hEzUh\r\n"
"xfCVLddfBxmokmsodc8qgHlsNkRLLZATyKgnFF5FGIroiNF6QcJ5QJXDZZyz2+6p\r\n"
"m0V1jHF51BtoGxBR1I1ERLT0QOJHC53R6+/OPIaADKdPoXHIpPVQhJww6e1X6CU1\r\n"
"IjIWmliqixhvId5WbU5et6ZpFNs2ZFbPGBk4RrKR5SjxwLj7Jm+THzV660xIYsow\r\n"
"/CP5ox7ga7+OFR6q4kFQldc6Ah5bRFKI8fgWHpnhlS9hB29BiMjrC/p7bm0CL2Su\r\n"
"QER4F/HCpBHBDwkweg1h9DrT62DYgqvgvRxR/j8GPL7Hqg2kLuFcHt3SkQeNZg==\r\n"
"-----END CERTIFICATE-----\r\n";
Compile the program using a Visual Studio Developer command prompt.
C:\Users\Test>cl.exe /nologo /W4 /Zi /TP /GR /EHsc cert_test.cxx /link /out:cert_test.exe
cert_test.cxx
cert_test.cxx(37) : warning C4100: 'argv' : unreferenced formal parameter
cert_test.cxx(37) : warning C4100: 'argc' : unreferenced formal parameter
And running the program results in the following.
C:\Users\Test>.\cert_test.exe
Magic: 0x31415352
Exponent: 65537
Modulus: e7:76:55:96:b9:1b:c7:25:c6:74:fd:d3:ed:66:90:21:a9:b9:f3:c0:11:72:8b:c1
:7e:d3:1e:12:0e:7f:4d:0d:61:1a:6e:9a:e8:6f:30:15:07:14:d7:07:e8:7d:7b:34:73:3f:8
9:ab:8b:69:c5:ec:60:82:65:1d:69:93:4e:5d:6d:d2:be:df:c7:35:53:03:83:45:f0:47:9d:
a6:ff:91:19:74:66:dd:e0:bf:7b:e3:73:fc:c9:1d:22:75:99:ee:a9:29:e8:01:3d:8e:e8:84
:a2:9f:6b:99:03:ed:c0:cf:df:e3:f2:f0:13:67:e2:f9:7c:a1:15:3c:0c:af:6c:c8:52:e1:1
5:96:4d:5f:59:a6:9b:39:5b:47:c1:e2:c2:5b:1b:ec:b5:6b:68:9b:13:78:7a:05:58:e2:40:
7a:df:8c:cc:57:2c:0c:19:1c:f8:62:b2:b3:17:14:9a:4d:85:ad:e7:03:68:32:83:d1:19:e1
:0a:17:08:41:7e:e5:e6:1c:83:bd:41:04:b1:a8:87:c1:12:8c:50:7c:17:69:a7:34:8d:e6:a
7:bc:e0:69:fd:9c:54:9e:a6:09:89:b7:0e:97:2f:19:b7:d5:28:0c:04:eb:8a:8b:01:5b:16:
78:be:a1:91:27:46:0f:6c:b3:2e:d4:62:66:80:6b:17:2a:7e:29
Here is a dump of the certificate using OpenSSL's x509
subcommand.
$ cat cert_test.cert.pem | openssl x509 -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
6f:67:79:f7:b3:68:c1:0d:c7:ff:85:04:fe:a8:eb:ab:34:fe:10:f9
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, ST = NY, L = New York, O = "Example, LLC", CN = John Doe, emailAddress = [email protected]
Validity
Not Before: Sep 17 15:59:05 2019 GMT
Not After : Sep 16 15:59:05 2020 GMT
Subject: C = US, ST = NY, L = New York, O = "Example, LLC", CN = John Doe, emailAddress = [email protected]
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:e7:76:55:96:b9:1b:c7:25:c6:74:fd:d3:ed:66:
90:21:a9:b9:f3:c0:11:72:8b:c1:7e:d3:1e:12:0e:
7f:4d:0d:61:1a:6e:9a:e8:6f:30:15:07:14:d7:07:
e8:7d:7b:34:73:3f:89:ab:8b:69:c5:ec:60:82:65:
1d:69:93:4e:5d:6d:d2:be:df:c7:35:53:03:83:45:
f0:47:9d:a6:ff:91:19:74:66:dd:e0:bf:7b:e3:73:
fc:c9:1d:22:75:99:ee:a9:29:e8:01:3d:8e:e8:84:
a2:9f:6b:99:03:ed:c0:cf:df:e3:f2:f0:13:67:e2:
f9:7c:a1:15:3c:0c:af:6c:c8:52:e1:15:96:4d:5f:
59:a6:9b:39:5b:47:c1:e2:c2:5b:1b:ec:b5:6b:68:
9b:13:78:7a:05:58:e2:40:7a:df:8c:cc:57:2c:0c:
19:1c:f8:62:b2:b3:17:14:9a:4d:85:ad:e7:03:68:
32:83:d1:19:e1:0a:17:08:41:7e:e5:e6:1c:83:bd:
41:04:b1:a8:87:c1:12:8c:50:7c:17:69:a7:34:8d:
e6:a7:bc:e0:69:fd:9c:54:9e:a6:09:89:b7:0e:97:
2f:19:b7:d5:28:0c:04:eb:8a:8b:01:5b:16:78:be:
a1:91:27:46:0f:6c:b3:2e:d4:62:66:80:6b:17:2a:
7e:29
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
D6:78:6C:43:F8:0A:95:26:9D:3F:DB:84:50:D3:23:E5:EC:E4:B7:B8
X509v3 Authority Key Identifier:
keyid:D6:78:6C:43:F8:0A:95:26:9D:3F:DB:84:50:D3:23:E5:EC:E4:B7:B8
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature
X509v3 Subject Alternative Name:
email:[email protected]
Netscape Comment:
OpenSSL Generated SSH Certificate
X509v3 Extended Key Usage:
SSH Client
Signature Algorithm: sha256WithRSAEncryption
52:40:98:f9:65:f9:e5:1b:aa:ce:eb:91:32:de:61:13:35:21:
c5:f0:95:2d:d7:5f:07:19:a8:92:6b:28:75:cf:2a:80:79:6c:
36:44:4b:2d:90:13:c8:a8:27:14:5e:45:18:8a:e8:88:d1:7a:
41:c2:79:40:95:c3:65:9c:b3:db:ee:a9:9b:45:75:8c:71:79:
d4:1b:68:1b:10:51:d4:8d:44:44:b4:f4:40:e2:47:0b:9d:d1:
eb:ef:ce:3c:86:80:0c:a7:4f:a1:71:c8:a4:f5:50:84:9c:30:
e9:ed:57:e8:25:35:22:32:16:9a:58:aa:8b:18:6f:21:de:56:
6d:4e:5e:b7:a6:69:14:db:36:64:56:cf:18:19:38:46:b2:91:
e5:28:f1:c0:b8:fb:26:6f:93:1f:35:7a:eb:4c:48:62:ca:30:
fc:23:f9:a3:1e:e0:6b:bf:8e:15:1e:aa:e2:41:50:95:d7:3a:
02:1e:5b:44:52:88:f1:f8:16:1e:99:e1:95:2f:61:07:6f:41:
88:c8:eb:0b:fa:7b:6e:6d:02:2f:64:ae:40:44:78:17:f1:c2:
a4:11:c1:0f:09:30:7a:0d:61:f4:3a:d3:eb:60:d8:82:ab:e0:
bd:1c:51:fe:3f:06:3c:be:c7:aa:0d:a4:2e:e1:5c:1e:dd:d2:
91:07:8d:66
Here is the OpenSSL configuration file from How to create a self-signed certificate with OpenSSL tweaked for a SSH client.
# Create a self signed certificate:
# openssl req -config cert_test.conf -new -x509 -sha256 -newkey rsa:2048 \
# -nodes -keyout cert_test.key.pem -days 365 -out cert_test.cert.pem
#
# Create a signing request (notice the lack of -x509 option):
# openssl req -config cert_test.conf -new -sha256 -newkey rsa:2048 \
# -nodes -keyout cert_test.key.pem -days 365 -out cert_test.cert.pem
[ req ]
default_bits = 2048
default_keyfile = server-key.pem
distinguished_name = subject
req_extensions = req_ext
x509_extensions = x509_ext
string_mask = utf8only
# The Subject DN can be formed using X501 or RFC 4514 (see RFC 4519 for a description).
[ subject ]
countryName = Country Name (2 letter code)
countryName_default = US
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = NY
localityName = Locality Name (eg, city)
localityName_default = New York
organizationName = Organization Name (eg, company)
organizationName_default = Example, LLC
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_default = John Doe
emailAddress = Email Address
emailAddress_default = [email protected]
# Section x509_ext is used when generating a self-signed certificate. I.e., openssl req -x509 ...
[ x509_ext ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer
basicConstraints = CA:FALSE
keyUsage = digitalSignature
subjectAltName = @alternate_names
nsComment = "OpenSSL Generated SSH Certificate"
extendedKeyUsage = secureShellClient
# Section req_ext is used when generating a certificate signing request. I.e., openssl req ...
[ req_ext ]
subjectKeyIdentifier = hash
basicConstraints = CA:FALSE
keyUsage = digitalSignature
subjectAltName = @alternate_names
nsComment = "OpenSSL Generated SSH Certificate"
extendedKeyUsage = secureShellClient
[ alternate_names ]
email = [email protected]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With