Why is certificate failing to generate?

I made my own ACME client in perl, using the Net::ACME module.

But now its failing:

The ACME function “https://acme-v01.api.letsencrypt.org/acme/new-cert” indicated an error: “Error creating new cert :: certificate public key must be different than account key (The request message was malformed)” (400, “Bad Request”, urn:acme:error:malformed). at /usr/local/share/perl/5.22.1/Net/ACME/HTTP.pm line 139.
Net::ACME::HTTP::_request(Net::ACME::HTTP=HASH(0x2096a18), “post”, “https://acme-v01.api.letsencrypt.org/acme/new-cert”, HASH(0x35e7700)) called at /usr/local/share/perl/5.22.1/Net/ACME/HTTP.pm line 158
Net::ACME::HTTP::_request_and_set_last_nonce(Net::ACME::HTTP=HASH(0x2096a18), “post”, “https://acme-v01.api.letsencrypt.org/acme/new-cert”, HASH(0x35e7700)) called at /usr/local/share/perl/5.22.1/Net/ACME/HTTP.pm line 79
Net::ACME::HTTP::post(Net::ACME::HTTP=HASH(0x2096a18), “https://acme-v01.api.letsencrypt.org/acme/new-cert”, HASH(0x35f7b08)) called at /usr/local/share/perl/5.22.1/Net/ACME.pm line 521
Net::ACME::_post_url(Net::ACME::LetsEncrypt=HASH(0x2aed320), “https://acme-v01.api.letsencrypt.org/acme/new-cert”, HASH(0x35f7b08)) called at /usr/local/share/perl/5.22.1/Net/ACME.pm line 510
Net::ACME::_post(Net::ACME::LetsEncrypt=HASH(0x2aed320), “new-cert”, HASH(0x35f7b08)) called at /usr/local/share/perl/5.22.1/Net/ACME.pm line 431
Net::ACME::get_certificate(Net::ACME::LetsEncrypt=HASH(0x2aed320), “-----BEGIN CERTIFICATE REQUEST-----\x{a}MIIFiDCCA3ACAQAwgbAxCzAJB”…) called at ./certbot.pl line 150

It looks like its failing because im using the same keypair for registration and CSR. But why? What is the problem?

You have to use different private keys for the certificate and account. There are negative security implications with this sort of key re-use.

What are the “negative security implications”?

As long as the private key is kept secret, it should not be a problem with key reuse for like a million different purposes?

It’s a key reuse thing really. Same reason you generally don’t want to use the same across multiple servers. Let’s Encrypt also encourages changing private keys with each renewal. I don’t know if it’s part of the ACME spec or not, off the top of my head, but key generation is trivial enough.

Even if it’s not a problem now, it could be in the future. With cryptography, you can never be too careful.

I know that key regeneration is “trivial”. Its just that theres a heck of different private keys to track. My NSD folder already contains 6 private keys, 2 DNSSEC keys, one Letsencrypt client key, one certificate private key (which are then synlinked from the apache2 and postfix directory), and then 2 private keys for the nsd service.

Im aware of the security implications of signing something with the same key used for decryption (with signature blinding, it can cause inadvertient decryption of data) but since its a hash being signed and not the data directly, this is not a danger.

So key reuse is actually safe. I Think LE should change so key reuse is permitted.

I’d be interested to hear from someone with more familiarity on the ACME spec with regards to the protocol implications of this change. I’ll look over the draft RFC and known divergences when I have a chance. Depending on who actually made this decision, this may be something you’d need to take up with the IETF instead.

To be honest, I mostly agree with you that this seems like behavior that should at least be allowed, even if frowned upon, much like reuse of private keys for renewed certificates is allowed, but not the norm for most clients.

I have checked through the standard (draft-08 of ACME) and theres nothing that restricts the user from using the same keypair for the certificate and account.

The only thing that is restricted is having 2 accounts registred with the same keypair, because that would of course make one of the accounts inaccessible.

The only problem I can see, is certificate revocation, if the account and cert has the same private key. Should the authorization be considered to be done with an account key, or should authorization be considered to be done with an certificate key?

The simplest solution to this is to combine both views, so when it comes to revocation, an if an key belongs to both an certificate and account, this key is authorized to request revocation for any certificate that shares the same account and/or public key.

Here is how my ACME client looks like now. But the nice thing with using the same keypair was that I could generate the CSR automatically using the account key. Thats not possible with this limitation.
(Currently in my cron.monthly, so it will hard-renew each month. If it fails it gets a new chance next month without expiry)


use Net::ACME;
use Net::ACME::LetsEncrypt;
use Digest::SHA;
use MIME::Base64;

$private_key = <<'PEMPRIVATEKEY';
**Wont disclose this**

$cert_request = <<'LECSR';

#$tos_url = Net::ACME::LetsEncrypt->get_terms_of_service();
$acme = Net::ACME::LetsEncrypt->new( key => $private_key );
#$reg = $acme->register('mailto:sebastian@sebbe.eu');
#$acme->accept_tos( $reg->uri(), $tos_url );
$key_jwk = Net::ACME::Crypt::parse_key($private_key)->get_struct_for_public_jwk();

@domains = ('sebbe.eu', 'www.sebbe.eu', 'dns1.sebbe.eu', 'dns2.sebbe.eu', 'printer.sebbe.eu', 'mail.sebbe.eu', 'smtp.sebbe.eu', 'imap.sebbe.eu');

foreach $domain (@domains) {
  $authz_p = $acme->start_domain_authz($domain);
  $pollcomplete{$domain} = $authz_p;
  foreach $cmb_ar ( $authz_p->combinations() ) {
    next if @$cmb_ar > 1;
    next if $cmb_ar->[0]->type() ne 'dns-01';
    $kauthz = $cmb_ar->[0]->make_key_authz( $key_jwk );
    $sha = Digest::SHA::sha256($kauthz);
    $b64 = MIME::Base64::encode_base64url($sha);
    print "Creating challenge for $domain\n";
    push(@writechallenges, $domain."!!".$b64);
    push(@pendingcompletion, $cmb_ar->[0]);

print "Writing challenges to zone file\n";

open(ZONEFILEA, ">/etc/nsd/sebbe.eu.zone.signed");
print ZONEFILEA "";
open(ZONEFILEB, ">/etc/nsd/sebbe.eu.zone");
print ZONEFILEB "";

open(ZONETEMPLATE, "/etc/nsd/sebbe.eu.template");
@zonetemp = <ZONETEMPLATE>;
open(ZONEFILE, ">/etc/nsd/sebbe.eu.zone");
foreach $zoneline (@zonetemp) {
  print ZONEFILE $zoneline;
foreach $challauth (@writechallenges) {
  ($domain, $b64) = split("!!", $challauth);
  print ZONEFILE "_acme-challenge.".$domain.". 3600 IN TXT \"$b64\"\n";

print "Signing DNSSEC data...\n";
$currenttime = time;
$dnssec_expiration = $currenttime + 7776060;
system("ldns-signzone -e ".$dnssec_expiration." /etc/nsd/sebbe.eu.zone /etc/nsd/Ksebbe.eu.+007+14838 /etc/nsd/Ksebbe.eu.+007+47438");
system("service nsd restart");
sleep 1;

print "Submitting challenges for validation...\n";
foreach $uchall (@pendingcompletion) {

print "Getting validation results...\n";
foreach $dom (keys %pollcomplete) {
  while (1) {
    if ( $pollcomplete{$dom}->is_time_to_poll() ) {
      $poll = $pollcomplete{$dom}->poll();
      last if $poll->status() eq 'valid';
      if ( $poll->status() eq 'invalid' ) {
        die "Failed authorization for \"$dom\"!";
    sleep 1;

print "Generating certificate...\n";
$cert = $acme->get_certificate($cert_request);
while ( !$cert->pem() ) {
  sleep 1;
  next if !$cert->is_time_to_poll();
  $cert = $cert->poll() || $cert;

print "Writing certificate...\n";
open(CAFILE, "/etc/nsd/cacert.pem");
@cacert = <CAFILE>;

open(CERTFILE, ">/etc/nsd/servercert.pem");
print CERTFILE $cert->pem();
print CERTFILE "\n";
foreach $caline (@cacert) {
print CERTFILE $caline;

print "Restarting services...\n";

system("service apache2 restart");
system("service postfix restart");

print "Successfully generated LE certificate!\n";

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.