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