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