From ffc4a824602e269cd42e869e9e1e044c98d6028b Mon Sep 17 00:00:00 2001 From: Kongqun Yang Date: Mon, 6 Jan 2014 15:38:39 -0800 Subject: [PATCH] Implement RsaPrivateKey and RsaPublicKey. Used for message signing, signature verification, encryption and decryption using RSA algorithm. Change-Id: Icacd5a994c532a7bd4179c44e98c3ee9db744e83 --- media/base/rsa_key.cc | 246 ++++++++++++++++++++++++++++++++++++++++++ media/base/rsa_key.h | 75 +++++++++++++ 2 files changed, 321 insertions(+) create mode 100644 media/base/rsa_key.cc create mode 100644 media/base/rsa_key.h diff --git a/media/base/rsa_key.cc b/media/base/rsa_key.cc new file mode 100644 index 0000000000..847743ab80 --- /dev/null +++ b/media/base/rsa_key.cc @@ -0,0 +1,246 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// RSA signature details: +// Algorithm: RSASSA-PSS +// Hash algorithm: SHA1 +// Mask generation function: mgf1SHA1 +// Salt length: 20 bytes +// Trailer field: 0xbc +// +// RSA encryption details: +// Algorithm: RSA-OAEP +// Mask generation function: mgf1SHA1 +// Label (encoding paramter): empty std::string + +#include "media/base/rsa_key.h" + +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/sha1.h" +#include "base/stl_util.h" + +namespace { + +const size_t kPssSaltLength = 20u; + +// Serialize rsa key from DER encoded PKCS#1 RSAPrivateKey. +RSA* DeserializeRsaKey(const std::string& serialized_key, + bool deserialize_private_key) { + if (serialized_key.empty()) { + LOG(ERROR) << "Serialized RSA Key is empty."; + return NULL; + } + + BIO* bio = BIO_new_mem_buf(const_cast(serialized_key.data()), + serialized_key.size()); + if (bio == NULL) { + LOG(ERROR) << "BIO_new_mem_buf returned NULL."; + return NULL; + } + RSA* rsa_key = deserialize_private_key ? d2i_RSAPrivateKey_bio(bio, NULL) + : d2i_RSAPublicKey_bio(bio, NULL); + BIO_free(bio); + return rsa_key; +} + +RSA* DeserializeRsaPrivateKey(const std::string& serialized_key) { + RSA* rsa_key = DeserializeRsaKey(serialized_key, true); + if (!rsa_key) { + LOG(ERROR) << "Private RSA key deserialization failure."; + return NULL; + } + if (RSA_check_key(rsa_key) != 1) { + LOG(ERROR) << "Invalid RSA Private key: " << ERR_error_string( + ERR_get_error(), NULL); + RSA_free(rsa_key); + return NULL; + } + return rsa_key; +} + +RSA* DeserializeRsaPublicKey(const std::string& serialized_key) { + RSA* rsa_key = DeserializeRsaKey(serialized_key, false); + if (!rsa_key) { + LOG(ERROR) << "Private RSA key deserialization failure."; + return NULL; + } + if (RSA_size(rsa_key) <= 0) { + LOG(ERROR) << "Invalid RSA Public key: " << ERR_error_string( + ERR_get_error(), NULL); + RSA_free(rsa_key); + return NULL; + } + return rsa_key; +} + +} // namespace + +namespace media { + +RsaPrivateKey::RsaPrivateKey(RSA* rsa_key) : rsa_key_(rsa_key) { + DCHECK(rsa_key); +} +RsaPrivateKey::~RsaPrivateKey() { + if (rsa_key_ != NULL) + RSA_free(rsa_key_); +} + +RsaPrivateKey* RsaPrivateKey::Create(const std::string& serialized_key) { + RSA* rsa_key = DeserializeRsaPrivateKey(serialized_key); + return rsa_key == NULL ? NULL : new RsaPrivateKey(rsa_key); +} + +bool RsaPrivateKey::Decrypt(const std::string& encrypted_message, + std::string* decrypted_message) { + DCHECK(decrypted_message); + + size_t rsa_size = RSA_size(rsa_key_); + if (encrypted_message.size() != rsa_size) { + LOG(ERROR) << "Encrypted RSA message has the wrong size (expected " + << rsa_size << ", actual " << encrypted_message.size() << ")."; + return false; + } + + decrypted_message->resize(rsa_size); + int decrypted_size = RSA_private_decrypt( + rsa_size, + reinterpret_cast(encrypted_message.data()), + reinterpret_cast(string_as_array(decrypted_message)), + rsa_key_, + RSA_PKCS1_OAEP_PADDING); + + if (decrypted_size == -1) { + LOG(ERROR) << "RSA private decrypt failure: " << ERR_error_string( + ERR_get_error(), NULL); + return false; + } + decrypted_message->resize(decrypted_size); + return true; +} + +bool RsaPrivateKey::GenerateSignature(const std::string& message, + std::string* signature) { + DCHECK(signature); + if (message.empty()) { + LOG(ERROR) << "Message to be signed is empty."; + return false; + } + + std::string message_digest = base::SHA1HashString(message); + + // Add PSS padding. + size_t rsa_size = RSA_size(rsa_key_); + std::vector padded_digest(rsa_size); + if (!RSA_padding_add_PKCS1_PSS( + rsa_key_, + &padded_digest[0], + reinterpret_cast(string_as_array(&message_digest)), + EVP_sha1(), + kPssSaltLength)) { + LOG(ERROR) << "RSA padding failure: " << ERR_error_string(ERR_get_error(), + NULL); + return false; + } + + // Encrypt PSS padded digest. + signature->resize(rsa_size); + int signature_size = + RSA_private_encrypt(padded_digest.size(), + &padded_digest[0], + reinterpret_cast(string_as_array(signature)), + rsa_key_, + RSA_NO_PADDING); + + if (signature_size != static_cast(rsa_size)) { + LOG(ERROR) << "RSA private encrypt failure: " << ERR_error_string( + ERR_get_error(), NULL); + return false; + } + return true; +} + +RsaPublicKey::RsaPublicKey(RSA* rsa_key) : rsa_key_(rsa_key) { + DCHECK(rsa_key); +} +RsaPublicKey::~RsaPublicKey() { + if (rsa_key_ != NULL) + RSA_free(rsa_key_); +} + +RsaPublicKey* RsaPublicKey::Create(const std::string& serialized_key) { + RSA* rsa_key = DeserializeRsaPublicKey(serialized_key); + return rsa_key == NULL ? NULL : new RsaPublicKey(rsa_key); +} + +bool RsaPublicKey::Encrypt(const std::string& clear_message, + std::string* encrypted_message) { + DCHECK(encrypted_message); + if (clear_message.empty()) { + LOG(ERROR) << "Message to be encrypted is empty."; + return false; + } + + size_t rsa_size = RSA_size(rsa_key_); + encrypted_message->resize(rsa_size); + int encrypted_size = RSA_public_encrypt( + clear_message.size(), + reinterpret_cast(clear_message.data()), + reinterpret_cast(string_as_array(encrypted_message)), + rsa_key_, + RSA_PKCS1_OAEP_PADDING); + + if (encrypted_size != static_cast(rsa_size)) { + LOG(ERROR) << "RSA public encrypt failure: " << ERR_error_string( + ERR_get_error(), NULL); + return false; + } + return true; +} + +bool RsaPublicKey::VerifySignature(const std::string& message, + const std::string& signature) { + if (message.empty()) { + LOG(ERROR) << "Signed message is empty."; + return false; + } + + size_t rsa_size = RSA_size(rsa_key_); + if (signature.size() != rsa_size) { + LOG(ERROR) << "Message signature is of the wrong size (expected " + << rsa_size << ", actual " << signature.size() << ")."; + return false; + } + + // Decrypt the signature. + std::vector padded_digest(signature.size()); + int decrypted_size = + RSA_public_decrypt(signature.size(), + reinterpret_cast(signature.data()), + &padded_digest[0], + rsa_key_, + RSA_NO_PADDING); + + if (decrypted_size != static_cast(rsa_size)) { + LOG(ERROR) << "RSA public decrypt failure: " << ERR_error_string( + ERR_get_error(), NULL); + return false; + } + + std::string message_digest = base::SHA1HashString(message); + + // Verify PSS padding. + return RSA_verify_PKCS1_PSS( + rsa_key_, + reinterpret_cast(message_digest.data()), + EVP_sha1(), + &padded_digest[0], + kPssSaltLength) != 0; +} + +} // namespace media diff --git a/media/base/rsa_key.h b/media/base/rsa_key.h new file mode 100644 index 0000000000..64eb6f0620 --- /dev/null +++ b/media/base/rsa_key.h @@ -0,0 +1,75 @@ +// Copyright (c) 2013 Google Inc. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Declaration of classes representing RSA private and public keys used +// for message signing, signature verification, encryption and decryption. + +#ifndef MEDIA_BASE_RSA_KEY_H_ +#define MEDIA_BASE_RSA_KEY_H_ + +#include + +#include "base/basictypes.h" + +struct rsa_st; +typedef struct rsa_st RSA; + +namespace media { + +class RsaPrivateKey { + public: + ~RsaPrivateKey(); + + // Create an RsaPrivateKey object using a DER encoded PKCS#1 RSAPrivateKey. + // Return NULL on failure. + static RsaPrivateKey* Create(const std::string& serialized_key); + + // Decrypt a message using RSA-OAEP. Caller retains ownership of all + // parameters. Return true if successful, false otherwise. + bool Decrypt(const std::string& encrypted_message, + std::string* decrypted_message); + + // Generate RSASSA-PSS signature. Caller retains ownership of all parameters. + // Return true if successful, false otherwise. + bool GenerateSignature(const std::string& message, std::string* signature); + + private: + // RsaPrivateKey takes owership of |rsa_key|. + explicit RsaPrivateKey(RSA* rsa_key); + + RSA* rsa_key_; // owned + + DISALLOW_COPY_AND_ASSIGN(RsaPrivateKey); +}; + +class RsaPublicKey { + public: + ~RsaPublicKey(); + + // Create an RsaPublicKey object using a DER encoded PKCS#1 RSAPublicKey. + // Return NULL on failure. + static RsaPublicKey* Create(const std::string& serialized_key); + + // Encrypt a message using RSA-OAEP. Caller retains ownership of all + // parameters. Return true if successful, false otherwise. + bool Encrypt(const std::string& clear_message, + std::string* encrypted_message); + + // Verify RSASSA-PSS signature. Caller retains ownership of all parameters. + // Return true if validation succeeds, false otherwise. + bool VerifySignature(const std::string& message, + const std::string& signature); + + private: + // RsaPublicKey takes owership of |rsa_key|. + explicit RsaPublicKey(RSA* rsa_key); + + RSA* rsa_key_; // owned + + DISALLOW_COPY_AND_ASSIGN(RsaPublicKey); +}; + +} // namespace media + +#endif // MEDIA_BASE_RSA_KEY_H_