9

Problem

I have generated keys and certificates by OpenSSL with the secp256k1, run rke version v1.2.8 from the Rancher Kubernetes Engine (RKE), and got the following error:

FATA[0000] Failed to read certificates from dir [/home/max/cluster_certs]: failed to read certificate [kube-apiserver-requestheader-ca.pem]: x509: unsupported elliptic curve

kubectl version:

Client Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.1", GitCommit:"5e58841cce77d4bc13713ad2b91fa0d961e69192", GitTreeState:"clean", BuildDate:"2021-05-12T14:18:45Z", GoVersion:"go1.16.4", Compiler:"gc", Platform:"linux/amd64"}

I have generated the root CA key and certificate the following way:

openssl ecparam -name secp256k1 -genkey -noout -out ca-pvt.pem -rand random.bin -writerand random.bin
openssl req -config .\openssl.cnf -x509 -sha256 -new -nodes -key ca-pvt.pem -days 10227 -out ca-cert.cer -rand random.bin -writerand random.bin

Then I used it to sign the CSRs generated by rke cert generate-csr from my Kubernetes Rancher cluster.yml.

The command line to approve a CSR was the following:

openssl ca -config openssl.cnf -batch -in %1 -out %2 -create_serial -notext -rand random.bin -writerand random.bin

Question

Which curves are supported today by Kubernetes for the certificates if secp256k1 yields the x509: unsupported elliptic curve error message?

P.S.

I have also tried the prime256v1, also known as secp256r1. It progressed further comparing to secp256k1, but still got an error.

With prime256v1, RKE did not complain x509: unsupported elliptic curve.

Instead, it gave an error panic: interface conversion: interface {} is *ecdsa.PrivateKey, not *rsa.PrivateKey. Here is the full error message:

Here is the full error message:

DEBU[0000] Certificate file [./cluster_certs/kube-apiserver-requestheader-ca.pem] content is greater than 0 
panic: interface conversion: interface {} is *ecdsa.PrivateKey, not *rsa.PrivateKey 
goroutine 1 [running]: github.com/rancher/rke/pki.getKeyFromFile(0x7ffe6294c74e, 0xf, 0xc00105cb10, 0x27, 0x8, 0xc00105cb10, 0x27) 
/go/src/github.com/rancher/rke/pki/util.go:656 +0x212
Maxim Masiutin
  • 3,991
  • 4
  • 55
  • 72
  • 1
    Hi @MaximMasiutin, did you try to change your curve to `secp256r1`? The main difference is that `secp256k1` is a Koblitz curve, while `secp256r1` is not. Koblitz curves are known to be a few bits weaker than other curves. – Mikołaj Głodziak Jun 16 '21 at 09:20
  • 1
    You can also try with curve `prime256v1` – Mikołaj Głodziak Jun 16 '21 at 09:29
  • 1
    @MikołajGłodziak - You are right, if I specify "secp256r1", OpenSSL gives the following message: `using curve name prime256v1 instead of secp256r1`. OpenSSL supports "secp256r1", it is just called "prime256v1". Check section 2.1.1.1 in RFC 5480, where the "secp192r1" curve is called "prime192v1" and the "secp256r1" curve is called "prime256v1". – Maxim Masiutin Jun 17 '21 at 03:12
  • 1
    is your problem now resolved? – Mikołaj Głodziak Jun 17 '21 at 07:16
  • 1
    I have already installed a cluster with RSA, so I now need to set up a test environment to test the EC curves (ECDSA) instead of RSA, for faster operation. I will let you know as soon as I do that. – Maxim Masiutin Jun 17 '21 at 11:40
  • 1
    Do you have any progress? – Mikołaj Głodziak Jun 20 '21 at 20:49
  • RKE did not work with prime256v1 (AKA secp256r1) also. It progressed further comparing to secp256k1, but still got an error. – Maxim Masiutin Jun 27 '21 at 14:20
  • I've tried to make a CA key file (kube-service-account-token-key.pem and kube-apiserver-requestheader-ca-key.pem - both having the same contents) with ECDSA and got the following error: – Maxim Masiutin Jun 27 '21 at 14:20
  • DEBU[0000] Certificate file [./cluster_certs/kube-apiserver-requestheader-ca.pem] content is greater than 0 panic: interface conversion: interface {} is *ecdsa.PrivateKey, not *rsa.PrivateKey goroutine 1 [running]: github.com/rancher/rke/pki.getKeyFromFile(0x7ffe6294c74e, 0xf, 0xc00105cb10, 0x27, 0x8, 0xc00105cb10, 0x27) /go/src/github.com/rancher/rke/pki/util.go:656 +0x212 – Maxim Masiutin Jun 27 '21 at 14:21
  • However, when I generate the CA key (kube-service-account-token-key.pem and kube-apiserver-requestheader-ca-key.pem) with RSA, so everything is the same except the algorithm - it works. – Maxim Masiutin Jun 27 '21 at 14:21
  • I have put the scripts that I have used to generate the keys to https://github.com/maximmasiutin/openssl-ca-automation – Maxim Masiutin Jun 27 '21 at 14:22

1 Answers1

12

Which curves are supported today by Kubernetes for the certificates if secp256k1 yields the x509: unsupported elliptic curve error message?

To try to answer this question I will look directly at the source code. You can find there lines, that gives an error unsupported elliptic curve:

case *ecdsa.PublicKey:
        publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
        oid, ok := oidFromNamedCurve(pub.Curve)
        if !ok {
            return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported elliptic curve")
        }

There are two functions here that are responsible for processing the curve:

  • Marshal:
// Marshal converts a point on the curve into the uncompressed form specified in
// section 4.3.6 of ANSI X9.62.
func Marshal(curve Curve, x, y *big.Int) []byte {
    byteLen := (curve.Params().BitSize + 7) / 8

    ret := make([]byte, 1+2*byteLen)
    ret[0] = 4 // uncompressed point

    x.FillBytes(ret[1 : 1+byteLen])
    y.FillBytes(ret[1+byteLen : 1+2*byteLen])

    return ret
}
  • oidFromNamedCurve:
// OIDFromNamedCurve returns the OID used to specify the use of the given
// elliptic curve.
func OIDFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) {
    switch curve {
    case elliptic.P224():
        return OIDNamedCurveP224, true
    case elliptic.P256():
        return OIDNamedCurveP256, true
    case elliptic.P384():
        return OIDNamedCurveP384, true
    case elliptic.P521():
        return OIDNamedCurveP521, true
    case secp192r1():
        return OIDNamedCurveP192, true
    }

    return nil, false
}

The final answer is therefore in the switch. Supported elliptic curves are:

You need to change your curve to secp256r1. The main difference is that secp256k1 is a Koblitz curve, while secp256r1 is not. Koblitz curves are known to be a few bits weaker than other curves.

OpenSSL supports "secp256r1", it is just called "prime256v1". Check section 2.1.1.1 in RFC 5480, where the "secp192r1" curve is called "prime192v1" and the "secp256r1" curve is called "prime256v1".

Mikołaj Głodziak
  • 4,775
  • 7
  • 28
  • It seems that ECDSA keys are not supported anyway. I've tried to make a CA key file (kube-service-account-token-key.pem and kube-apiserver-requestheader-ca-key.pem - both having the same contents) with ECDSA and got the following error: – Maxim Masiutin Jun 26 '21 at 18:40
  • DEBU[0000] Certificate file [./cluster_certs/kube-apiserver-requestheader-ca.pem] content is greater than 0 panic: interface conversion: interface {} is *ecdsa.PrivateKey, not *rsa.PrivateKey goroutine 1 [running]: github.com/rancher/rke/pki.getKeyFromFile(0x7ffe6294c74e, 0xf, 0xc00105cb10, 0x27, 0x8, 0xc00105cb10, 0x27) /go/src/github.com/rancher/rke/pki/util.go:656 +0x212 – Maxim Masiutin Jun 26 '21 at 18:41
  • However, when I generate the CA key (kube-service-account-token-key.pem and kube-apiserver-requestheader-ca-key.pem) with RSA, so everything is the same except the algorithm - it works. – Maxim Masiutin Jun 26 '21 at 18:41
  • I have given you a link to the source code. In the same function on which I based my answer you will find the [line](https://go.googlesource.com/go/+/8bf6e09f4cbb0242039dd4602f1f2d58e30e0f26/src/crypto/x509/x509.go#78): `return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: only RSA and ECDSA public keys supported")`. Based on this ECDSA keys are supported. – Mikołaj Głodziak Jul 02 '21 at 06:13
  • Thank you very much! It worked! I have put the scripts that I have used to generate the keys to https://github.com/maximmasiutin/openssl-ca-automation – Maxim Masiutin Jan 06 '22 at 17:19