#lang scribble/manual @(require scribble/example (for-label racket/base racket/contract openssl)) @title{Self-Signed Certificate Utilities} @author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]] @defmodule[racket-self-signed-cert] This module provides utilities for generating a self-signed X.509 certificate together with a corresponding private key. The implementation uses the @racketmodname[openssl] bindings that are distributed with Racket. In other words, the module relies on the OpenSSL library that ships with Racket and accesses it via Racket's FFI interface. The generated certificate and key are returned in PEM format and can be used directly with Racket networking libraries such as @racketmodname[openssl] or TLS-enabled servers. @section{OpenSSL Integration} The module dynamically integrates with the OpenSSL library that is present in the running Racket installation. During initialization the module performs the following steps: @itemlist[ @item{ It detects the major version of the OpenSSL library available through Racket's @racketmodname[openssl] bindings. } @item{ If OpenSSL version 3 is detected, the module raises an error because the required FFI bindings currently support only the OpenSSL 1.x API. } @item{ The module determines which native OpenSSL library must be loaded for FFI access. This allows the implementation to bind directly to the required cryptographic primitives. } @item{ Platform-specific loading of the native OpenSSL library is performed at runtime. } ] The implementation has been tested on the following platforms: @itemlist[ @item{Windows} @item{Linux} ] Other platforms may work provided that a compatible OpenSSL library is available through Racket. @section{Data Structures} @defstruct[self-signed-cert ([private-key string?] [certificate string?])]{ Represents a generated self-signed certificate together with its private key. Both fields contain PEM encoded text. @itemlist[ @item{@racket[private-key] — the RSA private key in PEM format.} @item{@racket[certificate] — the X.509 certificate in PEM format.} ] Instances of this structure are returned by @racket[generate-self-signed-cert]. } @defproc[(self-signed-cert? [v any/c]) boolean?]{ Returns @racket[#t] if @racket[v] is a @racket[self-signed-cert] structure. } @section{Accessors} @defproc[(private-key [ssc self-signed-cert?]) string?]{ Returns the private key stored in @racket[ssc]. The value is a PEM encoded RSA private key suitable for use with TLS libraries or for writing to disk. } @defproc[(certificate [ssc self-signed-cert?]) string?]{ Returns the X.509 certificate stored in @racket[ssc]. The value is a PEM encoded certificate. } @defthing[x509-cert (-> self-signed-cert? string?)]{ Alias for @racket[certificate]. This name is provided for situations where the API user prefers the term “X.509 certificate”. } @section{Certificate Generation} @defproc[(generate-self-signed-cert [bits integer?] [duration-in-days integer?] [hosts (or/c is-ip? is-dns? list-of-hosts?)] [country string?] [company string?]) self-signed-cert?]{ Generates a new self-signed RSA certificate and private key. The implementation uses the OpenSSL functionality provided through Racket's @racketmodname[openssl] library. @subsection{Arguments} @itemlist[ @item{@racket[bits] — size of the RSA key in bits (for example @racket[2048] or @racket[4096]).} @item{@racket[duration-in-days] — number of days for which the certificate remains valid.} @item{@racket[hosts] — a host name, IP address, or a list of such values. These values are written into the certificate's @italic{Subject Alternative Name} extension.} @item{@racket[country] — value for the certificate subject's @tt{C} (country) attribute.} @item{@racket[company] — value for the certificate subject's @tt{O} (organization) attribute.} ] The first host in the list is used as the certificate's Common Name (CN). @subsection{Result} Returns a @racket[self-signed-cert] structure containing: @itemlist[ @item{the private RSA key} @item{the corresponding self-signed X.509 certificate} ] Both values are returned as PEM encoded strings. @subsection{Example} @#reader scribble/comment-reader [racketblock (define cert (generate-self-signed-cert 2048 365 '("localhost" "127.0.0.1" "*.local.lan") "NL" "Example Company")) (private-key cert) (certificate cert) ] The returned values can be written to files or supplied directly to TLS-enabled servers. } @section{Notes} @itemlist[ @item{ This module relies on the OpenSSL library distributed with Racket and accessed through the @racketmodname[openssl] package. } @item{ Certificates are generated entirely in memory and returned as PEM strings. } @item{ The Subject Alternative Name (SAN) extension is automatically populated from the provided host names and IP addresses. } ]