OpenDDS  Snapshot(2023/04/28-20:55)
PrivateKey.cpp
Go to the documentation of this file.
1 /*
2  * Distributed under the OpenDDS License.
3  * See: http://www.OpenDDS.org/license.html
4  */
5 
6 #include "PrivateKey.h"
8 #include "Err.h"
9 
10 #include <openssl/pem.h>
11 #include "../OpenSSL_legacy.h" // Must come after all other OpenSSL includes
12 
13 #include <cstring>
14 #include <sstream>
15 
17 
18 namespace OpenDDS {
19 namespace Security {
20 namespace SSL {
21 
22 PrivateKey::PrivateKey(const std::string& uri, const std::string& password)
23  : k_(0)
24 {
25  load(uri, password);
26 }
27 
29  : k_(0)
30 {
31 }
32 
34 {
35  if (k_) {
36  EVP_PKEY_free(k_);
37  }
38 }
39 
40 void PrivateKey::load(const std::string& uri, const std::string& password)
41 {
42  using namespace CommonUtilities;
43 
44  if (k_) return;
45 
46  URI uri_info(uri);
47 
48  switch (uri_info.scheme) {
49  case URI::URI_FILE:
50  k_ = EVP_PKEY_from_pem(uri_info.everything_else, password);
51  break;
52 
53  case URI::URI_DATA:
54  k_ = EVP_PKEY_from_pem_data(uri_info.everything_else, password);
55  break;
56 
57  case URI::URI_PKCS11:
58  case URI::URI_UNKNOWN:
59  default:
61  ACE_TEXT("(%P|%t) SSL::PrivateKey::load: WARNING: Unsupported URI scheme in cert path '%C'\n"),
62  uri.c_str()));
63  break;
64  }
65 }
66 
68 {
69 public:
70  explicit sign_implementation(EVP_PKEY* pkey)
71  : private_key(pkey), md_ctx(0), pkey_ctx(0)
72  {
73  }
74 
76  {
77  if (md_ctx) {
78  EVP_MD_CTX_free(md_ctx);
79  }
80  }
81 
82  int operator()(const std::vector<const DDS::OctetSeq*>& src,
83  DDS::OctetSeq& dst)
84  {
85  if (!private_key) return 1;
86 
87  std::vector<const DDS::OctetSeq*>::const_iterator i, n;
88  size_t len = 0u;
89 
90  md_ctx = EVP_MD_CTX_new();
91  if (!md_ctx) {
92  OPENDDS_SSL_LOG_ERR("EVP_MD_CTX_new failed");
93  return 1;
94  }
95 
96  EVP_MD_CTX_init(md_ctx);
97 
98  if (1 != EVP_DigestSignInit(md_ctx, &pkey_ctx, EVP_sha256(), 0,
99  private_key)) {
100  OPENDDS_SSL_LOG_ERR("EVP_DigestSignInit failed");
101  return 1;
102  }
103 
104  // Determine which signature type is being signed
105  int pk_id = EVP_PKEY_id(private_key);
106 
107  if (pk_id == EVP_PKEY_RSA) {
108  if (1 !=
109  EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)) {
110  OPENDDS_SSL_LOG_ERR("EVP_PKEY_CTX_set_rsa_padding failed");
111  return 1;
112  }
113 
114  if (1 != EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha256())) {
115  OPENDDS_SSL_LOG_ERR("EVP_PKEY_CTX_set_rsa_mgf1_md failed");
116  return 1;
117  }
118  }
119 
120  n = src.end();
121  for (i = src.begin(); i != n; ++i) {
122  if ((*i)->length() > 0) {
123  if (1 != EVP_DigestSignUpdate(md_ctx, (*i)->get_buffer(),
124  (*i)->length())) {
125  OPENDDS_SSL_LOG_ERR("EVP_DigestSignUpdate failed");
126  return 1;
127  }
128  }
129  }
130 
131  // First call with 0 to extract size
132  if (1 != EVP_DigestSignFinal(md_ctx, 0, &len)) {
133  OPENDDS_SSL_LOG_ERR("EVP_DigestSignFinal failed");
134  return 1;
135  }
136 
137  // Second call to extract the data
138  dst.length(static_cast<unsigned int>(len));
139  if (1 != EVP_DigestSignFinal(md_ctx, dst.get_buffer(), &len)) {
140  OPENDDS_SSL_LOG_ERR("EVP_DigestSignFinal failed");
141  return 1;
142  }
143 
144  // The last call to EVP_DigestSignFinal can change the value of len so
145  // reassign the value to len to dst.length. This happens when using EC
146  dst.length(static_cast<unsigned int>(len));
147 
148  return 0;
149  }
150 
151 private:
153  EVP_MD_CTX* md_ctx;
154  EVP_PKEY_CTX* pkey_ctx;
155 };
156 
157 int PrivateKey::sign(const std::vector<const DDS::OctetSeq*>& src,
158  DDS::OctetSeq& dst) const
159 {
161  return sign(src, dst);
162 }
163 
164 EVP_PKEY* PrivateKey::EVP_PKEY_from_pem(const std::string& path,
165  const std::string& password)
166 {
167  EVP_PKEY* result = 0;
168 
169  BIO* filebuf = BIO_new_file(path.c_str(), "r");
170  if (filebuf) {
171  result = PEM_read_bio_PrivateKey(filebuf, 0, 0,
172  password.empty() ? 0 : (void*)password.c_str());
173  if (!result) {
174  OPENDDS_SSL_LOG_ERR("PEM_read_bio_PrivateKey failed");
175  }
176 
177  BIO_free(filebuf);
178 
179  } else {
180  std::stringstream errmsg;
181  errmsg << "failed to read file '" << path << "' using BIO_new_file";
182  OPENDDS_SSL_LOG_ERR(errmsg.str().c_str());
183  }
184 
185  return result;
186 }
187 
189  const std::string& password)
190 {
191  DDS::OctetSeq original_bytes;
192 
193  // The minus 1 is because path contains a comma in element 0 and that
194  // comma is not included in the cert string
195  original_bytes.length(static_cast<unsigned int>(data.size() - 1));
196  std::memcpy(original_bytes.get_buffer(), &data[1],
197  original_bytes.length());
198 
199  // To appease the other DDS security implementations which
200  // append a null byte at the end of the cert.
201  original_bytes.length(original_bytes.length() + 1);
202  original_bytes[original_bytes.length() - 1] = 0;
203 
204  EVP_PKEY* result = 0;
205  BIO* filebuf = BIO_new(BIO_s_mem());
206 
207  if (filebuf) {
208  if (0 >= BIO_write(filebuf, original_bytes.get_buffer(),
209  original_bytes.length())) {
210  OPENDDS_SSL_LOG_ERR("BIO_write failed");
211  }
212 
213  result = PEM_read_bio_PrivateKey(filebuf, 0, 0,
214  password.empty() ? 0 : (void*)password.c_str());
215 
216  if (!result) {
217  OPENDDS_SSL_LOG_ERR("PEM_read_bio_PrivateKey failed");
218  }
219 
220  BIO_free(filebuf);
221 
222  } else {
223  std::stringstream errmsg;
224  errmsg << "failed to create data '" << data << "' using BIO_new";
225  OPENDDS_SSL_LOG_ERR(errmsg.str().c_str());
226  }
227 
228  return result;
229 }
230 
231 bool operator==(const PrivateKey& lhs, const PrivateKey& rhs)
232 {
233  if (lhs.k_ && rhs.k_) {
234 #ifdef OPENSSL_V_3_0
235  return 1 == EVP_PKEY_eq(lhs.k_, rhs.k_);
236 #else
237  return 1 == EVP_PKEY_cmp(lhs.k_, rhs.k_);
238 #endif
239  }
240  return lhs.k_ == rhs.k_;
241 }
242 
243 } // namespace SSL
244 } // namespace Security
245 } // namespace OpenDDS
246 
This URI abstraction is currently naive and only separates the URI scheme on the LHS from the "everyt...
#define ACE_ERROR(X)
void load(const std::string &uri, const std::string &password="")
Definition: PrivateKey.cpp:40
friend OpenDDS_Security_Export bool operator==(const PrivateKey &lhs, const PrivateKey &rhs)
Definition: PrivateKey.cpp:231
struct evp_pkey_st EVP_PKEY
static EVP_PKEY * EVP_PKEY_from_pem(const std::string &path, const std::string &password="")
Definition: PrivateKey.cpp:164
LM_WARNING
sequence< octet > OctetSeq
Definition: DdsDcpsCore.idl:64
int sign(const std::vector< const DDS::OctetSeq *> &src, DDS::OctetSeq &dst) const
Definition: PrivateKey.cpp:157
ACE_TEXT("TCP_Factory")
int operator()(const std::vector< const DDS::OctetSeq *> &src, DDS::OctetSeq &dst)
Definition: PrivateKey.cpp:82
#define EVP_MD_CTX_new
#define EVP_MD_CTX_free
static EVP_PKEY * EVP_PKEY_from_pem_data(const std::string &data, const std::string &password)
Definition: PrivateKey.cpp:188
#define OPENDDS_END_VERSIONED_NAMESPACE_DECL
The Internal API and Implementation of OpenDDS.
Definition: AddressCache.h:28
#define OPENDDS_SSL_LOG_ERR(MSG)
Definition: Err.h:12