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