/**
 * Searches certificate by subject and returns a copy of it if found.
 * If not found returns <code>NULL</code>.
 * NB! The returned certificate must be freed with OpenSSL function X509_free(X509* cert).
 *
 * @param subject certificate subject.
 * @return returns copy of found certificate or <code>NULL</code> if certificate was not found.
 * @throws IOException exception is thrown if copying certificate failed.
 */
X509Cert X509CertStore::findIssuer(const X509Cert &cert, const set<string> &type) const
{
    activate(cert.issuerName("C"));
    SCOPE(AUTHORITY_KEYID, akid, X509_get_ext_d2i(cert.handle(), NID_authority_key_identifier, nullptr, nullptr));
    for(const TSL::Service &s: *d)
    {
        if(type.find(s.type) == type.cend())
            continue;
        for(const X509Cert &i: s.certs)
        {
            if(!akid || !akid->keyid)
            {
                if(X509_NAME_cmp(X509_get_subject_name(i.handle()), X509_get_issuer_name(cert.handle())))
                    return i;
            }
            else
            {
                SCOPE(ASN1_OCTET_STRING, skid, X509_get_ext_d2i(i.handle(), NID_subject_key_identifier, nullptr, nullptr));
                if(skid.get() && ASN1_OCTET_STRING_cmp(akid->keyid, skid.get()) == 0)
                    return i;
            }
        }
    }
    return X509Cert();
}
/**
 * Validate signature value.
 *
 * @throws throws exception if signature value did not match.
 */
void digidoc::SignatureBES::checkSignatureValue() const throw(SignatureException)
{
	DEBUG("SignatureBES::checkSignatureValue()");
	try
	{
	    // Initialize RSA crypter.
        X509Cert cert = getSigningCertificate();
        RSACrypt rsa(cert.handle());

        // Calculate SHA digest of the Signature->SignedInfo node.
        std::auto_ptr<Digest> calc(new Digest(getSignatureMethod()));
        std::vector<unsigned char> sha = calcDigestOnNode(calc.get(), URI_ID_DSIG, "SignedInfo");
        DEBUGMEM("Digest", &sha[0], sha.size());

        // Get signature value.
        std::vector<unsigned char> signatureShaRsa = getSignatureValue();

	    // Verify signature value with public RSA key.
        bool valid = rsa.verify(calc->getMethod(), sha, signatureShaRsa);

        // Check that signature matched.
        if(!valid)
        {
    	    THROW_SIGNATUREEXCEPTION("Signature is not valid.");
        }
	}
    catch(const IOException& e)
    {
	    THROW_SIGNATUREEXCEPTION_CAUSE(e, "Failed to validate signature.");
    }
}
Exemple #3
0
void TSL::validate(const std::vector<X509Cert> &certs)
{
    if(!tsl)
        THROW("Failed to parse XML");

    X509Cert signingCert;
    if(tsl->signature().present() &&
        tsl->signature()->keyInfo().present() &&
        !tsl->signature()->keyInfo()->x509Data().empty() &&
        !tsl->signature()->keyInfo()->x509Data().front().x509Certificate().empty())
    {
        const Base64Binary &base64 = tsl->signature()->keyInfo()->x509Data().front().x509Certificate().front();
        signingCert = X509Cert((const unsigned char*)base64.data(), base64.capacity());
    }

    if(find(certs.begin(), certs.end(), signingCert) == certs.end())
        THROW("TSL Signature is signed with untrusted certificate");

    try {
        XSECProvider prov;
        DSIGSignature *sig = prov.newSignatureFromDOM(tsl->_node()->getOwnerDocument());
        //sig->setKeyInfoResolver(new XSECKeyInfoResolverDefault);
        sig->setSigningKey(OpenSSLCryptoX509(signingCert.handle()).clonePublicKey());
        //sig->registerIdAttributeName(MAKE_UNICODE_STRING("ID"));
        sig->load();
        if(!sig->verify())
        {
            string msg = xsd::cxx::xml::transcode<char>(sig->getErrMsgs());
            THROW("TLS Signature is invalid: %s", msg.c_str());
        }
    }
    catch(XSECException &e)
    {
        string msg = xsd::cxx::xml::transcode<char>(e.getMsg());
        THROW("TSL Signature is invalid: %s", msg.c_str());
    }
    catch(const Exception &)
    {
        throw;
    }
    catch(...)
    {
        THROW("TSL Signature is invalid");
    }
}
Exemple #4
0
Signature *DDoc::prepareSignature(Signer *signer)
{
    d->throwDocOpenError( __LINE__ );
    if(d->documents.empty())
        THROW("No documents in container, can not sign container.");
    if(!signer)
        THROW("Null pointer in DDoc::sign");

    X509Cert cert = signer->cert();
    if(!cert)
        THROW("Failed to sign document, Certificate cannot be NULL");

    ostringstream role;
    vector<string> r = signer->signerRoles();
    for(vector<string>::const_iterator i = r.begin(); i != r.end(); ++i)
    {
        role << *i;
        if(i + 1 != r.end())
            role << " / ";
    }

#ifdef __APPLE__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
    SignatureInfo *info = nullptr;
    int err = d->lib->f_ddocPrepareSignature(d->doc, &info,
        (role.str().empty() ? 0 : role.str().c_str()),
        (signer->city().empty() ? 0 : signer->city().c_str()),
        (signer->stateOrProvince().empty() ? 0 : signer->stateOrProvince().c_str()),
        (signer->postalCode().empty() ? 0 : signer->postalCode().c_str()),
        (signer->countryName().empty() ? 0 : signer->countryName().c_str()),
        X509_dup(cert.handle()), 0);
#ifdef __APPLE__
#pragma GCC diagnostic pop
#endif
    d->throwSignError( info, err, "Failed to sign document", __LINE__ );
    if( !info )
        d->throwCodeError(ERR_NULL_POINTER, "Failed to sign document", __LINE__);
    d->loadSignatures();
    return d->signatures.back();
}
/**
 * Check if X509Cert is signed by trusted issuer
 * @throw Exception if error
 */
bool X509CertStore::verify(const X509Cert &cert, bool noqscd) const
{
    activate(cert.issuerName("C"));
    const ASN1_TIME *asn1time = X509_get0_notBefore(cert.handle());
    time_t time = util::date::ASN1TimeToTime_t(string((const char*)asn1time->data, size_t(asn1time->length)), asn1time->type == V_ASN1_GENERALIZEDTIME);
    SCOPE(X509_STORE, store, createStore(X509CertStore::CA, &time));
    SCOPE(X509_STORE_CTX, csc, X509_STORE_CTX_new());
    if(!X509_STORE_CTX_init(csc.get(), store.get(), cert.handle(), nullptr))
        THROW_OPENSSLEXCEPTION("Failed to init X509_STORE_CTX");
    if(X509_verify_cert(csc.get()) > 0)
    {
        if(noqscd)
            return true;

        const TSL::Validity *v = static_cast<const TSL::Validity*>(X509_STORE_CTX_get_ex_data(csc.get(), 0));
        const vector<string> policies = cert.certificatePolicies();
        const vector<string> qcstatement = cert.qcStatements();
        const vector<X509Cert::KeyUsage> keyUsage = cert.keyUsage();

        bool isQCCompliant = find(qcstatement.cbegin(), qcstatement.cend(), X509Cert::QC_COMPLIANT) != qcstatement.cend();
        bool isQSCD =
            find(policies.cbegin(), policies.cend(), X509Cert::QCP_PUBLIC_WITH_SSCD) != policies.cend() ||
            find(policies.cbegin(), policies.cend(), X509Cert::QCP_LEGAL_QSCD) != policies.cend() ||
            find(policies.cbegin(), policies.cend(), X509Cert::QCP_NATURAL_QSCD) != policies.cend() ||
            find(qcstatement.cbegin(), qcstatement.cend(), X509Cert::QC_SSCD) != qcstatement.cend();

        bool isESeal =  // Special treamtent for E-Seals
            find(policies.cbegin(), policies.cend(), X509Cert::QCP_LEGAL) != policies.cend() ||
            find(qcstatement.cbegin(), qcstatement.cend(), X509Cert::QCT_ESEAL) != qcstatement.cend();
        auto matchPolicySet = [&](const vector<string> &policySet){
            return all_of(policySet.cbegin(), policySet.cend(), [&](const string &policy){
                return find(policies.cbegin(), policies.cend(), policy) != policies.cend();
            });
        };
        auto matchKeyUsageSet = [&](const map<X509Cert::KeyUsage,bool> &keyUsageSet){
            return all_of(keyUsageSet.cbegin(), keyUsageSet.cend(), [&](pair<X509Cert::KeyUsage,bool> keyUsageBit){
                return (find(keyUsage.cbegin(), keyUsage.cend(), keyUsageBit.first) != keyUsage.cend()) == keyUsageBit.second;
            });
        };

        for(const TSL::Qualifier &q: v->qualifiers)
        {
            if(q.assert_ == "all")
            {
                if(!(all_of(q.policySet.cbegin(), q.policySet.cend(), matchPolicySet) &&
                     all_of(q.keyUsage.cbegin(), q.keyUsage.cend(), matchKeyUsageSet)))
                    continue;
            }
            else if(q.assert_ == "atLeastOne")
            {
                if(!(any_of(q.policySet.cbegin(), q.policySet.cend(), matchPolicySet) ||
                     any_of(q.keyUsage.cbegin(), q.keyUsage.cend(), matchKeyUsageSet) ))
                    continue;
            }
            else
            {
                WARN("Unable to handle Qualifier assert '%s'", q.assert_.c_str());
                continue;
            }

            for(const string &qc: q.qualifiers)
            {
                if(qc == "http://uri.etsi.org/TrstSvc/TrustedList/SvcInfoExt/QCStatement" ||
                   qc == "http://uri.etsi.org/TrstSvc/TrustedList/SvcInfoExt/QCForESig")
                    isQCCompliant = true;
                else if(qc == "http://uri.etsi.org/TrstSvc/TrustedList/SvcInfoExt/NotQualified")
                    isQCCompliant = false;
                else if(qc == "http://uri.etsi.org/TrstSvc/TrustedList/SvcInfoExt/QCSSCDStatusAsInCert" ||
                        qc == "http://uri.etsi.org/TrstSvc/TrustedList/SvcInfoExt/QCQSCDStatusAsInCert")
                    continue;
                else if(qc == "http://uri.etsi.org/TrstSvc/TrustedList/SvcInfoExt/QCWithSSCD" ||
                        qc == "http://uri.etsi.org/TrstSvc/TrustedList/SvcInfoExt/QCWithQSCD")
                    isQSCD = true;
                else if(qc == "http://uri.etsi.org/TrstSvc/TrustedList/SvcInfoExt/QCNoSSCD" ||
                        qc == "http://uri.etsi.org/TrstSvc/TrustedList/SvcInfoExt/QCNoQSCD")
                    isQSCD = false;
                else if(qc == "http://uri.etsi.org/TrstSvc/TrustedList/SvcInfoExt/QCForLegalPerson" ||
                        qc == "http://uri.etsi.org/TrstSvc/TrustedList/SvcInfoExt/QCForESeal")
                    isESeal = true;
            }
        }

        if(!((isQCCompliant && isQSCD) || isESeal))
        {
            Exception e(EXCEPTION_PARAMS("Signing certificate does not meet Qualification requirements"));
            e.setCode(Exception::CertificateIssuerMissing);
            throw e;
        }

        return true;
    }

    int err = X509_STORE_CTX_get_error(csc.get());
    Exception e(EXCEPTION_PARAMS(X509_verify_cert_error_string(err)), OpenSSLException());
    switch(err)
    {
    case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
        e.setCode(Exception::CertificateIssuerMissing);
        throw e;
    default: throw e;
    }
}
Exemple #6
0
Connect::Connect(const string &_url, const string &method, int timeout, const string &useragent, const X509Cert &cert)
    : _timeout(timeout)
{
    DEBUG("Connecting to URL: %s", _url.c_str());
    char *_host = nullptr, *_port = nullptr, *_path = nullptr;
    int usessl = 0;
    if(!OCSP_parse_url(const_cast<char*>(_url.c_str()), &_host, &_port, &_path, &usessl))
        THROW_OPENSSLEXCEPTION("Incorrect URL provided: '%s'.", _url.c_str());

    string host = _host ? _host : "";
    string port = _port ? _port : "80";
    string path = _path ? _path : "/";
    string url = (_path && strlen(_path) == 1 && _path[0] == '/' && _url[_url.size() - 1] != '/') ? _url + "/" : _url;
    OPENSSL_free(_host);
    OPENSSL_free(_port);
    OPENSSL_free(_path);

    string hostname = host + ":" + port;
    Conf *c = Conf::instance();
    if(!c->proxyHost().empty() && (usessl == 0 || (CONF(proxyForceSSL)) || (CONF(proxyTunnelSSL))))
    {
        hostname = c->proxyHost() + ":" + c->proxyPort();
        path = url;
    }

    DEBUG("Connecting to Host: %s timeout: %i", hostname.c_str(), _timeout);
    d = BIO_new_connect(const_cast<char*>(hostname.c_str()));
    if(!d)
        THROW_OPENSSLEXCEPTION("Failed to create connection with host: '%s'", hostname.c_str());

    BIO_set_nbio(d, _timeout > 0);
    auto start = chrono::high_resolution_clock::now();
    while(BIO_do_connect(d) != 1)
    {
        if(_timeout == 0)
            THROW_OPENSSLEXCEPTION("Failed to connect to host: '%s'", hostname.c_str());
        if(!BIO_should_retry(d))
            THROW_OPENSSLEXCEPTION("Failed to connect to host: '%s'", hostname.c_str());
        auto end = chrono::high_resolution_clock::now();
        if(chrono::duration_cast<chrono::seconds>(end - start).count() >= _timeout)
            THROW("Failed to create connection with host timeout: '%s'", hostname.c_str());
        this_thread::sleep_for(chrono::milliseconds(50));
    }

    if(usessl > 0)
    {
        if(!c->proxyHost().empty() && (CONF(proxyTunnelSSL)))
        {
            BIO_printf(d, "CONNECT %s:%s HTTP/1.0\r\n", host.c_str(), port.c_str());
            addHeader("Host", host + ":" + port);
            sendProxyAuth();
            _timeout = 1; // Don't wait additional data on read, case proxy tunnel
            Result r = exec();
            if(!r.isOK() || r.result.find("established") == string::npos)
                THROW_OPENSSLEXCEPTION("Failed to create proxy connection with host: '%s'", hostname.c_str());
            _timeout = timeout; // Restore
        }

        ssl.reset(SSL_CTX_new(SSLv23_client_method()), SSL_CTX_free);
        if(!ssl)
            THROW_OPENSSLEXCEPTION("Failed to create ssl connection with host: '%s'", hostname.c_str());
        SSL_CTX_set_mode(ssl.get(), SSL_MODE_AUTO_RETRY);
        SSL_CTX_set_quiet_shutdown(ssl.get(), 1);
        if(cert.handle())
        {
            SSL_CTX_set_verify(ssl.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr);
            SSL_CTX_set_cert_verify_callback(ssl.get(), [](X509_STORE_CTX *store, void *cert) -> int {
                X509 *x509 = X509_STORE_CTX_get0_cert(store);
                return x509 && X509_cmp(x509, (X509*)cert) == 0 ? 1 : 0;
            }, cert.handle());
        }
        BIO *sbio = BIO_new_ssl(ssl.get(), 1);
        if(!sbio)
            THROW_OPENSSLEXCEPTION("Failed to create ssl connection with host: '%s'", hostname.c_str());
        d = BIO_push(sbio, d);
        while(BIO_do_handshake(d) != 1)
        {
            if(_timeout == 0)
                THROW("Failed to create ssl connection with host: '%s'", hostname.c_str());
            auto end = chrono::high_resolution_clock::now();
            if(chrono::duration_cast<chrono::seconds>(end - start).count() >= _timeout)
                THROW("Failed to create ssl connection with host timeout: '%s'", hostname.c_str());
            this_thread::sleep_for(chrono::milliseconds(50));
        }
    }

    BIO_printf(d, "%s %s HTTP/1.0\r\n", method.c_str(), path.c_str());
    if(port == "80")
        addHeader("Host", host);
    else
        addHeader("Host", host + ":" + port);
    addHeader("User-Agent", "LIB libdigidocpp/" +
        string(VER_STR(MAJOR_VER.MINOR_VER.RELEASE_VER.BUILD_VER)) + " APP " + appInfo() + useragent);
    if(usessl == 0)
        sendProxyAuth();
}