-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
caddypki: Add support for multiple intermediates in signing chain #7057
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
Changes from all commits
c2a2626
80eb3e8
1869b03
364e503
43e4796
cd3daa4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,15 +17,20 @@ package caddypki | |
| import ( | ||
| "bytes" | ||
| "crypto" | ||
| "crypto/ecdsa" | ||
| "crypto/ed25519" | ||
| "crypto/rsa" | ||
| "crypto/x509" | ||
| "encoding/pem" | ||
| "errors" | ||
| "fmt" | ||
| "os" | ||
|
|
||
| "github.com/caddyserver/certmagic" | ||
| "go.step.sm/crypto/pemutil" | ||
| ) | ||
|
|
||
| func pemDecodeSingleCert(pemDER []byte) (*x509.Certificate, error) { | ||
| func pemDecodeCertificate(pemDER []byte) (*x509.Certificate, error) { | ||
| pemBlock, remaining := pem.Decode(pemDER) | ||
| if pemBlock == nil { | ||
| return nil, fmt.Errorf("no PEM block found") | ||
|
|
@@ -39,6 +44,15 @@ func pemDecodeSingleCert(pemDER []byte) (*x509.Certificate, error) { | |
| return x509.ParseCertificate(pemBlock.Bytes) | ||
| } | ||
|
|
||
| func pemDecodeCertificateChain(pemDER []byte) ([]*x509.Certificate, error) { | ||
| chain, err := pemutil.ParseCertificateBundle(pemDER) | ||
|
Comment on lines
+47
to
+48
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd rather either copy in the code of
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd still prefer this, but it's not a showstopper for now. Will await your reply in case you are OK with inlining the logic here.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nevermind, missed an above comment. |
||
| if err != nil { | ||
| return nil, fmt.Errorf("failed parsing certificate chain: %w", err) | ||
| } | ||
|
|
||
| return chain, nil | ||
| } | ||
|
|
||
| func pemEncodeCert(der []byte) ([]byte, error) { | ||
| return pemEncode("CERTIFICATE", der) | ||
| } | ||
|
|
@@ -70,15 +84,18 @@ type KeyPair struct { | |
| Format string `json:"format,omitempty"` | ||
| } | ||
|
|
||
| // Load loads the certificate and key. | ||
| func (kp KeyPair) Load() (*x509.Certificate, crypto.Signer, error) { | ||
| // Load loads the certificate chain and (optional) private key from | ||
| // the corresponding files, using the configured format. If a | ||
| // private key is read, it will be verified to belong to the first | ||
| // certificate in the chain. | ||
| func (kp KeyPair) Load() ([]*x509.Certificate, crypto.Signer, error) { | ||
| switch kp.Format { | ||
| case "", "pem_file": | ||
| certData, err := os.ReadFile(kp.Certificate) | ||
| if err != nil { | ||
| return nil, nil, err | ||
| } | ||
| cert, err := pemDecodeSingleCert(certData) | ||
| chain, err := pemDecodeCertificateChain(certData) | ||
| if err != nil { | ||
| return nil, nil, err | ||
| } | ||
|
|
@@ -93,11 +110,49 @@ func (kp KeyPair) Load() (*x509.Certificate, crypto.Signer, error) { | |
| if err != nil { | ||
| return nil, nil, err | ||
| } | ||
| if err := verifyKeysMatch(chain[0], key); err != nil { | ||
| return nil, nil, err | ||
| } | ||
| } | ||
|
|
||
| return cert, key, nil | ||
| return chain, key, nil | ||
|
|
||
| default: | ||
| return nil, nil, fmt.Errorf("unsupported format: %s", kp.Format) | ||
| } | ||
| } | ||
|
|
||
| // verifyKeysMatch verifies that the public key in the [x509.Certificate] matches | ||
| // the public key of the [crypto.Signer]. | ||
| func verifyKeysMatch(crt *x509.Certificate, signer crypto.Signer) error { | ||
| switch pub := crt.PublicKey.(type) { | ||
| case *rsa.PublicKey: | ||
| pk, ok := signer.Public().(*rsa.PublicKey) | ||
| if !ok { | ||
| return fmt.Errorf("private key type %T does not match issuer public key type %T", signer.Public(), pub) | ||
| } | ||
| if !pub.Equal(pk) { | ||
| return errors.New("private key does not match issuer public key") | ||
| } | ||
| case *ecdsa.PublicKey: | ||
| pk, ok := signer.Public().(*ecdsa.PublicKey) | ||
| if !ok { | ||
| return fmt.Errorf("private key type %T does not match issuer public key type %T", signer.Public(), pub) | ||
| } | ||
| if !pub.Equal(pk) { | ||
| return errors.New("private key does not match issuer public key") | ||
| } | ||
| case ed25519.PublicKey: | ||
| pk, ok := signer.Public().(ed25519.PublicKey) | ||
| if !ok { | ||
| return fmt.Errorf("private key type %T does not match issuer public key type %T", signer.Public(), pub) | ||
| } | ||
| if !pub.Equal(pk) { | ||
| return errors.New("private key does not match issuer public key") | ||
| } | ||
| default: | ||
| return fmt.Errorf("unsupported key type: %T", pub) | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.