1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be 3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file. 4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/base/keygen_handler.h" 6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <windows.h> 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <wincrypt.h> 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#pragma comment(lib, "crypt32.lib") 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <rpc.h> 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#pragma comment(lib, "rpcrt4.lib") 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <list> 14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <string> 15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <vector> 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/base64.h" 18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/basictypes.h" 19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/logging.h" 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_piece.h" 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h" 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h" 23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "crypto/capi_util.h" 24ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "crypto/scoped_capi_types.h" 25ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 26c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 27c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottnamespace net { 28c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Assigns the contents of a CERT_PUBLIC_KEY_INFO structure for the signing 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// key in |prov| to |output|. Returns true if encoding was successful. 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool GetSubjectPublicKeyInfo(HCRYPTPROV prov, std::vector<BYTE>* output) { 32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BOOL ok; 33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DWORD size = 0; 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // From the private key stored in HCRYPTPROV, obtain the public key, stored 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // as a CERT_PUBLIC_KEY_INFO structure. Currently, only RSA public keys are 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // supported. 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch szOID_RSA_RSA, 0, NULL, NULL, &size); 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(ok); 41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!ok) 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch output->resize(size); 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PCERT_PUBLIC_KEY_INFO public_key_casted = 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&(*output)[0]); 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ok = CryptExportPublicKeyInfoEx(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch szOID_RSA_RSA, 0, NULL, public_key_casted, 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &size); 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(ok); 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!ok) 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch output->resize(size); 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Generates a DER encoded SignedPublicKeyAndChallenge structure from the 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// signing key of |prov| and the specified ASCII |challenge| string and 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// appends it to |output|. 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// True if the encoding was successfully generated. 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool GetSignedPublicKeyAndChallenge(HCRYPTPROV prov, 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::string& challenge, 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string* output) { 67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring wide_challenge = ASCIIToWide(challenge); 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::vector<BYTE> spki; 69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!GetSubjectPublicKeyInfo(prov, &spki)) 71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // PublicKeyAndChallenge ::= SEQUENCE { 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // spki SubjectPublicKeyInfo, 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // challenge IA5STRING 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // } 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CERT_KEYGEN_REQUEST_INFO pkac; 78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch pkac.dwVersion = CERT_KEYGEN_REQUEST_V1; 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch pkac.SubjectPublicKeyInfo = 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *reinterpret_cast<PCERT_PUBLIC_KEY_INFO>(&spki[0]); 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch pkac.pwszChallengeString = const_cast<wchar_t*>(wide_challenge.c_str()); 82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CRYPT_ALGORITHM_IDENTIFIER sig_alg; 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch memset(&sig_alg, 0, sizeof(sig_alg)); 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch sig_alg.pszObjId = szOID_RSA_MD5RSA; 86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BOOL ok; 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DWORD size = 0; 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::vector<BYTE> signed_pkac; 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ok = CryptSignAndEncodeCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch X509_KEYGEN_REQUEST_TO_BE_SIGNED, 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &pkac, &sig_alg, NULL, 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NULL, &size); 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(ok); 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!ok) 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch signed_pkac.resize(size); 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ok = CryptSignAndEncodeCertificate(prov, AT_KEYEXCHANGE, X509_ASN_ENCODING, 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch X509_KEYGEN_REQUEST_TO_BE_SIGNED, 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &pkac, &sig_alg, NULL, 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &signed_pkac[0], &size); 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(ok); 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!ok) 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch output->assign(reinterpret_cast<char*>(&signed_pkac[0]), size); 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Generates a unique name for the container which will store the key that is 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// generated. The traditional Windows approach is to use a GUID here. 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstd::wstring GetNewKeyContainerId() { 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch RPC_STATUS status = RPC_S_OK; 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::wstring result; 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UUID id = { 0 }; 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch status = UuidCreateSequential(&id); 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return result; 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch RPC_WSTR rpc_string = NULL; 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch status = UuidToString(&id, &rpc_string); 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (status != RPC_S_OK) 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return result; 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // RPC_WSTR is unsigned short*. wchar_t is a built-in type of Visual C++, 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // so the type cast is necessary. 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch result.assign(reinterpret_cast<wchar_t*>(rpc_string)); 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch RpcStringFree(&rpc_string); 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return result; 133c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 134c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 135731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// This is a helper struct designed to optionally delete a key after releasing 136731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// the associated provider. 137731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickstruct KeyContainer { 138731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick public: 139731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick explicit KeyContainer(bool delete_keyset) 140731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick : delete_keyset_(delete_keyset) {} 141731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 142731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick ~KeyContainer() { 143731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (provider_) { 144731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick provider_.reset(); 145731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (delete_keyset_ && !key_id_.empty()) { 146731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick HCRYPTPROV provider; 147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen crypto::CryptAcquireContextLocked(&provider, key_id_.c_str(), NULL, 148731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick PROV_RSA_FULL, CRYPT_SILENT | CRYPT_DELETEKEYSET); 149731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 150731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 151731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 153ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen crypto::ScopedHCRYPTPROV provider_; 154731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick std::wstring key_id_; 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 156731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick private: 157731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick bool delete_keyset_; 158731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}; 159731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick 160731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickstd::string KeygenHandler::GenKeyAndSignChallenge() { 161731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick KeyContainer key_container(!stores_key_); 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(rsleevi): Have the user choose which provider they should use, which 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // needs to be filtered by those providers which can provide the key type 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // requested or the key size requested. This is especially important for 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // generating certificates that will be stored on smart cards. 167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const int kMaxAttempts = 5; 168731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick int attempt; 169731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick for (attempt = 0; attempt < kMaxAttempts; ++attempt) { 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Per MSDN documentation for CryptAcquireContext, if applications will be 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // creating their own keys, they should ensure unique naming schemes to 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // prevent overlap with any other applications or consumers of CSPs, and 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // *should not* store new keys within the default, NULL key container. 174731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick key_container.key_id_ = GetNewKeyContainerId(); 175731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (key_container.key_id_.empty()) 176731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return std::string(); 177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Only create new key containers, so that existing key containers are not 179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // overwritten. 180ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (crypto::CryptAcquireContextLocked(key_container.provider_.receive(), 181ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen key_container.key_id_.c_str(), NULL, PROV_RSA_FULL, 182ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen CRYPT_SILENT | CRYPT_NEWKEYSET)) 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch break; 184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 185731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (GetLastError() != NTE_BAD_KEYSET) { 186731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick LOG(ERROR) << "Keygen failed: Couldn't acquire a CryptoAPI provider " 187731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick "context: " << GetLastError(); 188731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return std::string(); 189731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 191731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (attempt == kMaxAttempts) { 192731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick LOG(ERROR) << "Keygen failed: Couldn't acquire a CryptoAPI provider " 193731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick "context: Max retries exceeded"; 194731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return std::string(); 195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 197731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick { 198ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen crypto::ScopedHCRYPTKEY key; 199731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (!CryptGenKey(key_container.provider_, CALG_RSA_KEYX, 200731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick (key_size_in_bits_ << 16) | CRYPT_EXPORTABLE, key.receive())) { 201731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick LOG(ERROR) << "Keygen failed: Couldn't generate an RSA key"; 202731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return std::string(); 203731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 205731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick std::string spkac; 206731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (!GetSignedPublicKeyAndChallenge(key_container.provider_, challenge_, 207731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick &spkac)) { 208731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick LOG(ERROR) << "Keygen failed: Couldn't generate the signed public key " 209731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick "and challenge"; 210731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return std::string(); 211731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 213731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick std::string result; 214731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (!base::Base64Encode(spkac, &result)) { 215731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick LOG(ERROR) << "Keygen failed: Couldn't convert signed key into base64"; 216731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return std::string(); 217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 219731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick VLOG(1) << "Keygen succeeded"; 220731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return result; 221731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 222c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 223c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 224c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} // namespace net 225