Hosting a Private Certificate Authority for Internal Network TLS and SSH

If you’re considering self-signed certificates for intranet sites and SSH, consider running your own certificate authority and automating the issuance of certificates for local network sites and SSH instead. Understand the general concepts, start with a relatively simple setup, then go from there.

Introduction

The Smallstep Blog post “Everything you should know about certificates and PKI but are too afraid to ask” is a good introduction to Public Key Infrastructure (PKI), but below is a succinct summary to gain a basic understanding and for quick reference. We start with the foundational cryptographic primitive called public key cryptography or asymmetric cryptography.

Public key cryptography is based on a cryptographic key pair, a public key and a corresponding private key, of which the public key is generally available to anyone and the private key is kept secret by the key pair’s owner. The following are two of the better-known variations:

Digital Signature applications of public key cryptography entail a sender owning a key pair and converting a message with a private key into a signature, and giving a) original message, b) signature, and c) public key to anyone to ascertain that the signature can be converted to the original message using the public key. This validates the authenticity and integrity of any message. The message is sent from key pair owner to others.

Public Key Encryption applications of public key cryptography allow anyone to encrypt a message using a public key that is made available by a key pair owner, to send a message in unreadable form to anyone but the owner of the private key. Only the private key can recover the original message. The message is received from others by the key pair owner.

PKI is a framework for managing secrets or digital certificates and always involves public key encryption. PKI includes key generation, storage, and distribution. Its design is important in a similar way to the proper design of cryptographic primitives. Generally, a top-down hierarchy of trust and of chains of certificates is used, but alternative schemes exist, such as PGP’s web of trust. We will focus on the former kind of PKI, where the .

Internet or Web PKI or PKIX is a PKI for applications such as browsing the web of publicly routable servers. Certificate Authorities (CAs) who are at the top of the chain of trust must be widely trusted for their certificates to be considered valid by default by the majority of web browsers.

Internal PKI is for everywhere else: internal networks or internal applications. One can use self-signed certificates, or better, to run a self-hosted internal CA. For the distribution of certificates, the same protocols can be used as in internet PKI.

A Digital Certificate is electronic data including at least a public key, a subject which is an identified entity, and a signature created by the issuer, or certificate authority, which is the signing entity. A signed certificate binds the information in the certificate together, which is only meaningful if the issuer is trustworthy. Certificates can have validity date ranges and support various protocols.

Certificates are commonly of X.509 v3 type, more specifically, PKIX variants (RFC 5280). Minimum requirements for internet PKI certifictes are prescribed by the CA/Browser (CAB) Forum. Certificates use ASN.1 notation for defining data types. One of the ASN.1 data types is the Object IDentifier (OID), which is used to tag data with a type, turning the string into an X.509 common name. Then there are rules for data encoding, notably Distinguished Encoding Rules (DER) or sometimes Basic Encoding Rules (BER). A raw DER file could carry a .der extension.

Envelope Formats are sometimes used to combine certificates with other data, still using ASN.1. For example, PKCS#7 (IETF CMS) encodes a chain of certificates, is commonly used in Java, used with .p7b or .p7c extensions. Another example is PKCS#12, which can contain a chain of certificates along with an encrypted private key, common in Microsoft products, with extensions .pfx and .p12. For keys, there is PKCS#8. with .pub, .prv extensions.

On top of that, there are packaging methods, notably Privacy Enhanced EMail (PEM), to package binary payload as pure text. The test includes a header (for example -----BEGIN CERTIFICATE-----, base64-encoded payload, and a footer (for example -----END CERTIFICATE-----. The extensions are .crt, or .cer for certificates, and .pub, or .prv for public and private keys, respectively, or sometimes just .pem.

Keys can be encrypted using a password or another key. When encrypted, the file includes Proc-Type and DEK-Info fields, or the information is encoded in the case of a PKCS#8 envelope.

Chains of Trust

In practical PKI used by internet browsers, trust originates from the root certificates of top-level Certificate Authorities. Root certificates are self-signed; the subject and issuer are the same entity. A root certificate can then be used to sign an intermediate certificate, creating a chain of trust or a tree structure. Each chain ends in a leaf certificate. Accessing a website or, more generally, using the TLS protocol for secure communications, includes validating a TLS (leaf) certificate. The leaf certificate must be bundled with the complete chain of certificates leading to the root certificate, to enable validation of the entire chain. This is called certificate path validation.

A web browser, or operating system, comes pre-loaded with a trust store of root certificates that the web browser or OS developer, think of Apple, Microsoft, Mozilla, and Google, trusts. Note that there are more than 100 CAs, and that internet PKI trustworthiness is limited by the least trustworthy of all of these. For internal PKI, it may be better to rely on a single internal CA instead. An internal PKI root certificate must be added to a browser’s or OSes trust store, to enable validation of Internal PKI leaf certificates by an application using it, and for better security, one could even use separate trust stores just for internal PKI.

Internet CA root certificate private keys must be kept offline because a compromised root certificate impacts a large tree of intermediate and hundreds of millions of leaf certificates. Internal root certificates could be kept online, but must be stored securely. In both scenarios, it is good practice to create intermediate certificates and use those for (automatically) generating leaf certificates.

A Certificate needs a subject, or name, to be bound to a newly generated public and private key pair, and a validity period. Multiple names can be included in a Certificate. A Subject Alternative Name (SAN) is the primary field TLS clients use to match an entity to certificate identity. For example, a physical person could be represented by an email address that the person has access to. For machines and code, DNS SANs are commonly used. Trust is often based on the assumption that no other Person than the actual target has access to the email address, and similarly, that a DNS SAN resolves to the IP address of a specific machine and only the machine can control the DNS or control what data is served via its IP address. For all other cases, the name can be a URI.

To avoid the complexity of certificate revocation, certificates should have a validity start and end time. Such certificates are passively revoked, simply using short-term expiration dates. The lifetime of root and intermediate certificates could be years, and that of leaf certificates just weeks, days, or even hours, depending on the specific circumstances. Dynamic operation with shorter time periods can increase the robustness of a PKI by keeping PKI managers involved and memory fresh. However, the validity period should allow sufficient time between detection and expiration relative to the time needed to fix any problem. Further, too frequent expiration events increase total duration of temporary service disruptions during Certificate renewal, as well as the importance of maintaining accurate time on each machine. Finally, consider the infrequent but potentially more significant impact of renewing root or intermediate certificate too. Continuous monitoring of certificate validity is a must in any scenario.

Setup Internal PKI with step-ca Service: “step ca init”

Internal PKI can be based on a free, but basic, “Tiny CA” step-ca server, using Smallstep step-ca open source software, on a Raspberry Pi, with a YubiKey 5 NFC for secure private key storage. The install works fine with Debian 13.2 on Raspberry Pi. Follow the instructions in the linked post, potentially creating a different user account instead of the Ubuntu image’s default ubuntu. The YubiKey stores root and private certificates with private keys in PIV slot ids 9a and 9c, respectively. The CA’s service is exposed at port 443.

Summary overview of basic install steps, with some additions to make things work with Debian 13.2 image:

[ ] Install OS image on Raspberry Pi using imager
[ ] Install yubikey-manager (ykman command)
[ ] Install Go language
[ ] Commands to prevent "invalid array length" error (Debian 13.2)
go get golang.org/x/tools@latest
go mod tidy
[ ] Install dependencies (Debian 13.2)
sudo apt-get install -y libpcsclite-dev gcc make pkg-config
[ ] Build step-ca and step from source (with YubiKey support) v0.28.4
[ ] Create root and intermediate certificates and private keys
|key command: step ca init ... with arguments on the command line or entered interactively
|The certificates and keys are created air-gapped, and directly on an external USB
|The certificates are then copied to /root/ before moving them to /etc/step-ca/
[ ] Install certificates and keys on YubiKey
[ ] Setup small-ca service
|key command: step-ca ... to run the service, best as systemd service
|The certificates created in this step in /etc/step-ca/ are dummy and will be replaced with the real ones
[ ] Add udev rule for YubiKey
[ ] Configure systemd service for step-ca
[ ] Add polkit rules to allow access to YubiKey
[ ] Open port 443 in firewall for step-ca service

Inspect X.509 Certificates using the step Command

After installation of step and step-ca on the server, we can have a look at the certificates in /etc/step-ca/certs/, which is specified in /etc/step-ca/config/ca.json.

Inspect a certificate using step certificate inspect <path> --format json. For example, replace <path> with /etc/step-ca/certs/root_ca.crt, or/etc/step-ca/certs/intermediate_ca.crt. Remove the --format json portion for a shorter summary, or use --short for even less.

For bootstrapping step clients, display root certificate’s fingerprint (sha256) with: sudo step certificate fingerprint /etc/step-ca/certs/root_ca.crt. For Windows installs to the trust store, take a look at the fingerprint in sha1 form, which is listed in the json formatted output.

Setup of a Local Client: “step ca bootstrap”

First install the step client on any client machine, for example

To access the Certificate Authority using the step client, at first run you will need the URL (either FQDN or IP address) of the CA just created, and the CA’s fingerprint. Bootstrap connects to the CA server and downloads the root certificate, and stores the connection information. This establishes a trust relationship between client and server.

For example, you can have your local DNS resolve tiny-ca.home.arpa resolve to the IP address of your server, in which case you replace <URL> by tiny-ca.home.arpa.

[ ] Bootstrap the step command in a user account, with URL and CA fingerprint
  step ca bootstrap --ca-url <URL> -- fingerprint <CA fingerprint>

On the machine where the CA server is running, you can do the following, to automatically insert the right fingerprint:

[ ] Bootstrap step client on the machine running the step-ca service, if you have sudo privileges
! step ca bootstrap --ca-url tiny-ca.home.arpa --fingerprint $(sudo step certificate fingerprint /etc/step-ca/certs/root_ca.crt)

The step command keeps its data in the path displayed with the command step path, typically $HOME/.step.

Request Certificate for localhost on step-ca Server

To test the default provisioner you named and for which you have set a password, on the step-ca Service Server:

[ ] Local request for certificate for "localhost"
  step ca certificate "localhost" localhost.crt localhost.key
[ ] Inspect the certificate
  step certificate inspect localhost.crt --short
|default validity is 1 day

Adding an ACME Provisioner to the CA Service

The server is at tiny-ca.home.arpa:

[ ] Bootstrap step in user account
step ca bootstrap --ca-url tiny-ca.home.arpa --fingerprint $(sudo step certificate fingerprint /etc/step-ca/certs/root_ca.crt)

Use any ACME client to request, and renew, Leaf Certificates from your CA Service.

Install step Command and add Root CA Certificate to the OS Trust Store

On Windows, in a PowerShell terminal, install step like this:

[ ] Install step on Windows (PowerShell)  
  curl.exe -LO https://dl.smallstep.com/cli/docs-cli-install/latest/step_windows_amd64.zip
  Expand-Archive -LiteralPath .\step_windows_amd64.zip -DestinationPath .
  rm .\step_windows_amd64.zip
[ ] Check version
  step_windows_amd64\bin\step.exe version
[ ] Bootstrap with tiny-ca server's root CA fingerprint (sha256)
  .\step_windows_amd64\bin\step.exe ca bootstrap --ca-url <URL of your CA> --fingerprint <fingerprint>
[ ] Inspect root CA certificate
  .\step_windows_amd64\bin\step.exe certificate inspect .\.step\certs\root_ca.crt
[ ] Install root CA in trust store, and check the "thumbprint" (sha1) with your root CA certificate's, then [yes]
  .\step_windows_amd64\bin\step.exe certificate install

Browsing using a new Brave v. 1.86.139 browser window pointed at the CA URL now showed certificate valid. Firefox v. 147.0.1 also picked up the root CA certificate correctly, as did LibreWolf v. 128.0.3-2.

The SmallStep step installation instructions give more details.

published: 20260120

Illustration was AI-generated.

Leave a Reply

Your email address will not be published. Required fields are marked *