Line data Source code
1 : /*
2 : * Distributed under the OpenDDS License.
3 : * See: http://www.OpenDDS.org/license.html
4 : */
5 :
6 : #include "PrivateKey.h"
7 : #include "dds/DCPS/security/CommonUtilities.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 :
16 : OPENDDS_BEGIN_VERSIONED_NAMESPACE_DECL
17 :
18 : namespace OpenDDS {
19 : namespace Security {
20 : namespace SSL {
21 :
22 37 : PrivateKey::PrivateKey(const std::string& uri, const std::string& password)
23 37 : : k_(0)
24 : {
25 37 : load(uri, password);
26 37 : }
27 :
28 0 : PrivateKey::PrivateKey()
29 0 : : k_(0)
30 : {
31 0 : }
32 :
33 59 : PrivateKey::~PrivateKey()
34 : {
35 37 : if (k_) {
36 37 : EVP_PKEY_free(k_);
37 : }
38 59 : }
39 :
40 37 : void PrivateKey::load(const std::string& uri, const std::string& password)
41 : {
42 : using namespace CommonUtilities;
43 :
44 37 : if (k_) return;
45 :
46 37 : URI uri_info(uri);
47 :
48 37 : switch (uri_info.scheme) {
49 32 : case URI::URI_FILE:
50 32 : k_ = EVP_PKEY_from_pem(uri_info.everything_else, password);
51 32 : break;
52 :
53 5 : case URI::URI_DATA:
54 5 : k_ = EVP_PKEY_from_pem_data(uri_info.everything_else, password);
55 5 : break;
56 :
57 0 : case URI::URI_PKCS11:
58 : case URI::URI_UNKNOWN:
59 : default:
60 0 : ACE_ERROR((LM_WARNING,
61 : ACE_TEXT("(%P|%t) SSL::PrivateKey::load: WARNING: Unsupported URI scheme in cert path '%C'\n"),
62 : uri.c_str()));
63 0 : break;
64 : }
65 37 : }
66 :
67 : class sign_implementation
68 : {
69 : public:
70 17 : explicit sign_implementation(EVP_PKEY* pkey)
71 17 : : private_key(pkey), md_ctx(0), pkey_ctx(0)
72 : {
73 17 : }
74 :
75 17 : ~sign_implementation()
76 : {
77 17 : if (md_ctx) {
78 17 : EVP_MD_CTX_free(md_ctx);
79 : }
80 17 : }
81 :
82 17 : int operator()(const std::vector<const DDS::OctetSeq*>& src,
83 : DDS::OctetSeq& dst)
84 : {
85 17 : if (!private_key) return 1;
86 :
87 17 : std::vector<const DDS::OctetSeq*>::const_iterator i, n;
88 17 : size_t len = 0u;
89 :
90 17 : md_ctx = EVP_MD_CTX_new();
91 17 : if (!md_ctx) {
92 0 : OPENDDS_SSL_LOG_ERR("EVP_MD_CTX_new failed");
93 0 : return 1;
94 : }
95 :
96 17 : EVP_MD_CTX_init(md_ctx);
97 :
98 17 : if (1 != EVP_DigestSignInit(md_ctx, &pkey_ctx, EVP_sha256(), 0,
99 : private_key)) {
100 0 : OPENDDS_SSL_LOG_ERR("EVP_DigestSignInit failed");
101 0 : return 1;
102 : }
103 :
104 : // Determine which signature type is being signed
105 17 : int pk_id = EVP_PKEY_id(private_key);
106 :
107 17 : if (pk_id == EVP_PKEY_RSA) {
108 16 : if (1 !=
109 16 : EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING)) {
110 0 : OPENDDS_SSL_LOG_ERR("EVP_PKEY_CTX_set_rsa_padding failed");
111 0 : return 1;
112 : }
113 :
114 16 : if (1 != EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha256())) {
115 0 : OPENDDS_SSL_LOG_ERR("EVP_PKEY_CTX_set_rsa_mgf1_md failed");
116 0 : return 1;
117 : }
118 : }
119 :
120 17 : n = src.end();
121 39 : for (i = src.begin(); i != n; ++i) {
122 22 : if ((*i)->length() > 0) {
123 21 : if (1 != EVP_DigestSignUpdate(md_ctx, (*i)->get_buffer(),
124 21 : (*i)->length())) {
125 0 : OPENDDS_SSL_LOG_ERR("EVP_DigestSignUpdate failed");
126 0 : return 1;
127 : }
128 : }
129 : }
130 :
131 : // First call with 0 to extract size
132 17 : if (1 != EVP_DigestSignFinal(md_ctx, 0, &len)) {
133 0 : OPENDDS_SSL_LOG_ERR("EVP_DigestSignFinal failed");
134 0 : return 1;
135 : }
136 :
137 : // Second call to extract the data
138 17 : dst.length(static_cast<unsigned int>(len));
139 17 : if (1 != EVP_DigestSignFinal(md_ctx, dst.get_buffer(), &len)) {
140 0 : OPENDDS_SSL_LOG_ERR("EVP_DigestSignFinal failed");
141 0 : 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 17 : dst.length(static_cast<unsigned int>(len));
147 :
148 17 : return 0;
149 : }
150 :
151 : private:
152 : EVP_PKEY* private_key;
153 : EVP_MD_CTX* md_ctx;
154 : EVP_PKEY_CTX* pkey_ctx;
155 : };
156 :
157 17 : int PrivateKey::sign(const std::vector<const DDS::OctetSeq*>& src,
158 : DDS::OctetSeq& dst) const
159 : {
160 17 : sign_implementation sign(k_);
161 34 : return sign(src, dst);
162 17 : }
163 :
164 32 : EVP_PKEY* PrivateKey::EVP_PKEY_from_pem(const std::string& path,
165 : const std::string& password)
166 : {
167 32 : EVP_PKEY* result = 0;
168 :
169 32 : BIO* filebuf = BIO_new_file(path.c_str(), "r");
170 32 : if (filebuf) {
171 32 : result = PEM_read_bio_PrivateKey(filebuf, 0, 0,
172 32 : password.empty() ? 0 : (void*)password.c_str());
173 32 : if (!result) {
174 0 : OPENDDS_SSL_LOG_ERR("PEM_read_bio_PrivateKey failed");
175 : }
176 :
177 32 : BIO_free(filebuf);
178 :
179 : } else {
180 0 : std::stringstream errmsg;
181 0 : errmsg << "failed to read file '" << path << "' using BIO_new_file";
182 0 : OPENDDS_SSL_LOG_ERR(errmsg.str().c_str());
183 0 : }
184 :
185 32 : return result;
186 : }
187 :
188 5 : EVP_PKEY* PrivateKey::EVP_PKEY_from_pem_data(const std::string& data,
189 : const std::string& password)
190 : {
191 5 : 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 5 : original_bytes.length(static_cast<unsigned int>(data.size() - 1));
196 5 : std::memcpy(original_bytes.get_buffer(), &data[1],
197 5 : 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 5 : original_bytes.length(original_bytes.length() + 1);
202 5 : original_bytes[original_bytes.length() - 1] = 0;
203 :
204 5 : EVP_PKEY* result = 0;
205 5 : BIO* filebuf = BIO_new(BIO_s_mem());
206 :
207 5 : if (filebuf) {
208 5 : if (0 >= BIO_write(filebuf, original_bytes.get_buffer(),
209 5 : original_bytes.length())) {
210 0 : OPENDDS_SSL_LOG_ERR("BIO_write failed");
211 : }
212 :
213 5 : result = PEM_read_bio_PrivateKey(filebuf, 0, 0,
214 5 : password.empty() ? 0 : (void*)password.c_str());
215 :
216 5 : if (!result) {
217 0 : OPENDDS_SSL_LOG_ERR("PEM_read_bio_PrivateKey failed");
218 : }
219 :
220 5 : BIO_free(filebuf);
221 :
222 : } else {
223 0 : std::stringstream errmsg;
224 0 : errmsg << "failed to create data '" << data << "' using BIO_new";
225 0 : OPENDDS_SSL_LOG_ERR(errmsg.str().c_str());
226 0 : }
227 :
228 5 : return result;
229 5 : }
230 :
231 0 : bool operator==(const PrivateKey& lhs, const PrivateKey& rhs)
232 : {
233 0 : if (lhs.k_ && rhs.k_) {
234 : #ifdef OPENSSL_V_3_0
235 0 : 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 0 : return lhs.k_ == rhs.k_;
241 : }
242 :
243 : } // namespace SSL
244 : } // namespace Security
245 : } // namespace OpenDDS
246 :
247 : OPENDDS_END_VERSIONED_NAMESPACE_DECL
|