Error signing certificate: error creating x509 certificate: x509: provided PrivateKey doesn't match parent's PublicKey

I've generate Let's Encrypt certificate by

certbot certonly --manual --debug --preferred-challenges DNS -d *.stag.example.com
# .example.com is replaced by my company domain

Then, I put into cert-manager as secret of chain1.pem into tls.crt and privkey1.pem into tls.key. After that, I create ClusterIssuer and direct those to the secret. I can verify the ClusterIssuer could detect the secret.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  annotations: {}
  name: letsencrypt-clusterissuer
spec:
  ca:
    secretName: letsencrypt-cert

Long story short, I create ingress tls and give annotation into this cluster issuer. But CertificateRequest give this error: Error signing certificate: error creating x509 certificate: x509: provided PrivateKey doesn't match parent's PublicKey.

I've try to argue the error and trying to verify public key on my local. I use openssl, I even use docker version of alpine/openssl. Trying to extract publickey from chain1.pem and privkey1.pem (ECDSA). The public key generated is not match.

openssl ec -in privkey1.pem -pubout -outform PEM -out xxx-privkey1.pub
openssl x509 -in chain1.pem -pubkey -noout -out xxx-chain1.pub

I know I should ask cert-manager about this one. But, in case I want to do it manually whatever my case is, and I receive the fullchain and private. My question for let's encrypt, does this is intended behavior that the public key is intended did not match? Is there something I miss?

I also try to generate RSA, and still in the end public key is not match. I've been exploring in hours about this topic and both Let's Encrypt and Cert Manager did not have a resolving answer. What could I do to solve this?

===

Please fill out the fields below so we can help you better. Note: you must provide your domain name to get help. Domain names for issued certificates are all made public in Certificate Transparency logs (e.g. crt.sh | example.com), so withholding your domain name here does not increase secrecy, but only makes it harder for us to provide help.

My domain is: I prefer not to give

I ran this command:

docker run --rm --name certbot \
    -v "${PWD}/etc_letsencrypt:/etc/letsencrypt" \
    -v "${PWD}/var_lib_letsencrypt:/var/lib/letsencrypt" \
    -v "${PWD}/var_log_letsencrypt:/var/log/letsencrypt" \
    certbot/certbot certonly --manual --debug --preferred-challenges DNS -d *.stag.example.com

It produced this output: DNS Acme challenge and all works

My web server is (include version): Cert-Manager

The operating system my web server runs on is (include version): Kubernetes

My hosting provider, if applicable, is: Kubernetes

I can login to a root shell on my machine (yes or no, or I don't know): yes

I'm using a control panel to manage my site (no, or provide the name and version of the control panel): yes

The version of my client is (e.g. output of certbot --version or certbot-auto --version if you're using Certbot): certbot 3.1.0

privkey1.pem and chain1.pem names suggest you are copying files from the internal /archive path of certbot. Copy the files from /live/<cert-name> instead.

1 Like

This doesn't make much sense. You're telling cert-manager to use a Let's Encrypt intermediate as a CA certificate (which cert-manager will then use to issue leaf certificates itself), but then specify your existing leaf certificate key as the corresponding CA key - that can't work, Let's Encrypts intermediate obviously uses a different private key than your certificate. That's why you get the private key mismatch error message.

What are you actually trying to do here? cert-manager is a k8s tool primarily intended to automatically issue and renew certificates for your cluster. You now have a manually obtained LE certificate that cannot be renewed automatically. You can import that cert into your k8s, but for that you don't really need cert-manager at all. The general idea is that cert-manager obtains a LE certificate for you (so don't use certbot) which it will then automatically renew.

6 Likes

I want to generate just one certs with wildcard under *.stag.example.com. If I use cert-manager, it would generate every certs for every ingress it created which is not what I intended to be happen. I have been read some articles and understand cert-manager could also do wildcard automatically. For my current objective, I want to achieve "what if I try to manually generate certs and push it cert-manager and let it do the rest. What could possibly go wrong? What else I need to reconfigure?"

So, I just thought I could manually create a single CA and PK for wildcard sub-domain. Then, I put to cert-manager secret letsencrypt-cert and apply ClusterIssuer to use that secret of chain and privkey. And then, CertificateRequest would complete the rest (which is not because unpaired pubkey).

Why I need to use cert-manager while the certs itself generated manually? Because, when I set ingress and define spec.tls[].host[], it would automatically generate a Kind of Secret with TLS type.

...

Alright, so I have cert1.pem, chain1.pem, fullchain1.pem, and privkey1.pem; and none of this would produce paired pubkey and could be use in those secret? For information, I just use chain1.pem because (mindlessly) its only that tls.key can be accepted by ClusterIssuer, if I use another .pem, it just said this cert is not CA.

Just to clarify this is intended behavior by Let's Encrypt, right? So, I need to find another way on Cert-Manager to fulfil my objective?

You're really going to struggle if you try to hack cert-manager into accepting your own certs without managing them. It's possible (I've done that for cluster migrations), but it's a rabbit hole you definetly don't want to go down.

If you insist on managing the certificate manually - no renewal, nothing, just import them into your cluster without cert-manager. To do that, just import the certificate as a secret:

kubectl create secret tls letsencrypt-cert --cert=fullchain.pem --key=privkey.pem

Where fullchain.pem and privkey.pem come from certbot's files (in the /live directory, with the same name). Then just use that secret in your ingress configuration (as you already have). You do not need to have cert-manager installed, and definetly do not configure any (Cluster)-Issuer for cert-manager.

4 Likes

Alright. Seems I get where it gone wrong. Thanks for your help.

2 Likes