PrivateKey.cpp

Go to the documentation of this file.
00001 /*
00002  * Distributed under the OpenDDS License.
00003  * See: http://www.OpenDDS.org/license.html
00004  */
00005 
00006 #include "PrivateKey.h"
00007 #include "dds/DCPS/security/CommonUtilities.h"
00008 #include "Err.h"
00009 #include <openssl/pem.h>
00010 #include "../OpenSSL_legacy.h"  // Must come after all other OpenSSL includes
00011 #include <cstring>
00012 #include <sstream>
00013 
00014 namespace OpenDDS {
00015 namespace Security {
00016 namespace SSL {
00017 
00018   PrivateKey::PrivateKey(const std::string& uri, const std::string password)
00019     : k_(NULL)
00020   {
00021     load(uri, password);
00022   }
00023 
00024   PrivateKey::PrivateKey()
00025     : k_(NULL)
00026   {
00027 
00028   }
00029 
00030   PrivateKey::PrivateKey(const PrivateKey& other)
00031     : k_(NULL)
00032   {
00033     if (other.k_) {
00034       k_ = other.k_;
00035 
00036 #ifndef OPENSSL_V_1_0
00037       EVP_PKEY_up_ref(k_);
00038 #endif
00039 
00040     }
00041   }
00042 
00043   PrivateKey::~PrivateKey()
00044   {
00045     if (k_) EVP_PKEY_free(k_);
00046   }
00047 
00048   PrivateKey& PrivateKey::operator=(const PrivateKey& rhs)
00049   {
00050     if (this != &rhs) {
00051       if (rhs.k_) {
00052         k_ = rhs.k_;
00053 
00054 #ifndef OPENSSL_V_1_0
00055         EVP_PKEY_up_ref(k_);
00056 #endif
00057 
00058       } else {
00059         k_ = NULL;
00060       }
00061     }
00062     return *this;
00063   }
00064 
00065   void PrivateKey::load(const std::string& uri, const std::string& password)
00066   {
00067     using namespace CommonUtilities;
00068 
00069     if (k_) return;
00070 
00071     URI uri_info(uri);
00072 
00073     switch (uri_info.scheme) {
00074       case URI::URI_FILE:
00075         k_ = EVP_PKEY_from_pem(uri_info.everything_else, password);
00076         break;
00077 
00078       case URI::URI_DATA:
00079         k_ = EVP_PKEY_from_pem_data(uri_info.everything_else, password);
00080         break;
00081 
00082       case URI::URI_PKCS11:
00083       case URI::URI_UNKNOWN:
00084       default:
00085         ACE_ERROR((LM_WARNING,
00086                    ACE_TEXT("(%P|%t) SSL::PrivateKey::load: WARNING: Unsupported URI scheme in cert path '%C'\n"),
00087                    uri.c_str()));
00088 
00089         break;
00090     }
00091   }
00092 
00093   class sign_implementation
00094   {
00095    public:
00096     sign_implementation(EVP_PKEY* pkey)
00097       : private_key(pkey), md_ctx(NULL), pkey_ctx(NULL)
00098     {
00099     }
00100     ~sign_implementation()
00101     {
00102       if (md_ctx) EVP_MD_CTX_free(md_ctx);
00103     }
00104 
00105     int operator()(const std::vector<const DDS::OctetSeq*>& src,
00106                    DDS::OctetSeq& dst)
00107     {
00108       if (!private_key) return 1;
00109 
00110       std::vector<const DDS::OctetSeq*>::const_iterator i, n;
00111       size_t len = 0u;
00112 
00113       md_ctx = EVP_MD_CTX_new();
00114       if (!md_ctx) {
00115         OPENDDS_SSL_LOG_ERR("EVP_MD_CTX_new failed");
00116         return 1;
00117       }
00118 
00119       EVP_MD_CTX_init(md_ctx);
00120 
00121       if (1 != EVP_DigestSignInit(md_ctx, &pkey_ctx, EVP_sha256(), NULL,
00122                                   private_key)) {
00123         OPENDDS_SSL_LOG_ERR("EVP_DigestSignInit failed");
00124         return 1;
00125       }
00126 
00127       // Determine which signature type is being signed
00128       int pk_id = EVP_PKEY_id(private_key);
00129 
00130       if (pk_id == EVP_PKEY_RSA) {
00131         if (1 !=
00132             EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)) {
00133           OPENDDS_SSL_LOG_ERR("EVP_PKEY_CTX_set_rsa_padding failed");
00134           return 1;
00135         }
00136 
00137         if (1 != EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha256())) {
00138           OPENDDS_SSL_LOG_ERR("EVP_PKEY_CTX_set_rsa_mgf1_md failed");
00139           return 1;
00140         }
00141       }
00142 
00143       n = src.end();
00144       for (i = src.begin(); i != n; ++i) {
00145         if ((*i)->length() > 0) {
00146           if (1 != EVP_DigestSignUpdate(md_ctx, (*i)->get_buffer(),
00147                                         (*i)->length())) {
00148             OPENDDS_SSL_LOG_ERR("EVP_DigestSignUpdate failed");
00149             return 1;
00150           }
00151         }
00152       }
00153 
00154       // First call with NULL to extract size
00155       if (1 != EVP_DigestSignFinal(md_ctx, NULL, &len)) {
00156         OPENDDS_SSL_LOG_ERR("EVP_DigestSignFinal failed");
00157         return 1;
00158       }
00159 
00160       // Second call to extract the data
00161       dst.length(static_cast<unsigned int>(len));
00162       if (1 != EVP_DigestSignFinal(md_ctx, dst.get_buffer(), &len)) {
00163         OPENDDS_SSL_LOG_ERR("EVP_DigestSignFinal failed");
00164         return 1;
00165       }
00166 
00167       // The last call to EVP_DigestSignFinal can change the value of len so
00168       // reassign the value to len to dst.length.  This happens when using EC
00169       dst.length(static_cast<unsigned int>(len));
00170 
00171       return 0;
00172     }
00173 
00174    private:
00175     EVP_PKEY* private_key;
00176     EVP_MD_CTX* md_ctx;
00177     EVP_PKEY_CTX* pkey_ctx;
00178   };
00179 
00180   int PrivateKey::sign(const std::vector<const DDS::OctetSeq*>& src,
00181                        DDS::OctetSeq& dst) const
00182   {
00183     sign_implementation sign(k_);
00184     return sign(src, dst);
00185   }
00186 
00187   EVP_PKEY* PrivateKey::EVP_PKEY_from_pem(const std::string& path,
00188                                           const std::string& password)
00189   {
00190     EVP_PKEY* result = NULL;
00191 
00192     BIO* filebuf = BIO_new_file(path.c_str(), "r");
00193     if (filebuf) {
00194       if (password != "") {
00195         result = PEM_read_bio_PrivateKey(filebuf, NULL, NULL,
00196                                          (void*)password.c_str());
00197         if (!result) {
00198           OPENDDS_SSL_LOG_ERR("PEM_read_bio_PrivateKey failed");
00199         }
00200 
00201       } else {
00202         result = PEM_read_bio_PrivateKey(filebuf, NULL, NULL, NULL);
00203         if (!result) {
00204           OPENDDS_SSL_LOG_ERR("PEM_read_bio_PrivateKey failed");
00205         }
00206       }
00207 
00208       BIO_free(filebuf);
00209 
00210     } else {
00211       std::stringstream errmsg;
00212       errmsg << "failed to read file '" << path << "' using BIO_new_file";
00213       OPENDDS_SSL_LOG_ERR(errmsg.str().c_str());
00214     }
00215 
00216     return result;
00217   }
00218 
00219   EVP_PKEY* PrivateKey::EVP_PKEY_from_pem_data(const std::string& data,
00220                                                const std::string& password)
00221   {
00222     DDS::OctetSeq original_bytes;
00223 
00224     // The minus 1 is because path contains a comma in element 0 and that
00225     // comma is not included in the cert string
00226     original_bytes.length(data.size() - 1);
00227     std::memcpy(original_bytes.get_buffer(), &data[1],
00228                 original_bytes.length());
00229 
00230     // To appease the other DDS security implementations which
00231     // append a null byte at the end of the cert.
00232     original_bytes.length(original_bytes.length() + 1);
00233     original_bytes[original_bytes.length() - 1] = 0;
00234 
00235     EVP_PKEY* result = NULL;
00236     BIO* filebuf = BIO_new(BIO_s_mem());
00237 
00238     if (filebuf) {
00239       if (0 >= BIO_write(filebuf, original_bytes.get_buffer(),
00240                          original_bytes.length())) {
00241         OPENDDS_SSL_LOG_ERR("BIO_write failed");
00242       }
00243 
00244       if (password != "") {
00245         result = PEM_read_bio_PrivateKey(filebuf, NULL, NULL,
00246                                          (void*)password.c_str());
00247 
00248         if (!result) {
00249           OPENDDS_SSL_LOG_ERR("PEM_read_bio_PrivateKey failed");
00250         }
00251       } else {
00252         result = PEM_read_bio_PrivateKey(filebuf, NULL, NULL, NULL);
00253 
00254         if (!result) {
00255           OPENDDS_SSL_LOG_ERR("PEM_read_bio_PrivateKey failed");
00256         }
00257       }
00258 
00259       BIO_free(filebuf);
00260     } else {
00261       std::stringstream errmsg;
00262       errmsg << "failed to create data '" << data << "' using BIO_new";
00263       OPENDDS_SSL_LOG_ERR(errmsg.str().c_str());
00264     }
00265 
00266     return result;
00267   }
00268 
00269   bool operator==(const PrivateKey& lhs, const PrivateKey& rhs)
00270   {
00271     if (lhs.k_ && rhs.k_) {
00272       return (1 == EVP_PKEY_cmp(lhs.k_, rhs.k_));
00273     }
00274     return lhs.k_ == rhs.k_;
00275   }
00276 
00277 }  // namespace SSL
00278 }  // namespace Security
00279 }  // namespace OpenDDS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated on 10 Aug 2018 for OpenDDS by  doxygen 1.6.1