Skip to content

Comments

NO-JIRA: Add ECDSA P-256 support for certificates and CAs#2116

Open
fabiendupont wants to merge 1 commit intoopenshift:masterfrom
fabiendupont:add-ecdsa-support
Open

NO-JIRA: Add ECDSA P-256 support for certificates and CAs#2116
fabiendupont wants to merge 1 commit intoopenshift:masterfrom
fabiendupont:add-ecdsa-support

Conversation

@fabiendupont
Copy link

@fabiendupont fabiendupont commented Feb 4, 2026

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:

  • KeyAlgorithm type for algorithm selection (AlgorithmRSA, AlgorithmECDSA)
  • ECDSA P-256 key pair generation with proper SubjectKeyIdentifier hashing (SHA-1 per RFC 5280)
  • newKeyPairWithAlgorithm() for unified key generation dispatch

Server certificates:

  • CA.MakeServerCertWithAlgorithm() and CA.MakeServerCertForDurationWithAlgorithm()

CA certificates:

  • MakeSelfSignedCAConfigForDurationWithAlgorithm() for ECDSA root CAs
  • MakeCAConfigForDurationWithAlgorithm() for ECDSA intermediate CAs

Template cleanup:

  • Removed hardcoded SignatureAlgorithm: x509.SHA256WithRSA from certificate templates
  • x509.CreateCertificate infers the correct algorithm from the signing key (SHA256WithRSA for RSA, ECDSAWithSHA256 for ECDSA P-256)
  • This is backward compatible and enables correct cross-algorithm signing (e.g. RSA CA signing ECDSA leaf certs)

All existing APIs remain unchanged — new functionality is opt-in through *WithAlgorithm variants.

Test coverage

  • Key generation, signature algorithm detection, and ECDSA PEM encoding
  • Server certs: RSA CA + ECDSA leaf, ECDSA CA + RSA leaf, ECDSA CA + ECDSA leaf
  • MakeServerCertForDurationWithAlgorithm (production code path from certrotation/target.go)
  • Self-signed ECDSA CAs and mixed-algorithm intermediate CA chains
  • Backwards compatibility: all existing RSA tests pass unchanged

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Feb 4, 2026
@openshift-ci-robot
Copy link

@fabiendupont: This pull request explicitly references no jira issue.

Details

In response to this:

Extends the crypto package to generate ECDSA P-256 key pairs in addition to existing RSA support, enabling OpenShift components to use modern elliptic curve cryptography.

Adds:

  • KeyAlgorithm type for algorithm selection (RSA, ECDSA)
  • newECDSAKeyPair() and newECDSAKeyPairWithHash() functions using P-256 curve
  • newKeyPairWithAlgorithm() for unified key generation
  • signatureAlgorithmForKey() for automatic algorithm detection
  • CA.MakeServerCertWithAlgorithm() and CA.MakeServerCertForDurationWithAlgorithm()

All existing APIs remain unchanged, preserving 100% backwards compatibility. New functionality is opt-in through *WithAlgorithm functions.

ECDSA P-256 provides equivalent security to 3072-bit RSA with smaller keys (~87% smaller), faster operations, and better performance. This prepares OpenShift for modern TLS deployments and aligns with industry best practices.

Test coverage includes:

  • Unit tests for key generation, signature algorithm detection, and encoding
  • Integration tests for RSA CA + ECDSA server and vice versa
  • Backwards compatibility tests verifying existing RSA functionality

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.

@openshift-ci openshift-ci bot requested review from deads2k and p0lyn0mial February 4, 2026 17:26
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 4, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: fabiendupont
Once this PR has been reviewed and has the lgtm label, please assign p0lyn0mial for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

fabiendupont added a commit to fabiendupont/openshift-service-ca-operator that referenced this pull request Feb 4, 2026
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>
}

// 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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...)
}

case AlgorithmRSA:
return newKeyPairWithHash()
default:
return newKeyPairWithHash() // Default to RSA for backwards compatibility
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a private method I think we could return an error for unsupported algorithms.

}

// 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good feedback. Makes sense.

@fabiendupont fabiendupont changed the title NO-JIRA: Add ECDSA P-256 key generation support NO-JIRA: Add ECDSA P-256 support for certificates and CAs Feb 13, 2026
@fabiendupont
Copy link
Author

@p0lyn0mial, I extended the changes to add the ECDSA CA support as well, so we can have a full ECDSA chain.

@fabiendupont fabiendupont force-pushed the add-ecdsa-support branch 2 times, most recently from 3e1f176 to 62ba714 Compare February 13, 2026 16:48
@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 13, 2026
@openshift-merge-robot openshift-merge-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 13, 2026
template := &x509.Certificate{
Subject: subject,

SignatureAlgorithm: x509.SHA256WithRSA,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. I think that the SignatureAlgorithm will be set by x509.CreateCertificate and our unit tests assert this. Is that correct ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

return makeSelfSignedCAConfigForSubjectAndDuration(subject, time.Now, caLifetime, AlgorithmRSA)
}

func MakeSelfSignedCAConfigForDurationWithAlgorithm(name string, caLifetime time.Duration, algorithm KeyAlgorithm) (*TLSCertificateConfig, error) {
Copy link
Contributor

@p0lyn0mial p0lyn0mial Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 19, 2026

@fabiendupont: all tests passed!

Full PR test history. Your PR dashboard.

Details

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 kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants