HTTPS/Client-authenticated

Revision as of 11:35, 14 March 2020 by Thundergnat (talk | contribs) (Rename Perl 6 -> Raku, alphabetize, minor clean-up)

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.

Task
HTTPS/Client-authenticated
You are encouraged to solve this task according to the task description, using any language you may know.

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#

Works with: C sharp version 3.0

<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'))

  1. x->set(CURLOPT_SSLCERT, #sslcert->readstring)
  2. sslcert->close
  3. 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>

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

Library: libcurl

Exactly the same as the HTTP#Phix task, except for the CURLOPT_SSLCERT part. <lang Phix>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)</lang>

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>

  1. 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>

  1. cert creation commands
  1. openssl req -newkey rsa:4096 -keyout my_key.pem -out my_csr.pem -nodes -subj "/CN=ME"
  1. 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>

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...

}

  1. Make a secure authenticated connection

set token [http::geturl https://verysecure.example.com/]

  1. Now as for conventional use of the “http” package

set data [http::data $token] http::cleanup $token</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>