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