GithubHelp home page GithubHelp logo

ndilieto / uacme Goto Github PK

View Code? Open in 1sVSCode Editor NEW
219.0 12.0 26.0 1.1 MB

ACMEv2 client written in plain C with minimal dependencies

License: GNU General Public License v3.0

Makefile 0.93% C 84.44% Shell 8.05% M4 3.30% Roff 3.28%
letsencrypt letsencrypt-cli acme-client acme-protocol acme-v2 uacme tls-certificate ssl-certificate rcf8555 ualpn challenge

uacme's Introduction

uacme manual


lightweight client for the RFC8555 ACMEv2 protocol, written in plain C with minimal dependencies (libcurl and one of GnuTLS, OpenSSL or mbedTLS. The ACMEv2 protocol allows a Certificate Authority (Let's Encrypt is a popular one) and an applicant to automate the process of verification and certificate issuance. The protocol also provides facilities for other certificate management functions, such as certificate revocation.


  • Written in C - It runs on any unix machine, including Linux, BSD, ...
  • Minimal dependencies - Other than the standard C library, uacme depends only on libcurl and one of GnuTLS, OpenSSL or mbedTLS. It does all the cryptography and network communications without spawning external processes. Particularly when using mbedTLS, it is small enough to run on embedded systems with severe RAM and program memory restrictions (such as OpenWRT routers, for example). This is in contrast to solutions based on python or shell scripts, which may well be a few hundred lines but require many other large applications such as python or openssl to work.
  • Native ECC support - Elliptic Curve keys and certificates can be generated with a commmand line option (-t EC)
  • Easily extensible - It optionally calls an external hook program with the tokens required for domain authorization by the server. The hook program can be an executable, shell script, perl script, python script, or any file that the operating system can execute.
  • ACME challenge agnostic - It provides the user or hook program with all tokens and information required to complete any challenge type but leaves the task of setting up and cleaning up the challenge environment to the user or hook. Example shell scripts to handle http-01, dns-01 and tls-alpn-01 challenges are provided.
  • Zero downtime tls-alpn-01 support - The distribution also includes ualpn, a lightweight proxying tls-alpn-01 challenge responder compliant with RFC8737 and RFC8738.
  • Can run as a cron job - to renew certificates automatically when needed, even for remote machines
  • Robust - It checks every operation, retrying or failing gracefully as appropriate
  • Detailed error reporting - By default totally quiet when everything works ok, it reports precise and detailed error information on stderr when something goes wrong. Optionally it can also print debug information by specifying the --verbose flag once or more.


Note: pristine releases are in the upstream/latest branch, tagged as upstream/x.x.x

mkdir uacme
wget -O - | tar zx -C uacme --strip-components=1
cd uacme
./configure --disable-maintainer-mode
make install

If you just want to check out the latest pristine release from github:

git clone -b upstream/latest

uacme packages are available for several distributions:

Getting started

Once you have obtained uacme (see Installation above) the next step is creating an ACME account:

uacme -v -c /path/to/uacme.d new

The configuration directory and account private key should have been created:


You can then issue a certificate for your domain by doing

uacme -v -c /path/to/uacme.d issue

If everything goes well uacme asks you to set up a challenge, for example

uacme: challenge=http-01 token=kZjqYgAss_sl4XXDfFq-jeQV1_lqsE76v2BoCGegFk4

Note the challenge type in the example is http-01 which means you should set up your web server to serve a URL based on the token:

The URL must return a text file containing a single line with the key authorization:


After setting up the web server you can then type 'y' followed by a newline. This notifies the ACME server that it can proceed with the challenge verification. If the procedure is successful uacme saves the certificate and the key at:


Note several challenge types are possible. If you type anything other than 'y', uacme skips the challenge and proposes a different one. The easiest is http-01 but any other type can be dealt with. Keep in mind that challenge types may be served in random order by the server. Do not make any assumptions and read what uacme outputs carefully.

Automating updates

Use the -h flag to manage the challenge with a hook script:

uacme -v -c /path/to/uacme.d -h /usr/share/uacme/ issue

or (depending on your installation)

uacme -v -c /path/to/uacme.d -h /usr/local/share/uacme/ issue

This will use the example hook script included in the distribution to manage http-01 challenges. You might need to edit the script to match your webserver's environment.

Once everything works correctly you can also set up cron, for example

6 15 * * * /usr/bin/uacme -c /path/to/uacme.d -h /usr/share/uacme/ issue 

The cron job will automatically update the certificate when needed. Note the absence of -v flag, this makes uacme only produce output upon errors.

Note also that you will need to restart or reload any service that uses the certificate, to make sure it uses the renewed one. This is system and installation dependent. I normally put the necessary instructions in another script (for example /usr/share/uacme/ that is executed by cron when uacme returns 0 (indicating the certificate has been reissued).

6 15 * * * /usr/bin/uacme -c /path/to/uacme.d -h /usr/share/uacme/ issue && /usr/share/uacme/

Check for a complete, ready-to-go solution.

dns-01 challenge support

The hook script included in the distribution allows managing dns-01 challenges with nsupdate. This only works if your name server supports RFC2136 (bind does, nsd doesn't). is another example that works with nsd. shows how to integrate with Cloudflare API. works with

tls-alpn-01 challenge support

ualpn is a lightweight proxying tls-alpn-01 challenge responder, designed to handle incoming HTTPS connections on port 443. Most of the time it just transparently proxies connections to the real web server (which can be on either another machine, or a different TCP port on the same machine). When a tls-alpn-01 challenge handshake comes in ualpn handles it on the fly instead of proxying it to the webserver. This means that unlike other available tls-alpn-01 responders, ualpn does not require your webserver to stop during the challenge (zero downtime).

The high performance event-driven implementation is based on libev which considerably reduces the cost of context switches and memory usage. In addition on systems such as Linux supporting the splice() system call, ualpn is able to move network data entirely in kernel memory without a round trip to user space, which further enhances performance.

ualpn also listens to a UNIX domain socket so that it can be fed the necessary tls-alpn-01 key authorizations for the domains being validated by the ACME server. ualpn was designed to be easy to integrate with not only uacme (check the example hook script) but also other ACME clients. A certbot plugin is also available.

To get started with ualpn:

  • move your real HTTPS server to port 4443 which doesn't need to be open to the outside (only ualpn will connect to it) and set it up to accept the PROXY protocol:
  • launch ualpn as a daemon and check the logs (by default in syslog)
    sudo ualpn -v -d -u nobody:nogroup -c [email protected] -S 666
  • create an ACME account
    uacme -v -s -c /path/to/uacme.d -y new
  • try obtaining a certificate with tls-alpn-01 challenge
    uacme -v -s -c /path/to/uacme.d -h /usr/share/uacme/ issue
    or, depending on your installation
    uacme -v -s -c /path/to/uacme.d -h /usr/local/share/uacme/ issue


There are regular unix man pages in the distribution, also available in HTML: uacme ualpn

Bugs and suggestions

If you believe you have found a bug, please log it at

If you have any suggestions for improvements, pull requests are welcome.

uacme's People


ndilieto avatar alexshpilkin avatar jirutka avatar sthen avatar kolbma avatar dengqf6 avatar HalosGhost avatar


Sven Lukas avatar Nicholas Black avatar  avatar Max Beier avatar Zero King avatar LiangTan avatar Benson Muite avatar kota avatar  avatar RDX avatar Siyuan Wu avatar irohane avatar Chase avatar  avatar fudanchii avatar hequn avatar mesibo avatar Nick Hainke avatar Julian R avatar Salvinia Natans avatar Kirill Zholnay avatar  avatar Hugo Rodrigues avatar  avatar  avatar uybe avatar Tony Harris avatar  avatar  avatar Wing avatar deax avatar Sergey Ponomarev avatar  avatar Panagiotis Karagiannis avatar mateusz avatar  avatar Vladi Belperchinov-Shabanski avatar Serg avatar lalawue avatar leen avatar  avatar Oskar avatar  avatar Bogus X. Chang avatar Sean avatar Abell avatar Hung-I Wang avatar  avatar Constantin avatar Lukas Martini avatar boatmac avatar Linz avatar Tomasz C. avatar yndgle avatar  avatar Patrick Lambein avatar Jacob Middag avatar  avatar Tuquone S'mthin avatar Dave Cottlehuber avatar Farhad Shahbazi avatar Herts Li avatar Björn avatar  avatar  avatar George avatar  avatar Jan Graichen avatar Moritz Marquardt avatar Filippo Giunchedi avatar Federico Ceratto avatar Adrian Fedoreanu avatar Akhil Velagapudi avatar Chen Yijun avatar Csoban Kesmarki avatar Matt Johnston avatar Allen avatar Dario Balboni avatar Christopher Bayliss avatar  avatar Y@rik avatar  avatar  avatar Yuuta Liang avatar Richard Yu avatar Mario Campos avatar Joel Taylor avatar Herby Gillot avatar Igor Vuk avatar Aldis Berjoza avatar ryan avatar  avatar Ananda Umamil avatar Randy Tarampi avatar Theodore Keloglou avatar Muh. Fani Akbar avatar René Hickersberger avatar  avatar Wei Xie avatar Mikhail Subbotin avatar


Vladi Belperchinov-Shabanski avatar Daniel McCarney avatar James Cloos avatar Juri avatar Benson Muite avatar RokkuCode avatar Rick Blundell avatar Allen avatar  avatar Linz avatar Rob White avatar  avatar

uacme's Issues

No joy with install on Raspbian / Buster

Good morning,

I tried installing UACME on my Pi 4 (running Buster). I ran into two installation issues so far when I executed the "./configure --disable-maintainer-mode" command.

  • The first time, it failed due to a lack of libcurl. I installed that and re-ran the installation script.
  • The installer made it past libcurl but then got caught on GNUTLS missing. So I installed that too, via "sudo apt-get install gnutls-bin". The installer script still stalls and errors out, claiming that a suitable GNUTLS has not been installed.

According to the GNUTLS installer, version 3.6.7 was installed "Setting up gnutls-bin (3.6.7-4+deb10u6) ..."
Per the UACME installer, "checking for GnuTLS >= 3.3.30... no" followed by "configure: error: gnutls not found"

I must be doing something wrong so I wonder if you could make a suggestion re: GNUTLS or the version of LIBCURL that should be installed as a pre-requisite to installing UACME. I would also like to make the suggestion to reference these and any other packages that must be installed before UACME to make the process easier for newbies like me. Thank you for writing UACME!


support for --reuse-key

Hi, uacme looks very interesting to me but i could not find any information in the docs about the lifecycle of the key, I guess its renewed on renew? If so is it possible to a use or create similar option as certbot uses? ie --reuse-key. can generate invalid challenge due to echo -n uses /bin/sh in the shebang which often forces shells such as bash into compatibility mode which interprets echo -n as literal output and thus creates an invalid challenge:

uacme: the server reported the following error:
    "type": "urn:ietf:params:acme:error:unauthorized",
    "detail": "The key authorization file from the server did not match this challenge \"tt[...]Wuo\" != \"-n tt[...]Wuo\"",
    "status": 403

above example was produced with

$ /bin/sh --version
GNU bash, version 3.2.57(1)-release (arm64-apple-darwin20)

and the echo behavior can be checked with

$ /bin/sh -c 'echo -n'

The specs say that servers SHOULD accept challenges with trailing whitespace so the easiest fix is to remove -n. A more advanced fix would be to first check that -n is safe and not use it if it's not.

RFC 8738 support / externalAccountBinding problem with ZeroSSL API?

Hi, this is probably two issues in one ;) I need to automate certificate installation for a large network of decentralized clients with static IP addresses but no domain. I came across RFC 8738, I believe uacme is one of the first ACME clients to support this new standard? Let's Encrypt doesn't support it yet (it's in pebble but not boulder), which CA did you test against?

For now I'm trying to use it with ZeroSSL, which supports ACME and IP certificates, but I'm getting the following authentication error (probably unrelated to RFC 8738):

uacme: version 1.5 starting on Sun, 29 Nov 2020 18:11:04 -0800
uacme: loading key from ./private/key.pem
uacme: fetching directory at
uacme: creating new account at
uacme: type 'y' to accept the terms at
uacme: failed to create account at
uacme: the server reported the following error:
    "type": "urn:ietf:params:acme:error:externalAccountRequired",
    "status": 400,
    "detail": "The request must include a value for the \"externalAccountBinding\" field"

Is it possible to specify an externalAccountBinding with uacme? Thanks for thoughts on these issues! ns_getdomain selects wrong server to send updates

I just came across uacme when finally getting around to move my dns-01 setup across from some ACMEv1-only software. Thanks for writing it - I like the balance of useful features without being over-complex. It seems I picked a good time because was added recently - I ran into a few things I needed to change to get that to work for me, I'm happy to split into PRs if you like but I thought I'd discuss here first.

When trying to identify which servers should receive updates, ns_getdomain picks the last two components of the domain name ( -> This is fine for simple "domain.tld" cases with no subdomains, but fails for country TLDs like .uk where many names are registered under, or where a zone is delegated privately.

I'm using this instead, which I think should handle all cases, at the expense of another callout to dig:

    local domain=$1

    [ -n "$domain" ] || return
    set -- $($DIG +noall +authority "$domain" SOA 2>/dev/null)

    echo $1

A couple of other smaller things I noticed,

  • the various calls to dig using the system's default nameserver use -k RNDC_KEY, which fails at least in some situations - should those be dropped and RNDC_KEY just used for nsupdate calls?

  • there's a bashism ${res//"/} in ns_ispresent but the hashbang line is for /bin/sh (and the other scripts look like plain shell). It could be rewritten like this, or would it be preferable to change to a bash #!?

    for NS in $nameservers; do
        set -- $($DIG +short "@$NS" "$fqhn" TXT 2>/dev/null)
        [ "$*" = "$expect" ] || return 1
  • ns_gethostname has ${ret:1} which could be replaced with ${ret#?}, but the function is unused anyway, should it just be dropped?

Potential memory allocation issue in read-file.c

You may be interested that compiling uacme with gcc v7 and
Using built-in specs. COLLECT_GCC=/opt/local/gcc7/bin/cc COLLECT_LTO_WRAPPER=/opt/local/gcc7/libexec/gcc/x86_64-sun-solaris2.11/7.4.0/lto-wrapper Target: x86_64-sun-solaris2.11
give diagnostic from realloc.
read-file.c:119:14: warning: argument 2 value '18446744073709551615' exceeds maximum object size 9223372036854775807 [-Walloc-size-larger-than=] if (!(new_buf = realloc (buf, alloc)))

segfault in X509_get0_notAfter during certificate issuing

Hey @ndilieto, thanks for uacme. I like it.

I wanted to try out master, and found a segfault arising from a null pointer dereference. I bisected to 1809518 and am building it with the following configuration: ./configure --disable-maintainer-mode --with-openssl --disable-docs

(gdb) run -v issue
Starting program: /usr/local/bin/uacme -v issue
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/".
uacme: version 1.1.2 starting on Sun, 15 Mar 2020 13:00:19 +0000
uacme: loading key from /etc/ssl/uacme/private/key.pem
uacme: loading key from /etc/ssl/uacme/private/
uacme: checking existence and expiration of /etc/ssl/uacme/
uacme: /etc/ssl/uacme/ does not exist

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7dc08a0 in X509_get0_notAfter ()
   from /usr/lib/x86_64-linux-gnu/
(gdb) bt
#0  0x00007ffff7dc08a0 in X509_get0_notAfter ()
   from /usr/lib/x86_64-linux-gnu/
#1  0x000055555555ff75 in cert_valid (certdir=0x55555559bf70 "/etc/ssl/uacme/",
    names=0x7fffffffeb60, validity=30, status_check=true) at crypto.c:2716
#2  0x000055555555c1bb in main (argc=4, argv=0x7fffffffeb48) at uacme.c:1466
(gdb) break crypto.c:2716
Breakpoint 1 at 0x55555555ff69: file crypto.c, line 2716.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /usr/local/bin/uacme -v issue
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/".
uacme: version 1.1.2 starting on Sun, 15 Mar 2020 13:01:24 +0000
uacme: loading key from /etc/ssl/uacme/private/key.pem
uacme: loading key from /etc/ssl/uacme/private/
uacme: checking existence and expiration of /etc/ssl/uacme/
uacme: /etc/ssl/uacme/ does not exist

Breakpoint 1, cert_valid (certdir=0x55555559bf70 "/etc/ssl/uacme/",
    names=0x7fffffffeb60, validity=30, status_check=true) at crypto.c:2716
2716        const ASN1_TIME *tm = X509_get0_notAfter(crt[0]);
(gdb) p crt[0]
$1 = (X509 *) 0x0
(gdb) p ncrt
$2 = 32767

I'm not entirely sure where 32767 is coming from, but I assume the expected behavior is for cert_load to have returned <= 0. Here's a patch for that, which fixed the issue for me:

diff --git a/crypto.c b/crypto.c
index 00488af..71acfa0 100644
--- a/crypto.c
+++ b/crypto.c
@@ -2371,6 +2371,7 @@ static int cert_load(mbedtls_x509_crt **crt, const char *format, ...)
             msg(1, "%s does not exist", certfile);
             warn("cert_load: failed to open %s", certfile);
+        r = 0;
         goto out;
     for (r = 0; r < (int)crt_size; r++) {

Although, you might want to double check that the patch looks good. I see that r's being reused a lot.

chain and fullchain.pem


I tried uacme and it gets cert and the key. However, it does not get chain and fullchain as done by certbot.

Can you please add feature or let me know how to do it?


uacme doesn't work with smallstep ca

Hey all,

I have a smallstep certificate authority set up for in-house ACME provisioning, and am running into issues getting uacme to work with it. When running the uacme new command, I get an error saying that the "account does not exist" (see verbose logs below). I am able to create an account and issue a cert using certbot, so I suspect this is either an issue with uacme or a combination of uacme and the smallstep setup.

I really like uacme and would love to get it working with smallstep, so let me know if there's anything else I can do to help debug. Thanks for writing a great tool!


# uacme -v -v -v -a https://<DOMAIN>/acme/acme/directory new <EMAIL>

uacme: version 1.0.21 starting on Sat, 01 Feb 2020 14:41:55 +0000
uacme: loading key from /etc/ssl/uacme/private/key.pem
uacme: fetching directory at https://<DOMAIN>/acme/acme/directory
uacme: acme_get: url=https://<DOMAIN>/acme/acme/directory
uacme: acme_get: HTTP headers
HTTP/1.1 200 OK
Cache-Control: no-store
Content-Type: application/json
Replay-Nonce: <NONCE>
Date: Sat, 01 Feb 2020 14:41:55 GMT
Content-Length: 307

uacme: acme_get: HTTP body

uacme: acme_get: return code 200, json=
    "newNonce": "https://<DOMAIN>/acme/acme/new-nonce",
    "newAccount": "https://<DOMAIN>/acme/acme/new-account",
    "newOrder": "https://<DOMAIN>/acme/acme/new-order",
    "revokeCert": "https://<DOMAIN>/acme/acme/revoke-cert",
    "keyChange": "https://<DOMAIN>/acme/acme/key-change"
uacme: fetching new nonce at https://<DOMAIN>/acme/acme/new-nonce
uacme: acme_get: url=https://<DOMAIN>/acme/acme/new-nonce
uacme: acme_get: HTTP headers
HTTP/1.1 204 No Content
Cache-Control: no-store
Replay-Nonce: <NONCE>
Date: Sat, 01 Feb 2020 14:41:55 GMT

uacme: acme_get: HTTP body

uacme: acme_get: return code 204
uacme: creating new account at https://<DOMAIN>/acme/acme/new-account
uacme: acme_post: url=https://<DOMAIN>/acme/acme/new-account payload={"onlyReturnExisting":true} protected={"alg":"RS256","nonce":"<NONCE>","url":"https://<DOMAIN>/acme/acme/new-account","jwk":{"kty":"RSA","n":"<REDACTED>","e":"<REDACTED>"}} jws={"protected":"<REDACTED>","payload":"<REDACTED>","signature":"<REDACTED>"}
uacme: acme_post: HTTP headers:
HTTP/1.1 100 Continue

HTTP/1.1 404 Not Found
Cache-Control: no-store
Content-Type: application/problem+json
Link: <https://<DOMAIN>/acme/acme/directory>;rel="index"
Replay-Nonce: <NONCE>
Date: Sat, 01 Feb 2020 14:41:55 GMT
Content-Length: 92

uacme: acme_post: HTTP body:
{"type":"urn:ietf:params:acme:error:accountDoesNotExist","detail":"Account does not exist"}

uacme: acme_post: return code 404, json=
    "type": "urn:ietf:params:acme:error:accountDoesNotExist",
    "detail": "Account does not exist"
uacme: failed to create account at https://<DOMAIN>/acme/acme/new-account
uacme: the server reported the following error:
    "type": "urn:ietf:params:acme:error:accountDoesNotExist",
    "detail": "Account does not exist"

Incompatibility with Mac OS X Monterrey

When I run make install I get this:

config.status: executing depfiles commands
/Library/Developer/CommandLineTools/usr/bin/make  install-am
  CC       ualpn-ualpn.o
ualpn.c:538:10: error: implicit declaration of function 'sem_timedwait' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
    rc = sem_timedwait(&g_shm->sem, &ts);
ualpn.c:538:10: note: did you mean 'sem_trywait'?
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/semaphore.h:58:5: note: 'sem_trywait' declared here
int sem_trywait(sem_t *);
ualpn.c:3628:13: warning: 'sem_destroy' is deprecated [-Wdeprecated-declarations]
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/semaphore.h:53:26: note: 'sem_destroy' has been explicitly marked deprecated here
int sem_destroy(sem_t *) __deprecated;
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/cdefs.h:204:40: note: expanded from macro '__deprecated'
#define __deprecated    __attribute__((__deprecated__))
ualpn.c:3631:13: warning: 'sem_destroy' is deprecated [-Wdeprecated-declarations]
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/semaphore.h:53:26: note: 'sem_destroy' has been explicitly marked deprecated here
int sem_destroy(sem_t *) __deprecated;
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/cdefs.h:204:40: note: expanded from macro '__deprecated'
#define __deprecated    __attribute__((__deprecated__))
ualpn.c:4653:9: warning: 'sem_init' is deprecated [-Wdeprecated-declarations]
    if (sem_init(&g_shm->sem, 1, 1)) {
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/semaphore.h:55:42: note: 'sem_init' has been explicitly marked deprecated here
int sem_init(sem_t *, int, unsigned int) __deprecated;
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/cdefs.h:204:40: note: expanded from macro '__deprecated'
#define __deprecated    __attribute__((__deprecated__))
ualpn.c:4658:9: warning: 'sem_init' is deprecated [-Wdeprecated-declarations]
    if (sem_init(&g_shm->logsem, 1, 1)) {
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/semaphore.h:55:42: note: 'sem_init' has been explicitly marked deprecated here
int sem_init(sem_t *, int, unsigned int) __deprecated;
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/cdefs.h:204:40: note: expanded from macro '__deprecated'
#define __deprecated    __attribute__((__deprecated__))
4 warnings and 1 error generated.
make[1]: *** [ualpn-ualpn.o] Error 1
make: *** [install] Error 2

add option to define custom certificate subdirectory (?)


would you consider support for an option by which the default "IDENTIFIER" part in determining the storage location for the certificate to be issued can be replaced by a custom, arbitrary value?

While it does probably make not too much sense to have multiple certificates with the same first identifier/common name, it is not strictly invalid and would not work well/at all with the default paths.

I'm in a situation where the actual certificate configuration is outside of my control and I would like to not have to restrict the choice of certificate (subject alternatives) names and at the same time avoid any potential collisions, which I currently do by using a different unique identifier.

PR using "-u" for this: #34

failed to find newNonce URL in directory

I'm not sure why it says this, because curling the directory shows it contains newNonce.

[email protected]:~$ uacme -v -c uacme.d issue fractaldemo.comy
uacme: version 1.0.18 starting on Wed, 04 Dec 2019 18:39:08 +0000
uacme: loading key from uacme.d/private/key.pem
uacme: loading key from uacme.d/private/
uacme: checking existence and expiration of uacme.d/
uacme: uacme.d/ does not exist
uacme: fetching directory at
uacme: failed to find newNonce URL in directory
[email protected]:~$ curl -i
HTTP/2 200 
server: nginx
date: Wed, 04 Dec 2019 18:39:41 GMT
content-type: application/json
content-length: 658
cache-control: public, max-age=0, no-cache
x-frame-options: DENY
strict-transport-security: max-age=604800

  "iiOi2bpTbTM": "",
  "keyChange": "",
  "meta": {
    "caaIdentities": [
    "termsOfService": "",
    "website": ""
  "newAccount": "",
  "newNonce": "",
  "newOrder": "",
  "revokeCert": ""

Could not create new account on OpenWrt


This is less of an issue than a request for help because I could barely find any documentation for getting uacme to work on OpenWrt.

I successfully compiled and installed the uacme package for OpenWrt from its package source at

Then I tried to create a new account:
# uacme -v -c /etc/acme/ new
uacme: version 1.2.1 starting on Wed, 28 Oct 2020 06:28:46 -0700
uacme: created directory /etc/acme//private
uacme: loading key from /etc/acme//private/key.pem
uacme: /etc/acme//private/key.pem not found
uacme: generating new 2048-bit RSA key
uacme: key saved to /etc/acme//private/key.pem
uacme: fetching directory at
uacme: creating new account at
uacme: type 'y' to accept the terms at
uacme: account created at
uacme -v -c /etc/acme/ new

However, open the link, I got the following error:
"type": "urn:ietf:params:acme:error:malformed",
"detail": "Method not allowed",
"status": 405

Could I get any help here?

Could not find pkg-config

If I am trying to install like described on the main page, I'll get an error after running ./configure --disable-maintainer-mode.

checking sys/socket.h usability... yes
checking sys/socket.h presence... yes
checking for sys/socket.h... yes
checking for sys/stat.h... (cached) yes
checking for sys/types.h... (cached) yes
checking sys/wait.h usability... yes
checking sys/wait.h presence... yes
checking for sys/wait.h... yes
checking for asprintf... yes
checking for vasprintf... yes
checking for getopt_long... yes
checking for setproctitle... no
checking for pkg-config... no
configure: error: Could not find pkg-config

Unpredictable behavior between `issue IDENTIFIER` and `issue CSRFILE`

I ran into an issue recently where running this command:

doas -u acme uacme -v

resulted in the somewhat cryptic message:

uacme: failed to stat Permission denied

This was a result of running uacme as an unprivileged user while in a directory to which it has no read-access (ie /root), which then causes this call to fail, since uacme doesn't know whether the single argument is for a CSR file or a domain name.

Would it be possible to check if the stat error is due to permission denied instead of just a missing file, or alternatively add an additional command-line flag to force uacme to treat the input as a domain name instead of a file? I think it would at least be good to have a better error message, since I assumed it was due to uacme not having access to one of the directories it needed for writing the certificates.

Compilation failed on ArchLinux.

CDPATH="${ZSH_VERSION+.}:" && cd . && /bin/sh /home/build.dir/uacme/src/uacme/build-aux/missing aclocal-1.14 
/home/build.dir/uacme/src/uacme/build-aux/missing: line 81: aclocal-1.14: command not found

Missing aclocal-1.14, but installed aclocal-1.16

Can't compile uacme on busybox with armv7 hardware because the dependency to libcurl is not resolved correctly

Hello ndilieto,

I am trying to build uacme on my router Netgear R7800
which is running busybox on armv7l hardware.
(500 MB ram, and two cores at 1.7 ghz)

I've installed a gcc compiler , and am running the ./configure script.
the script is reporting a error that libcurl dependency cannot be resolved.

checking for pkg-config... /opt/bin/pkg-config
checking pkg-config is at least version 0.28... yes
checking for libcurl >= 7.38.0... no
configure: error: libcurl not found

I have checked if the libcurl i have is meeting this requirement , and
it seems so. Opkg my package manager is listing libcurl as installed.

[email protected]:/opt/home/frank/uacme$ opkg list-installed | grep libcurl
libcurl - 7.68.0-1

What can i do to make uacme build?

How to request ocsp-must-staple on certificates

How do I pass ocsp-must-staple flag to uacme?
I grepped the source code but did not find this flag or the option.
I then looked at go-acme/lego specifically this thinking that I would create a pull request but my C language skills are failing me.
What do you suggest?

Allow creating private keys and directory with g+rX

I have various users on my system that need access to ssl private keys, so I use group ssl-cert for them, but uacme always sets umask such that private/* and key.pem files all end up not group readable. Would be nice if there were an option to allow group reading (setting correct group is handled because I have g+s on private/ in my case)

Return code from --version should probably be 0

uacme --version 2> /dev/null && echo "OK" || echo "Nope"

This is a common way to determine whether or not the program is present. However, uacme returns a failure code, non-zero, from this command and from uacme --help. may fail silently with exit status to be 0


I am adapting the sample hook script for Cloudflare API. I notice a possible problem that may cause the script to fail silently.

At the line

[ $res -eq 0 ] || break
, break is executed if ns_doupdate returns non-zero code. Then it flows to the line
return $?
, where $? is always 0 because previous res=$? (and [ $res -eq 0 ] ?) itself updates $? to be 0.

Finally, the script returns 0 even though ns_doupdate has failed.

A quick fix is to replace break with return $res.

(I do not have a bind server to test the original script. Please let me know if I missed something by mistake.)

1.7.1 release tarball is missing configure script

The README instructs to start with running ./configure, but the latest release tarball does not contain one, requiring it to be generated by the user before compiling. Please update the documentation if this is intentional, or bundle a configure script in the source tarball.

Error recognition fails if parameter is present in reply's "Content-Type" header


fooling around with uacme and pebble I noticed that account creation always fails. I found that pebble returns the header Content-Type: application/probjem+json; charset=utf-8 in its error replies, which is perfectly valid according to

Due to using strcasecmp for comparision, this fails on several places, including account creation, which is then wrongly aborted.

I guess the most simple fix would be to just use strncasecmp on those 4 occasions.

Update autotools dependency

It looks like there's currently a hard dependency on aclocal-1.14. On archlinux, for example, the only version of automake available is 1.16, which leads to the following build error:

CDPATH="${ZSH_VERSION+.}:" && cd . && /bin/sh /home/halosghost/prj/pkg/uacme-git/src/uacme/build-aux/missing aclocal-1.14
/home/halosghost/prj/pkg/uacme-git/src/uacme/build-aux/missing: line 81: aclocal-1.14: command not found
WARNING: 'aclocal-1.14' is missing on your system.
         You should only need it if you modified 'acinclude.m4' or
         '' or m4 files included by ''.
         The 'aclocal' program is part of the GNU Automake package:
         It also requires GNU Autoconf, GNU m4 and Perl in order to run:
make: *** [Makefile:385: aclocal.m4] Error 127

I am not hugely familiar with autotools; is this something that is easy to change so that newer versions of automake will continue to build uacme?

clang reports warnings

Just found your client, and decided that before using it, it would be interesting to see what clang --analyze would say about it.

Turns out that there are several warnings here (I will join the full log and how to generate it, since clang joins the traces it took to find the warnings):

  • null pointer passed as argument to strcasecmp/strdup
  • Potential leak of memory
  • Attempt to free released memory
  • The left operand of '==' is a garbage value
  • Access to field 'tls' results in a dereference of a null pointer

Here is how I generated that:
clang -D SYSCONFDIR='"/etc/"' -D RUNSTATEDIR='"/var/run"' -I./libev --analyze --analyzer-output text *.c > build.log 2>&1

I have also noticed warnings with a different combination of flags:

  • reports expression results being unused in the asserts. Nice trick you have, but could I suggest using assert( condition && "error message" ); instead? It does the same, but I still have to see a compiler whining about it.
  • implicit conversion changes signedness
  • macro name is a reserved identifier
  • format string is not a string literal
  • implicit conversion loses integer precision:
  • enumeration values not explicitly handled in switch (but the default keyword is used at least sometimes, so...)
  • no previous prototype for function
  • declaration shadows a local variable
  • cast from 'struct sockaddr *' to 'struct sockaddr_in *' increases required alignment (can't really be helped though)
  • padding size of 'curldata_t' with 4 bytes to alignment boundary (again, probably can't be helped, and this one only impacts memory impact AFAIK)
  • disabled expansion of recursive macro (I still have to understand the impact of this one)
  • 'HAVE_MAP_DEVZERO' is not defined, evaluates to 0 (this is my fault, since I short-circuited the makefile)
  • cast from 'const void *' to 'unsigned char *' drops const qualifier
  • cast from 'uint8_t *' (aka 'unsigned char *') to 'client_t *' (aka 'struct client *') increases required alignment from (this might be ok, but it also might trigger bugs, and that one could maybe be avoided)
  • cast from function call of type 'long' to non-matching type 'float'
  • implicit conversion increases floating-point precision:

Of course, several of those warnings have only memory performance impact or are false positives, but I believe fixing most of those would allow an easier to maintain code (shadow declarations, names reserved by the standard,...) or less prone to errors (implicit casts, for example).

Those were generated with:
clang -Wall -Weverything -D SYSCONFDIR='"/etc/"' -D RUNSTATEDIR='"/var/run"' -I./libev *.c > build2.log 2>&1
Of course, this generates linking error too, but that's unimportant considering my goal here.

You will find both the analyzer log (build.log) and the clang complains (build2.log) attached.
I intend to check if I can patch some of those quickly.


uacme exit codes with systemd

uacme's exit codes aren't ideal for use as a systemd oneshot service:

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

By default systemd considers a nonzero exit code as a failure:

While systemd has facilities to deal with this like SuccessExitStatus= it's far from ideal, as the status code lookup still shows 1/FAILURE. As exit 1 is the standard failure exit code for libc.


You might be interested to know that I wrote a http-01 challenge hook script that is designed for the case when you don’t need/wanna continuously run a (full-blown) web server (e.g. domain for just OpenVPN, PostgreSQL, …). Read more…

Can't call binary hook.


`uacme -c /var/lib/uacme/ec -t EC -b 384 -h uacme_hook_knot issue "" "*"`



Compile it crystal build ./
Move binary to server and try to execute

[[email protected] ~]# ./main 
uacme: uacme_hook_knot: No such file or directory

[[email protected] ~]# ls -lha /usr/bin/uacme_hook_knot 
-rwxr-xr-x 1 root root 331K Jul 31 13:43 /usr/bin/uacme_hook_knot

[[email protected] ~]# uacme_hook_knot 1 2 3 4 5
["1", "2", "3", "4", "5"]

But it works on laptop, where i compile it.
With python i got similar issue.
Upd. Now works with -h /usr/bin/uacme_hook_knot, but before it doesn't

Works only on POSIX filesystems: hardlink required

I am using a filesystem which does not support hard links.
So the code like here:


Lines 1180 to 1186 in 7f1ffbd

if (link(certfile, bakfile) < 0) {
if (errno != ENOENT) {
warn("failed to link %s to %s", bakfile, certfile);
goto out;
} else
msg(1, "backed up %s as %s", certfile, bakfile);

to backup the current file fails and the cert.pem.tmp is left at its place, so I've to manually move cert.pem.tmp to cert.pem.

Similar for...


Lines 745 to 757 in 7f1ffbd

msg(1, "backing up %s as %s", keyfile, bakfile);
if (link(keyfile, bakfile) < 0)
warn("failed to link %s to %s", bakfile, keyfile);
else {
msg(1, "renaming %s to %s", newkeyfile, keyfile);
if (rename(newkeyfile, keyfile) < 0) {
warn("failed to rename %s to %s", newkeyfile, keyfile);
} else {
msg(1, "account key changed");
success = true;

Is it doable to copy the file to backup when link fails?

Like with code from here:


Maybe helpful to add some automated tests of correct functionality, can make pull requests with possible tests that can be used.

Feature request: print thumbprint of account key

I was thinking this could be useful for people that have configured their web server to statelessly respond to HTTP challenges. This has limitations but it can be done if the thumbprint of the account key is known. Any thoughts on adding a method for uacme to print the thumbprint of the current account key and then exit or similar?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.