blob: d40600eec3bd644be54ad4c26d90cbee568c3633 [file] [log] [blame]
/**
* 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 */