1 /**
2 * Licensed to the University Corporation for Advanced Internet
3 * Development, Inc. (UCAID) under one or more contributor license
4 * agreements. See the NOTICE file distributed with this work for
5 * additional information regarding copyright ownership.
6 *
7 * UCAID licenses this file to you under the Apache License,
8 * Version 2.0 (the "License"); you may not use this file except
9 * in compliance with the License. You may obtain a copy of the
10 * License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17 * either express or implied. See the License for the specific
18 * language governing permissions and limitations under the License.
19 */
20
21 /**
22 * SecurityHelper.cpp
23 *
24 * A helper class for working with keys, certificates, etc.
25 */
26
27 #include "internal.h"
28 #include "logging.h"
29 #include "io/HTTPResponse.h"
30 #include "security/OpenSSLCryptoX509CRL.h"
31 #include "security/SecurityHelper.h"
32 #include "security/X509Credential.h"
33 #include "security/impl/OpenSSLSupport.h"
34 #include "soap/HTTPSOAPTransport.h"
35 #include "util/NDC.h"
36
37 #include <fstream>
38 #include <openssl/evp.h>
39 #include <openssl/pem.h>
40 #include <openssl/pkcs12.h>
41 #include <xsec/enc/XSECCryptoException.hpp>
42 #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp>
43 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyRSA.hpp>
44 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
45 #include <xsec/enc/OpenSSL/OpenSSLCryptoKeyEC.hpp>
46 #include <xercesc/util/Base64.hpp>
47
48 #include "security/OpenSSLSecurityHelper.h"
49
50 using namespace xmltooling::logging;
51 using namespace xmltooling;
52 using namespace std;
53
54 // OpenSSL password callback...
passwd_callback(char * buf,int len,int verify,void * passwd)55 static int passwd_callback(char* buf, int len, int verify, void* passwd)
56 {
57 if(!verify)
58 {
59 if(passwd && len > strlen(reinterpret_cast<char*>(passwd)))
60 {
61 strcpy(buf,reinterpret_cast<char*>(passwd));
62 return strlen(buf);
63 }
64 }
65 return 0;
66 }
67
guessEncodingFormat(const char * pathname)68 const char* SecurityHelper::guessEncodingFormat(const char* pathname)
69 {
70 const char* format=nullptr;
71 BIO* in=BIO_new(BIO_s_file_internal());
72 if (in && BIO_read_filename(in, pathname)>0) {
73 const int READSIZE = 1;
74 char buf[READSIZE];
75 int mark;
76
77 // Examine the first byte.
78 try {
79 if ((mark = BIO_tell(in)) < 0)
80 throw XMLSecurityException("Error loading file: BIO_tell() can't get the file position.");
81 if (BIO_read(in, buf, READSIZE) <= 0)
82 throw XMLSecurityException("Error loading file: BIO_read() can't read from the stream.");
83 if (BIO_seek(in, mark) < 0)
84 throw XMLSecurityException("Error loading file: BIO_seek() can't reset the file position.");
85 }
86 catch (exception&) {
87 log_openssl();
88 BIO_free(in);
89 throw;
90 }
91
92 // Check the first byte of the file. If it's some kind of DER-encoded structure
93 // (including PKCS12), it will begin with ASCII 048. Otherwise, assume it's PEM.
94 if (buf[0] != 48) {
95 format = "PEM";
96 }
97 else {
98 // Here we know it's DER-encoded, now try to parse it as a PKCS12 ASN.1 structure.
99 // If it fails, must be another kind of DER-encoded structure.
100 PKCS12* p12;
101 if ((p12=d2i_PKCS12_bio(in, nullptr)) == nullptr) {
102 format = "DER";
103 }
104 else {
105 format = "PKCS12";
106 PKCS12_free(p12);
107 }
108 }
109 }
110 if (in)
111 BIO_free(in);
112 if (format)
113 return format;
114 throw XMLSecurityException("Unable to determine encoding for file ($1).", params(1,pathname));
115 }
116
loadKeyFromFile(const char * pathname,const char * format,const char * password)117 XSECCryptoKey* SecurityHelper::loadKeyFromFile(const char* pathname, const char* format, const char* password)
118 {
119 #ifdef _DEBUG
120 NDC ndc("loadKeyFromFile");
121 #endif
122 Category& log = Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper");
123 log.info("loading private key from file (%s)", pathname);
124
125 // Native objects.
126 PKCS12* p12=nullptr;
127 EVP_PKEY* pkey=nullptr;
128
129 BIO* in=BIO_new(BIO_s_file_internal());
130 if (in && BIO_read_filename(in, pathname)>0) {
131 // If the format isn't set, try and guess it.
132 if (!format || !*format) {
133 const int READSIZE = 1;
134 char buf[READSIZE];
135 int mark;
136
137 // Examine the first byte.
138 try {
139 if ((mark = BIO_tell(in)) < 0)
140 throw XMLSecurityException("Error loading key: BIO_tell() can't get the file position.");
141 if (BIO_read(in, buf, READSIZE) <= 0)
142 throw XMLSecurityException("Error loading key: BIO_read() can't read from the stream.");
143 if (BIO_seek(in, mark) < 0)
144 throw XMLSecurityException("Error loading key: BIO_seek() can't reset the file position.");
145 }
146 catch (exception&) {
147 log_openssl();
148 BIO_free(in);
149 throw;
150 }
151
152 // Check the first byte of the file. If it's some kind of DER-encoded structure
153 // (including PKCS12), it will begin with ASCII 048. Otherwise, assume it's PEM.
154 if (buf[0] != 48) {
155 format = "PEM";
156 }
157 else {
158 // Here we know it's DER-encoded, now try to parse it as a PKCS12 ASN.1 structure.
159 // If it fails, must be another kind of DER-encoded structure.
160 if ((p12=d2i_PKCS12_bio(in, nullptr)) == nullptr) {
161 format = "DER";
162 if (BIO_seek(in, mark) < 0) {
163 log_openssl();
164 BIO_free(in);
165 throw XMLSecurityException("Error loading key: BIO_seek() can't reset the file position.");
166 }
167 }
168 else {
169 format = "PKCS12";
170 }
171 }
172 log.debug("key encoding format for (%s) dynamically resolved as (%s)", pathname, format);
173 }
174
175 // The format should be known, so parse accordingly.
176 if (!strcmp(format, "PEM")) {
177 pkey = PEM_read_bio_PrivateKey(in, nullptr, passwd_callback, const_cast<char*>(password));
178 }
179 else if (!strcmp(format, "DER")) {
180 pkey=d2i_PrivateKey_bio(in, nullptr);
181 }
182 else if (!strcmp(format, "PKCS12")) {
183 if (!p12)
184 p12 = d2i_PKCS12_bio(in, nullptr);
185 if (p12) {
186 X509* x=nullptr;
187 PKCS12_parse(p12, const_cast<char*>(password), &pkey, &x, nullptr);
188 PKCS12_free(p12);
189 X509_free(x);
190 }
191 }
192 else {
193 log.error("unknown key encoding format (%s)", format);
194 }
195 }
196 if (in)
197 BIO_free(in);
198
199 // Now map it to an XSEC wrapper.
200 if (pkey) {
201 XSECCryptoKey* ret=nullptr;
202 switch (EVP_PKEY_id(pkey)) {
203 case EVP_PKEY_RSA:
204 ret=new OpenSSLCryptoKeyRSA(pkey);
205 break;
206
207 case EVP_PKEY_DSA:
208 ret=new OpenSSLCryptoKeyDSA(pkey);
209 break;
210
211 #ifdef XSEC_OPENSSL_HAVE_EC
212 case EVP_PKEY_EC:
213 ret=new OpenSSLCryptoKeyEC(pkey);
214 break;
215 #endif
216 default:
217 log.error("unsupported private key type");
218 }
219 EVP_PKEY_free(pkey);
220 if (ret)
221 return ret;
222 }
223
224 log_openssl();
225 throw XMLSecurityException("Unable to load private key from file ($1).", params(1, pathname));
226 }
227
loadCertificatesFromFile(vector<XSECCryptoX509 * > & certs,const char * pathname,const char * format,const char * password)228 vector<XSECCryptoX509*>::size_type SecurityHelper::loadCertificatesFromFile(
229 vector<XSECCryptoX509*>& certs, const char* pathname, const char* format, const char* password
230 )
231 {
232 #ifdef _DEBUG
233 NDC ndc("loadCertificatesFromFile");
234 #endif
235 Category& log = Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper");
236 log.info("loading certificate(s) from file (%s)", pathname);
237
238 vector<XSECCryptoX509*>::size_type count = certs.size();
239
240 // Native objects.
241 X509* x=nullptr;
242 PKCS12* p12=nullptr;
243
244 BIO* in=BIO_new(BIO_s_file_internal());
245 if (in && BIO_read_filename(in, pathname)>0) {
246 // If the format isn't set, try and guess it.
247 if (!format || !*format) {
248 const int READSIZE = 1;
249 char buf[READSIZE];
250 int mark;
251
252 // Examine the first byte.
253 try {
254 if ((mark = BIO_tell(in)) < 0)
255 throw XMLSecurityException("Error loading certificate: BIO_tell() can't get the file position.");
256 if (BIO_read(in, buf, READSIZE) <= 0)
257 throw XMLSecurityException("Error loading certificate: BIO_read() can't read from the stream.");
258 if (BIO_seek(in, mark) < 0)
259 throw XMLSecurityException("Error loading certificate: BIO_seek() can't reset the file position.");
260 }
261 catch (exception&) {
262 log_openssl();
263 BIO_free(in);
264 throw;
265 }
266
267 // Check the first byte of the file. If it's some kind of DER-encoded structure
268 // (including PKCS12), it will begin with ASCII 048. Otherwise, assume it's PEM.
269 if (buf[0] != 48) {
270 format = "PEM";
271 }
272 else {
273 // Here we know it's DER-encoded, now try to parse it as a PKCS12 ASN.1 structure.
274 // If it fails, must be another kind of DER-encoded structure.
275 if ((p12=d2i_PKCS12_bio(in, nullptr)) == nullptr) {
276 format = "DER";
277 if (BIO_seek(in, mark) < 0) {
278 log_openssl();
279 BIO_free(in);
280 throw XMLSecurityException("Error loading certificate: BIO_seek() can't reset the file position.");
281 }
282 }
283 else {
284 format = "PKCS12";
285 }
286 }
287 }
288
289 // The format should be known, so parse accordingly.
290 if (!strcmp(format, "PEM")) {
291 while ((x=PEM_read_bio_X509(in, nullptr, nullptr, nullptr))) {
292 certs.push_back(new OpenSSLCryptoX509(x));
293 X509_free(x);
294 }
295 }
296 else if (!strcmp(format, "DER")) {
297 x=d2i_X509_bio(in, nullptr);
298 if (x) {
299 certs.push_back(new OpenSSLCryptoX509(x));
300 X509_free(x);
301 }
302 }
303 else if (!strcmp(format, "PKCS12")) {
304 if (!p12)
305 p12 = d2i_PKCS12_bio(in, nullptr);
306 if (p12) {
307 EVP_PKEY* pkey=nullptr;
308 STACK_OF(X509)* CAstack = sk_X509_new_null();
309 PKCS12_parse(p12, const_cast<char*>(password), &pkey, &x, &CAstack);
310 PKCS12_free(p12);
311 EVP_PKEY_free(pkey);
312 if (x) {
313 certs.push_back(new OpenSSLCryptoX509(x));
314 X509_free(x);
315 }
316 x = sk_X509_pop(CAstack);
317 while (x) {
318 certs.push_back(new OpenSSLCryptoX509(x));
319 X509_free(x);
320 x = sk_X509_pop(CAstack);
321 }
322 sk_X509_free(CAstack);
323 }
324 }
325 }
326 if (in)
327 BIO_free(in);
328
329 if (certs.size() == count) {
330 log_openssl();
331 throw XMLSecurityException("Unable to load certificate(s) from file ($1).", params(1, pathname));
332 }
333
334 return certs.size();
335 }
336
loadCRLsFromFile(vector<XSECCryptoX509CRL * > & crls,const char * pathname,const char * format)337 vector<XSECCryptoX509CRL*>::size_type SecurityHelper::loadCRLsFromFile(
338 vector<XSECCryptoX509CRL*>& crls, const char* pathname, const char* format
339 )
340 {
341 #ifdef _DEBUG
342 NDC ndc("loadCRLsFromFile");
343 #endif
344 Category& log = Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper");
345 log.info("loading CRL(s) from file (%s)", pathname);
346
347 vector<XSECCryptoX509CRL*>::size_type count = crls.size();
348
349 BIO* in=BIO_new(BIO_s_file_internal());
350 if (in && BIO_read_filename(in, pathname)>0) {
351 // If the format isn't set, try and guess it.
352 if (!format || !*format) {
353 const int READSIZE = 1;
354 char buf[READSIZE];
355 int mark;
356
357 // Examine the first byte.
358 try {
359 if ((mark = BIO_tell(in)) < 0)
360 throw XMLSecurityException("Error loading CRL: BIO_tell() can't get the file position.");
361 if (BIO_read(in, buf, READSIZE) <= 0)
362 throw XMLSecurityException("Error loading CRL: BIO_read() can't read from the stream.");
363 if (BIO_seek(in, mark) < 0)
364 throw XMLSecurityException("Error loading CRL: BIO_seek() can't reset the file position.");
365 }
366 catch (exception&) {
367 log_openssl();
368 BIO_free(in);
369 throw;
370 }
371
372 // Check the first byte of the file. If it's some kind of DER-encoded structure
373 // it will begin with ASCII 048. Otherwise, assume it's PEM.
374 if (buf[0] != 48) {
375 format = "PEM";
376 }
377 else {
378 format = "DER";
379 }
380 log.debug("CRL encoding format for (%s) dynamically resolved as (%s)", pathname, format);
381 }
382
383 X509_CRL* crl=nullptr;
384 if (!strcmp(format, "PEM")) {
385 while ((crl=PEM_read_bio_X509_CRL(in, nullptr, nullptr, nullptr))) {
386 crls.push_back(new OpenSSLCryptoX509CRL(crl));
387 X509_CRL_free(crl);
388 }
389 }
390 else if (!strcmp(format, "DER")) {
391 crl=d2i_X509_CRL_bio(in, nullptr);
392 if (crl) {
393 crls.push_back(new OpenSSLCryptoX509CRL(crl));
394 X509_CRL_free(crl);
395 }
396 }
397 else {
398 log.error("unknown CRL encoding format (%s)", format);
399 }
400 }
401 if (in)
402 BIO_free(in);
403
404 if (crls.size() == count) {
405 log_openssl();
406 throw XMLSecurityException("Unable to load CRL(s) from file ($1).", params(1, pathname));
407 }
408
409 return crls.size();
410 }
411
loadKeyFromURL(SOAPTransport & transport,const char * backing,const char * format,const char * password)412 XSECCryptoKey* SecurityHelper::loadKeyFromURL(SOAPTransport& transport, const char* backing, const char* format, const char* password)
413 {
414 // Fetch the data.
415 transport.send();
416 istream& msg = transport.receive();
417
418 // Check for "not modified" status.
419 if (dynamic_cast<HTTPSOAPTransport*>(&transport) && transport.getStatusCode() == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED)
420 throw (long)HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED;
421
422 // Dump to output file.
423 ofstream out(backing, fstream::trunc|fstream::binary);
424 out << msg.rdbuf();
425 out.close();
426
427 return loadKeyFromFile(backing, format, password);
428 }
429
loadCertificatesFromURL(vector<XSECCryptoX509 * > & certs,SOAPTransport & transport,const char * backing,const char * format,const char * password)430 vector<XSECCryptoX509*>::size_type SecurityHelper::loadCertificatesFromURL(
431 vector<XSECCryptoX509*>& certs, SOAPTransport& transport, const char* backing, const char* format, const char* password
432 )
433 {
434 transport.send();
435 istream& msg = transport.receive();
436
437 // Check for "not modified" status.
438 if (dynamic_cast<HTTPSOAPTransport*>(&transport) && transport.getStatusCode() == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED)
439 throw (long)HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED;
440
441 // Dump to output file.
442 ofstream out(backing, fstream::trunc|fstream::binary);
443 out << msg.rdbuf();
444 out.close();
445
446 return loadCertificatesFromFile(certs, backing, format, password);
447 }
448
loadCRLsFromURL(vector<XSECCryptoX509CRL * > & crls,SOAPTransport & transport,const char * backing,const char * format)449 vector<XSECCryptoX509CRL*>::size_type SecurityHelper::loadCRLsFromURL(
450 vector<XSECCryptoX509CRL*>& crls, SOAPTransport& transport, const char* backing, const char* format
451 )
452 {
453 // Fetch the data.
454 transport.send();
455 istream& msg = transport.receive();
456
457 // Check for "not modified" status.
458 if (dynamic_cast<HTTPSOAPTransport*>(&transport) && transport.getStatusCode() == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED)
459 throw (long)HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED;
460
461 // Dump to output file.
462 ofstream out(backing, fstream::trunc|fstream::binary);
463 out << msg.rdbuf();
464 out.close();
465
466 return loadCRLsFromFile(crls, backing, format);
467 }
468
matches(const XSECCryptoKey & key1,const XSECCryptoKey & key2)469 bool SecurityHelper::matches(const XSECCryptoKey& key1, const XSECCryptoKey& key2)
470 {
471 if (key1.getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL ||
472 key2.getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
473 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").warn("comparison of non-OpenSSL keys not supported");
474 return false;
475 }
476
477 if (key1.getKeyType()==XSECCryptoKey::KEY_RSA_PUBLIC || key1.getKeyType()==XSECCryptoKey::KEY_RSA_PAIR) {
478 return OpenSSLSecurityHelper::matchesPublic(static_cast<const OpenSSLCryptoKeyRSA&>(key1).getOpenSSLRSA(), key2);
479 }
480
481 if (key1.getKeyType()==XSECCryptoKey::KEY_RSA_PRIVATE) {
482 return OpenSSLSecurityHelper::matchesPrivate(static_cast<const OpenSSLCryptoKeyRSA&>(key1).getOpenSSLRSA(), key2);
483 }
484
485 if (key1.getKeyType()==XSECCryptoKey::KEY_DSA_PUBLIC || key1.getKeyType()==XSECCryptoKey::KEY_DSA_PAIR) {
486 return OpenSSLSecurityHelper::matchesPublic(static_cast<const OpenSSLCryptoKeyDSA&>(key1).getOpenSSLDSA(), key2);
487 }
488
489 if (key1.getKeyType()==XSECCryptoKey::KEY_DSA_PRIVATE) {
490 return OpenSSLSecurityHelper::matchesPrivate(static_cast<const OpenSSLCryptoKeyDSA&>(key1).getOpenSSLDSA(), key2);
491 }
492
493 #ifdef XSEC_OPENSSL_HAVE_EC
494 if (key1.getKeyType()==XSECCryptoKey::KEY_EC_PUBLIC || key1.getKeyType()==XSECCryptoKey::KEY_EC_PAIR) {
495 return OpenSSLSecurityHelper::matchesPublic(static_cast<const OpenSSLCryptoKeyEC&>(key1).getOpenSSLEC(), key2);
496 }
497
498 if (key1.getKeyType()==XSECCryptoKey::KEY_EC_PRIVATE) {
499 return OpenSSLSecurityHelper::matchesPrivate(static_cast<const OpenSSLCryptoKeyEC&>(key1).getOpenSSLEC(), key2);
500 }
501 #endif
502
503 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").warn("unsupported key type for comparison");
504 return false;
505 }
506
doHash(const char * hashAlg,const char * buf,unsigned long buflen,bool toHex)507 string SecurityHelper::doHash(const char* hashAlg, const char* buf, unsigned long buflen, bool toHex)
508 {
509 static char DIGITS[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
510 string ret;
511
512 const EVP_MD* md = EVP_get_digestbyname(hashAlg);
513 if (!md) {
514 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").error("hash algorithm (%s) not available", hashAlg);
515 return ret;
516 }
517
518 BIO* chain = BIO_new(BIO_s_mem());
519 BIO* b = BIO_new(BIO_f_md());
520 BIO_set_md(b, md);
521 chain = BIO_push(b, chain);
522 BIO_write(chain, buf, buflen);
523 BIO_flush(chain);
524
525 char digest[EVP_MAX_MD_SIZE];
526 int len = BIO_gets(chain, digest, EVP_MD_size(md));
527 BIO_free_all(chain);
528 if (len != EVP_MD_size(md)) {
529 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").error(
530 "hash result length (%d) did not match expected value (%d)", len, EVP_MD_size(md)
531 );
532 return ret;
533 }
534 if (toHex) {
535 for (int i=0; i < len; ++i) {
536 ret += (DIGITS[((unsigned char)(0xF0 & digest[i])) >> 4 ]);
537 ret += (DIGITS[0x0F & digest[i]]);
538 }
539 }
540 else {
541 for (int i=0; i < len; ++i) {
542 ret += digest[i];
543 }
544 }
545 return ret;
546 }
547
getDEREncoding(const XSECCryptoKey & key,const char * hash,bool nowrap)548 string SecurityHelper::getDEREncoding(const XSECCryptoKey& key, const char* hash, bool nowrap)
549 {
550 string ret;
551
552 if (key.getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
553 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").warn("encoding of non-OpenSSL keys not supported");
554 return ret;
555 }
556
557 const RSA* rsa = nullptr;
558 const DSA* dsa = nullptr;
559 #ifdef XSEC_OPENSSL_HAVE_EC
560 const EC_KEY* ec = nullptr;
561 #endif
562
563 if (key.getKeyType() == XSECCryptoKey::KEY_RSA_PUBLIC || key.getKeyType() == XSECCryptoKey::KEY_RSA_PAIR) {
564 rsa = static_cast<const OpenSSLCryptoKeyRSA&>(key).getOpenSSLRSA();
565 if (!rsa) {
566 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").warn("key was not populated");
567 return ret;
568 }
569 }
570 else if (key.getKeyType() == XSECCryptoKey::KEY_DSA_PUBLIC || key.getKeyType() == XSECCryptoKey::KEY_DSA_PAIR) {
571 dsa = static_cast<const OpenSSLCryptoKeyDSA&>(key).getOpenSSLDSA();
572 if (!dsa) {
573 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").warn("key was not populated");
574 return ret;
575 }
576 }
577 #ifdef XSEC_OPENSSL_HAVE_EC
578 else if (key.getKeyType() == XSECCryptoKey::KEY_EC_PUBLIC || key.getKeyType() == XSECCryptoKey::KEY_EC_PAIR) {
579 ec = static_cast<const OpenSSLCryptoKeyEC&>(key).getOpenSSLEC();
580 if (!ec) {
581 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").warn("key was not populated");
582 return ret;
583 }
584 }
585 #endif
586 else {
587 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").warn("public key type not supported");
588 return ret;
589 }
590
591 const EVP_MD* md=nullptr;
592 if (hash) {
593 md = EVP_get_digestbyname(hash);
594 if (!md) {
595 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").error("hash algorithm (%s) not available", hash);
596 return ret;
597 }
598 }
599
600 BIO* chain = BIO_new(BIO_s_mem());
601 BIO* b = BIO_new(BIO_f_base64());
602 if (nowrap)
603 BIO_set_flags(b, BIO_FLAGS_BASE64_NO_NL);
604 chain = BIO_push(b, chain);
605 if (md) {
606 b = BIO_new(BIO_f_md());
607 BIO_set_md(b, md);
608 chain = BIO_push(b, chain);
609 }
610
611 if (rsa)
612 i2d_RSA_PUBKEY_bio(chain, const_cast<RSA*>(rsa));
613 else if (dsa)
614 i2d_DSA_PUBKEY_bio(chain, const_cast<DSA*>(dsa));
615 #ifdef XSEC_OPENSSL_HAVE_EC
616 else
617 i2d_EC_PUBKEY_bio(chain, const_cast<EC_KEY*>(ec));
618 #endif
619
620 BIO_flush(chain);
621 if (md) {
622 char digest[EVP_MAX_MD_SIZE];
623 int len = BIO_gets(chain, digest, EVP_MD_size(md));
624 if (len != EVP_MD_size(md)) {
625 BIO_free_all(chain);
626 return ret;
627 }
628 b = BIO_pop(chain);
629 BIO_free(chain);
630 chain = b;
631 BIO_reset(chain);
632 BIO_write(chain, digest, len);
633 BIO_flush(chain);
634 }
635 BUF_MEM* bptr=nullptr;
636 BIO_get_mem_ptr(chain, &bptr);
637 if (bptr && bptr->length > 0)
638 ret.append(bptr->data, bptr->length);
639 BIO_free_all(chain);
640
641 return ret;
642 }
643
getDEREncoding(const XSECCryptoX509 & cert,const char * hash,bool nowrap)644 string SecurityHelper::getDEREncoding(const XSECCryptoX509& cert, const char* hash, bool nowrap)
645 {
646 string ret;
647
648 if (cert.getProviderName()!=DSIGConstants::s_unicodeStrPROVOpenSSL) {
649 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").warn("encoding of non-OpenSSL keys not supported");
650 return ret;
651 }
652
653 const EVP_MD* md=nullptr;
654 if (hash) {
655 md = EVP_get_digestbyname(hash);
656 if (!md) {
657 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").error("hash algorithm (%s) not available", hash);
658 return ret;
659 }
660 }
661
662 const X509* x = static_cast<const OpenSSLCryptoX509&>(cert).getOpenSSLX509();
663 EVP_PKEY* key = X509_get_pubkey(const_cast<X509*>(x));
664
665 BIO* chain = BIO_new(BIO_s_mem());
666 BIO* b = BIO_new(BIO_f_base64());
667 if (nowrap)
668 BIO_set_flags(b, BIO_FLAGS_BASE64_NO_NL);
669 chain = BIO_push(b, chain);
670 if (md) {
671 b = BIO_new(BIO_f_md());
672 BIO_set_md(b, md);
673 chain = BIO_push(b, chain);
674 }
675 i2d_PUBKEY_bio(chain, key);
676 EVP_PKEY_free(key);
677 BIO_flush(chain);
678 if (md) {
679 char digest[EVP_MAX_MD_SIZE];
680 int len = BIO_gets(chain, digest, EVP_MD_size(md));
681 if (len != EVP_MD_size(md)) {
682 BIO_free_all(chain);
683 return ret;
684 }
685 b = BIO_pop(chain);
686 BIO_free(chain);
687 chain = b;
688 BIO_reset(chain);
689 BIO_write(chain, digest, len);
690 BIO_flush(chain);
691 }
692 BUF_MEM* bptr=nullptr;
693 BIO_get_mem_ptr(chain, &bptr);
694 if (bptr && bptr->length > 0)
695 ret.append(bptr->data, bptr->length);
696 BIO_free_all(chain);
697 return ret;
698 }
699
getDEREncoding(const Credential & cred,const char * hash,bool nowrap)700 string SecurityHelper::getDEREncoding(const Credential& cred, const char* hash, bool nowrap)
701 {
702 const X509Credential* x509 = dynamic_cast<const X509Credential*>(&cred);
703 if (x509 && !x509->getEntityCertificateChain().empty())
704 return getDEREncoding(*(x509->getEntityCertificateChain().front()), hash, nowrap);
705 else if (cred.getPublicKey())
706 return getDEREncoding(*(cred.getPublicKey()), hash, nowrap);
707 return "";
708 }
709
fromDEREncoding(const char * buf,unsigned long buflen,bool base64)710 XSECCryptoKey* SecurityHelper::fromDEREncoding(const char* buf, unsigned long buflen, bool base64)
711 {
712 XMLSize_t x;
713 XMLByte* decoded=nullptr;
714 if (base64) {
715 decoded = xercesc::Base64::decode(reinterpret_cast<const XMLByte*>(buf), &x);
716 if (!decoded) {
717 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").error("base64 decode failed");
718 return nullptr;
719 }
720 }
721
722 BIO* b = BIO_new_mem_buf((void*)(base64 ? (char*)decoded : buf), (base64 ? x : buflen));
723 EVP_PKEY* pkey = d2i_PUBKEY_bio(b, nullptr);
724 BIO_free(b);
725 if (base64) {
726 XMLString::release((char**)&decoded);
727 }
728
729 if (pkey) {
730 // Now map it to an XSEC wrapper.
731 XSECCryptoKey* ret = nullptr;
732 try {
733 switch (EVP_PKEY_id(pkey)) {
734 case EVP_PKEY_RSA:
735 ret = new OpenSSLCryptoKeyRSA(pkey);
736 break;
737
738 case EVP_PKEY_DSA:
739 ret = new OpenSSLCryptoKeyDSA(pkey);
740 break;
741
742 #ifdef XSEC_OPENSSL_HAVE_EC
743 case EVP_PKEY_EC:
744 ret = new OpenSSLCryptoKeyEC(pkey);
745 break;
746 #endif
747 default:
748 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").error("unsupported public key type");
749 }
750 }
751 catch (XSECCryptoException& ex) {
752 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").error(ex.getMsg());
753 }
754 EVP_PKEY_free(pkey);
755 return ret;
756 }
757
758 return nullptr;
759 }
760
fromDEREncoding(const XMLCh * buf)761 XSECCryptoKey* SecurityHelper::fromDEREncoding(const XMLCh* buf)
762 {
763 XMLSize_t x;
764 XMLByte* decoded = xercesc::Base64::decodeToXMLByte(buf, &x);
765 if (!decoded) {
766 Category::getInstance(XMLTOOLING_LOGCAT ".SecurityHelper").error("base64 decode failed");
767 return nullptr;
768 }
769 XSECCryptoKey* ret = fromDEREncoding((const char*)decoded, x, false);
770 XMLString::release((char**)&decoded);
771 return ret;
772 }
773