NO-JIRA: Add ECDSA P-256 support for certificates and CAs#2116
NO-JIRA: Add ECDSA P-256 support for certificates and CAs#2116fabiendupont wants to merge 1 commit intoopenshift:masterfrom
Conversation
|
@fabiendupont: This pull request explicitly references no jira issue. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: fabiendupont The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
8dd03c3 to
724c6f6
Compare
Adds support for generating ECDSA P-256 certificates by specifying the key algorithm via the new service annotation: service.beta.openshift.io/serving-cert-key-algorithm: ecdsa When the annotation is not specified or set to "rsa", the operator generates RSA certificates for full backwards compatibility. This enables services to opt into modern elliptic curve cryptography, which provides equivalent security to 3072-bit RSA with significantly smaller keys (~87% smaller) and better performance. Implementation: - Added ServingCertKeyAlgorithmAnnotation constant in api.go - Modified MakeServingCert() to check annotation and select algorithm - Uses library-go's new MakeServerCertWithAlgorithm() API - Validates annotation values (rsa, ecdsa) with helpful error messages - Case-insensitive algorithm matching Testing: - Added TestECDSACertificateGeneration with 5 test cases - Verifies RSA (default and explicit), ECDSA, and invalid inputs - All existing tests pass with no regressions Depends on: openshift/library-go#2116 Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: Fabien Dupont <fdupont@redhat.com>
724c6f6 to
b5ed9f4
Compare
| } | ||
|
|
||
| // MakeServerCertWithAlgorithm creates a server certificate with the specified key algorithm | ||
| func (ca *CA) MakeServerCertWithAlgorithm(hostnames sets.Set[string], lifetime time.Duration, algorithm KeyAlgorithm, fns ...CertificateExtensionFunc) (*TLSCertificateConfig, error) { |
There was a problem hiding this comment.
Hmm, I might be wrong, but it looks like MakeServerCert and MakeServerCertWithAlgorithm are very similar. There are two differences. The first is the key algorithm, and the second is that MakeServerCertWithAlgorithm sets the signature algorithm.
I’m wondering if it would make sense to create a private common function for both. I think something like the following could work:
func (ca *CA) MakeServerCertWithAlgorithm(hostnames sets.Set[string], lifetime time.Duration, algorithm KeyAlgorithm, fns ...CertificateExtensionFunc) (*TLSCertificateConfig, error) {
sigFn := func(template *x509.Certificate) error {
template.SignatureAlgorithm = signatureAlgorithmForKey(ca.Config.Key)
return nil
}
fns = append([]CertificateExtensionFunc{sigFn}, fns...)
return ca.makeServerCertWithAlgorithm(hostnames, lifetime, algorithm, fns...)
}
and
func (ca *CA) MakeServerCert(hostnames sets.Set[string], lifetime time.Duration, fns ...CertificateExtensionFunc) (*TLSCertificateConfig, error) {
return ca.makeServerCertWithAlgorithm(hostnames, lifetime, AlgorithmRSA, fns...)
}
pkg/crypto/crypto.go
Outdated
| case AlgorithmRSA: | ||
| return newKeyPairWithHash() | ||
| default: | ||
| return newKeyPairWithHash() // Default to RSA for backwards compatibility |
There was a problem hiding this comment.
Since this is a private method I think we could return an error for unsupported algorithms.
pkg/crypto/crypto.go
Outdated
| } | ||
|
|
||
| // MakeServerCertForDurationWithAlgorithm creates a server certificate with specified duration and algorithm | ||
| func (ca *CA) MakeServerCertForDurationWithAlgorithm(hostnames sets.Set[string], lifetime time.Duration, algorithm KeyAlgorithm, fns ...CertificateExtensionFunc) (*TLSCertificateConfig, error) { |
There was a problem hiding this comment.
am I right that the differences between MakeServerCertForDuration and MakeServerCertForDurationWithAlgorithm mirror those between MakeServerCert and MakeServerCertWithAlgorithm (key algorithm and signature algorithm)? If so, could we refactor them in the same way?
There was a problem hiding this comment.
Good feedback. Makes sense.
b5ed9f4 to
854a902
Compare
|
@p0lyn0mial, I extended the changes to add the ECDSA CA support as well, so we can have a full ECDSA chain. |
3e1f176 to
62ba714
Compare
62ba714 to
3a9f301
Compare
| template := &x509.Certificate{ | ||
| Subject: subject, | ||
|
|
||
| SignatureAlgorithm: x509.SHA256WithRSA, |
There was a problem hiding this comment.
OK. I think that the SignatureAlgorithm will be set by x509.CreateCertificate and our unit tests assert this. Is that correct ?
There was a problem hiding this comment.
Yes, that's correct. When SignatureAlgorithm is zero-valued in the template, x509.CreateCertificate infers it from the signing key — SHA256WithRSA for RSA keys, ECDSAWithSHA256 for ECDSA P-256 keys. Our tests assert the resulting SignatureAlgorithm on the generated certs for all combinations (RSA CA + RSA leaf, RSA CA + ECDSA leaf, ECDSA CA + RSA leaf, ECDSA CA + ECDSA leaf).
| case AlgorithmRSA: | ||
| return newKeyPairWithHash() | ||
| default: | ||
| return nil, nil, nil, fmt.Errorf("unsupported key algorithm: %d", algo) |
There was a problem hiding this comment.
It looks like the error message will take the form of unsupported key algorithm: 2, which isn't meaningful to someone debugging. Is there a way to make it more descriptive ?
There was a problem hiding this comment.
Good point. This default branch can only be reached if a new KeyAlgorithm constant is added to the const block (e.g. AlgorithmEd25519) without adding a corresponding case in this switch.
Since newKeyPairWithAlgorithm is private and the callers always pass one of the defined constants, a developer hitting this error would know they forgot to wire up their new algorithm.
I added a comment to clarify that, and kept %d so the numeric value is visible for debugging.
3a9f301 to
212d47c
Compare
| return makeSelfSignedCAConfigForSubjectAndDuration(subject, time.Now, caLifetime, AlgorithmRSA) | ||
| } | ||
|
|
||
| func MakeSelfSignedCAConfigForDurationWithAlgorithm(name string, caLifetime time.Duration, algorithm KeyAlgorithm) (*TLSCertificateConfig, error) { |
There was a problem hiding this comment.
I might be wrong but
MakeSelfSignedCAConfigForDurationWithAlgorithm & MakeCAConfigForDurationWithAlgorithm use newSigningCertificateTemplateForDuration which sets
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
I found RFC 5480 where Section 3 says:
If the keyUsage extension is present in a Certification Authority
(CA) certificate that indicates id-ecPublicKey in
SubjectPublicKeyInfo, then any combination of the following values
MAY be present:
digitalSignature;
nonRepudiation;
keyAgreement;
keyCertSign; and
cRLSign.
which tells me that x509.KeyUsageKeyEncipherment (in newSigningCertificateTemplateForDuration) should not be used for ECDSA only for RSA.
There was a problem hiding this comment.
Added a keyUsageForAlgorithm helper that returns:
- RSA: KeyEncipherment | DigitalSignature
- ECDSA: DigitalSignature only
Per RFC 5480 Section 3, KeyEncipherment is not valid for ECDSA keys. The CA, server, and intermediate cert templates now receive the algorithm and set KeyUsage accordingly. Tests assert the correct KeyUsage for both RSA and ECDSA certs.
The public client cert template functions (NewClientCertificateTemplate / NewClientCertificateTemplateForDuration) are unchanged since client cert creation currently always uses RSA.
| } | ||
|
|
||
| // MakeServerCertWithAlgorithm creates a server certificate with the specified key algorithm | ||
| func (ca *CA) MakeServerCertWithAlgorithm(hostnames sets.Set[string], lifetime time.Duration, algorithm KeyAlgorithm, fns ...CertificateExtensionFunc) (*TLSCertificateConfig, error) { |
There was a problem hiding this comment.
I think the issue described at https://github.com/openshift/library-go/pull/2116/changes#r2826380341 is also applicable to the server, client cert template.
Extends the crypto package to generate ECDSA P-256 key pairs and certificates in addition to existing RSA support, enabling OpenShift components to use modern elliptic curve cryptography. Adds: - KeyAlgorithm type for algorithm selection (RSA, ECDSA) - ECDSA P-256 key pair generation with proper SubjectKeyIdentifier hashing - MakeServerCertWithAlgorithm() and MakeServerCertForDurationWithAlgorithm() - MakeSelfSignedCAConfigForDurationWithAlgorithm() for ECDSA root CAs - MakeCAConfigForDurationWithAlgorithm() for ECDSA intermediate CAs Removes hardcoded SignatureAlgorithm from certificate templates, letting x509.CreateCertificate infer the correct algorithm from the signing key. This is backward compatible and enables correct cross-algorithm signing (e.g. RSA CA signing ECDSA leaf certs). All existing APIs remain unchanged, preserving backwards compatibility. New functionality is opt-in through *WithAlgorithm variants. Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Fabien Dupont <fdupont@redhat.com>
212d47c to
4f7ae88
Compare
|
@fabiendupont: all tests passed! Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
Extends the crypto package to generate ECDSA P-256 key pairs and certificates in addition to existing RSA support, enabling OpenShift components to use modern elliptic curve cryptography.
Changes
Key generation:
KeyAlgorithmtype for algorithm selection (AlgorithmRSA,AlgorithmECDSA)newKeyPairWithAlgorithm()for unified key generation dispatchServer certificates:
CA.MakeServerCertWithAlgorithm()andCA.MakeServerCertForDurationWithAlgorithm()CA certificates:
MakeSelfSignedCAConfigForDurationWithAlgorithm()for ECDSA root CAsMakeCAConfigForDurationWithAlgorithm()for ECDSA intermediate CAsTemplate cleanup:
SignatureAlgorithm: x509.SHA256WithRSAfrom certificate templatesx509.CreateCertificateinfers the correct algorithm from the signing key (SHA256WithRSA for RSA, ECDSAWithSHA256 for ECDSA P-256)All existing APIs remain unchanged — new functionality is opt-in through
*WithAlgorithmvariants.Test coverage
MakeServerCertForDurationWithAlgorithm(production code path fromcertrotation/target.go)