From 0ae22b92a0b714f000b3870a3dc3e17b2741ddf7 Mon Sep 17 00:00:00 2001 From: KOVACS Krisztian Date: Thu, 1 Oct 2020 10:23:56 +0200 Subject: [PATCH] cmd/sign: make it possible to specify additional extended key usage Certain Microsoft services (like Remote Desktop Services) require certificates with special Extended Key Usage values. Microsoft has a registered OID (1.3.6.1.4.1.311.54.1.2) for "Remote Desktop Authentication": http://oid-info.com/cgi-bin/display?oid=1.3.6.1.4.1.311.54.1.2&action=display This change makes it possible to add an additional custom Extended Key Usage value to the signed certificate by allowing the user to specify an OID value explicitly which is then added to the list. --- cmd/oid.go | 20 ++++++++++++++++++++ cmd/oid_test.go | 22 ++++++++++++++++++++++ cmd/revoke_test.go | 2 +- cmd/sign.go | 18 +++++++++++++++++- pkix/cert_host.go | 5 +++-- pkix/cert_host_test.go | 2 +- 6 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 cmd/oid.go create mode 100644 cmd/oid_test.go diff --git a/cmd/oid.go b/cmd/oid.go new file mode 100644 index 0000000..e620bc0 --- /dev/null +++ b/cmd/oid.go @@ -0,0 +1,20 @@ +package cmd + +import ( + "encoding/asn1" + "strconv" + "strings" +) + +func parseOid(oid string) (asn1.ObjectIdentifier, error) { + result := make([]int, 0) + for _, part := range strings.Split(oid, ".") { + number, err := strconv.ParseInt(part, 10, 32) + if err != nil { + return asn1.ObjectIdentifier{}, err + } + result = append(result, int(number)) + } + + return asn1.ObjectIdentifier(result), nil +} diff --git a/cmd/oid_test.go b/cmd/oid_test.go new file mode 100644 index 0000000..1ad8103 --- /dev/null +++ b/cmd/oid_test.go @@ -0,0 +1,22 @@ +package cmd + +import ( + "encoding/asn1" + "testing" +) + +func TestValidOid(t *testing.T) { + parsed, _ := parseOid("1.3.6.1.5.5.7.3.1") + expected := asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1} + + if !parsed.Equal(expected) { + t.Fatalf("Parsed OID does not match expected (expected: %s, got %s)", expected, parsed) + } +} + +func TestInvalidOid(t *testing.T) { + _, err := parseOid("not an OID") + if err == nil { + t.Fatalf("Expected error when parsing invalid OID") + } +} diff --git a/cmd/revoke_test.go b/cmd/revoke_test.go index c96b0a7..447fcce 100644 --- a/cmd/revoke_test.go +++ b/cmd/revoke_test.go @@ -113,7 +113,7 @@ func setupCN(t *testing.T, dt depot.Depot) { t.Fatalf("could not get cert: %v", err) } - cnCert, err := pkix.CreateCertificateHost(caCert, key, csr, time.Now().Add(1*time.Hour)) + cnCert, err := pkix.CreateCertificateHost(caCert, key, csr, time.Now().Add(1*time.Hour), nil) if err != nil { t.Fatalf("could not create cert host: %v", err) } diff --git a/cmd/sign.go b/cmd/sign.go index a1d19e7..4ebb06a 100644 --- a/cmd/sign.go +++ b/cmd/sign.go @@ -18,6 +18,7 @@ package cmd import ( + "encoding/asn1" "fmt" "os" "strings" @@ -67,6 +68,10 @@ func NewSignCommand() cli.Command { Name: "intermediate", Usage: "Whether generated certificate should be a intermediate", }, + cli.StringFlag{ + Name: "extended-key-usage", + Usage: "OID of additional Extended Key Usage value to add to the certificate (example: 1.3.6.1.5.5.7.3.1)", + }, }, Action: newSignAction, } @@ -99,6 +104,17 @@ func newSignAction(c *cli.Context) { os.Exit(1) } + additionalExtendedKeyUsages := make([]asn1.ObjectIdentifier, 0) + additionalExtendedKeyUsage := c.String("extended-key-usage") + if additionalExtendedKeyUsage != "" { + oid, err := parseOid(additionalExtendedKeyUsage) + if err != nil { + fmt.Fprintln(os.Stderr, "Invalid OID value:", err) + os.Exit(1) + } + additionalExtendedKeyUsages = append(additionalExtendedKeyUsages, oid) + } + csr, err := getCertificateSigningRequest(c, d, formattedReqName) if err != nil { fmt.Fprintln(os.Stderr, "Get certificate request error:", err) @@ -142,7 +158,7 @@ func newSignAction(c *cli.Context) { fmt.Fprintln(os.Stderr, "Building intermediate") crtOut, err = pkix.CreateIntermediateCertificateAuthority(crt, key, csr, expiresTime) } else { - crtOut, err = pkix.CreateCertificateHost(crt, key, csr, expiresTime) + crtOut, err = pkix.CreateCertificateHost(crt, key, csr, expiresTime, additionalExtendedKeyUsages) } if err != nil { diff --git a/pkix/cert_host.go b/pkix/cert_host.go index 305ef75..a223dca 100644 --- a/pkix/cert_host.go +++ b/pkix/cert_host.go @@ -21,13 +21,14 @@ import ( "crypto/rand" "crypto/x509" "crypto/x509/pkix" + "encoding/asn1" "math/big" "time" ) // CreateCertificateHost creates certificate for host. // The arguments include CA certificate, CA key, certificate request. -func CreateCertificateHost(crtAuth *Certificate, keyAuth *Key, csr *CertificateSigningRequest, proposedExpiry time.Time) (*Certificate, error) { +func CreateCertificateHost(crtAuth *Certificate, keyAuth *Key, csr *CertificateSigningRequest, proposedExpiry time.Time, extendedKeyUsages []asn1.ObjectIdentifier) (*Certificate, error) { // Build CA based on RFC5280 hostTemplate := x509.Certificate{ // **SHOULD** be filled in a unique number @@ -45,7 +46,7 @@ func CreateCertificateHost(crtAuth *Certificate, keyAuth *Key, csr *CertificateS x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth, }, - UnknownExtKeyUsage: nil, + UnknownExtKeyUsage: extendedKeyUsages, BasicConstraintsValid: false, diff --git a/pkix/cert_host_test.go b/pkix/cert_host_test.go index b34cd0d..68cdf69 100644 --- a/pkix/cert_host_test.go +++ b/pkix/cert_host_test.go @@ -39,7 +39,7 @@ func TestCreateCertificateHost(t *testing.T) { t.Fatal("Failed parsing certificate request from PEM:", err) } - crt, err := CreateCertificateHost(crtAuth, key, csr, time.Now().AddDate(5000, 0, 0)) + crt, err := CreateCertificateHost(crtAuth, key, csr, time.Now().AddDate(5000, 0, 0), nil) if err != nil { t.Fatal("Failed creating certificate for host:", err) }