Initial support for client certificates LibCurl implementation

This commit is contained in:
2020-02-26 22:15:23 +01:00
parent 0afccef5e2
commit 5ac7ce7483
8 changed files with 419 additions and 5 deletions
@@ -0,0 +1,177 @@
note
description: "Summary description for {HTTP_CLIENT_SECURE_CONFIG}."
date: "$Date$"
revision: "$Revision$"
class
HTTP_CLIENT_SECURE_CONFIG
feature -- Access
verify_peer: BOOLEAN
-- Verify the peer's SSL certificate.
-- If the verification fails to prove that the certificate is authentic, the connection fails.
-- by default is disabled. call enable_verify_peer.
verify_host: BOOLEAN
-- Verify the certificate's name against host.
-- Checks the server's certificate's claimed identity.
-- by default is disabled. call enable_verify_host.
certificate_type: detachable STRING
-- specify type of the client SSL certificate.
-- PEM, DER, P12
--| LibCurl: Supported formats are "PEM" and "DER", except with Secure Transport.
--| OpenSSL (versions 0.9.3 and later) and Secure Transport (on iOS 5 or later, or OS X 10.7 or later) also support "P12" for PKCS#12-encoded files.
--| https://curl.haxx.se/libcurl/c/CURLOPT_SSLCERTTYPE.html
client_certificate: detachable STRING_32
-- specify the certificate for client authentication.
passphrase: detachable STRING_32
-- password required for a private key.
certificate_authority: detachable STRING_32
-- path to Certificate Authority (CA) bundle.
tls_version: INTEGER
-- TLS version 1.2 or 1.3
feature -- Change Element
enable_verify_host
--Enable verify the certificate's name against host.
do
verify_host := True
ensure
verify_host_set: verify_host = True
end
enable_verify_peer
-- Enable verify the certificate's name against host.
do
verify_peer := True
ensure
verify_peer_set: verify_peer = True
end
set_certificate_type (a_cert: STRING)
-- Set type of the client SSL certificate (PEM, P12) `certificate_type` with `a_cert`.
--| TODO add a precondition to verify the type of the certificate.
note
eis:"name=certificates formats", "src=https://www.ssls.com/knowledgebase/what-are-certificate-formats-and-what-is-the-difference-between-them/", "protocol=uri"
require
is_valid_certificate_type: certificates_formats.has (a_cert.as_lower)
do
certificate_type := a_cert
ensure
certificate_type_set: certificate_type = a_cert
end
set_client_certificate (a_client_certificate: STRING_32)
-- Set path to client certificate `client_certificate` with `a_client_certificate` for authentication.
do
client_certificate := a_client_certificate
ensure
client_certificate_set: client_certificate = a_client_certificate
end
set_passphrase (a_password: STRING_32)
-- Set the passphrase `passphrase` with `a_password`, if the private key required it.
do
passphrase := a_password
ensure
passphrase_set: passphrase = a_password
end
set_certificate_authority (a_certificate: STRING_32)
-- set path to Certificate Authority `certificate_authority` with `a_certificate`.
do
certificate_authority := a_certificate
ensure
certificate_authority_set: certificate_authority = a_certificate
end
set_tls_version (a_version: INTEGER)
-- set preferred TLS/SSL version `tls_version` with `a_version`
require
valid_tls_version: is_valid_tls_verion (a_version)
do
tls_version := a_version
ensure
tls_version_set: tls_version = a_version
end
feature -- Certificates Types
certificates_formats: SET [STRING]
note
eis:"name=certificates formats", "src=https://www.ssls.com/knowledgebase/what-are-certificate-formats-and-what-is-the-difference-between-them/", "protocol=uri"
do
create {ARRAYED_SET [STRING]} Result.make (9)
-- Base64
-- PEM
Result.put ("pem")
Result.put ("crt")
Result.put ("ca-bundle")
--PKCS#7
Result.put ("p7b")
Result.put ("p7s")
-- Binary
-- DER
Result.put ("der")
Result.put ("cer")
--PKCS#12
Result.put ("pfx")
Result.put ("p12")
Result.compare_objects
ensure
is_class: class
end
feature -- TLS versions
tls_1_2: INTEGER = 2
tls_1_3: INTEGER = 3
is_valid_tls_verion (a_version: INTEGER): BOOLEAN
-- Is `a_version` a valid and supported TLS version?
do
Result := a_version = tls_1_2 or a_version = tls_1_3
end
is_tls_1_2: BOOLEAN
do
Result := tls_version = tls_1_2
end
is_tls_1_3: BOOLEAN
do
Result := tls_version = tls_1_3
end
feature -- Reset
reset
-- Clear config.
do
verify_host := False
verify_peer := False
certificate_type := Void
client_certificate := Void
passphrase := Void
end
note
copyright: "2011-2019, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
5949 Hollister Ave., Goleta, CA 93117 USA
Telephone 805-685-1006, Fax 805-685-6869
Website http://www.eiffel.com
Customer support http://support.eiffel.com
]"
end
@@ -270,6 +270,10 @@ feature -- Settings
proxy: detachable TUPLE [host: READABLE_STRING_8; port: INTEGER]
-- Proxy information [`host' and `port']
secure_config: detachable HTTP_CLIENT_SECURE_CONFIG
-- http client secure configuration.
--| HTTPS usage with client certificates.
feature -- Access
base_url: READABLE_STRING_8
@@ -445,8 +449,17 @@ feature -- Element change
ciphers_setting_set: attached ciphers_setting as c_setting and then c_setting.same_string (a_ciphers_setting)
end
set_secure_config (a_config: like secure_config)
-- Set 'secure_config' with 'a_config'.
do
secure_config := a_config
ensure
secure_config_set: secure_config = a_config
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2019, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software
@@ -5,8 +5,8 @@ note
]"
status: "See notice at end of class."
legal: "See notice at end of class."
date: "$Date: 2009-04-09 20:51:20 +0200 (Thu, 09 Apr 2009) $"
revision: "$Revision: 78146 $"
date: "$Date$"
revision: "$Revision$"
class
LIBCURL_DEFAULT_FUNCTION
@@ -82,6 +82,11 @@ feature -- Execution
--| Configure cURL session
initialize_curl_session (ctx, curl, curl_easy, curl_handle)
--| Condigure cURL secure session
if attached {HTTP_CLIENT_SECURE_CONFIG} session.secure_config as l_config then
initialize_curl_security_session (curl_easy, curl_handle, l_config)
end
--| URL
l_url := url
@@ -300,7 +305,11 @@ feature -- Execution
end
end
else
Result.set_error_message ("Error: cURL Error[" + l_result.out + "]")
if attached curl.error_message (l_result) as err_msg then
Result.set_error_message ("Error: " + {UTF_CONVERTER}.utf_32_string_to_utf_8_string_8 (err_msg))
else
Result.set_error_message ("Error: cURL Error[" + l_result.out + "]")
end
Result.status := response_status_code (curl_easy, curl_handle)
end
@@ -400,6 +409,56 @@ feature -- Execution
end
end
initialize_curl_security_session (curl_easy: CURL_EASY_EXTERNALS; curl_handle: POINTER; a_config: HTTP_CLIENT_SECURE_CONFIG)
do
--| TLS version.
if a_config.is_valid_tls_verion (a_config.tls_version) then
if a_config.is_tls_1_2 then
-- ask libcurl to use TLS version 1.2 or later */
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.CURLOPT_SSLVERSION, {CURL_OPT_CONSTANTS}.CURL_SSLVERSION_TLSv1_2)
end
if a_config.is_tls_1_3 then
-- ask libcurl to use TLS version 1.3 or later */
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.CURLOPT_SSLVERSION, {CURL_OPT_CONSTANTS}.CURL_SSLVERSION_TLSv1_3)
end
end
--| Cert Type
if attached a_config.certificate_type as cert_type then
-- Format P12, PEM
curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.CURLOPT_SSLCERTTYPE, cert_type)
end
--| set the passphrase (if the key has one...)
if attached a_config.passphrase as passphrase then
curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.CURLOPT_KEYPASSWD, passphrase)
end
--| set the cert for client authentication
if attached a_config.client_certificate as client_certificate then
curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.CURLOPT_SSLCERT, client_certificate)
end
--| set the file with the certs vaildating the server
if attached a_config.certificate_authority as certificate_authority then
curl_easy.setopt_string (curl_handle, {CURL_OPT_CONSTANTS}.CURLOPT_CAINFO, certificate_authority)
end
--| Verify the peer's SSL certificate
if a_config.verify_peer then
-- if the verification fails to prove that the certificate is authentic, the connection fails.
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.CURLOPT_SSL_VERIFYPEER, 1)
end
--| Verify the certificate's name against host
if a_config.verify_host then
-- checking the server's certificate's claimed identity.
curl_easy.setopt_integer (curl_handle, {CURL_OPT_CONSTANTS}.CURLOPT_SSL_VERIFYHOST, 2)
end
end
feature {NONE} -- Implementation
response_status_code (curl_easy: CURL_EASY_EXTERNALS; curl_handle: POINTER): INTEGER
@@ -452,7 +511,7 @@ feature {NONE} -- Implementation
end
note
copyright: "2011-2018, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2019, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software