mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-29 18:36:30 +02:00
Add a new 'user_principal_name' type to the downstream mTLS match_subject_alt_names option. This corresponds to the 'OtherName' type with type-id 1.3.6.1.4.1.311.20.2.3 and a UTF8String value. Add support for UserPrincipalName SAN matching to the policy evaluator.
251 lines
7.4 KiB
Go
251 lines
7.4 KiB
Go
//go:build ignore
|
|
|
|
package main
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/rand"
|
|
"crypto/x509"
|
|
"crypto/x509/pkix"
|
|
"encoding/asn1"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"log"
|
|
"math/big"
|
|
"net"
|
|
"net/url"
|
|
"time"
|
|
)
|
|
|
|
// Returns a new self-signed certificate, as both PEM data and an
|
|
// *x509.Certificate, along with the corresponding private key.
|
|
func newSelfSignedCertificate(template *x509.Certificate) (
|
|
string, *x509.Certificate, *ecdsa.PrivateKey,
|
|
) {
|
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
der, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
cert, err := x509.ParseCertificate(der)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
return string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})), cert, key
|
|
}
|
|
|
|
// Returns a new certificate, as both PEM data and an *x509.Certificate, along
|
|
// with the new certificate's corresponding private key.
|
|
func newCertificate(template, issuer *x509.Certificate, issuerKey *ecdsa.PrivateKey) (
|
|
string, *x509.Certificate, *ecdsa.PrivateKey,
|
|
) {
|
|
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
der, err := x509.CreateCertificate(rand.Reader, template, issuer, key.Public(), issuerKey)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
cert, err := x509.ParseCertificate(der)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
return string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})), cert, key
|
|
}
|
|
|
|
// Returns a new CRL in PEM format.
|
|
func newCRL(
|
|
template *x509.RevocationList, issuer *x509.Certificate, issuerKey *ecdsa.PrivateKey,
|
|
) string {
|
|
der, err := x509.CreateRevocationList(rand.Reader, template, issuer, issuerKey)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
return string(pem.EncodeToMemory(&pem.Block{Type: "X509 CRL", Bytes: der}))
|
|
}
|
|
|
|
// Returns a raw SubjectAltName extension with a single UserPrincipalName.
|
|
func newSANUserPrincipalName(upnValue string) []byte {
|
|
type UPN struct {
|
|
UTF8String string `asn1:"utf8"`
|
|
}
|
|
type OtherName struct {
|
|
OID asn1.ObjectIdentifier
|
|
Value UPN `asn1:"tag:0"`
|
|
}
|
|
type GeneralNames struct {
|
|
OtherName OtherName `asn1:"tag:0"`
|
|
}
|
|
san, err := asn1.Marshal(GeneralNames{
|
|
OtherName: OtherName{
|
|
OID: asn1.ObjectIdentifier{
|
|
1, 3, 6, 1, 4, 1, 311, 20, 2, 3},
|
|
Value: UPN{
|
|
UTF8String: upnValue,
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
return san
|
|
}
|
|
|
|
// Generates new test certificates and CRLs.
|
|
func main() {
|
|
notAfter := time.Now().Add(3650 * 24 * time.Hour)
|
|
|
|
rootPEM, rootCA, rootKey := newSelfSignedCertificate(&x509.Certificate{
|
|
SerialNumber: big.NewInt(0x1000),
|
|
Subject: pkix.Name{
|
|
CommonName: "Trusted Root CA",
|
|
},
|
|
NotAfter: notAfter,
|
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
BasicConstraintsValid: true,
|
|
IsCA: true,
|
|
})
|
|
|
|
trustedClientCertPEM, _, _ := newCertificate(&x509.Certificate{
|
|
SerialNumber: big.NewInt(0x1001),
|
|
Subject: pkix.Name{
|
|
CommonName: "trusted client cert",
|
|
},
|
|
NotAfter: notAfter,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
}, rootCA, rootKey)
|
|
|
|
intermediatePEM, intermediateCA, intermediateKey := newCertificate(&x509.Certificate{
|
|
SerialNumber: big.NewInt(0x1003),
|
|
Subject: pkix.Name{
|
|
CommonName: "Trusted Intermediate CA",
|
|
},
|
|
NotAfter: notAfter,
|
|
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
|
BasicConstraintsValid: true,
|
|
IsCA: true,
|
|
}, rootCA, rootKey)
|
|
|
|
trustedClientCert2PEM, _, _ := newCertificate(&x509.Certificate{
|
|
SerialNumber: big.NewInt(0x1000),
|
|
Subject: pkix.Name{
|
|
CommonName: "client cert from intermediate",
|
|
},
|
|
NotAfter: notAfter,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
}, intermediateCA, intermediateKey)
|
|
|
|
_, untrustedCA, untrustedCAKey := newSelfSignedCertificate(&x509.Certificate{
|
|
SerialNumber: big.NewInt(0x1000),
|
|
Subject: pkix.Name{
|
|
CommonName: "Untrusted Root CA",
|
|
},
|
|
NotAfter: notAfter,
|
|
BasicConstraintsValid: true,
|
|
IsCA: true,
|
|
})
|
|
|
|
untrustedClientCertPEM, _, _ := newCertificate(&x509.Certificate{
|
|
SerialNumber: big.NewInt(0x1001),
|
|
Subject: pkix.Name{
|
|
CommonName: "untrusted client cert",
|
|
},
|
|
NotAfter: notAfter,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
}, untrustedCA, untrustedCAKey)
|
|
|
|
revokedClientCertPEM, revokedClientCert, _ := newCertificate(&x509.Certificate{
|
|
SerialNumber: big.NewInt(0x1002),
|
|
Subject: pkix.Name{
|
|
CommonName: "revoked client cert",
|
|
},
|
|
NotAfter: notAfter,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
}, rootCA, rootKey)
|
|
|
|
crlPEM := newCRL(&x509.RevocationList{
|
|
Number: big.NewInt(0x2000),
|
|
RevokedCertificates: []pkix.RevokedCertificate{
|
|
{
|
|
SerialNumber: revokedClientCert.SerialNumber,
|
|
RevocationTime: time.Now(),
|
|
},
|
|
},
|
|
}, rootCA, rootKey)
|
|
|
|
trustedClientCert3PEM, _, _ := newCertificate(&x509.Certificate{
|
|
SerialNumber: big.NewInt(0x1004),
|
|
Subject: pkix.Name{
|
|
CommonName: "client cert 3",
|
|
},
|
|
DNSNames: []string{"a.client3.example.com", "b.client3.example.com"},
|
|
NotAfter: notAfter,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
}, rootCA, rootKey)
|
|
|
|
trustedClientCert4PEM, _, _ := newCertificate(&x509.Certificate{
|
|
SerialNumber: big.NewInt(0x1005),
|
|
Subject: pkix.Name{
|
|
CommonName: "client cert 4",
|
|
},
|
|
EmailAddresses: []string{"client4@example.com"},
|
|
NotAfter: notAfter,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
}, rootCA, rootKey)
|
|
|
|
trustedClientCert5PEM, _, _ := newCertificate(&x509.Certificate{
|
|
SerialNumber: big.NewInt(0x1006),
|
|
Subject: pkix.Name{
|
|
CommonName: "client cert 5",
|
|
},
|
|
IPAddresses: []net.IP{net.ParseIP("192.168.10.10")},
|
|
NotAfter: notAfter,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
}, rootCA, rootKey)
|
|
|
|
trustedClientCert6PEM, _, _ := newCertificate(&x509.Certificate{
|
|
SerialNumber: big.NewInt(0x1007),
|
|
Subject: pkix.Name{
|
|
CommonName: "client cert 6",
|
|
},
|
|
URIs: []*url.URL{{Scheme: "spiffe", Host: "example.com", Path: "/foo/bar"}},
|
|
NotAfter: notAfter,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
}, rootCA, rootKey)
|
|
|
|
trustedClientCert7PEM, _, _ := newCertificate(&x509.Certificate{
|
|
SerialNumber: big.NewInt(0x1007),
|
|
Subject: pkix.Name{
|
|
CommonName: "client cert 7",
|
|
},
|
|
NotAfter: notAfter,
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
ExtraExtensions: []pkix.Extension{{
|
|
Id: asn1.ObjectIdentifier{2, 5, 29, 17},
|
|
Critical: true, // requires special handling during verification
|
|
Value: newSANUserPrincipalName("test_device"),
|
|
}},
|
|
}, rootCA, rootKey)
|
|
|
|
fmt.Println(`
|
|
const (
|
|
testCA = ` + "`\n" + rootPEM + "`" + `
|
|
testValidCert = ` + "`\n" + trustedClientCertPEM + "`" + `
|
|
testUntrustedCert = ` + "`\n" + untrustedClientCertPEM + "`" + `
|
|
testRevokedCert = ` + "`\n" + revokedClientCertPEM + "`" + `
|
|
testCRL = ` + "`\n" + crlPEM + "`" + `
|
|
testIntermediateCA = ` + "`\n" + intermediatePEM + "`" + `
|
|
testValidIntermediateCert = ` + "`\n" + trustedClientCert2PEM + "`" + `
|
|
testValidCertWithDNSSANs = ` + "`\n" + trustedClientCert3PEM + "`" + `
|
|
testValidCertWithEmailSAN = ` + "`\n" + trustedClientCert4PEM + "`" + `
|
|
testValidCertWithIPSAN = ` + "`\n" + trustedClientCert5PEM + "`" + `
|
|
testValidCertWithURISAN = ` + "`\n" + trustedClientCert6PEM + "`" + `
|
|
testValidCertWithUPNSAN = ` + "`\n" + trustedClientCert7PEM + "`" + `
|
|
)
|
|
`)
|
|
}
|