HTTPS/Client-authenticated
You are encouraged to solve this task according to the task description, using any language you may know.
Demonstrate how to connect to a web server over HTTPS where that server requires that the client present a certificate to prove who (s)he is. Unlike with the HTTPS request with authentication task, it is not acceptable to perform the authentication by a username/password or a set cookie.
This task is in general useful for use with webservice clients as it offers a high level of assurance that the client is an acceptable counterparty for the server. For example, Amazon Web Services uses this style of authentication.
C#
<lang csharp> using System; using System.Net;
class Program {
class MyWebClient : WebClient { protected override WebRequest GetWebRequest(Uri address) { HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address); request.ClientCertificates.Add(new X509Certificate()); return request; } } static void Main(string[] args) { var client = new MyWebClient();
var data = client.DownloadString("https://example.com");
Console.WriteLine(data); }
} </lang>
Go
<lang Go>package main
import ( "crypto/tls" "io/ioutil" "log" "net/http" )
func main() {
// load key pair cert, err := tls.LoadX509KeyPair( "./client.local.tld/client.local.tld.crt", "./client.local.tld/client.local.tld.key", )
if err != nil { log.Fatal("Error while loading x509 key pair", err) }
// Create TLS Config in order to had client certificate tlsConfig := &tls.Config{Certificates: []tls.Certificate{cert}}
tlsConfig.BuildNameToCertificate() transport := &http.Transport{TLSClientConfig: tlsConfig}
// create http client with our custom transport with TLS config client := &http.Client{Transport: transport}
res, err := client.Get("https://www.example.com/") if err != nil { log.Fatal(err) } contents, err := ioutil.ReadAll(res.Body) log.Print(string(contents))
} </lang>
Julia
<lang julia>using HTTP, MbedTLS
conf = MbedTLS.SSLConfig(true, log_secrets="/utl/secret_key_log.log") resp = HTTP.get("https://httpbin.org/ip", sslconfig=conf)
println(resp)
</lang>
- Output:
HTTP.Messages.Response: """ HTTP/1.1 200 OK Connection: keep-alive Server: gunicorn/19.9.0 Date: Wed, 28 Nov 2018 08:42:25 GMT Content-Type: application/json Content-Length: 30 Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true Via: 1.1 vegur
{
"origin": "104.28.10.103"} """
Kotlin
<lang scala>// version 1.2.0
import java.security.KeyStore import javax.net.ssl.KeyManagerFactory import javax.net.ssl.SSLContext import javax.net.ssl.HttpsURLConnection import java.net.URL import java.io.FileInputStream import java.io.InputStreamReader import java.io.BufferedReader
fun getSSLContext(p12Path: String, password: String): SSLContext {
val ks = KeyStore.getInstance("pkcs12") val fis = FileInputStream(p12Path) val pwd = password.toCharArray() ks.load(fis, pwd) val kmf = KeyManagerFactory.getInstance("PKIX") kmf.init(ks, pwd) val sc = SSLContext.getInstance("TLS") sc.init(kmf.keyManagers, null, null) return sc
}
fun main(args: Array<String>) {
// The .p12 file contains the client certificate and private key val sc = getSSLContext("whatever.p12", "password") val url = URL("https://somehost.com") val con = url.openConnection() as HttpsURLConnection con.sslSocketFactory = sc.socketFactory val isr = InputStreamReader(con.inputStream) val br = BufferedReader(isr) while (true) { val line = br.readLine() if (line == null) break println(line) }
}</lang>
Lasso
<lang Lasso>local(sslcert = file('myCert.pem')) local(x = curl('https://sourceforge.net'))
- x->set(CURLOPT_SSLCERT, #sslcert->readstring)
- sslcert->close
- x->result->asString</lang>
Mathematica / Wolfram Language
<lang Mathematica>a = RunThrough["curl -E myCert.pem https://www.example.com", 1] For[ i=0, i < Length[a] , i++, SomeFunction[a]]</lang>
Nim
<lang nim>import httpclient, net var client = newHttpClient(sslContext = newContext(certFile = "mycert.pem")) var r = client.get("https://www.example.com")</lang>
Perl
<lang python>#!/usr/bin/env perl -T use 5.018_002; use warnings; use LWP;
our $VERSION = 1.000_000;
my $ua = LWP::UserAgent->new(
ssl_opts => { SSL_cert_file => 'certificate.pem', SSL_key_file => 'key.pem', verify_hostname => 1, }
); my $req = HTTP::Request->new( GET => 'https://www.example.com' ); my $res = $ua->request($req); if ( $res->is_success ) {
say $res->content;
} else {
say $res->status_line;
}</lang>
Phix
Exactly the same as the HTTP#Phix task, except for the CURLOPT_SSLCERT part.
without js include builtins\libcurl.e curl_global_init() atom curl = curl_easy_init() curl_easy_setopt(curl, CURLOPT_URL, "https://sourceforge.net") integer fn = open("myCert.pem","r") curl_easy_setopt(curl, CURLOPT_SSLCERT, get_text(fn)) close(fn) object res = curl_easy_perform_ex(curl) curl_easy_cleanup(curl) curl_global_cleanup() puts(1,res)
PicoLisp
<lang PicoLisp>(in '(curl "-E" "myCert.pem" "https://www.example.com")
(while (line) (doSomeProcessingWithLine @) ) )</lang>
Python
<lang python>import httplib
connection = httplib.HTTPSConnection('www.example.com',cert_file='myCert.PEM') connection.request('GET','/index.html') response = connection.getresponse() data = response.read() </lang>
Racket
Skeleton code to connect to a server: <lang racket>
- lang racket
(require openssl/mzssl) (define ctx (ssl-make-client-context)) (ssl-set-verify! ctx #t) ; verify the connection (ssl-load-verify-root-certificates! ctx "my-cert.pem") (define-values [I O] (ssl-connect "www.example.com" 443 ctx)) </lang>
Raku
(formerly Perl 6) <lang perl6>
- cert creation commands
- openssl req -newkey rsa:4096 -keyout my_key.pem -out my_csr.pem -nodes -subj "/CN=ME"
- openssl x509 -req -in my_csr.pem -signkey my_key.pem -out my_cert.pem
use v6; use OpenSSL;
my $host = "github.com";
my $ssl = OpenSSL.new(:client);
$ssl.use-certificate-file("./my_cert.pem"); $ssl.use-privatekey-file("./my_key.pem"); $ssl.check-private-key;
my $s = IO::Socket::INET.new(:$host, :port(443));
$ssl.set-socket($s); $ssl.set-connect-state; $ssl.connect; $ssl.write("GET / HTTP/1.1\r\n\r\n"); say $ssl.read(1024); $ssl.close; $s.close;
</lang>
Ruby
<lang Ruby>require 'uri' require 'net/http'
uri = URI.parse('https://www.example.com') pem = File.read("/path/to/my.pem") cert = OpenSSL::X509::Certificate.new(pem) key = OpenSSL::PKey::RSA.new(pem) response = Net::HTTP.start(uri.host, uri.port, use_ssl: true,
cert: cert, key: key) do |http| request = Net::HTTP::Get.new uri http.request request
end</lang>
Rust
This implementation uses reqwest, the de facto standard high-level HTTP(S) rust library. It is roughly equivalent in purpose and functionality to Python's requests.
Cargo.toml dependencies
The blocking variant of the reqwest library is used here for simplicity's sake. An asynchronous API is also available.
Native (system) TLS libraries are used instead of Rustls, the Rust TLS implementation, because we use a PKCS#12 certificate which at the time of writing does not seem to be available on Rustls. A PKCS#12 certificate is used instead of its PEM equivalent because reading password-protected PEM files does not seem to be available either. <lang toml>reqwest = {version = "0.11", features = ["native-tls", "blocking"]}</lang>
src/main.rs
<lang rust>use std::fs::File; use std::io::Read;
use reqwest::blocking::Client; use reqwest::Identity;
fn main() -> std::io::Result<()> {
let identity = { let mut buf = Vec::new();
// Downloaded from https://badssl.com/certs/badssl.com-client.p12 File::open("badssl.com-client.p12")?.read_to_end(&mut buf)?;
// Password is badssl.com Identity::from_pkcs12_der(&buf, "badssl.com").unwrap() };
let client = Client::builder().identity(identity).build().unwrap(); let response = client.get("https://client.badssl.com/").send().unwrap();
if !response.status().is_success() { eprintln!("HTTP error requesting URL: {}", response.status()); }
println!("Got response from server: {}", response.text().unwrap());
Ok(())
}</lang>
Scala
<lang Scala>import java.io.FileInputStream import java.net.URL import java.security.KeyStore
import javax.net.ssl.{HttpsURLConnection, KeyManagerFactory, SSLContext}
import scala.io.BufferedSource
object ClientAuthenticated extends App {
val con: HttpsURLConnection = new URL("https://somehost.com").openConnection().asInstanceOf[HttpsURLConnection]
def getSSLContext(p12Path: String, password: String): SSLContext = { val ks = KeyStore.getInstance("pkcs12") val pwd = password.toCharArray ks.load(new FileInputStream(p12Path), pwd) val kmf = KeyManagerFactory.getInstance("PKIX") kmf.init(ks, pwd) val sc = SSLContext.getInstance("TLS") sc.init(kmf.getKeyManagers, null, null) sc }
// The .p12 file contains the client certificate and private key HttpsURLConnection.setDefaultSSLSocketFactory(getSSLContext("whatever.p12", "password").getSocketFactory) new BufferedSource(con.getInputStream).getLines.foreach(println(_))
}</lang>
Tcl
Uses the Tls package. <lang tcl>package require http package require tls
set cert myCert.p12 http::register https 443 [list \
::tls::socket -certfile $cert -password getPass]
proc getPass {} {
return "myPassword"; # Just a noddy example...
}
- Make a secure authenticated connection
set token [http::geturl https://verysecure.example.com/]
- Now as for conventional use of the “http” package
set data [http::data $token] http::cleanup $token</lang>
Wren
An embedded program so we can ask the C host to communicate with libcurl for us. <lang ecmascript>/* https_client-authenticated.wren */
var CURLOPT_URL = 10002 var CURLOPT_SSLCERT = 10025 var CURLOPT_SSLKEY = 10087 var CURLOPT_KEYPASSWD = 10258
foreign class Curl {
construct easyInit() {}
foreign easySetOpt(opt, param)
foreign easyPerform()
foreign easyCleanup()
}
var curl = Curl.easyInit() if (curl == 0) {
System.print("Error initializing cURL.") return
}
curl.easySetOpt(CURLOPT_URL, "https://example.com/") curl.easySetOpt(CURLOPT_SSLCERT, "cert.pem") curl.easySetOpt(CURLOPT_SSLKEY, "key.pem") curl.easySetOpt(CURLOPT_KEYPASSWD, "s3cret")
var status = curl.easyPerform() if (status != 0) {
System.print("Failed to perform task.") return
}
curl.easyCleanup()</lang>
We now embed this in the following C program, compile and run it.
<lang c>/* gcc https_client-authenticated.c -o https_client-authenticated -lcurl -lwren -lm */
- include <stdio.h>
- include <stdlib.h>
- include <string.h>
- include <curl/curl.h>
- include "wren.h"
/* C <=> Wren interface functions */
void C_curlAllocate(WrenVM* vm) {
CURL** pcurl = (CURL**)wrenSetSlotNewForeign(vm, 0, 0, sizeof(CURL*)); *pcurl = curl_easy_init();
}
void C_easyPerform(WrenVM* vm) {
CURL* curl = *(CURL**)wrenGetSlotForeign(vm, 0); CURLcode cc = curl_easy_perform(curl); wrenSetSlotDouble(vm, 0, (double)cc);
}
void C_easyCleanup(WrenVM* vm) {
CURL* curl = *(CURL**)wrenGetSlotForeign(vm, 0); curl_easy_cleanup(curl);
}
void C_easySetOpt(WrenVM* vm) {
CURL* curl = *(CURL**)wrenGetSlotForeign(vm, 0); CURLoption opt = (CURLoption)wrenGetSlotDouble(vm, 1); const char *arg = wrenGetSlotString(vm, 2); curl_easy_setopt(curl, opt, arg);
}
WrenForeignClassMethods bindForeignClass(WrenVM* vm, const char* module, const char* className) {
WrenForeignClassMethods methods; methods.allocate = NULL; methods.finalize = NULL; if (strcmp(module, "main") == 0) { if (strcmp(className, "Curl") == 0) { methods.allocate = C_curlAllocate; } } return methods;
}
WrenForeignMethodFn bindForeignMethod(
WrenVM* vm, const char* module, const char* className, bool isStatic, const char* signature) { if (strcmp(module, "main") == 0) { if (strcmp(className, "Curl") == 0) { if (!isStatic && strcmp(signature, "easySetOpt(_,_)") == 0) return C_easySetOpt; if (!isStatic && strcmp(signature, "easyPerform()") == 0) return C_easyPerform; if (!isStatic && strcmp(signature, "easyCleanup()") == 0) return C_easyCleanup; } } return NULL;
}
static void writeFn(WrenVM* vm, const char* text) {
printf("%s", text);
}
void errorFn(WrenVM* vm, WrenErrorType errorType, const char* module, const int line, const char* msg) {
switch (errorType) { case WREN_ERROR_COMPILE: printf("[%s line %d] [Error] %s\n", module, line, msg); break; case WREN_ERROR_STACK_TRACE: printf("[%s line %d] in %s\n", module, line, msg); break; case WREN_ERROR_RUNTIME: printf("[Runtime Error] %s\n", msg); break; }
}
char *readFile(const char *fileName) {
FILE *f = fopen(fileName, "r"); fseek(f, 0, SEEK_END); long fsize = ftell(f); rewind(f); char *script = malloc(fsize + 1); fread(script, 1, fsize, f); fclose(f); script[fsize] = 0; return script;
}
int main(int argc, char **argv) {
WrenConfiguration config; wrenInitConfiguration(&config); config.writeFn = &writeFn; config.errorFn = &errorFn; config.bindForeignClassFn = &bindForeignClass; config.bindForeignMethodFn = &bindForeignMethod; WrenVM* vm = wrenNewVM(&config); const char* module = "main"; const char* fileName = "https_client-authenticated.wren"; char *script = readFile(fileName); WrenInterpretResult result = wrenInterpret(vm, module, script); switch (result) { case WREN_RESULT_COMPILE_ERROR: printf("Compile Error!\n"); break; case WREN_RESULT_RUNTIME_ERROR: printf("Runtime Error!\n"); break; case WREN_RESULT_SUCCESS: break; } wrenFreeVM(vm); free(script); return 0;
}</lang>
zkl
Uses libCurl. <lang zkl>var CURL=Import("zklCurl"), c=CURL(); c.setOpt("SSLCERT","certFile.pem"); c.setOpt("SSLCERTTYPE","pem"); c.get("http://zenkinetic.com"); // lame example to show how to read</lang>
- Programming Tasks
- Programming environment operations
- Networking and Web Interaction
- C sharp
- Go
- Julia
- Kotlin
- Lasso
- Mathematica
- Wolfram Language
- Nim
- Perl
- Phix
- Phix/libcurl
- PicoLisp
- Python
- Racket
- Raku
- Ruby
- Rust
- Scala
- Tcl
- Wren
- Libcurl
- Zkl
- Batch File/Omit
- Brainf***/Omit
- Commodore BASIC/Omit
- Inform 7/Omit
- Locomotive Basic/Omit
- Lotus 123 Macro Scripting/Omit
- Openscad/Omit
- M4/Omit
- Maxima/Omit
- ML/I/Omit
- PARI/GP/Omit
- PostScript/Omit
- Retro/Omit
- SQL PL/Omit
- TI-83 BASIC/Omit
- TI-89 BASIC/Omit
- Unlambda/Omit
- Yorick/Omit
- ZX Spectrum Basic/Omit