This source file includes following definitions.
- GetSubjectPublicKeyInfo
- GetSignedPublicKeyAndChallenge
- GetNewKeyContainerId
- GenKeyAndSignChallenge
#include "net/base/keygen_handler.h"
#include <windows.h>
#include <wincrypt.h>
#pragma comment(lib, "crypt32.lib")
#include <rpc.h>
#pragma comment(lib, "rpcrt4.lib")
#include <list>
#include <string>
#include <vector>
#include "base/base64.h"
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "crypto/capi_util.h"
#include "crypto/scoped_capi_types.h"
namespace net {
bool GetSubjectPublicKeyInfo(HCRYPTPROV prov, std::vector<BYTE>* output) {
BOOL ok;
DWORD size = 0;
ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
szOID_RSA_RSA, 0, NULL, NULL, &size);
DCHECK(ok);
if (!ok)
return false;
output->resize(size);
PCERT_PUBLIC_KEY_INFO public_key_casted =
reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&(*output)[0]);
ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
szOID_RSA_RSA, 0, NULL, public_key_casted,
&size);
DCHECK(ok);
if (!ok)
return false;
output->resize(size);
return true;
}
bool GetSignedPublicKeyAndChallenge(HCRYPTPROV prov,
const std::string& challenge,
std::string* output) {
std::wstring wide_challenge = base::ASCIIToWide(challenge);
std::vector<BYTE> spki;
if (!GetSubjectPublicKeyInfo(prov, &spki))
return false;
CERT_KEYGEN_REQUEST_INFO pkac;
pkac.dwVersion = CERT_KEYGEN_REQUEST_V1;
pkac.SubjectPublicKeyInfo =
*reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&spki[0]);
pkac.pwszChallengeString = const_cast<wchar_t*>(wide_challenge.c_str());
CRYPT_ALGORITHM_IDENTIFIER sig_alg;
memset(&sig_alg, 0, sizeof(sig_alg));
sig_alg.pszObjId = szOID_RSA_MD5RSA;
BOOL ok;
DWORD size = 0;
std::vector<BYTE> signed_pkac;
ok = CryptSignAndEncodeCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
X509_KEYGEN_REQUEST_TO_BE_SIGNED,
&pkac, &sig_alg, NULL,
NULL, &size);
DCHECK(ok);
if (!ok)
return false;
signed_pkac.resize(size);
ok = CryptSignAndEncodeCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING,
X509_KEYGEN_REQUEST_TO_BE_SIGNED,
&pkac, &sig_alg, NULL,
&signed_pkac[0], &size);
DCHECK(ok);
if (!ok)
return false;
output->assign(reinterpret_cast<char*>(&signed_pkac[0]), size);
return true;
}
std::wstring GetNewKeyContainerId() {
RPC_STATUS status = RPC_S_OK;
std::wstring result;
UUID id = { 0 };
status = UuidCreateSequential(&id);
if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY)
return result;
RPC_WSTR rpc_string = NULL;
status = UuidToString(&id, &rpc_string);
if (status != RPC_S_OK)
return result;
result.assign(reinterpret_cast<wchar_t*>(rpc_string));
RpcStringFree(&rpc_string);
return result;
}
struct KeyContainer {
public:
explicit KeyContainer(bool delete_keyset)
: delete_keyset_(delete_keyset) {}
~KeyContainer() {
if (provider_) {
provider_.reset();
if (delete_keyset_ && !key_id_.empty()) {
HCRYPTPROV provider;
crypto::CryptAcquireContextLocked(&provider, key_id_.c_str(), NULL,
PROV_RSA_FULL, CRYPT_SILENT | CRYPT_DELETEKEYSET);
}
}
}
crypto::ScopedHCRYPTPROV provider_;
std::wstring key_id_;
private:
bool delete_keyset_;
};
std::string KeygenHandler::GenKeyAndSignChallenge() {
KeyContainer key_container(!stores_key_);
const int kMaxAttempts = 5;
int attempt;
for (attempt = 0; attempt < kMaxAttempts; ++attempt) {
key_container.key_id_ = GetNewKeyContainerId();
if (key_container.key_id_.empty())
return std::string();
if (crypto::CryptAcquireContextLocked(key_container.provider_.receive(),
key_container.key_id_.c_str(), NULL, PROV_RSA_FULL,
CRYPT_SILENT | CRYPT_NEWKEYSET))
break;
if (GetLastError() != NTE_BAD_KEYSET) {
LOG(ERROR) << "Keygen failed: Couldn't acquire a CryptoAPI provider "
"context: " << GetLastError();
return std::string();
}
}
if (attempt == kMaxAttempts) {
LOG(ERROR) << "Keygen failed: Couldn't acquire a CryptoAPI provider "
"context: Max retries exceeded";
return std::string();
}
{
crypto::ScopedHCRYPTKEY key;
if (!CryptGenKey(key_container.provider_, CALG_RSA_KEYX,
(key_size_in_bits_ << 16) | CRYPT_EXPORTABLE, key.receive())) {
LOG(ERROR) << "Keygen failed: Couldn't generate an RSA key";
return std::string();
}
std::string spkac;
if (!GetSignedPublicKeyAndChallenge(key_container.provider_, challenge_,
&spkac)) {
LOG(ERROR) << "Keygen failed: Couldn't generate the signed public key "
"and challenge";
return std::string();
}
std::string result;
base::Base64Encode(spkac, &result);
VLOG(1) << "Keygen succeeded";
return result;
}
}
}