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