Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for setting pathlen on CA certificates and intermediate CA #135

Merged
merged 32 commits into from
Jun 24, 2022
Merged

Add support for setting pathlen on CA certificates and intermediate CA #135

merged 32 commits into from
Jun 24, 2022

Conversation

socheatsok78
Copy link
Contributor

Taken from RFC 5280, section 4.2.1.9:

A pathLenConstraint of zero indicates that no non-self-issued intermediate CA certificates may follow in a valid certification path. Where it appears, the pathLenConstraint field MUST be greater than or equal to zero. Where pathLenConstraint does not appear, no limit is imposed.

I.e. a pathLenConstraintof 0 does still allow the CA to issue certificates, but these certificates must be end-entity-certificates (the CA flag in BasicConstraints is false - these are the "normal" certificates that are issued to people or organizations).

It also implies that with this certificate, the CA must not issue intermediate CA certificates (where the CA flag is true again - these are certificates that could potentially issue further certificates, thereby increasing the pathLen by 1).

An absent pathLenConstraint on the other hand means that there is no limitation considering the length of certificate paths built from an end-entity certificate that would lead up to our example CA certificate. This implies that the CA could issue an intermediate certificate for a sub CA, this sub CA could again issue an intermediate certificate, this sub CA could again... until finally one sub CA would issue an end-entity certificate.

If the pathLenConstraintof a given CA certificate is > 0, then it expresses the number of possible intermediate CA certificates in a path built from an end-entity certificate up to the CA certificate. Let's say CA X has a pathLenConstraint of 2, the end-entity certificate is issued to EE. Then the following scenarios are valid (I denoting an intermediate CA certificate)

Example

Create CA key

$ certstrap init --common-name CA --exclude-path-length
[...]
$ openssl x509 -noout -text -in out/CA.crt
[...]
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE
            X509v3 Subject Key Identifier:
                AE:1B:C3:B5:2C:29:05:3B:D2:94:D7:09:27:15:28:5A:04:D7:78:C7
[...]

Create intermediate CA 1 key

$ certstrap request-cert --common-name ICA1
[...]
$ certstrap sign ICA1 --CA CA --intermediate --path-length 1
[...]
$ openssl x509 -noout -text -in out/ICA1.crt
[...]
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:1
            X509v3 Subject Key Identifier:
                1D:86:5B:08:0B:81:4A:12:02:0E:F0:B0:32:A1:F8:6D:88:F5:3A:4E
            X509v3 Authority Key Identifier:
                keyid:AE:1B:C3:B5:2C:29:05:3B:D2:94:D7:09:27:15:28:5A:04:D7:78:C7
[...]

Create intermediate CA 2 key

$ certstrap request-cert --common-name ICA2
[...]
$ certstrap sign ICA2 --CA ICA1 --intermediate
[...]
$ openssl x509 -noout -text -in out/ICA12crt
[...]
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0
            X509v3 Subject Key Identifier:
                47:0C:F5:1B:A8:72:64:D1:56:4B:D6:FB:AC:40:68:39:58:77:38:AA
            X509v3 Authority Key Identifier:
                keyid:1D:86:5B:08:0B:81:4A:12:02:0E:F0:B0:32:A1:F8:6D:88:F5:3A:4E
[...]

@CLAassistant
Copy link

CLAassistant commented Jan 13, 2022

CLA assistant check
All committers have signed the CLA.

@socheatsok78
Copy link
Contributor Author

Thanks to #112 for original idea.

@socheatsok78 socheatsok78 changed the title Add support for setting pathlen on CA certificates and intermediate CA Add support for setting pathlen on CA certificates and intermediate CA Jan 13, 2022
@MousyBusiness
Copy link

I've recently hit this as an issue. A rootCA created with CreateCertificateAuthority used to create intermediaries with CreateIntermediateCertificateAuthority will create the certs, the intermediary even work in Go. But when using python or node (which require the full authority chain), it fails.

if !excludePathlen {
if pathlen > 0 {
authTemplate.MaxPathLen = pathlen
authTemplate.MaxPathLenZero = false
Copy link
Contributor

Choose a reason for hiding this comment

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

MaxPathLenZero is only checked when MaxPathLen == 0. With that in mind, can we simplify this logic? Something like:

authTemplate.MaxPathLen = pathlen
if excludePathLen {
  authTemplate.MaxPathLen = -1
}

Then MaxPathLenZero can remain true and we use the explicit MaxPathLen = -1 to unset it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry for late to response. I should have went with that in the first place xD. Thank you for pointing out.

Copy link
Contributor Author

@socheatsok78 socheatsok78 Jun 18, 2022

Choose a reason for hiding this comment

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

Already made the change. Please give it a look.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To be honest, the change I've just made doesn't seem right. Let me see what I can do or if you have any suggestion for making it better. Please let me know.

Copy link
Contributor

Choose a reason for hiding this comment

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

Added a comment on the current iteration.

@@ -69,7 +78,7 @@ func CreateCertificateAuthority(key *Key, organizationalUnit string, expiry time

// CreateIntermediateCertificateAuthority creates an intermediate
// CA certificate signed by the given authority.
func CreateIntermediateCertificateAuthority(crtAuth *Certificate, keyAuth *Key, csr *CertificateSigningRequest, proposedExpiry time.Time) (*Certificate, error) {
func CreateIntermediateCertificateAuthority(crtAuth *Certificate, keyAuth *Key, csr *CertificateSigningRequest, proposedExpiry time.Time, pathlen int) (*Certificate, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, it's taken me so long to review these changes because I needed to be sure that this change to the public pkix package wouldn't break anyone. Unfortunately, looks like it will, for example https://cs.github.com/cloudfoundry/locket/blob/ad30c800960d38a0090acd7aa2acfc3f439b1f82/cmd/locket/certauthority/certauthority.go#L88.

Since we're not ready for a v2, I propose we add new functions (CreateIntermediateCertificateAuthorityWithOptions and CreateCertificateAuthorityWithOptions) to take opts ...Option to keep backwards compatibility:

type Option func(*x509.Certificate)

func WithPathlen(pathlen int, excludePathlen bool) {
  return func(template *x509.Certificate) {
    // Set MaxPathLen appropriately....
  }
}

Copy link
Contributor

Choose a reason for hiding this comment

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

(Note that part of the v2 changes will be moving things into an internal package so that it's clear we won't guarantee backward compatibility of thee APIs -- this isn't the first time it's hampered contributions. Thanks again for your patience.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, let me revert the original function back. I kinda don't want other people code to break as well :D

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For the init command, it is okay to add the extra flag right?

This comment was marked as resolved.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I think it's fine to have the exclude flag in init.

@socheatsok78
Copy link
Contributor Author

socheatsok78 commented Jun 18, 2022

Should we check the MaxPathLen when trying to sign an intermediate certificate?

if raw_crt.MaxPathLen == 0 {
	fmt.Fprintln(os.Stderr, "Selected CA certificate is not allowed to issue intermediate certificates.")
	os.Exit(1)
}

@jdtw
Copy link
Contributor

jdtw commented Jun 21, 2022

Should we check the MaxPathLen when trying to sign an intermediate certificate?

That might be a nice future improvement, but let's leave it out of this PR to keep the change minimal.

pkix/cert_auth.go Show resolved Hide resolved
pkix/cert_auth.go Show resolved Hide resolved
pkix/cert_auth.go Outdated Show resolved Hide resolved
if !excludePathlen {
if pathlen > 0 {
authTemplate.MaxPathLen = pathlen
authTemplate.MaxPathLenZero = false
Copy link
Contributor

Choose a reason for hiding this comment

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

Added a comment on the current iteration.

@@ -69,7 +78,7 @@ func CreateCertificateAuthority(key *Key, organizationalUnit string, expiry time

// CreateIntermediateCertificateAuthority creates an intermediate
// CA certificate signed by the given authority.
func CreateIntermediateCertificateAuthority(crtAuth *Certificate, keyAuth *Key, csr *CertificateSigningRequest, proposedExpiry time.Time) (*Certificate, error) {
func CreateIntermediateCertificateAuthority(crtAuth *Certificate, keyAuth *Key, csr *CertificateSigningRequest, proposedExpiry time.Time, pathlen int) (*Certificate, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, I think it's fine to have the exclude flag in init.

Copy link
Contributor

@jdtw jdtw left a comment

Choose a reason for hiding this comment

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

Thanks again for your patience on this one!

// CreateCertificateAuthority creates Certificate Authority using existing key.
// CertificateAuthorityInfo returned is the extra infomation required by Certificate Authority.
func CreateCertificateAuthority(key *Key, organizationalUnit string, expiry time.Time, organization string, country string, province string, locality string, commonName string, permitDomains []string) (*Certificate, error) {
// Passing all arguments to CreateCertificateAuthorityWithOptions
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: you can remove these comments (here and below).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do in the morning, it's 3am over here! And I'm commenting from my phone 😄

@socheatsok78
Copy link
Contributor Author

Thanks again for your patience on this one!

Thank you for reviewing the PR!

@jdtw jdtw merged commit 825fe42 into square:master Jun 24, 2022
winkingturtle-vmw added a commit to cloudfoundry/inigo that referenced this pull request Apr 5, 2023
- Change pkix function to have WithPathLen options for intermediate
  certs Context: square/certstrap#135
winkingturtle-vmw added a commit to cloudfoundry/inigo that referenced this pull request Apr 6, 2023
* Bump to ginkgo/v2 & lager/v3

- Change pkix function to have WithPathLen options for intermediate
  certs Context: square/certstrap#135

* fix `go vet` failures
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants