| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "crypto/rsa_private_key.h" |
| |
| #include <list> |
| |
| #include "base/logging.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "crypto/cssm_init.h" |
| |
| namespace crypto { |
| |
| // static |
| RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { |
| scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey); |
| |
| CSSM_CC_HANDLE cc_handle; |
| CSSM_RETURN crtn; |
| crtn = CSSM_CSP_CreateKeyGenContext(GetSharedCSPHandle(), CSSM_ALGID_RSA, |
| num_bits, NULL, NULL, NULL, NULL, NULL, |
| &cc_handle); |
| if (crtn) { |
| NOTREACHED() << "CSSM_CSP_CreateKeyGenContext failed: " << crtn; |
| return NULL; |
| } |
| |
| CSSM_DATA label = { 9, |
| const_cast<uint8*>(reinterpret_cast<const uint8*>("temp_key")) }; |
| crtn = CSSM_GenerateKeyPair(cc_handle, |
| CSSM_KEYUSE_VERIFY, |
| CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, &label, |
| result->public_key(), CSSM_KEYUSE_SIGN, |
| CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, &label, NULL, |
| result->key()); |
| CSSM_DeleteContext(cc_handle); |
| if (crtn) { |
| NOTREACHED() << "CSSM_CSP_CreateKeyGenContext failed: " << crtn; |
| return NULL; |
| } |
| |
| return result.release(); |
| } |
| |
| // static |
| RSAPrivateKey* RSAPrivateKey::CreateSensitive(uint16 num_bits) { |
| NOTIMPLEMENTED(); |
| return NULL; |
| } |
| |
| // static |
| RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( |
| const std::vector<uint8>& input) { |
| if (input.empty()) |
| return NULL; |
| |
| scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey); |
| |
| CSSM_KEY key; |
| memset(&key, 0, sizeof(key)); |
| key.KeyData.Data = const_cast<uint8*>(&input.front()); |
| key.KeyData.Length = input.size(); |
| key.KeyHeader.Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS8; |
| key.KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION; |
| key.KeyHeader.BlobType = CSSM_KEYBLOB_RAW; |
| key.KeyHeader.AlgorithmId = CSSM_ALGID_RSA; |
| key.KeyHeader.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY; |
| key.KeyHeader.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; |
| key.KeyHeader.KeyUsage = CSSM_KEYUSE_ANY; |
| |
| CSSM_KEY_SIZE key_size; |
| CSSM_RETURN crtn; |
| crtn = CSSM_QueryKeySizeInBits( |
| GetSharedCSPHandle(), CSSM_INVALID_HANDLE, &key, &key_size); |
| if (crtn) { |
| NOTREACHED() << "CSSM_QueryKeySizeInBits failed: " << crtn; |
| return NULL; |
| } |
| key.KeyHeader.LogicalKeySizeInBits = key_size.LogicalKeySizeInBits; |
| |
| // Perform a NULL unwrap operation on the key so that result's key_ |
| // instance variable points to a key that can be released via CSSM_FreeKey(). |
| CSSM_ACCESS_CREDENTIALS creds; |
| memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); |
| CSSM_CC_HANDLE cc_handle; |
| crtn = CSSM_CSP_CreateSymmetricContext(GetSharedCSPHandle(), CSSM_ALGID_NONE, |
| CSSM_ALGMODE_NONE, &creds, NULL, NULL, CSSM_PADDING_NONE, 0, &cc_handle); |
| if (crtn) { |
| NOTREACHED() << "CSSM_CSP_CreateSymmetricContext failed: " << crtn; |
| return NULL; |
| } |
| CSSM_DATA label_data, desc_data = { 0, NULL }; |
| label_data.Data = |
| const_cast<uint8*>(reinterpret_cast<const uint8*>("unwrapped")); |
| label_data.Length = 9; |
| crtn = CSSM_UnwrapKey(cc_handle, NULL, &key, CSSM_KEYUSE_ANY, |
| CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, &label_data, |
| NULL, result->key(), &desc_data); |
| if (crtn) { |
| NOTREACHED() << "CSSM_UnwrapKey failed: " << crtn; |
| return NULL; |
| } |
| |
| // Extract a public key from the private key. |
| // Apple doesn't accept CSSM_KEYBLOB_RAW_FORMAT_X509 as a valid key |
| // format when attempting to generate certs, so use PKCS1 instead. |
| PrivateKeyInfoCodec codec(true); |
| std::vector<uint8> private_key_data; |
| private_key_data.assign(key.KeyData.Data, |
| key.KeyData.Data + key.KeyData.Length); |
| if (!codec.Import(private_key_data)) { |
| return NULL; |
| } |
| std::vector<uint8> public_key_data; |
| if (!codec.ExportPublicKey(&public_key_data)) { |
| return NULL; |
| } |
| |
| CSSM_KEY* public_key = result->public_key(); |
| size_t size = public_key_data.size(); |
| public_key->KeyData.Data = reinterpret_cast<uint8*>(CSSMMalloc(size)); |
| if (!public_key->KeyData.Data) { |
| NOTREACHED() << "CSSMMalloc failed"; |
| return NULL; |
| } |
| memcpy(public_key->KeyData.Data, &public_key_data.front(), size); |
| public_key->KeyData.Length = size; |
| public_key->KeyHeader.Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; |
| public_key->KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION; |
| public_key->KeyHeader.BlobType = CSSM_KEYBLOB_RAW; |
| public_key->KeyHeader.AlgorithmId = CSSM_ALGID_RSA; |
| public_key->KeyHeader.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY; |
| public_key->KeyHeader.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; |
| public_key->KeyHeader.KeyUsage = CSSM_KEYUSE_ANY; |
| |
| crtn = CSSM_QueryKeySizeInBits( |
| GetSharedCSPHandle(), CSSM_INVALID_HANDLE, public_key, &key_size); |
| if (crtn) { |
| DLOG(ERROR) << "CSSM_QueryKeySizeInBits failed " << crtn; |
| return NULL; |
| } |
| public_key->KeyHeader.LogicalKeySizeInBits = key_size.LogicalKeySizeInBits; |
| |
| return result.release(); |
| } |
| |
| // static |
| RSAPrivateKey* RSAPrivateKey::CreateSensitiveFromPrivateKeyInfo( |
| const std::vector<uint8>& input) { |
| NOTIMPLEMENTED(); |
| return NULL; |
| } |
| |
| // static |
| RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfo( |
| const std::vector<uint8>& input) { |
| NOTIMPLEMENTED(); |
| return NULL; |
| } |
| |
| RSAPrivateKey::RSAPrivateKey() { |
| memset(&key_, 0, sizeof(key_)); |
| memset(&public_key_, 0, sizeof(public_key_)); |
| |
| EnsureCSSMInit(); |
| } |
| |
| RSAPrivateKey::~RSAPrivateKey() { |
| if (key_.KeyData.Data) { |
| CSSM_FreeKey(GetSharedCSPHandle(), NULL, &key_, CSSM_FALSE); |
| } |
| if (public_key_.KeyData.Data) { |
| CSSM_FreeKey(GetSharedCSPHandle(), NULL, &public_key_, CSSM_FALSE); |
| } |
| } |
| |
| RSAPrivateKey* RSAPrivateKey::Copy() const { |
| std::vector<uint8> key_bytes; |
| if (!ExportPrivateKey(&key_bytes)) |
| return NULL; |
| return CreateFromPrivateKeyInfo(key_bytes); |
| } |
| |
| bool RSAPrivateKey::ExportPrivateKey(std::vector<uint8>* output) const { |
| if (!key_.KeyData.Data || !key_.KeyData.Length) { |
| return false; |
| } |
| output->clear(); |
| output->insert(output->end(), key_.KeyData.Data, |
| key_.KeyData.Data + key_.KeyData.Length); |
| return true; |
| } |
| |
| bool RSAPrivateKey::ExportPublicKey(std::vector<uint8>* output) const { |
| PrivateKeyInfoCodec private_key_info(true); |
| std::vector<uint8> private_key_data; |
| private_key_data.assign(key_.KeyData.Data, |
| key_.KeyData.Data + key_.KeyData.Length); |
| return (private_key_info.Import(private_key_data) && |
| private_key_info.ExportPublicKeyInfo(output)); |
| } |
| |
| } // namespace crypto |