refactor: private key decoding and generators (#4116)
parent
32bd2eba60
commit
3f39914c8f
|
@ -1,6 +1,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -181,6 +183,8 @@ var decodedTypes = []reflect.Type{
|
|||
reflect.TypeOf(url.URL{}),
|
||||
reflect.TypeOf(time.Duration(0)),
|
||||
reflect.TypeOf(schema.Address{}),
|
||||
reflect.TypeOf(rsa.PrivateKey{}),
|
||||
reflect.TypeOf(ecdsa.PrivateKey{}),
|
||||
}
|
||||
|
||||
func containsType(needle reflect.Type, haystack []reflect.Type) (contains bool) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
---
|
||||
title: "Commit Message Guidelines"
|
||||
title: "Commit Message"
|
||||
description: "Authelia Development Commit Message Guidelines"
|
||||
lead: "This section covers the git commit message guidelines we use for development."
|
||||
date: 2021-01-30T19:29:07+11:00
|
||||
|
@ -7,8 +7,8 @@ draft: false
|
|||
images: []
|
||||
menu:
|
||||
contributing:
|
||||
parent: "development"
|
||||
weight: 231
|
||||
parent: "guidelines"
|
||||
weight: 320
|
||||
toc: true
|
||||
aliases:
|
||||
- /docs/contributing/commitmsg-guidelines.html
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
title: "Documentation"
|
||||
description: "Authelia Development Documentation Guidelines"
|
||||
lead: "This section covers the guidelines we use when writing documentation."
|
||||
date: 2021-01-30T19:29:07+11:00
|
||||
date: 2022-10-02T14:32:16+11:00
|
||||
draft: false
|
||||
images: []
|
||||
menu:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
title: "Guidelines"
|
||||
description: "An introduction into guidelines for contributing to the Authelia project."
|
||||
lead: "An introduction into guidelines for contributing to the Authelia project."
|
||||
date: 2022-06-15T17:51:47+10:00
|
||||
date: 2022-10-02T14:32:16+11:00
|
||||
draft: false
|
||||
images: []
|
||||
menu:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
title: "Istio"
|
||||
description: "A guide to integrating Authelia with the Istio Kubernetes Ingress."
|
||||
lead: "A guide to integrating Authelia with the Istio Kubernetes Ingress."
|
||||
date: 2022-06-15T17:51:47+10:00
|
||||
date: 2022-10-02T13:59:09+11:00
|
||||
draft: false
|
||||
images: []
|
||||
menu:
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,7 @@
|
|||
package configuration
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
|
@ -343,8 +344,8 @@ func StringToX509CertificateChainHookFunc() mapstructure.DecodeHookFuncType {
|
|||
}
|
||||
}
|
||||
|
||||
// StringToRSAPrivateKeyHookFunc decodes strings to rsa.PrivateKey's.
|
||||
func StringToRSAPrivateKeyHookFunc() mapstructure.DecodeHookFuncType {
|
||||
// StringToPrivateKeyHookFunc decodes strings to rsa.PrivateKey's.
|
||||
func StringToPrivateKeyHookFunc() mapstructure.DecodeHookFuncType {
|
||||
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {
|
||||
if f.Kind() != reflect.String {
|
||||
return data, nil
|
||||
|
@ -354,28 +355,53 @@ func StringToRSAPrivateKeyHookFunc() mapstructure.DecodeHookFuncType {
|
|||
return data, nil
|
||||
}
|
||||
|
||||
expectedType := reflect.TypeOf(rsa.PrivateKey{})
|
||||
expectedTypeRSA := reflect.TypeOf(rsa.PrivateKey{})
|
||||
expectedTypeECDSA := reflect.TypeOf(ecdsa.PrivateKey{})
|
||||
|
||||
if t.Elem() != expectedType {
|
||||
return data, nil
|
||||
}
|
||||
var (
|
||||
i any
|
||||
expectedType reflect.Type
|
||||
)
|
||||
|
||||
dataStr := data.(string)
|
||||
|
||||
var result *rsa.PrivateKey
|
||||
switch t.Elem() {
|
||||
case expectedTypeRSA:
|
||||
var result *rsa.PrivateKey
|
||||
|
||||
if dataStr == "" {
|
||||
return result, nil
|
||||
if dataStr == "" {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
expectedType = expectedTypeRSA
|
||||
case expectedTypeECDSA:
|
||||
var result *ecdsa.PrivateKey
|
||||
|
||||
if dataStr == "" {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
expectedType = expectedTypeECDSA
|
||||
default:
|
||||
return data, nil
|
||||
}
|
||||
|
||||
var i interface{}
|
||||
|
||||
if i, err = utils.ParseX509FromPEM([]byte(dataStr)); err != nil {
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, err)
|
||||
}
|
||||
|
||||
switch r := i.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
if expectedType != expectedTypeRSA {
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, fmt.Errorf("the data is for a %T not a *%s", r, expectedType))
|
||||
}
|
||||
|
||||
return r, nil
|
||||
case *ecdsa.PrivateKey:
|
||||
if expectedType != expectedTypeECDSA {
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, fmt.Errorf("the data is for a %T not a *%s", r, expectedType))
|
||||
}
|
||||
|
||||
return r, nil
|
||||
default:
|
||||
return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "*", expectedType, fmt.Errorf("the data is for a %T not a *%s", r, expectedType))
|
||||
|
|
|
@ -23,8 +23,8 @@ import (
|
|||
func TestStringToMailAddressHookFunc(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
have interface{}
|
||||
want interface{}
|
||||
have any
|
||||
want any
|
||||
err string
|
||||
decode bool
|
||||
}{
|
||||
|
@ -78,8 +78,8 @@ func TestStringToMailAddressHookFunc(t *testing.T) {
|
|||
func TestStringToMailAddressHookFuncPointer(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
have interface{}
|
||||
want interface{}
|
||||
have any
|
||||
want any
|
||||
err string
|
||||
decode bool
|
||||
}{
|
||||
|
@ -139,8 +139,8 @@ func TestStringToMailAddressHookFuncPointer(t *testing.T) {
|
|||
func TestStringToURLHookFunc(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
have interface{}
|
||||
want interface{}
|
||||
have any
|
||||
want any
|
||||
err string
|
||||
decode bool
|
||||
}{
|
||||
|
@ -212,8 +212,8 @@ func TestStringToURLHookFunc(t *testing.T) {
|
|||
func TestStringToURLHookFuncPointer(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
have interface{}
|
||||
want interface{}
|
||||
have any
|
||||
want any
|
||||
err string
|
||||
decode bool
|
||||
}{
|
||||
|
@ -285,8 +285,8 @@ func TestStringToURLHookFuncPointer(t *testing.T) {
|
|||
func TestToTimeDurationHookFunc(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
have interface{}
|
||||
want interface{}
|
||||
have any
|
||||
want any
|
||||
err string
|
||||
decode bool
|
||||
}{
|
||||
|
@ -405,8 +405,8 @@ func TestToTimeDurationHookFunc(t *testing.T) {
|
|||
func TestToTimeDurationHookFuncPointer(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
have interface{}
|
||||
want interface{}
|
||||
have any
|
||||
want any
|
||||
err string
|
||||
decode bool
|
||||
}{
|
||||
|
@ -526,8 +526,8 @@ func TestToTimeDurationHookFuncPointer(t *testing.T) {
|
|||
func TestStringToRegexpFunc(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
have interface{}
|
||||
want interface{}
|
||||
have any
|
||||
want any
|
||||
err string
|
||||
decode bool
|
||||
wantGrps []string
|
||||
|
@ -640,8 +640,8 @@ func TestStringToRegexpFunc(t *testing.T) {
|
|||
func TestStringToRegexpFuncPointers(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
have interface{}
|
||||
want interface{}
|
||||
have any
|
||||
want any
|
||||
err string
|
||||
decode bool
|
||||
wantGrps []string
|
||||
|
@ -775,8 +775,8 @@ func TestStringToAddressHookFunc(t *testing.T) {
|
|||
|
||||
testCases := []struct {
|
||||
name string
|
||||
have interface{}
|
||||
expected interface{}
|
||||
have any
|
||||
expected any
|
||||
err string
|
||||
decode bool
|
||||
}{
|
||||
|
@ -867,51 +867,108 @@ func TestStringToAddressHookFunc(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestStringToRSAPrivateKeyHookFunc(t *testing.T) {
|
||||
var nilkey *rsa.PrivateKey
|
||||
func TestStringToPrivateKeyHookFunc(t *testing.T) {
|
||||
var (
|
||||
nilRSA *rsa.PrivateKey
|
||||
nilECDSA *ecdsa.PrivateKey
|
||||
nilCert *x509.Certificate
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
have interface{}
|
||||
want interface{}
|
||||
have any
|
||||
want any
|
||||
err string
|
||||
decode bool
|
||||
}{
|
||||
{
|
||||
desc: "ShouldDecodeRSAPrivateKey",
|
||||
have: x509PrivateKeyRSA1,
|
||||
want: mustParseRSAPrivateKey(x509PrivateKeyRSA1),
|
||||
want: MustParseRSAPrivateKey(x509PrivateKeyRSA1),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeECDSAPrivateKey",
|
||||
have: x509PrivateKeyEC1,
|
||||
want: MustParseECDSAPrivateKey(x509PrivateKeyEC1),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeToECDSAPrivateKey",
|
||||
have: x509PrivateKeyRSA1,
|
||||
want: &ecdsa.PrivateKey{},
|
||||
decode: false,
|
||||
decode: true,
|
||||
err: "could not decode to a *ecdsa.PrivateKey: the data is for a *rsa.PrivateKey not a *ecdsa.PrivateKey",
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeEmptyKey",
|
||||
desc: "ShouldNotDecodeEmptyRSAKey",
|
||||
have: "",
|
||||
want: nilkey,
|
||||
want: nilRSA,
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeEmptyECDSAKey",
|
||||
have: "",
|
||||
want: nilECDSA,
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeECDSAKeyToRSAKey",
|
||||
have: x509PrivateKeyEC1,
|
||||
want: nilkey,
|
||||
want: nilRSA,
|
||||
decode: true,
|
||||
err: "could not decode to a *rsa.PrivateKey: the data is for a *ecdsa.PrivateKey not a *rsa.PrivateKey",
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeRSAKeyToECDSAKey",
|
||||
have: x509PrivateKeyRSA1,
|
||||
want: nilECDSA,
|
||||
decode: true,
|
||||
err: "could not decode to a *ecdsa.PrivateKey: the data is for a *rsa.PrivateKey not a *ecdsa.PrivateKey",
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeBadRSAPrivateKey",
|
||||
have: x509PrivateKeyRSA2,
|
||||
want: nilkey,
|
||||
want: nilRSA,
|
||||
decode: true,
|
||||
err: "could not decode to a *rsa.PrivateKey: failed to parse PEM block containing the key",
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeBadECDSAPrivateKey",
|
||||
have: x509PrivateKeyEC2,
|
||||
want: nilECDSA,
|
||||
decode: true,
|
||||
err: "could not decode to a *ecdsa.PrivateKey: failed to parse PEM block containing the key",
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeCertificateToRSAPrivateKey",
|
||||
have: x509CertificateRSA1,
|
||||
want: nilRSA,
|
||||
decode: true,
|
||||
err: "could not decode to a *rsa.PrivateKey: the data is for a *x509.Certificate not a *rsa.PrivateKey",
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeCertificateToECDSAPrivateKey",
|
||||
have: x509CertificateRSA1,
|
||||
want: nilECDSA,
|
||||
decode: true,
|
||||
err: "could not decode to a *ecdsa.PrivateKey: the data is for a *x509.Certificate not a *ecdsa.PrivateKey",
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeRSAKeyToCertificate",
|
||||
have: x509PrivateKeyRSA1,
|
||||
want: nilCert,
|
||||
decode: false,
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeECDSAKeyToCertificate",
|
||||
have: x509PrivateKeyEC1,
|
||||
want: nilCert,
|
||||
decode: false,
|
||||
},
|
||||
}
|
||||
|
||||
hook := configuration.StringToRSAPrivateKeyHookFunc()
|
||||
hook := configuration.StringToPrivateKeyHookFunc()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
|
@ -944,25 +1001,25 @@ func TestStringToX509CertificateHookFunc(t *testing.T) {
|
|||
{
|
||||
desc: "ShouldDecodeRSACertificate",
|
||||
have: x509CertificateRSA1,
|
||||
want: mustParseX509Certificate(x509CertificateRSA1),
|
||||
want: MustParseX509Certificate(x509CertificateRSA1),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeECDSACertificate",
|
||||
have: x509CACertificateECDSA,
|
||||
want: mustParseX509Certificate(x509CACertificateECDSA),
|
||||
want: MustParseX509Certificate(x509CACertificateECDSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeRSACACertificate",
|
||||
have: x509CACertificateRSA,
|
||||
want: mustParseX509Certificate(x509CACertificateRSA),
|
||||
want: MustParseX509Certificate(x509CACertificateRSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeECDSACACertificate",
|
||||
have: x509CACertificateECDSA,
|
||||
want: mustParseX509Certificate(x509CACertificateECDSA),
|
||||
want: MustParseX509Certificate(x509CACertificateECDSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
|
@ -1020,50 +1077,50 @@ func TestStringToX509CertificateChainHookFunc(t *testing.T) {
|
|||
{
|
||||
desc: "ShouldDecodeRSACertificate",
|
||||
have: x509CertificateRSA1,
|
||||
expected: mustParseX509CertificateChain(x509CertificateRSA1),
|
||||
expected: MustParseX509CertificateChain(x509CertificateRSA1),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeRSACertificateNoPtr",
|
||||
have: x509CertificateRSA1,
|
||||
expected: *mustParseX509CertificateChain(x509CertificateRSA1),
|
||||
expected: *MustParseX509CertificateChain(x509CertificateRSA1),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeRSACertificateChain",
|
||||
have: buildChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||
expected: mustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||
have: BuildChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||
expected: MustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeRSACertificateChainNoPtr",
|
||||
have: buildChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||
expected: *mustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||
have: BuildChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||
expected: *MustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateRSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldNotDecodeBadRSACertificateChain",
|
||||
have: buildChain(x509CertificateRSA1, x509CACertificateECDSA),
|
||||
expected: mustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateECDSA),
|
||||
have: BuildChain(x509CertificateRSA1, x509CACertificateECDSA),
|
||||
expected: MustParseX509CertificateChain(x509CertificateRSA1, x509CACertificateECDSA),
|
||||
verr: "certificate #1 in chain is not signed properly by certificate #2 in chain: x509: signature algorithm specifies an RSA public key, but have public key of type *ecdsa.PublicKey",
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeECDSACertificate",
|
||||
have: x509CACertificateECDSA,
|
||||
expected: mustParseX509CertificateChain(x509CACertificateECDSA),
|
||||
expected: MustParseX509CertificateChain(x509CACertificateECDSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeRSACACertificate",
|
||||
have: x509CACertificateRSA,
|
||||
expected: mustParseX509CertificateChain(x509CACertificateRSA),
|
||||
expected: MustParseX509CertificateChain(x509CACertificateRSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
desc: "ShouldDecodeECDSACACertificate",
|
||||
have: x509CACertificateECDSA,
|
||||
expected: mustParseX509CertificateChain(x509CACertificateECDSA),
|
||||
expected: MustParseX509CertificateChain(x509CACertificateECDSA),
|
||||
decode: true,
|
||||
},
|
||||
{
|
||||
|
@ -1175,6 +1232,11 @@ bad key
|
|||
MHcCAQEEIMn970LSn8aKVhBM4vyUmpZyEdCT4riN+Lp4QU04zUhYoAoGCCqGSM49
|
||||
AwEHoUQDQgAEMD69n22nd78GmaRDzy/s7muqhbc/OEnFS2mNtiRAA5FaX+kbkCB5
|
||||
8pu/k2jkaSVNZtBYKPVAibHkhvakjVb66A==
|
||||
-----END EC PRIVATE KEY-----`
|
||||
|
||||
x509PrivateKeyEC2 = `
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
bad key
|
||||
-----END EC PRIVATE KEY-----`
|
||||
|
||||
x509CertificateRSA1 = `
|
||||
|
@ -1232,14 +1294,14 @@ uDv6M2spMi0CIQC8uOSMcv11vp1ylsGg38N6XYA+GQa1BHRd79+91hC+7w==
|
|||
-----END CERTIFICATE-----`
|
||||
)
|
||||
|
||||
func mustParseRSAPrivateKey(data string) *rsa.PrivateKey {
|
||||
func MustParseRSAPrivateKey(data string) *rsa.PrivateKey {
|
||||
block, _ := pem.Decode([]byte(data))
|
||||
if block == nil || block.Bytes == nil || len(block.Bytes) == 0 {
|
||||
panic("not pem encoded")
|
||||
}
|
||||
|
||||
if block.Type != "RSA PRIVATE KEY" {
|
||||
panic("not private key")
|
||||
panic("not rsa private key")
|
||||
}
|
||||
|
||||
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
|
@ -1250,7 +1312,25 @@ func mustParseRSAPrivateKey(data string) *rsa.PrivateKey {
|
|||
return key
|
||||
}
|
||||
|
||||
func mustParseX509Certificate(data string) *x509.Certificate {
|
||||
func MustParseECDSAPrivateKey(data string) *ecdsa.PrivateKey {
|
||||
block, _ := pem.Decode([]byte(data))
|
||||
if block == nil || block.Bytes == nil || len(block.Bytes) == 0 {
|
||||
panic("not pem encoded")
|
||||
}
|
||||
|
||||
if block.Type != "EC PRIVATE KEY" {
|
||||
panic("not ecdsa private key")
|
||||
}
|
||||
|
||||
key, err := x509.ParseECPrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
func MustParseX509Certificate(data string) *x509.Certificate {
|
||||
block, _ := pem.Decode([]byte(data))
|
||||
if block == nil || len(block.Bytes) == 0 {
|
||||
panic("not a PEM")
|
||||
|
@ -1264,7 +1344,7 @@ func mustParseX509Certificate(data string) *x509.Certificate {
|
|||
return cert
|
||||
}
|
||||
|
||||
func buildChain(pems ...string) string {
|
||||
func BuildChain(pems ...string) string {
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
for i, data := range pems {
|
||||
|
@ -1278,8 +1358,8 @@ func buildChain(pems ...string) string {
|
|||
return buf.String()
|
||||
}
|
||||
|
||||
func mustParseX509CertificateChain(datas ...string) *schema.X509CertificateChain {
|
||||
chain, err := schema.NewX509CertificateChain(buildChain(datas...))
|
||||
func MustParseX509CertificateChain(datas ...string) *schema.X509CertificateChain {
|
||||
chain, err := schema.NewX509CertificateChain(BuildChain(datas...))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ func unmarshal(ko *koanf.Koanf, val *schema.StructValidator, path string, o inte
|
|||
StringToAddressHookFunc(),
|
||||
StringToX509CertificateHookFunc(),
|
||||
StringToX509CertificateChainHookFunc(),
|
||||
StringToRSAPrivateKeyHookFunc(),
|
||||
StringToPrivateKeyHookFunc(),
|
||||
ToTimeDurationHookFunc(),
|
||||
),
|
||||
Metadata: nil,
|
||||
|
|
Loading…
Reference in New Issue