Auth cohttp request with X509 certificate

Hi here,

I’ve spent some time playing with cohttp to make it authenticate a connection with my X509 certificate. Working code is below:

(* ocamlfind ocamlopt -thread -package lwt,tls,x509,cohttp,cohttp-lwt,lwt_ppx,cohttp-lwt-unix -linkpkg cohttp_tls.ml *)

open Lwt
open Printf
open Cohttp
open Cohttp_lwt_unix


let ca_file = "ca-chain.crt"
let cert_file = "your.crt"
let key_file = "your_private.key"
let get_url = "https://your.server/path/to/resource"

let tls_own_key = `TLS (
  (`Crt_file_path cert_file),
  (`Key_file_path key_file),
  (`No_password )
)

let request () =
  let%lwt anchors =
    Lwt_io.(with_file ~mode:Input ca_file read) >>= fun str ->
    match
      X509.Certificate.decode_pem_multiple @@ Cstruct.of_string str
    with Ok r -> return r | Error (`Msg s) ->
      failwith @@ sprintf "can't decode PEM: %s" s
  in
  let tls_authenticator ?ip ~host certs =
    ignore ip;
    ignore host;
    X509.Validation.verify_chain_of_trust
      ~host:None
      ~time:(fun () -> None)
      ~allowed_hashes:[ `SHA256 ; `SHA384 ; `SHA512 ]
      ~anchors certs
  in
  let%lwt ctx = Conduit_lwt_unix.init ~tls_own_key ~tls_authenticator () in
  let ctx = { Net.default_ctx with ctx } in
  let%lwt (resp,body) =
    Lwt_unix.with_timeout 20.0 @@
    (fun () -> Client.get ~ctx (Uri.of_string get_url))
  in
  let rc = resp |> Response.status |> Code.code_of_status in
  let%lwt body = Cohttp_lwt.Body.to_string body in
  printf "I: request is sent, answer: %d\n%s\n%!" rc body;
  return_unit

let _ = Lwt_main.run @@ request ()

Important detail: for this code to work you must have tls-lwt and ca-certs opam packages installed due to conditional compilation of ocaml-conduit.

P.S.: code is dirty and not very elegant, sorry for that :slight_smile:

2 Likes

I would like to advise http-lwt-client which expects an authenticator argument and a ocaml-tls client configuration which you just built with your code. However, unlike CoHTTP, Conduit is not involved and neither is optional compilation (which makes the behaviour of your software more reproducible on other machines). And thank you for your advice. I know a lot of people want to go through this use case, which has never been easy with Conduit.

I’m going to use Cohttp for both client and server so in my case it’s easier to share one approach for both sides of conduit :slight_smile:

1 Like