UACME(1)

πŸ’… uacme(1) β€” the ACMEv2 client that's literally written in plain C

okay bestie, welcome to uacme(1), the user command page for uacme β€” and yes it's giving minimal-dependencies main character energy πŸŽ€. Strap in, this one's long but it's SO worth it.


✨ NAME ✨

uacme β€” an ACMEv2 client πŸ¦‹ written in plain C with minimal dependencies. No cap, that's the whole vibe.


🌸 SYNOPSIS 🌸

uacme [-a|--acme-url URL] [-b|--bits BITS] [-c|--confdir DIR] [-d|--days
DAYS] [-e|--eab KEYID:KEY[:BITS]] [-f|--force] [-h|--hook PROGRAM]
[-i|--no-ari] [-k|--rotate-key] [-l|--alternate N | FP]
[-m|--must-staple] [-n|--never-create] [-o|--no-ocsp] [-p|--profile
PROFILE] [-r|--reason CODE] [-s|--staging] [-t|--type RSA|EC]
[-v|--verbose ...] [-V|--version] [-y|--yes] [-?|--help] new [EMAIL] |
update [EMAIL] | deactivate | newkey | issue IDENTIFIER [ALTNAME ...]] |
issue CSRFILE | check IDENTIFIER [ALTNAME ...]] | check CSRFILE | revoke
CERTFILE [CERTKEYFILE]

(yes, copy it exactly, brackets and all β€” don't be cute with it πŸ™ˆ)


πŸ¦‹ DESCRIPTION πŸ¦‹

So uacme is a client for the ACMEv2 protocol, the one described in RFC8555 πŸ“œ. It's written in plain C with minimal dependencies β€” like, actually minimal: libcurl plus one of GnuTLS, OpenSSL or mbedTLS. That's the whole friend group. πŸ’œ

The ACMEv2 protocol is the thing that lets a Certificate Authority (and yes, https://letsencrypt.org is the popular one πŸ‘‘) and an applicant automate the whole verify-then-issue-a-certificate process. And it's not just issuance β€” the protocol also provides facilities for other certificate management functions, such as certificate revocation. So it's a whole ecosystem, not a one-trick pony.

Want the full lore? It's at https://tools.ietf.org/html/rfc8555 πŸ“– β€” go read it, iykyk.


πŸ’– OPTIONS πŸ’–

okay this is the part everyone scrolls to. Every single flag, no skipping, we don't do that here 🫢

  • -a, --acme-url URL β€” the ACMEv2 server directory object URL. If you don't specify it, uacme uses one of these:
  • -b, --bits BITS β€” key bit length. Default is 2048 for RSA and 256 for EC. This only applies to newly generated keys, so don't expect it to retroactively glow up your old ones. Rules fr fr:
    • RSA key length must be a multiple of 8 between 2048 and 8192.
    • EC key length must be either 256 (NID_X9_62_prime256v1 curve) or 384 (NID_secp384r1 curve). That's it, those are the only two options, periodt.
  • -c, --confdir CONFDIR β€” use configuration directory CONFDIR (default /etc/ssl/uacme). The structure is as follows, and multiple IDENTIFIERs are allowed πŸ’…:
    • CONFDIR/private/key.pem β€” the ACME account private key πŸ”‘
    • CONFDIR/private/IDENTIFIER/key.pem β€” the certificate key for IDENTIFIER
    • CONFDIR/IDENTIFIER/cert.pem β€” the certificate for IDENTIFIER πŸ“œ
  • -d, --days DAYS β€” don't reissue certificates that are still valid for longer than DAYS (default 30). Important caveat bestie: this only applies as a fallback if no server renewal information is available. See also -i, --no-ari and -o, --no-ocsp.
  • -e, --eab KEYID:KEY[:BITS] β€” specify RFC8555 External Account Binding credentials, according to https://tools.ietf.org/html/rfc8555#section-7.3.4 πŸ”—, in order to associate a new ACME account with an existing account in a non-ACME system such as a CA customer database. The deets:
    • KEYID must be an ASCII string.
    • KEY must be base64url-encoded.
    • BITS is either 256, 384 or 512 and determines the number of bits of the EAB signature algorithm (default HMAC-SHA-256 when omitted).
  • -f, --force β€” force certificate reissuance regardless of expiration date and renewal information from the server. Like, "I said what I said" energy πŸ’€.
  • -h, --hook PROGRAM β€” the challenge hook program. If you don't specify it, uacme interacts with you (the user 🫡) for every ACME challenge, printing information about the challenge type, token and authorization on stderr. If you do specify it, uacme executes PROGRAM β€” a binary, a shell script, or any file that can be executed by the operating system β€” for every challenge with the following 5 string arguments:
    • METHOD β€” one of begin, done or failed.
      • begin β€” is called at the beginning of the challenge. PROGRAM must return 0 to accept it. Any other return code declines the challenge. Neither done nor failed method calls are made for declined challenges.
      • done β€” is called upon successful completion of an accepted challenge. πŸŽ‰
      • failed β€” is called upon failure of an accepted challenge. 😭
    • TYPE β€” challenge type (dns-01, dns-persist-01, http-01 or tls-alpn-01)
    • IDENT β€” the identifier the challenge refers to
    • TOKEN β€” the challenge token
    • AUTH β€” the key authorization (for dns-01 and tls-alpn-01 already converted to the base64url-encoded SHA256 digest format)
  • -i, --no-ari β€” do not query or use the server's certificate renewal information window to decide whether to reissue an existing certificate. See also -d, --days and -o, --no-ocsp.
  • -k, --rotate-key β€” always generate a new private key when issuing a certificate πŸ”„πŸ”‘.
  • -l, --alternate N | FP β€” according to https://tools.ietf.org/html/rfc8555#section-7.4.2 the server MAY provide one or more additional certificate download URLs, each pointing to alternative certificate chains starting with the same end-entity certificate. This option lets you select one such chain, in one of two ways:
    • A positive integer N makes uacme select the Nth alternative chain in the order presented by the server.
    • A colon (:) separated list of two or more 2-digit hexadecimal numbers FP makes uacme select the first alternative chain containing either a certificate whose SHA256 fingerprint begins with FP, or a certificate in which the Authority Key Identifier extension contains a keyIdentifier field beginning with FP.
    • In all cases uacme falls back to the main certificate URL if it cannot match an alternative chain or the download thereof fails. Safety net, slay πŸ’….
  • -m, --must-staple β€” request certificates with the RFC7633 Certificate Status Request TLS Feature Extension, informally also known as "OCSP Must-Staple". Heads up: this option is ignored when using an externally supplied Certificate Signing Request file (see USAGE below).
  • -n, --never-create β€” by default uacme creates directories/keys if they do not exist. When this option is specified uacme never does so and instead exits with an error if anything required is missing. Strict queen behavior πŸ‘‘.
  • -o, --no-ocsp β€” when this flag is not specified and the certificate has an Authority Information Access extension with an OCSP server location according to https://tools.ietf.org/html/rfc5280#section-4.2.2.1, uacme makes an OCSP request to the server; if the certificate is reported as revoked uacme forces reissuance regardless of the expiration date. See also -d, --days.
  • -p, --profile PROFILE β€” if the server supports it, use PROFILE πŸŽ€ β€” a collection of attributes about the certificate that will be issued, such as what extensions it will contain, how long it will be valid for, and more. Customization era.
  • -r, --reason CODE β€” use CODE (default 0) as reason code in revocation requests. The full list of values lives at https://tools.ietf.org/html/rfc5280#section-5.3.1.
  • -s, --staging β€” use the Let's Encrypt staging URL for testing πŸ§ͺ. Warning bestie: this only works if -a, --acme-url is NOT specified.
  • -t, --type=RSA | EC β€” key type, either RSA or EC. Only applies to newly generated keys. The bit length can be specified with -b, --bits.
  • -v, --verbose β€” by default uacme only produces output upon errors or when user interaction is required. When this option is specified uacme prints information about what is going on on stderr πŸ—£οΈ. And you can specify this option more than once to increase verbosity β€” stack it, why not.
  • -V, --version β€” print program version on stderr and exit.
  • -y, --yes β€” autoaccept ACME server terms (if any) upon new account creation. Consent, but make it automated βœ….
  • -?, --help β€” print a brief usage text on stderr and exit.

πŸ’… USAGE πŸ’…

🌱 new β€” make a whole new account, bestie

uacme [OPTIONS ...] new [EMAIL]

Creates a new ACME account, with optional EMAIL contact. If the account private key does not exist at CONFDIR/private/key.pem, a new key is generated unless -n, --never-create is specified. A valid account has to be created before any other operation can succeed β€” the one exception is certificate revocation requests signed by the certificate private key. Any certificate issued by the ACME server is associated with a single account. One account can be associated with multiple certificates, subject of course to the rate limits imposed by the ACME server. πŸ’–


πŸ› οΈ update β€” change the email on the account

uacme [OPTIONS ...] update [EMAIL]

Updates the EMAIL associated with the ACME account corresponding to the account private key. If EMAIL is not specified, the account contact email is removed. ✨


πŸ’€ deactivate β€” the irreversible one

uacme [OPTIONS ...] deactivate

Deactivates the ACME account corresponding to the account private key.

⚠️ WARNING: this action is irreversible. Users may wish to do this when the account key is compromised or decommissioned. A deactivated account can no longer request certificate issuances and revocations or access resources related to the account.

πŸ”‘ newkey β€” new account key, who dis

uacme [OPTIONS ...] newkey

Changes the ACME account private key. If the new account private key does not exist at CONFDIR/private/newkey.pem, it is generated unless -n, --never-create is specified. The new key is then submitted to the server, and if the operation succeeds the old key is hardlinked to CONFDIR/private/key-TIMESTAMP.pem before renaming CONFDIR/private/newkey.pem to CONFDIR/private/key.pem. πŸŽ€


πŸ“œ issue IDENTIFIER [ALTNAME ...] β€” certificate time, periodt

uacme [OPTIONS ...] issue IDENTIFIER [ALTNAME ...]

Issues a certificate for IDENTIFIER with zero or more ALTNAMEs and saves it to CONFDIR/IDENTIFIER/cert.pem. The private key for the certificate is loaded from CONFDIR/private/IDENTIFIER/key.pem. If no such file exists, a new key is generated unless -n, --never-create is specified. With -k, --rotate-key, the private key is loaded from CONFDIR/private/IDENTIFIER/newkey.pem instead. If no such file exists, it is generated.

Upon successful certificate issuance, the old private key is hardlinked to CONFDIR/private/IDENTIFIER/key-TIMESTAMP.pem before renaming CONFDIR/private/IDENTIFIER/newkey.pem to CONFDIR/private/IDENTIFIER/key.pem.

Wildcard IDENTIFIERs or ALTNAMEs are dealt with correctly, as long as the ACME server supports them; note that any such wildcards are automatically removed from the configuration subdirectory name: for example a certificate for *.test.com is saved to CONFDIR/test.com/cert.pem. IP address IDENTIFIERs and ALTNAMEs are also supported according to https://tools.ietf.org/html/rfc8738#section-3.

If a certificate is already available at CONFDIR/IDENTIFIER/cert.pem for the specified IDENTIFIER and ALTNAMEs and needs renewal, it is hardlinked to CONFDIR/IDENTIFIER/cert-TIMESTAMP.pem before overwriting. Renewal happens if:

  • -f, --force is specified,
  • or the certificate expires in less than DAYS and either -i, --no-ari is specified or the server does not provide renewal information,
  • or -i, --no-ari is not specified and the server indicates the certificate is due for renewal,
  • or -o, --no-ocsp is not specified and the certificate is reported as revoked by the OCSP server.

πŸ“„ issue CSRFILE β€” CSR mode, no private key needed

uacme [OPTIONS ...] issue CSRFILE

Issues a certificate based on a RFC2986 Certificate Signing Request contained in CSRFILE, which must be in PEM format. In this mode of issuance uacme neither needs nor generates the certificate private key, but it is of course the responsibility of the user to ensure that the CSR is constructed and signed appropriately.

Wildcard identifiers in the CSR are dealt with correctly, as long as the ACME server supports them. IP addresses are also supported according to https://tools.ietf.org/html/rfc8738#section-3.

If a certificate file CSRBASE-cert.pem (where CSRBASE is obtained by stripping the extension, if any, from CSRFILE) is already available in the same directory containing CSRFILE, it matches the identifiers in CSRFILE and needs renewal, it is hardlinked to BASENAME-cert-TIMESTAMP.pem before overwriting. Renewal happens if:

  • -f, --force is specified,
  • or the certificate expires in less than DAYS and either -i, --no-ari is specified or the server does not provide renewal information,
  • or -i, --no-ari is not specified and the server indicates the certificate is due for renewal,
  • or -o, --no-ocsp is not specified and the certificate is reported as revoked by the OCSP server.

πŸ” check IDENTIFIER [ALTNAME ...] β€” vibe check, but for certs

uacme [OPTIONS ...] check IDENTIFIER [ALTNAME ...]

Same as uacme [OPTIONS ...] issue IDENTIFIER [ALTNAME ...], except that no certificate is issued or renewed. The exit code reflects existence and validity of the certificate. ✨


πŸ”Ž check CSRFILE β€” CSR vibe check

uacme [OPTIONS ...] check CSRFILE

Same as uacme [OPTIONS ...] issue CSRFILE, except that no certificate is issued or renewed. The exit code reflects existence and validity of the certificate. 🌸


🚫 revoke CERTFILE [CERTKEYFILE] β€” it's giving revocation

uacme [OPTIONS ...] revoke CERTFILE [CERTKEYFILE]

Revokes the certificate stored in CERTFILE. The revocation request is signed with the private key of either the certificate, when CERTKEYFILE is specified; or the ACME account associated with the certificate, when only CERTFILE is specified. In the first instance the account key and the configuration directory are not required. If successful CERTFILE is renamed to revoked-TIMESTAMP.pem. The reason code in the revocation request defaults to 0 but it can be specified by the user with -r, --reason. 🫢


🌿 ENVIRONMENT 🌿

  • UACME_CAINFO β€” String naming a file holding one or more CA certificates to verify the ACME server with.
  • UACME_CAPATH β€” String naming a directory holding multiple CA certificates to verify the ACME server with. If libcurl is built against OpenSSL, the certificate directory must be prepared using the OpenSSL c_rehash utility.
  • UACME_DNS_SERVERS β€” Comma separated list of DNS servers to be used instead of the system default. The format of the dns servers option is host[:port][,host[:port]]...
  • UACME_INTERFACE β€” String setting the interface name to use as outgoing network interface. The name can be an interface name, an IP address, or a hostname. If you prefer one of these, you can use the following special prefixes:
    • if!<name> β€” Interface name
    • host!<name> β€” IP address or hostname
    • ifhost!<interface>!<host> β€” Interface name and IP address or hostname
  • UACME_PROXY β€” String holding the proxy hostname or dotted numerical IP address. A numerical IPv6 address must be written within [brackets]. To specify port number in this string, append :[port] to the end of the host name. If not specified, default to using port 1080. The proxy string may be prefixed with [scheme]:// to specify which kind of proxy is used (http://, https://, socks4://, socks4a://, socks5://, socks5h://). The proxy can also be specified with its associated credentials like for ordinary URLs in the style: scheme://username:password@hostname

πŸͺ HOOK ENVIRONMENT πŸͺ

The following environment variables are exported for use by the hook program:

  • UACME_CONFDIR β€” Path to CONFDIR, see -c, --confdir
  • UACME_VERBOSE β€” Verbosity, see -v, --verbose
  • UACME_METHOD, UACME_TYPE, UACME_IDENT, UACME_TOKEN, UACME_AUTH β€” Copies of the hook program arguments, see -h, --hook

🚦 EXIT STATUS 🚦

  • 0 β€” Success
  • 1 β€” Certificate not reissued because it is still current
  • 2 β€” Failure (syntax or usage error; configuration error; processing failure; unexpected error).

🧚 EXAMPLE HOOK SCRIPT 🧚

The uacme.sh hook script included in the distribution can be used to automate the certificate issuance with http-01 challenges, provided a web server for the domain being validated runs on the same machine, with webroot at /var/www

#!/bin/sh
CHALLENGE_PATH=/var/www/.well-known/acme-challenge
ARGS=5
E_BADARGS=85

if test $# -ne "$ARGS"
then
    echo "Usage: $(basename "$0") method type ident token auth" 1>&2
    exit $E_BADARGS
fi

METHOD=$1
TYPE=$2
IDENT=$3
TOKEN=$4
AUTH=$5

case "$METHOD" in
    "begin")
        case "$TYPE" in
            http-01)
                echo -n "${AUTH}" > "${CHALLENGE_PATH}/${TOKEN}"
                exit $?
                ;;
            *)
                exit 1
                ;;
        esac
        ;;
    "done"|"failed")
        case "$TYPE" in
            http-01)
                rm "${CHALLENGE_PATH}/${TOKEN}"
                exit $?
                ;;
            *)
                exit 1
                ;;
        esac
        ;;
    *)
        echo "$0: invalid method" 1>&2
        exit 1
esac

πŸ› BUGS πŸ›

If you believe you have found a bug, please create a new issue at https://github.com/ndilieto/uacme/issues with any applicable information.


πŸ‘€ SEE ALSO πŸ‘€

ualpn(1)


πŸ‘‘ AUTHOR πŸ‘‘

uacme was written by Nicola Di Lieto


Copyright Β© 2019-2026 Nicola Di Lieto <nicola.dilieto@gmail.com>

This file is part of uacme.

uacme is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

uacme is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.


πŸ’– That's the whole page, bestie β€” source/version uacme 1.8.1, dated 04/07/2026, manual page UACME(1). Slay. πŸŽ€

πŸ“¦ where this page comes from