feat: implement func getLeafCertificate so that Certificate.Leaf can be nil. This behavior was changed with Go 1.23, according to https://go-review.googlesource.com/c/go/+/585856, also provieded tests

This commit is contained in:
theexiile1305 2024-10-30 15:40:22 +01:00
parent faffc025cb
commit 1970b24151
No known key found for this signature in database
GPG key ID: A1BDDE98F2BF6E40
3 changed files with 94 additions and 6 deletions

30
msg.go
View file

@ -367,23 +367,45 @@ func (m *Msg) SignWithTLSCertificate(keyPairTlS *tls.Certificate) error {
return fmt.Errorf("failed to parse intermediate certificate: %w", err)
}
leafCertificate, err := getLeafCertificate(keyPairTlS)
if err != nil {
return fmt.Errorf("failed to get leaf certificate: %w", err)
}
switch keyPairTlS.PrivateKey.(type) {
case *rsa.PrivateKey:
if intermediateCertificate == nil {
return m.SignWithSMimeRSA(keyPairTlS.PrivateKey.(*rsa.PrivateKey), keyPairTlS.Leaf, nil)
return m.SignWithSMimeRSA(keyPairTlS.PrivateKey.(*rsa.PrivateKey), leafCertificate, nil)
}
return m.SignWithSMimeRSA(keyPairTlS.PrivateKey.(*rsa.PrivateKey), keyPairTlS.Leaf, intermediateCertificate)
return m.SignWithSMimeRSA(keyPairTlS.PrivateKey.(*rsa.PrivateKey), leafCertificate, intermediateCertificate)
case *ecdsa.PrivateKey:
if intermediateCertificate == nil {
return m.SignWithSMimeECDSA(keyPairTlS.PrivateKey.(*ecdsa.PrivateKey), keyPairTlS.Leaf, nil)
return m.SignWithSMimeECDSA(keyPairTlS.PrivateKey.(*ecdsa.PrivateKey), leafCertificate, nil)
}
return m.SignWithSMimeECDSA(keyPairTlS.PrivateKey.(*ecdsa.PrivateKey), keyPairTlS.Leaf, intermediateCertificate)
return m.SignWithSMimeECDSA(keyPairTlS.PrivateKey.(*ecdsa.PrivateKey), leafCertificate, intermediateCertificate)
default:
return fmt.Errorf("unsupported private key type: %T", keyPairTlS.PrivateKey)
}
}
// getLeafCertificate returns the leaf certificate from a tls.Certificate.
// PLEASE NOTE: Before Go 1.23 Certificate.Leaf was left nil, and the parsed certificate was
// discarded. This behavior can be re-enabled by setting "x509keypairleaf=0"
// in the GODEBUG environment variable.
func getLeafCertificate(keyPairTlS *tls.Certificate) (*x509.Certificate, error) {
if keyPairTlS.Leaf != nil {
return keyPairTlS.Leaf, nil
}
cert, err := x509.ParseCertificate(keyPairTlS.Certificate[0])
if err != nil {
return nil, err
}
return cert, nil
}
// This method allows you to specify a character set for the email message. The charset is
// important for ensuring that the content of the message is correctly interpreted by
// mail clients. Common charset values include UTF-8, ISO-8859-1, and others. If a charset

View file

@ -3379,6 +3379,47 @@ func TestSignWithSMime_ValidECDSAKeyPair(t *testing.T) {
}
}
// TestSignWithTLSCertificate tests SignWithTLSCertificate with given *tls.Certificate
func TestSignWithTLSCertificate(t *testing.T) {
keyPairTLS, err := getDummyKeyPairTLS()
if err != nil {
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
}
m := NewMsg()
if err := m.SignWithTLSCertificate(keyPairTLS); err != nil {
t.Errorf("failed to set sMime. Cause: %v", err)
}
if m.sMime.privateKey.ecdsa == nil {
t.Errorf("SignWithTLSCertificate() - no private key is given")
}
if m.sMime.certificate == nil {
t.Errorf("SignWithTLSCertificate() - no certificate is given")
}
}
// TestSignWithTLSCertificate tests SignWithTLSCertificate with given *tls.Certificate and nil leaf certificate
// PLEASE NOTE: Before Go 1.23 Certificate.Leaf was left nil, and the parsed certificate was
// discarded. This behavior can be re-enabled by setting "x509keypairleaf=0"
// in the GODEBUG environment variable.
func TestSignWithTLSCertificate_WithKeyPairLeafNil(t *testing.T) {
t.Setenv("GODEBUG", "x509keypairleaf=0")
keyPairTLS, err := getDummyKeyPairTLS()
if err != nil {
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
}
m := NewMsg()
if err := m.SignWithTLSCertificate(keyPairTLS); err != nil {
t.Errorf("failed to set sMime. Cause: %v", err)
}
if m.sMime.privateKey.ecdsa == nil {
t.Errorf("SignWithTLSCertificate() - no private key is given")
}
if m.sMime.certificate == nil {
t.Errorf("SignWithTLSCertificate() - no certificate is given")
}
}
// TestSignWithSMime_InvalidPrivateKey tests WithSMimeSinging with given invalid private key
func TestSignWithSMime_InvalidPrivateKey(t *testing.T) {
m := NewMsg()
@ -3511,3 +3552,19 @@ func TestMsg_signMessage(t *testing.T) {
t.Errorf("createSignaturePart() method failed. Expected content should not be equal: %s, got: %s", body, signaturePart.GetEncoding())
}
}
// TestGetLeafCertificate tests the Msg.getLeafCertificate method
func TestGetLeafCertificate(t *testing.T) {
keyPairTLS, err := getDummyKeyPairTLS()
if err != nil {
t.Errorf("failed to laod dummy crypto material. Cause: %v", err)
}
leafCertificate, err := getLeafCertificate(keyPairTLS)
if err != nil {
t.Errorf("failed to get leaf certificate. Cause: %v", err)
}
if leafCertificate == nil {
t.Errorf("failed to get leaf certificate")
}
}

View file

@ -19,7 +19,7 @@ const (
keyECDSAFilePath = "dummy-child-key-ecdsa.pem"
)
// getDummyRSACryptoMaterial loads a certificate (RSA) and the associated private key (ECDSA) form local disk for testing purposes
// getDummyRSACryptoMaterial loads a certificate (RSA), the associated private key and certificate (RSA) is loaded from local disk for testing purposes
func getDummyRSACryptoMaterial() (*rsa.PrivateKey, *x509.Certificate, *x509.Certificate, error) {
keyPair, err := tls.LoadX509KeyPair(certRSAFilePath, keyRSAFilePath)
if err != nil {
@ -41,7 +41,7 @@ func getDummyRSACryptoMaterial() (*rsa.PrivateKey, *x509.Certificate, *x509.Cert
return privateKey, certificate, intermediateCertificate, nil
}
// getDummyECDSACryptoMaterial loads a certificate (ECDSA) and the associated private key (ECDSA) form local disk for testing purposes
// getDummyECDSACryptoMaterial loads a certificate (ECDSA), the associated private key and certificate (ECDSA) is loaded from local disk for testing purposes
func getDummyECDSACryptoMaterial() (*ecdsa.PrivateKey, *x509.Certificate, *x509.Certificate, error) {
keyPair, err := tls.LoadX509KeyPair(certECDSAFilePath, keyECDSAFilePath)
if err != nil {
@ -62,3 +62,12 @@ func getDummyECDSACryptoMaterial() (*ecdsa.PrivateKey, *x509.Certificate, *x509.
return privateKey, certificate, intermediateCertificate, nil
}
// getDummyKeyPairTLS loads a certificate (ECDSA) as *tls.Certificate, the associated private key and certificate (ECDSA) is loaded from local disk for testing purposes
func getDummyKeyPairTLS() (*tls.Certificate, error) {
keyPair, err := tls.LoadX509KeyPair(certECDSAFilePath, keyECDSAFilePath)
if err != nil {
return nil, err
}
return &keyPair, err
}