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; }
/** * 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); }
/** * 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."); } }
/** * * 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; }
/// 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" ); } }
/** * 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); }
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"); } }
/// 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()); } }
/** * 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); }
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 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" ); } }
/** * 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); }
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()); } }
/** * 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; } }
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(); }