Ejemplo n.º 1
0
bool XmlSignature::addCertificateInfo(const X509Cert &cert)
{
    try
    {
        if (_signed)
        {
            DSIGKeyInfoX509 * keyInfoX509 = _signature->appendX509Data();

            XercesString base64Certificate{ cert.base64Encoded() };
            XercesString issuer{ cert.issuer() };
            XercesString serial{ cert.serial() };

            keyInfoX509->appendX509Certificate(base64Certificate);
            keyInfoX509->setX509IssuerSerial(issuer, serial);
        }
        else
        {
            _error = "addCertificateInfo() called prior to sign() method";

            return false;
        }
    }
    catch (XSECException &ex)
    {
        rethrowWithMessage(ex, "Failed to append certificate info");
    }

    return true;
}
Ejemplo n.º 2
0
/**
 * Set OCSP responder cert.
 * @param x509 certificate that was used identify OCSP responder
 */
void digidoc::SignatureTM::setOCSPCertificate(const X509Cert& x509)
{
    //XXX: copied from digidoc::Signature::setSigningCertificate

    DEBUG("digidoc::SignatureTM::setOCSPCertificate()");

    std::vector<unsigned char> derEncodedX509 = x509.encodeDER();

    // Calculate SHA1 digest of the certificate.
    std::auto_ptr<Digest> calc = Digest::create();
    calc->update(derEncodedX509);
    dsig::DigestMethodType digestMethod(xml_schema::Uri(calc->getUri()));
    dsig::DigestValueType digestValue(xml_schema::Base64Binary(&calc->getDigest()[0], calc->getSize()));
    xades::DigestAlgAndValueType certDigest(digestMethod, digestValue);

    // Add certificate issuer info.
    dsig::X509IssuerSerialType issuerSerial(xml_schema::String(x509.getIssuerName()), xml_schema::Integer(x509.getSerial()));

    digidoc::xades::CertIDType cert(certDigest, issuerSerial);
    xades::CertIDListType certList;
    certList.cert().push_back(cert);

    xades::CompleteCertificateRefsType certificateRefs(certList);

    //certificateRefs.certRefs(certList);

    unsignedSignatureProperties()->completeCertificateRefs().push_back(certificateRefs);

    //CertificateValues
    addCertificateValue(std::string("S0-RESPONDER_CERT"), x509);
}
Ejemplo n.º 3
0
/**
 * 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();
}
Ejemplo n.º 4
0
/**
 * 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.");
    }
}
Ejemplo n.º 5
0
/**
 *
 * return
 * @throws SignatureException
 */
digidoc::OCSP::CertStatus digidoc::SignatureBES::validateOnline() const throw(SignatureException)
{
    // FIXME: Add exception handling.

    // Get signing signature.
    X509Cert cert = getSigningCertificate();

    // Get issuer certificate.
    X509* issuerCert = X509CertStore::getInstance()->getCert(*(cert.getIssuerNameAsn1()));
    X509_scope issuerCertScope(&issuerCert);
    if(issuerCert == NULL)
    {
        THROW_SIGNATUREEXCEPTION("Failed to load issuer certificate.");
    }

    Conf* conf = Conf::getInstance();

    // Get OCSP responder certificate.
    // FIXME: throws IOException, handle it
    Conf::OCSPConf ocspConf = conf->getOCSP(cert.getIssuerName());
    if(ocspConf.issuer.empty())
    {
        SignatureException e(__FILE__, __LINE__, "Failed to find ocsp responder.");
        e.setCode( Exception::OCSPResponderMissing );
        throw e;
    }
    STACK_OF(X509)* ocspCerts = X509Cert::loadX509Stack(ocspConf.cert);
    X509Stack_scope ocspCertsScope(&ocspCerts);

    // Check the certificate validity from OCSP server.
    try
    {
        OCSP ocsp;
        ocsp.setSkew(120);//XXX: load from conf
        ocsp.setOCSPCerts(ocspCerts);
        ocsp.setUrl(ocspConf.url);
        std::auto_ptr<Digest> calc = Digest::create();
        calc->update(getSignatureValue());
        return ocsp.checkCert(cert.getX509(), issuerCert, calc->getDigest());
    }
    catch(const IOException& e)
    {
        THROW_SIGNATUREEXCEPTION("Failed to check the certificate validity from OCSP server.");
    }
    catch(const OCSPException& e)
    {
        THROW_SIGNATUREEXCEPTION("Failed to check the certificate validity from OCSP server.");
    }
    return digidoc::OCSP::GOOD;
}
Ejemplo n.º 6
0
/// Check if signing certificate was issued by trusted party.
/// @throws SignatureException on a problem with signing certificate
void digidoc::SignatureBES::checkSigningCertificate() const throw(SignatureException)
{
    try
    {
        X509Cert signingCert = getSigningCertificate();
        std::vector<digidoc::X509Cert::KeyUsage> usage = signingCert.getKeyUsage();
        if( find( usage.begin(), usage.end(), digidoc::X509Cert::NonRepudiation ) == usage.end() )
            THROW_SIGNATUREEXCEPTION("Signing certificate does not contain NonRepudiation key usage flag %s", signingCert.getSubject().c_str());
        if( signingCert.verify() <= 0 )
            THROW_SIGNATUREEXCEPTION("Unable to verify signing certificate %s", signingCert.getSubject().c_str());
    }
    catch( const IOException &e )
    {
        THROW_SIGNATUREEXCEPTION_CAUSE( e, "Unable to verify signing certificate" );
    }
}
Ejemplo n.º 7
0
/**
 * Adds signing certificate to the signature XML. The DER encoded X.509 certificate is added to
 * Signature->KeyInfo->X509Data->X509Certificate. Certificate info is also added to
 * Signature->Object->QualifyingProperties->SignedProperties->SignedSignatureProperties->SigningCertificate.
 *
 * @param cert certificate that is used for signing the signature XML.
 */
void SignatureBES::setSigningCertificate(const X509Cert& x509)
{
    DEBUG("SignatureBES::setSigningCertificate()");
    // Estoniand ID-Card specific hack for older cards, they support only max SHA224
    string method = Conf::instance()->digestUri();
    X509Crypto key(x509);
    if(!key.rsaModulus().empty())
    {
        if(method != URI_SHA1 && method != URI_SHA224)
        {
            vector<string> pol = x509.certificatePolicies();
            for(vector<string>::const_iterator i = pol.begin(); i != pol.end(); ++i)
            {
                if((i->compare(0, 22, "1.3.6.1.4.1.10015.1.1.") == 0 ||
                    i->compare(0, 22, "1.3.6.1.4.1.10015.3.1.") == 0) &&
                    key.rsaModulus().size() <= 128)
                {
                     method = URI_SHA224;
                     break;
                }
            }
        }
        signature->signedInfo().signatureMethod(Uri(Digest::toRsaUri(method)));
    }
    else
        signature->signedInfo().signatureMethod(Uri(Digest::toEcUri(method)));

    // Signature->KeyInfo->X509Data->X509Certificate
    // BASE64 encoding of a DER-encoded X.509 certificate = PEM encoded.
    X509DataType x509Data;
    x509Data.x509Certificate().push_back(toBase64(x509));

    KeyInfoType keyInfo;
    keyInfo.x509Data().push_back(x509Data);
    signature->keyInfo(keyInfo);

    // Signature->Object->QualifyingProperties->SignedProperties->SignedSignatureProperties->SigningCertificate
    // Calculate digest of the X.509 certificate.
    auto_ptr<Digest> digest(new Digest());
    digest->update(x509);
    CertIDListType signingCertificate;
    signingCertificate.cert().push_back(CertIDType(
        DigestAlgAndValueType(DigestMethodType(digest->uri()), toBase64(digest->digest())),
        X509IssuerSerialType(x509.issuerName(), x509.serial())));

    getSignedSignatureProperties().signingCertificate(signingCertificate);
}
Ejemplo n.º 8
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");
    }
}
Ejemplo n.º 9
0
/// Check if signing certificate was issued by trusted party.
/// @throws SignatureException on a problem with signing certificate
void digidoc::SignatureBES::checkSigningCertificate() const throw(SignatureException)
{
    X509Cert signingCert = getSigningCertificate();

    bool valid = false;
    try
    {
        valid = signingCert.verify();
    }
    catch( const IOException &e )
    {
        THROW_SIGNATUREEXCEPTION_CAUSE( e, "Unable to verify signing certificate" );
    }
    if(!valid)
    {
        THROW_SIGNATUREEXCEPTION("Unable to verify signing certificate %s", signingCert.getSubject().c_str());
    }
}
Ejemplo n.º 10
0
/**
 * Add certificate under CertificateValues element
 * @param certId id attribute of EncapsulatedX509Certificate
 * @param x509 value of EncapsulatedX509Certificate
 */
void digidoc::SignatureTM::addCertificateValue(const std::string& certId, const X509Cert& x509)
{
    DEBUG("digidoc::SignatureTM::setCertificateValue(%s, X509Cert{%ld,%s})", certId.c_str(), x509.getSerial(), x509.getSubject().c_str());
    //CertificateValues
    std::vector<unsigned char> certBytes = x509.encodeDER();
    xades::CertificateValuesType::EncapsulatedX509CertificateType certData(
        xml_schema::Base64Binary(&certBytes[0], certBytes.size()));
    certData.id(xml_schema::Id(certId.c_str()));
    unsignedSignatureProperties()->certificateValues()[0].encapsulatedX509Certificate().push_back(certData);
}
Ejemplo n.º 11
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();
}
Ejemplo n.º 12
0
/// Check if signing certificate was issued by trusted party.
/// @throws SignatureException on a problem with signing certificate
void SignatureBES::checkSigningCertificate() const
{
    try
    {
        X509Cert signingCert = signingCertificate();
        vector<X509Cert::KeyUsage> usage = signingCert.keyUsage();
        if(find(usage.begin(), usage.end(), X509Cert::NonRepudiation) == usage.end())
            THROW("Signing certificate does not contain NonRepudiation key usage flag");
        string time = trustedSigningTime();
        if(time.empty())
            THROW("SigningTime missing");
        time_t signingTime_t = util::date::string2time_t(time);
        if(!X509CertStore::instance()->verify(signingCert, &signingTime_t))
            THROW("Unable to verify signing certificate");
    }
    catch(const Exception &e)
    {
        THROW_CAUSE( e, "Unable to verify signing certificate" );
    }
}
Ejemplo n.º 13
0
/**
 * Adds signing certificate to the signature XML. The DER encoded X.509 certificate is added to
 * Signature->KeyInfo->X509Data->X509Certificate. Certificate info is also added to
 * Signature->Object->QualifyingProperties->SignedProperties->SignedSignatureProperties->SigningCertificate.
 *
 * @param cert certificate that is used for signing the signature XML.
 */
void SignatureBES::setSigningCertificate(const X509Cert& x509)
{
    DEBUG("SignatureBES::setSigningCertificate()");
    // Signature->KeyInfo->X509Data->X509Certificate
    // BASE64 encoding of a DER-encoded X.509 certificate = PEM encoded.
    X509DataType x509Data;
    x509Data.x509Certificate().push_back(toBase64(x509));

    KeyInfoType keyInfo;
    keyInfo.x509Data().push_back(x509Data);
    signature->keyInfo(keyInfo);

    // Signature->Object->QualifyingProperties->SignedProperties->SignedSignatureProperties->SigningCertificate
    // Calculate digest of the X.509 certificate.
    Digest digest;
    digest.update(x509);
    CertIDListType signingCertificate;
    signingCertificate.cert().push_back(CertIDType(
        DigestAlgAndValueType(DigestMethodType(digest.uri()), toBase64(digest.result())),
        X509IssuerSerialType(x509.issuerName(), x509.serial())));

    getSignedSignatureProperties().signingCertificate(signingCertificate);
}
Ejemplo n.º 14
0
void bdoc::Signature::checkSigningCertificate(bdoc::X509CertStore *store) const
{
    X509Cert signingCert = getSigningCertificate();

    if (store == NULL) {
        THROW_STACK_EXCEPTION(
            "Unable to verify signing certificate %s",
            signingCert.getSubject().c_str());
    }
    X509_STORE *st = NULL;
    st = store->getCertStore();

    int res = signingCert.verify(st);

    X509_STORE_free(st);
    st = NULL;

    if (!res) {
        THROW_STACK_EXCEPTION(
            "Unable to verify signing certificate %s",
            signingCert.getSubject().c_str());
    }

}
Ejemplo n.º 15
0
/**
 * 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;
    }
}
Ejemplo n.º 16
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();
}