| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| /* |
| * XSEC |
| * |
| * InteropResolver := Class to resolve key elements into certificates for |
| * merlin-18 interop test |
| * |
| * $Id$ |
| * |
| */ |
| |
| // XSEC |
| |
| #include "InteropResolver.hpp" |
| |
| #include <xsec/framework/XSECDefs.hpp> |
| #include <xsec/enc/XSECKeyInfoResolver.hpp> |
| #include <xsec/dsig/DSIGKeyInfoX509.hpp> |
| #include <xsec/dsig/DSIGKeyInfoName.hpp> |
| #include <xsec/enc/OpenSSL/OpenSSLCryptoX509.hpp> |
| |
| #include <xsec/enc/OpenSSL/OpenSSLCryptoBase64.hpp> |
| #include <xsec/enc/OpenSSL/OpenSSLSupport.hpp> |
| |
| #include "../../utils/XSECDOMUtils.hpp" |
| |
| #include <xercesc/util/Janitor.hpp> |
| #include <xercesc/util/XMLUniDefs.hpp> |
| |
| XERCES_CPP_NAMESPACE_USE |
| |
| #include <iostream> |
| |
| #if defined (XSEC_HAVE_OPENSSL) |
| |
| InteropResolver::InteropResolver(const XMLCh * baseURI) { |
| |
| if (baseURI != NULL) |
| mp_baseURI = XMLString::replicate(baseURI); |
| else |
| mp_baseURI = NULL; |
| |
| m_searchStarted = false; |
| m_searchFinished = false; |
| |
| #if !defined(_WIN32) |
| m_fcount = 0; |
| #endif |
| |
| } |
| |
| |
| InteropResolver::~InteropResolver() { |
| |
| if (mp_baseURI != NULL) |
| XSEC_RELEASE_XMLCH(mp_baseURI); |
| |
| } |
| |
| #if defined(_WIN32) |
| |
| void reverseSlash(safeBuffer &path) { |
| |
| for (int i = 0; i < (int) strlen(path.rawCharBuffer()); ++i) { |
| |
| if (path[i] == '/') |
| path[i] = '\\'; |
| |
| } |
| |
| } |
| |
| #endif |
| |
| |
| X509 * InteropResolver::nextFile2Cert(void) const { |
| |
| |
| if (m_searchFinished) |
| return NULL; |
| |
| int res; |
| |
| if (!m_searchStarted) { |
| |
| char * base = XMLString::transcode(mp_baseURI); |
| safeBuffer path = base; |
| XSEC_RELEASE_XMLCH(base); |
| |
| path.sbStrcatIn("/certs/*.crt"); |
| |
| #if defined(_WIN32) |
| |
| // Reverse the "backslash" characters |
| |
| reverseSlash(path); |
| |
| m_handle = (long) _findfirst(path.rawCharBuffer(), &m_finder); |
| res = m_handle; |
| #else |
| if (glob(path.rawCharBuffer(), 0, NULL, &m_globbuf) != 0) |
| res = -1; |
| else |
| res = 0; |
| #endif |
| |
| m_searchStarted = true; |
| |
| } |
| else { |
| |
| #if defined(_WIN32) |
| res = _findnext(m_handle, &m_finder); |
| #else |
| if (m_fcount == (int) m_globbuf.gl_pathc) |
| res = -1; |
| else |
| res = 0; |
| #endif |
| |
| } |
| |
| if (res == -1) { |
| |
| m_searchFinished = true; |
| return NULL; |
| |
| } |
| |
| /* |
| * Create the OpenSSL BIOs necessary to read in the X509 cert |
| */ |
| |
| BIO * bioCert; |
| if ((bioCert = BIO_new(BIO_s_file())) == NULL) { |
| |
| std::cerr << "Error opening certificate file\n\n"; |
| exit (1); |
| |
| } |
| |
| // Create the filename |
| safeBuffer fname; |
| #if defined(_WIN32) |
| fname.sbTranscodeIn(mp_baseURI); |
| fname.sbStrcatIn("/certs/"); |
| fname.sbStrcatIn(m_finder.name); |
| reverseSlash(fname); |
| #else |
| fname.sbStrcpyIn(m_globbuf.gl_pathv[m_fcount++]); |
| #endif |
| |
| if (BIO_read_filename(bioCert, fname.rawCharBuffer()) <= 0) { |
| |
| std::cerr << "Error opening certificate file\n" << fname.rawCharBuffer() << std::endl; |
| return NULL; |
| |
| } |
| |
| X509 * x509 = d2i_X509_bio(bioCert, NULL); |
| BIO_free(bioCert); |
| |
| return x509; |
| |
| |
| } |
| |
| X509_NAME * X509_NAME_create_from_txt(const char * n) { |
| |
| // I'm sure there must be a function to do this in OpenSSL, but I'm |
| // darned if I can find it. |
| |
| int idx = 0; |
| int j; |
| bool ok = true; |
| |
| X509_NAME_ENTRY * entries[10]; |
| int entCount = 0; |
| |
| char * name = new char[strlen(n)]; |
| char * value = new char[strlen(n)]; |
| while (true) { |
| |
| while (n[idx] == ' ' || |
| n[idx] == '\t' || |
| n[idx] == '\n' || |
| n[idx] == '\r') |
| idx++; |
| |
| if (n[idx] == 0) |
| break; |
| |
| j = 0; |
| while (n[idx] != 0 && n[idx] != '=') { |
| name[j++] = n[idx++]; |
| } |
| |
| if (j == 0 || n[idx] == 0) { |
| ok = false; |
| break; |
| } |
| |
| name[j] = '\0'; |
| idx++; |
| |
| // Now the value |
| j = 0; |
| while (n[idx] != 0 && n[idx] != '\n' && n[idx] != '\r') { |
| |
| if (n[idx] == ',') { |
| |
| // find out if this is a marker for end of RDN |
| int jdx = idx + 1; |
| while (n[jdx] != '\0' && n[jdx] != '=' && n[jdx] != ',') |
| ++jdx; |
| |
| if (n[jdx] != ',') |
| break; |
| |
| } |
| |
| value[j++] = n[idx++]; |
| |
| } |
| |
| if (j == 0) { |
| ok = false; |
| break; |
| } |
| |
| if (n[idx] != 0) |
| idx++; |
| |
| value[j] = '\0'; |
| X509_NAME_ENTRY * xne; |
| |
| xne = X509_NAME_ENTRY_create_by_txt(NULL, name, MBSTRING_ASC, (unsigned char *) value, -1); |
| |
| if (xne != NULL) { |
| entries[entCount++] = xne; |
| if (entCount == 10) { |
| ok = false; |
| break; |
| } |
| } |
| else { |
| ok = false; |
| break; |
| } |
| |
| } |
| |
| delete[] name; |
| delete[] value; |
| |
| X509_NAME *ret = NULL; |
| int i; |
| |
| if (ok) { |
| |
| // Create the return value |
| ret = X509_NAME_new(); |
| for (i = entCount - 1; i >= 0; --i) { |
| |
| if (!X509_NAME_add_entry(ret, entries[i], -1, 0)) { |
| X509_NAME_free(ret); |
| ret = NULL; |
| break; |
| } |
| } |
| |
| } |
| |
| // Clean out the entries |
| for (i = 0; i < entCount; ++i) { |
| X509_NAME_ENTRY_free(entries[i]); |
| } |
| |
| return ret; |
| } |
| |
| bool InteropResolver::checkMatch(const DSIGKeyInfoList * lst, X509 * x) const { |
| |
| // Check if the parameters in x match the required certificate |
| |
| |
| int sz = (int) lst->getSize(); |
| const DSIGKeyInfo* k; |
| |
| for (int i = 0; i < sz; ++i) { |
| |
| k = lst->item(i); |
| |
| if (k->getKeyInfoType() == DSIGKeyInfo::KEYINFO_X509) { |
| |
| const DSIGKeyInfoX509 * kx = static_cast<const DSIGKeyInfoX509 *>(k); |
| |
| const XMLCh * serial = kx->getX509IssuerSerialNumber(); |
| |
| if (serial != NULL) { |
| |
| char * cserial = XMLString::transcode(serial); |
| char * xserial; |
| |
| BIGNUM * bnserial = ASN1_INTEGER_to_BN(X509_get_serialNumber(x), NULL); |
| xserial = BN_bn2dec(bnserial); |
| BN_free(bnserial); |
| |
| if (strcmp(xserial, cserial) == 0) { |
| |
| OPENSSL_free(xserial); |
| XSEC_RELEASE_XMLCH(cserial); |
| return true; |
| |
| } |
| //delete[] xserial; |
| XSEC_RELEASE_XMLCH(cserial); |
| OPENSSL_free(xserial); |
| |
| } |
| |
| /* |
| * Either it's not a serial number, or we didn't pass, so lets |
| * look at the next option. |
| */ |
| |
| const XMLCh * ski = kx->getX509SKI(); |
| |
| if (ski != NULL) { |
| |
| char * cski = XMLString::transcode(ski); |
| int clen = (int) strlen(cski); |
| unsigned char * xski = new unsigned char[clen]; |
| ArrayJanitor <unsigned char> j_xski(xski); |
| |
| // Decode |
| |
| OpenSSLCryptoBase64 b64; |
| b64.decodeInit(); |
| int xlen = b64.decode((unsigned char *) cski, clen, xski, clen); |
| xlen += b64.decodeFinish(&xski[xlen], clen - xlen); |
| XSEC_RELEASE_XMLCH(cski); |
| |
| if (xlen != 0) { |
| |
| // Have a buffer with a number in it |
| const STACK_OF(X509_EXTENSION) *exts; |
| exts = X509_get0_extensions(x); |
| |
| if (exts != NULL) { |
| |
| // Find the Subject Key Identifier OID |
| X509_EXTENSION * ext; |
| ASN1_OBJECT * objski = OBJ_nid2obj(NID_subject_key_identifier); |
| int extn = X509v3_get_ext_by_OBJ(exts, objski, -1); |
| if (extn != -1) { |
| // Dummy up an OCTET_STRING from the xski |
| unsigned char * octxski = new unsigned char[xlen + 2]; |
| ArrayJanitor<unsigned char> j_octxski(octxski); |
| |
| octxski[0] = 4; |
| octxski[1] = xlen; |
| memcpy(&octxski[2], xski, xlen); |
| |
| ext = sk_X509_EXTENSION_value(exts,extn); |
| ASN1_OCTET_STRING *skid = X509_EXTENSION_get_data(ext); |
| ASN1_OCTET_STRING * xskid = ASN1_OCTET_STRING_new(); |
| |
| ASN1_STRING_set(xskid, octxski, xlen+2); |
| |
| if (ASN1_OCTET_STRING_cmp(xskid, skid) == 0) { |
| ASN1_OCTET_STRING_free(xskid); |
| return true; |
| } |
| } |
| |
| } |
| |
| } |
| |
| } |
| |
| /* Not a subject key identifier */ |
| const XMLCh *sn = kx->getX509SubjectName(); |
| |
| if (sn != NULL) { |
| |
| char * csn = XMLString::transcode(sn); |
| |
| X509_NAME * x509name = X509_get_subject_name(x); |
| X509_NAME * snname = X509_NAME_create_from_txt(csn); |
| XSEC_RELEASE_XMLCH(csn); |
| |
| if (snname != NULL) { |
| |
| if (!X509_NAME_cmp(x509name, snname)) { |
| X509_NAME_free(snname); |
| return true; |
| } |
| |
| X509_NAME_free(snname); |
| } |
| } |
| } |
| } |
| |
| return false; |
| |
| } |
| |
| |
| XSECCryptoKey * InteropResolver::openCertURI(const XMLCh * uri) const { |
| |
| // Open a certificate from a file URI relative to the signature file |
| BIO * bioCert; |
| if ((bioCert = BIO_new(BIO_s_file())) == NULL) { |
| |
| return NULL; |
| |
| } |
| |
| safeBuffer fname; |
| char * u = XMLString::transcode(uri); |
| fname.sbTranscodeIn(mp_baseURI); |
| fname.sbStrcatIn("/"); |
| fname.sbStrcatIn(u); |
| XSEC_RELEASE_XMLCH(u); |
| |
| #if defined(_WIN32) |
| reverseSlash(fname); |
| #endif |
| |
| if (BIO_read_filename(bioCert, fname.rawCharBuffer()) <= 0) { |
| |
| return NULL; |
| |
| } |
| |
| X509 * x509 = d2i_X509_bio(bioCert, NULL); |
| BIO_free(bioCert); |
| |
| OpenSSLCryptoX509 oX509(x509); |
| X509_free(x509); |
| |
| return oX509.clonePublicKey(); |
| |
| } |
| |
| XSECCryptoKey * InteropResolver::resolveKey(const DSIGKeyInfoList * lst) const { |
| |
| |
| // First check if this has an X509 cert + an X509 CRL |
| const XMLCh * b64cert = NULL; |
| const XMLCh * b64crl = NULL; |
| |
| int lstSize = (int) lst->getSize(); |
| |
| for (int i = 0; i < lstSize; ++i) { |
| |
| const DSIGKeyInfo * ki; |
| ki = lst->item(i); |
| const XMLCh * rawuri; |
| |
| if (ki->getKeyInfoType() == DSIGKeyInfo::KEYINFO_X509) { |
| |
| const DSIGKeyInfoX509 * kix509 = static_cast<const DSIGKeyInfoX509 *>(ki); |
| |
| if ((rawuri = kix509->getRawRetrievalURI()) != NULL) { |
| |
| // We have a raw certificate by de-reference |
| // Assume it is just a file dereference and open the cert |
| |
| return openCertURI(rawuri); |
| |
| } |
| |
| if (kix509->getCertificateListSize() == 1) { |
| |
| b64cert = kix509->getCertificateItem(0); |
| |
| } |
| |
| if (b64crl == NULL) { |
| |
| b64crl = kix509->getX509CRL(); |
| |
| } |
| } |
| |
| else if (ki->getKeyInfoType() == DSIGKeyInfo::KEYINFO_NAME) { |
| |
| const DSIGKeyInfoName * kn = static_cast<const DSIGKeyInfoName *>(ki); |
| |
| if (kn->getKeyName() != NULL) { |
| |
| static XMLCh certStr[] = { |
| XERCES_CPP_NAMESPACE_QUALIFIER chLatin_c, |
| XERCES_CPP_NAMESPACE_QUALIFIER chLatin_e, |
| XERCES_CPP_NAMESPACE_QUALIFIER chLatin_r, |
| XERCES_CPP_NAMESPACE_QUALIFIER chLatin_t, |
| XERCES_CPP_NAMESPACE_QUALIFIER chLatin_s, |
| XERCES_CPP_NAMESPACE_QUALIFIER chForwardSlash, |
| XERCES_CPP_NAMESPACE_QUALIFIER chNull |
| }; |
| static XMLCh extStr[] = { |
| XERCES_CPP_NAMESPACE_QUALIFIER chPeriod, |
| XERCES_CPP_NAMESPACE_QUALIFIER chLatin_c, |
| XERCES_CPP_NAMESPACE_QUALIFIER chLatin_r, |
| XERCES_CPP_NAMESPACE_QUALIFIER chLatin_t, |
| XERCES_CPP_NAMESPACE_QUALIFIER chNull |
| }; |
| |
| safeBuffer fname; |
| fname = certStr; |
| fname.sbXMLChCat(kn->getKeyName()); |
| fname.sbXMLChCat(extStr); |
| |
| fname.sbStrlwr(); |
| |
| return openCertURI(fname.rawXMLChBuffer()); |
| |
| } |
| |
| } |
| |
| } |
| |
| if (b64cert != NULL && b64crl != NULL) { |
| |
| // We have a certificate and a crl, lets get the cert and check in the crl |
| |
| OpenSSLCryptoBase64 b64; |
| char * transb64cert = XMLString::transcode(b64cert); |
| unsigned char * x509buf = new unsigned char[strlen(transb64cert)]; |
| ArrayJanitor<unsigned char> j_x509buf(x509buf); |
| |
| int x509bufLen; |
| |
| X509 *x; |
| |
| b64.decodeInit(); |
| x509bufLen = b64.decode((unsigned char *) transb64cert, (unsigned int) strlen(transb64cert), x509buf, (unsigned int) strlen(transb64cert)); |
| x509bufLen += b64.decodeFinish(&x509buf[x509bufLen], (unsigned int) strlen(transb64cert) - x509bufLen); |
| XSEC_RELEASE_XMLCH(transb64cert); |
| |
| if (x509bufLen > 0) { |
| #if defined(XSEC_OPENSSL_D2IX509_CONST_BUFFER) |
| x = d2i_X509(NULL, (const unsigned char **) (&x509buf), x509bufLen |
| ); |
| #else |
| x = d2i_X509(NULL, &x509buf, x509bufLen); |
| #endif |
| } |
| else |
| return NULL; // Something has gone wrong |
| |
| if (x == NULL) |
| return NULL; |
| |
| // Now the CRL |
| char * transb64crl = XMLString::transcode(b64crl); |
| unsigned char * crlbuf = new unsigned char[strlen(transb64crl)]; |
| ArrayJanitor<unsigned char> j_crlbuf(crlbuf); |
| |
| int crlbufLen; |
| |
| X509_CRL * c; |
| |
| b64.decodeInit(); |
| crlbufLen = b64.decode((unsigned char*) transb64crl, (unsigned int) strlen(transb64crl), crlbuf, (unsigned int) strlen(transb64crl)); |
| crlbufLen += b64.decodeFinish(&crlbuf[crlbufLen], (unsigned int) strlen(transb64crl) - crlbufLen); |
| XSEC_RELEASE_XMLCH(transb64crl); |
| |
| if (crlbufLen > 0) { |
| #if defined(XSEC_OPENSSL_D2IX509_CONST_BUFFER) |
| c = d2i_X509_CRL(NULL, (const unsigned char **) (&crlbuf), crlbufLen); |
| #else |
| c = d2i_X509_CRL(NULL, &crlbuf, crlbufLen); |
| #endif |
| } |
| else |
| return NULL; // Something has gone wrong |
| |
| if (c == NULL) |
| return NULL; |
| |
| // Now check if the cert is in the CRL (code lifted from OpenSSL x509_vfy.c |
| |
| int idx; |
| X509_REVOKED *rtmp = X509_REVOKED_new(); |
| |
| /* Look for serial number of certificate in CRL */ |
| X509_REVOKED_set_serialNumber(rtmp, X509_get_serialNumber(x)); |
| idx = sk_X509_REVOKED_find(X509_CRL_get_REVOKED(c), rtmp); |
| |
| X509_REVOKED_free(rtmp); |
| |
| /* Not found: OK */ |
| |
| if(idx != -1) { |
| |
| std::cerr << "Warning - certificate revoked in attached CRL" << std::endl; |
| |
| } |
| |
| OpenSSLCryptoX509 ox(x); |
| X509_free(x); |
| X509_CRL_free(c); |
| return ox.clonePublicKey(); |
| |
| } |
| |
| // Do a run through each match in the directory |
| |
| while (m_searchFinished == false) { |
| |
| X509 * x = nextFile2Cert(); |
| |
| if (x != NULL) { |
| |
| if (checkMatch(lst, x)) { |
| |
| OpenSSLCryptoX509 ox(x); |
| X509_free(x); |
| return ox.clonePublicKey(); |
| |
| } |
| |
| } |
| X509_free(x); |
| |
| } |
| |
| return NULL; |
| |
| } |
| |
| |
| |
| XSECKeyInfoResolver * InteropResolver::clone(void) const { |
| |
| return new InteropResolver(mp_baseURI); |
| |
| } |
| |
| |
| |
| #endif /* XSEC_HAVE_OPENSSL */ |