/************************************************* * PKCS #8 Source File * * (C) 1999-2006 The Botan Project * *************************************************/ #include #include #include #include #include #include #include #include #include #include namespace Botan { namespace PKCS8 { namespace { /************************************************* * Get info from an EncryptedPrivateKeyInfo * *************************************************/ SecureVector PKCS8_extract(DataSource& source, AlgorithmIdentifier& alg_id) { SecureVector enc_pkcs8_key; try { BER_Decoder decoder(source); BER_Decoder sequence = decoder.start_cons(SEQUENCE); sequence.decode(alg_id); sequence.decode(enc_pkcs8_key, OCTET_STRING); sequence.verify_end(); } catch(Decoding_Error) { throw PKCS8_Exception("Private key decoding failed"); } return enc_pkcs8_key; } /************************************************* * PEM decode and/or decrypt a private key * *************************************************/ SecureVector PKCS8_decode(DataSource& source, const User_Interface& ui, AlgorithmIdentifier& pk_alg_id) { AlgorithmIdentifier pbe_alg_id; SecureVector key_data, key; bool is_encrypted = true; try { if(ASN1::maybe_BER(source) && !PEM_Code::matches(source)) key_data = PKCS8_extract(source, pbe_alg_id); else { std::string label; key_data = PEM_Code::decode(source, label); if(label == "PRIVATE KEY") is_encrypted = false; else if(label == "ENCRYPTED PRIVATE KEY") { DataSource_Memory key_source(key_data); key_data = PKCS8_extract(key_source, pbe_alg_id); } else throw PKCS8_Exception("Unknown PEM label " + label); } if(key_data.is_empty()) throw PKCS8_Exception("No key data found"); } catch(Decoding_Error) { throw Decoding_Error("PKCS #8 private key decoding failed"); } if(!is_encrypted) key = key_data; const u32bit MAX_TRIES = global_config().option_as_u32bit("base/pkcs8_tries"); u32bit tries = 0; while(true) { try { if(MAX_TRIES && tries >= MAX_TRIES) break; if(is_encrypted) { DataSource_Memory params(pbe_alg_id.parameters); PBE* pbe = get_pbe(pbe_alg_id.oid, params); User_Interface::UI_Result result = User_Interface::OK; const std::string passphrase = ui.get_passphrase("PKCS #8 private key", source.id(), result); if(result == User_Interface::CANCEL_ACTION) break; pbe->set_key(passphrase); Pipe decryptor(pbe); decryptor.process_msg(key_data, key_data.size()); key = decryptor.read_all(); } u32bit version; BER_Decoder(key) .start_cons(SEQUENCE) .decode(version) .decode(pk_alg_id) .decode(key, OCTET_STRING) .discard_remaining() .end_cons(); if(version != 0) throw Decoding_Error("PKCS #8: Unknown version number"); break; } catch(Decoding_Error) { ++tries; } } if(key.is_empty()) throw Decoding_Error("PKCS #8 private key decoding failed"); return key; } } /************************************************* * DER or PEM encode a PKCS #8 private key * *************************************************/ void encode(const PKCS8_PrivateKey& key, Pipe& pipe, X509_Encoding encoding) { std::auto_ptr encoder(key.pkcs8_encoder()); if(!encoder.get()) throw Encoding_Error("PKCS8::encode: Key does not support encoding"); const u32bit PKCS8_VERSION = 0; SecureVector contents = DER_Encoder() .start_cons(SEQUENCE) .encode(PKCS8_VERSION) .encode(encoder->alg_id()) .encode(encoder->key_bits(), OCTET_STRING) .end_cons() .get_contents(); if(encoding == PEM) pipe.write(PEM_Code::encode(contents, "PRIVATE KEY")); else pipe.write(contents); } /************************************************* * Encode and encrypt a PKCS #8 private key * *************************************************/ void encrypt_key(const PKCS8_PrivateKey& key, Pipe& pipe, const std::string& pass, const std::string& pbe_algo, X509_Encoding encoding) { const std::string DEFAULT_PBE = global_config().option("base/default_pbe"); Pipe raw_key; raw_key.start_msg(); encode(key, raw_key, RAW_BER); raw_key.end_msg(); PBE* pbe = get_pbe(((pbe_algo != "") ? pbe_algo : DEFAULT_PBE)); pbe->set_key(pass); Pipe key_encrytor(pbe); key_encrytor.process_msg(raw_key); SecureVector enc_key = DER_Encoder() .start_cons(SEQUENCE) .encode(AlgorithmIdentifier(pbe->get_oid(), pbe->encode_params())) .encode(key_encrytor.read_all(), OCTET_STRING) .end_cons() .get_contents(); if(encoding == PEM) pipe.write(PEM_Code::encode(enc_key, "ENCRYPTED PRIVATE KEY")); else pipe.write(enc_key); } /************************************************* * PEM encode a PKCS #8 private key * *************************************************/ std::string PEM_encode(const PKCS8_PrivateKey& key) { Pipe pem; pem.start_msg(); encode(key, pem, PEM); pem.end_msg(); return pem.read_all_as_string(); } /************************************************* * Encrypt and PEM encode a PKCS #8 private key * *************************************************/ std::string PEM_encode(const PKCS8_PrivateKey& key, const std::string& pass, const std::string& pbe_algo) { if(pass == "") return PEM_encode(key); Pipe pem; pem.start_msg(); encrypt_key(key, pem, pass, pbe_algo, PEM); pem.end_msg(); return pem.read_all_as_string(); } /************************************************* * Extract a private key and return it * *************************************************/ PKCS8_PrivateKey* load_key(DataSource& source, const User_Interface& ui) { AlgorithmIdentifier alg_id; SecureVector pkcs8_key = PKCS8_decode(source, ui, alg_id); const std::string alg_name = OIDS::lookup(alg_id.oid); if(alg_name == "" || alg_name == alg_id.oid.as_string()) throw PKCS8_Exception("Unknown algorithm OID: " + alg_id.oid.as_string()); std::auto_ptr key(get_private_key(alg_name)); if(!key.get()) throw PKCS8_Exception("Unknown PK algorithm/OID: " + alg_name + ", " + alg_id.oid.as_string()); std::auto_ptr decoder(key->pkcs8_decoder()); if(!decoder.get()) throw Decoding_Error("Key does not support PKCS #8 decoding"); decoder->alg_id(alg_id); decoder->key_bits(pkcs8_key); return key.release(); } /************************************************* * Extract a private key and return it * *************************************************/ PKCS8_PrivateKey* load_key(const std::string& fsname, const User_Interface& ui) { DataSource_Stream source(fsname, true); return PKCS8::load_key(source, ui); } /************************************************* * Extract a private key and return it * *************************************************/ PKCS8_PrivateKey* load_key(DataSource& source, const std::string& pass) { return PKCS8::load_key(source, User_Interface(pass)); } /************************************************* * Extract a private key and return it * *************************************************/ PKCS8_PrivateKey* load_key(const std::string& fsname, const std::string& pass) { return PKCS8::load_key(fsname, User_Interface(pass)); } /************************************************* * Make a copy of this private key * *************************************************/ PKCS8_PrivateKey* copy_key(const PKCS8_PrivateKey& key) { Pipe bits; bits.start_msg(); PKCS8::encode(key, bits); bits.end_msg(); DataSource_Memory source(bits.read_all()); return PKCS8::load_key(source); } } }