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