From c8d225a7638a4844eca7d9d6b3da2ad94a531efd Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 5 Aug 2020 16:02:46 -0700 Subject: [PATCH] Use x509util from go.step.sm/crypto/x509util --- acme/order.go | 2 +- authority/provisioner/aws.go | 2 +- authority/provisioner/azure.go | 2 +- authority/provisioner/gcp.go | 2 +- authority/provisioner/jwk.go | 2 +- authority/provisioner/k8sSA.go | 4 +- authority/provisioner/oidc.go | 2 +- authority/provisioner/options.go | 2 +- authority/provisioner/options_test.go | 2 +- authority/provisioner/sign_options.go | 4 +- authority/provisioner/x5c.go | 2 +- authority/tls.go | 2 +- go.mod | 5 +- go.sum | 12 + x509util/algorithms.go | 88 --- x509util/algorithms_test.go | 76 --- x509util/certificate.go | 139 ---- x509util/certificate_request.go | 64 -- x509util/certificate_request_test.go | 212 ------ x509util/certificate_test.go | 432 ------------- x509util/extensions.go | 473 -------------- x509util/extensions_test.go | 892 -------------------------- x509util/marshal_utils.go | 212 ------ x509util/marshal_utils_test.go | 300 --------- x509util/name.go | 127 ---- x509util/name_test.go | 384 ----------- x509util/options.go | 91 --- x509util/options_test.go | 237 ------- x509util/templates.go | 156 ----- x509util/templates_test.go | 260 -------- x509util/testdata/ed25519.crt | 10 - x509util/testdata/example.tpl | 21 - x509util/testdata/fullsimple.tpl | 36 -- x509util/testdata/google.crt | 28 - x509util/testdata/smallstep.crt | 31 - x509util/utils.go | 76 --- x509util/utils_test.go | 60 -- 37 files changed, 29 insertions(+), 4421 deletions(-) delete mode 100644 x509util/algorithms.go delete mode 100644 x509util/algorithms_test.go delete mode 100644 x509util/certificate.go delete mode 100644 x509util/certificate_request.go delete mode 100644 x509util/certificate_request_test.go delete mode 100644 x509util/certificate_test.go delete mode 100644 x509util/extensions.go delete mode 100644 x509util/extensions_test.go delete mode 100644 x509util/marshal_utils.go delete mode 100644 x509util/marshal_utils_test.go delete mode 100644 x509util/name.go delete mode 100644 x509util/name_test.go delete mode 100644 x509util/options.go delete mode 100644 x509util/options_test.go delete mode 100644 x509util/templates.go delete mode 100644 x509util/templates_test.go delete mode 100644 x509util/testdata/ed25519.crt delete mode 100644 x509util/testdata/example.tpl delete mode 100644 x509util/testdata/fullsimple.tpl delete mode 100644 x509util/testdata/google.crt delete mode 100644 x509util/testdata/smallstep.crt delete mode 100644 x509util/utils.go delete mode 100644 x509util/utils_test.go diff --git a/acme/order.go b/acme/order.go index 29bbfe39..57168419 100644 --- a/acme/order.go +++ b/acme/order.go @@ -10,8 +10,8 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/authority/provisioner" - "github.com/smallstep/certificates/x509util" "github.com/smallstep/nosql" + "go.step.sm/crypto/x509util" ) var defaultOrderExpiry = time.Hour * 24 diff --git a/authority/provisioner/aws.go b/authority/provisioner/aws.go index 9f3764c0..33e583c7 100644 --- a/authority/provisioner/aws.go +++ b/authority/provisioner/aws.go @@ -17,8 +17,8 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/x509util" "github.com/smallstep/cli/jose" + "go.step.sm/crypto/x509util" ) // awsIssuer is the string used as issuer in the generated tokens. diff --git a/authority/provisioner/azure.go b/authority/provisioner/azure.go index a677f9a9..ba281133 100644 --- a/authority/provisioner/azure.go +++ b/authority/provisioner/azure.go @@ -14,8 +14,8 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/x509util" "github.com/smallstep/cli/jose" + "go.step.sm/crypto/x509util" ) // azureOIDCBaseURL is the base discovery url for Microsoft Azure tokens. diff --git a/authority/provisioner/gcp.go b/authority/provisioner/gcp.go index 69a42ec2..5ad00a96 100644 --- a/authority/provisioner/gcp.go +++ b/authority/provisioner/gcp.go @@ -15,8 +15,8 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/x509util" "github.com/smallstep/cli/jose" + "go.step.sm/crypto/x509util" ) // gcpCertsURL is the url that serves Google OAuth2 public keys. diff --git a/authority/provisioner/jwk.go b/authority/provisioner/jwk.go index 2133727b..2716a8d9 100644 --- a/authority/provisioner/jwk.go +++ b/authority/provisioner/jwk.go @@ -8,8 +8,8 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/x509util" "github.com/smallstep/cli/jose" + "go.step.sm/crypto/x509util" ) // jwtPayload extends jwt.Claims with step attributes. diff --git a/authority/provisioner/k8sSA.go b/authority/provisioner/k8sSA.go index 3c37cae7..b830e098 100644 --- a/authority/provisioner/k8sSA.go +++ b/authority/provisioner/k8sSA.go @@ -3,6 +3,7 @@ package provisioner import ( "context" "crypto/ecdsa" + "crypto/ed25519" "crypto/rsa" "crypto/x509" "encoding/pem" @@ -10,10 +11,9 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/x509util" "github.com/smallstep/cli/crypto/pemutil" "github.com/smallstep/cli/jose" - "golang.org/x/crypto/ed25519" + "go.step.sm/crypto/x509util" ) // NOTE: There can be at most one kubernetes service account provisioner configured diff --git a/authority/provisioner/oidc.go b/authority/provisioner/oidc.go index 8cbe849d..295dbb59 100644 --- a/authority/provisioner/oidc.go +++ b/authority/provisioner/oidc.go @@ -13,8 +13,8 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/x509util" "github.com/smallstep/cli/jose" + "go.step.sm/crypto/x509util" ) // openIDConfiguration contains the necessary properties in the diff --git a/authority/provisioner/options.go b/authority/provisioner/options.go index c56b6064..88e22523 100644 --- a/authority/provisioner/options.go +++ b/authority/provisioner/options.go @@ -5,8 +5,8 @@ import ( "strings" "github.com/pkg/errors" - "github.com/smallstep/certificates/x509util" "github.com/smallstep/cli/jose" + "go.step.sm/crypto/x509util" ) // CertificateOptions is an interface that returns a list of options passed when diff --git a/authority/provisioner/options_test.go b/authority/provisioner/options_test.go index 54b07bf3..519f1a58 100644 --- a/authority/provisioner/options_test.go +++ b/authority/provisioner/options_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/smallstep/certificates/x509util" "github.com/smallstep/cli/crypto/pemutil" + "go.step.sm/crypto/x509util" ) func parseCertificateRequest(t *testing.T, filename string) *x509.CertificateRequest { diff --git a/authority/provisioner/sign_options.go b/authority/provisioner/sign_options.go index d3ca4683..29d8320e 100644 --- a/authority/provisioner/sign_options.go +++ b/authority/provisioner/sign_options.go @@ -2,6 +2,7 @@ package provisioner import ( "crypto/ecdsa" + "crypto/ed25519" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" @@ -13,8 +14,7 @@ import ( "time" "github.com/pkg/errors" - "github.com/smallstep/certificates/x509util" - "golang.org/x/crypto/ed25519" + "go.step.sm/crypto/x509util" ) // DefaultCertValidity is the default validity for a certificate if none is specified. diff --git a/authority/provisioner/x5c.go b/authority/provisioner/x5c.go index ff01b151..02abf684 100644 --- a/authority/provisioner/x5c.go +++ b/authority/provisioner/x5c.go @@ -9,8 +9,8 @@ import ( "github.com/pkg/errors" "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/x509util" "github.com/smallstep/cli/jose" + "go.step.sm/crypto/x509util" ) // x5cPayload extends jwt.Claims with step attributes. diff --git a/authority/tls.go b/authority/tls.go index 6b98f9d9..76edf360 100644 --- a/authority/tls.go +++ b/authority/tls.go @@ -16,11 +16,11 @@ import ( "github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/db" "github.com/smallstep/certificates/errs" - "github.com/smallstep/certificates/x509util" "github.com/smallstep/cli/crypto/pemutil" "github.com/smallstep/cli/crypto/tlsutil" x509legacy "github.com/smallstep/cli/crypto/x509util" "github.com/smallstep/cli/jose" + "go.step.sm/crypto/x509util" ) // GetTLSOptions returns the tls options configured. diff --git a/go.mod b/go.mod index fa518bb5..d30e78d0 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.13 require ( cloud.google.com/go v0.51.0 - github.com/Masterminds/sprig/v3 v3.0.0 + github.com/Masterminds/sprig/v3 v3.1.0 github.com/aws/aws-sdk-go v1.30.29 github.com/go-chi/chi v4.0.2+incompatible github.com/go-piv/piv-go v1.5.0 @@ -17,7 +17,8 @@ require ( github.com/smallstep/cli v0.14.7-rc.1.0.20200721180458-731b7c4c8c95 github.com/smallstep/nosql v0.3.0 github.com/urfave/cli v1.22.2 - golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 + go.step.sm/crypto v0.0.0-20200805202904-ec18b6df3cf0 + golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 golang.org/x/net v0.0.0-20200202094626-16171245cfb2 google.golang.org/api v0.15.0 google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb diff --git a/go.sum b/go.sum index fa7accea..d2907d00 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,12 @@ github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITg github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.0.1 h1:2kKm5lb7dKVrt5TYUiAavE6oFc1cFT0057UVGT+JqLk= github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk= +github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/sprig/v3 v3.0.0 h1:KSQz7Nb08/3VU9E4ns29dDxcczhOD1q7O1UfM4G3t3g= github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U= +github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TNDYD9Y= +github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA= github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= @@ -274,10 +278,14 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= +github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo= github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI= github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -569,6 +577,8 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.step.sm/crypto v0.0.0-20200805202904-ec18b6df3cf0 h1:FymMl8TrXGxFf80BWpO0CnkSfLnw0BkDdRrhbMGf5zE= +go.step.sm/crypto v0.0.0-20200805202904-ec18b6df3cf0/go.mod h1:8VYxmvSKt5yOTBx3MGsD2Gk4F1Es/3FIxrjnfeYWE8U= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM= @@ -597,6 +607,8 @@ golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90 golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= +golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= diff --git a/x509util/algorithms.go b/x509util/algorithms.go deleted file mode 100644 index 93e60246..00000000 --- a/x509util/algorithms.go +++ /dev/null @@ -1,88 +0,0 @@ -package x509util - -import ( - "crypto/x509" - "strings" - - "github.com/pkg/errors" -) - -// List of signature algorithms, all of them have values in upper case to match -// them with the string representation. -// nolint:golint -const ( - MD2_RSA = "MD2-RSA" - MD5_RSA = "MD5-RSA" - SHA1_RSA = "SHA1-RSA" - SHA256_RSA = "SHA256-RSA" - SHA384_RSA = "SHA384-RSA" - SHA512_RSA = "SHA512-RSA" - SHA256_RSAPSS = "SHA256-RSAPSS" - SHA384_RSAPSS = "SHA384-RSAPSS" - SHA512_RSAPSS = "SHA512-RSAPSS" - DSA_SHA1 = "DSA-SHA1" - DSA_SHA256 = "DSA-SHA256" - ECDSA_SHA1 = "ECDSA-SHA1" - ECDSA_SHA256 = "ECDSA-SHA256" - ECDSA_SHA384 = "ECDSA-SHA384" - ECDSA_SHA512 = "ECDSA-SHA512" - Ed25519 = "ED25519" -) - -// SignatureAlgorithm is the JSON representation of the X509 signature algorithms -type SignatureAlgorithm x509.SignatureAlgorithm - -// Set sets the signature algorithm in the given certificate. -func (s SignatureAlgorithm) Set(c *x509.Certificate) { - c.SignatureAlgorithm = x509.SignatureAlgorithm(s) -} - -// UnmarshalJSON implements the json.Unmarshal interface and unmarshals and -// validates a string as a SignatureAlgorithm. -func (s *SignatureAlgorithm) UnmarshalJSON(data []byte) error { - name, err := unmarshalString(data) - if err != nil { - return err - } - - var sa x509.SignatureAlgorithm - switch strings.ToUpper(name) { - case MD2_RSA: - sa = x509.MD2WithRSA - case MD5_RSA: - sa = x509.MD5WithRSA - case SHA1_RSA: - sa = x509.SHA1WithRSA - case SHA256_RSA: - sa = x509.SHA256WithRSA - case SHA384_RSA: - sa = x509.SHA384WithRSA - case SHA512_RSA: - sa = x509.SHA512WithRSA - case SHA256_RSAPSS: - sa = x509.SHA256WithRSAPSS - case SHA384_RSAPSS: - sa = x509.SHA384WithRSAPSS - case SHA512_RSAPSS: - sa = x509.SHA512WithRSAPSS - case DSA_SHA1: - sa = x509.DSAWithSHA1 - case DSA_SHA256: - sa = x509.DSAWithSHA256 - case ECDSA_SHA1: - sa = x509.ECDSAWithSHA1 - case ECDSA_SHA256: - sa = x509.ECDSAWithSHA256 - case ECDSA_SHA384: - sa = x509.ECDSAWithSHA384 - case ECDSA_SHA512: - sa = x509.ECDSAWithSHA512 - case Ed25519: - sa = x509.PureEd25519 - default: - return errors.Errorf("unsupported signatureAlgorithm %s", name) - } - - *s = SignatureAlgorithm(sa) - return nil -} diff --git a/x509util/algorithms_test.go b/x509util/algorithms_test.go deleted file mode 100644 index a735a0c4..00000000 --- a/x509util/algorithms_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package x509util - -import ( - "crypto/x509" - "reflect" - "testing" -) - -func TestSignatureAlgorithm_Set(t *testing.T) { - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - s SignatureAlgorithm - args args - want *x509.Certificate - }{ - {"ok", SignatureAlgorithm(x509.ECDSAWithSHA256), args{&x509.Certificate{}}, &x509.Certificate{SignatureAlgorithm: x509.ECDSAWithSHA256}}, - {"ok", SignatureAlgorithm(x509.PureEd25519), args{&x509.Certificate{}}, &x509.Certificate{SignatureAlgorithm: x509.PureEd25519}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.s.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("SignatureAlgorithm.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} - -func TestSignatureAlgorithm_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want SignatureAlgorithm - wantErr bool - }{ - {"MD2_RSA", args{[]byte(`"MD2-RSA"`)}, SignatureAlgorithm(x509.MD2WithRSA), false}, - {"MD5-RSA", args{[]byte(`"MD5-RSA"`)}, SignatureAlgorithm(x509.MD5WithRSA), false}, - {"SHA1-RSA", args{[]byte(`"SHA1-RSA"`)}, SignatureAlgorithm(x509.SHA1WithRSA), false}, - {"SHA256-RSA", args{[]byte(`"SHA256-RSA"`)}, SignatureAlgorithm(x509.SHA256WithRSA), false}, - {"SHA384-RSA", args{[]byte(`"SHA384-RSA"`)}, SignatureAlgorithm(x509.SHA384WithRSA), false}, - {"SHA512-RSA", args{[]byte(`"SHA512-RSA"`)}, SignatureAlgorithm(x509.SHA512WithRSA), false}, - {"SHA256-RSAPSS", args{[]byte(`"SHA256-RSAPSS"`)}, SignatureAlgorithm(x509.SHA256WithRSAPSS), false}, - {"SHA384-RSAPSS", args{[]byte(`"SHA384-RSAPSS"`)}, SignatureAlgorithm(x509.SHA384WithRSAPSS), false}, - {"SHA512-RSAPSS", args{[]byte(`"SHA512-RSAPSS"`)}, SignatureAlgorithm(x509.SHA512WithRSAPSS), false}, - {"DSA-SHA1", args{[]byte(`"DSA-SHA1"`)}, SignatureAlgorithm(x509.DSAWithSHA1), false}, - {"DSA-SHA256", args{[]byte(`"DSA-SHA256"`)}, SignatureAlgorithm(x509.DSAWithSHA256), false}, - {"ECDSA-SHA1", args{[]byte(`"ECDSA-SHA1"`)}, SignatureAlgorithm(x509.ECDSAWithSHA1), false}, - {"ECDSA-SHA256", args{[]byte(`"ECDSA-SHA256"`)}, SignatureAlgorithm(x509.ECDSAWithSHA256), false}, - {"ECDSA-SHA384", args{[]byte(`"ECDSA-SHA384"`)}, SignatureAlgorithm(x509.ECDSAWithSHA384), false}, - {"ECDSA-SHA512", args{[]byte(`"ECDSA-SHA512"`)}, SignatureAlgorithm(x509.ECDSAWithSHA512), false}, - {"Ed25519", args{[]byte(`"Ed25519"`)}, SignatureAlgorithm(x509.PureEd25519), false}, - {"lowercase", args{[]byte(`"ecdsa-sha256"`)}, SignatureAlgorithm(x509.ECDSAWithSHA256), false}, - {"empty", args{[]byte(`""`)}, SignatureAlgorithm(0), true}, - {"unknown", args{[]byte(`"unknown"`)}, SignatureAlgorithm(0), true}, - {"null", args{[]byte(`null`)}, SignatureAlgorithm(0), true}, - {"number", args{[]byte(`0`)}, SignatureAlgorithm(0), true}, - {"object", args{[]byte(`{}`)}, SignatureAlgorithm(0), true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got SignatureAlgorithm - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("SignatureAlgorithm.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("SignatureAlgorithm.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/x509util/certificate.go b/x509util/certificate.go deleted file mode 100644 index 9786a0bd..00000000 --- a/x509util/certificate.go +++ /dev/null @@ -1,139 +0,0 @@ -package x509util - -import ( - "crypto" - "crypto/rand" - "crypto/x509" - "encoding/json" - - "github.com/pkg/errors" -) - -type Certificate struct { - Version int `json:"version"` - Subject Subject `json:"subject"` - Issuer Issuer `json:"issuer"` - SerialNumber SerialNumber `json:"serialNumber"` - DNSNames MultiString `json:"dnsNames"` - EmailAddresses MultiString `json:"emailAddresses"` - IPAddresses MultiIP `json:"ipAddresses"` - URIs MultiURL `json:"uris"` - SANs []SubjectAlternativeName `json:"sans"` - Extensions []Extension `json:"extensions"` - KeyUsage KeyUsage `json:"keyUsage"` - ExtKeyUsage ExtKeyUsage `json:"extKeyUsage"` - SubjectKeyID SubjectKeyID `json:"subjectKeyId"` - AuthorityKeyID AuthorityKeyID `json:"authorityKeyId"` - OCSPServer OCSPServer `json:"ocspServer"` - IssuingCertificateURL IssuingCertificateURL `json:"issuingCertificateURL"` - CRLDistributionPoints CRLDistributionPoints `json:"crlDistributionPoints"` - PolicyIdentifiers PolicyIdentifiers `json:"policyIdentifiers"` - BasicConstraints *BasicConstraints `json:"basicConstraints"` - NameConstraints *NameConstraints `json:"nameConstraints"` - SignatureAlgorithm SignatureAlgorithm `json:"signatureAlgorithm"` - PublicKeyAlgorithm x509.PublicKeyAlgorithm `json:"-"` - PublicKey interface{} `json:"-"` -} - -func NewCertificate(cr *x509.CertificateRequest, opts ...Option) (*Certificate, error) { - if err := cr.CheckSignature(); err != nil { - return nil, errors.Wrap(err, "error validating certificate request") - } - - o, err := new(Options).apply(cr, opts) - if err != nil { - return nil, err - } - - // If no template use only the certificate request with the default leaf key - // usages. - if o.CertBuffer == nil { - return newCertificateRequest(cr).GetLeafCertificate(), nil - } - - // With templates - var cert Certificate - if err := json.NewDecoder(o.CertBuffer).Decode(&cert); err != nil { - return nil, errors.Wrap(err, "error unmarshaling certificate") - } - - // Complete with certificate request - cert.PublicKey = cr.PublicKey - cert.PublicKeyAlgorithm = cr.PublicKeyAlgorithm - - return &cert, nil -} - -func (c *Certificate) GetCertificate() *x509.Certificate { - cert := new(x509.Certificate) - // Unparsed data - cert.PublicKey = c.PublicKey - cert.PublicKeyAlgorithm = c.PublicKeyAlgorithm - - // SANs are directly converted. - cert.DNSNames = c.DNSNames - cert.EmailAddresses = c.EmailAddresses - cert.IPAddresses = c.IPAddresses - cert.URIs = c.URIs - - // SANs slice. - for _, san := range c.SANs { - san.Set(cert) - } - - // Subject. - c.Subject.Set(cert) - - // Defined extensions. - c.KeyUsage.Set(cert) - c.ExtKeyUsage.Set(cert) - c.SubjectKeyID.Set(cert) - c.AuthorityKeyID.Set(cert) - c.OCSPServer.Set(cert) - c.IssuingCertificateURL.Set(cert) - c.CRLDistributionPoints.Set(cert) - c.PolicyIdentifiers.Set(cert) - if c.BasicConstraints != nil { - c.BasicConstraints.Set(cert) - } - if c.NameConstraints != nil { - c.NameConstraints.Set(cert) - } - - // Custom Extensions. - for _, e := range c.Extensions { - e.Set(cert) - } - - // Others. - c.SerialNumber.Set(cert) - c.SignatureAlgorithm.Set(cert) - - return cert -} - -func CreateCertificate(template, parent *x509.Certificate, pub crypto.PublicKey, signer crypto.Signer) (*x509.Certificate, error) { - var err error - // Complete certificate. - if template.SerialNumber == nil { - if template.SerialNumber, err = generateSerialNumber(); err != nil { - return nil, err - } - } - if template.SubjectKeyId == nil { - if template.SubjectKeyId, err = generateSubjectKeyID(pub); err != nil { - return nil, err - } - } - - // Sign certificate - asn1Data, err := x509.CreateCertificate(rand.Reader, template, parent, pub, signer) - if err != nil { - return nil, errors.Wrap(err, "error creating certificate") - } - cert, err := x509.ParseCertificate(asn1Data) - if err != nil { - return nil, errors.Wrap(err, "error parsing certificate") - } - return cert, nil -} diff --git a/x509util/certificate_request.go b/x509util/certificate_request.go deleted file mode 100644 index c14338c9..00000000 --- a/x509util/certificate_request.go +++ /dev/null @@ -1,64 +0,0 @@ -package x509util - -import ( - "crypto/rsa" - "crypto/x509" -) - -type CertificateRequest struct { - Version int `json:"version"` - Subject Subject `json:"subject"` - DNSNames MultiString `json:"dnsNames"` - EmailAddresses MultiString `json:"emailAddresses"` - IPAddresses MultiIP `json:"ipAddresses"` - URIs MultiURL `json:"uris"` - Extensions []Extension `json:"extensions"` - PublicKey interface{} `json:"-"` - PublicKeyAlgorithm x509.PublicKeyAlgorithm `json:"-"` - Signature []byte `json:"-"` - SignatureAlgorithm x509.SignatureAlgorithm `json:"-"` -} - -func newCertificateRequest(cr *x509.CertificateRequest) *CertificateRequest { - return &CertificateRequest{ - Version: cr.Version, - Subject: newSubject(cr.Subject), - DNSNames: cr.DNSNames, - EmailAddresses: cr.EmailAddresses, - IPAddresses: cr.IPAddresses, - URIs: cr.URIs, - Extensions: newExtensions(cr.Extensions), - PublicKey: cr.PublicKey, - PublicKeyAlgorithm: cr.PublicKeyAlgorithm, - Signature: cr.Signature, - SignatureAlgorithm: cr.SignatureAlgorithm, - } -} - -func (c *CertificateRequest) GetCertificate() *Certificate { - return &Certificate{ - Subject: c.Subject, - DNSNames: c.DNSNames, - EmailAddresses: c.EmailAddresses, - IPAddresses: c.IPAddresses, - URIs: c.URIs, - Extensions: c.Extensions, - PublicKey: c.PublicKey, - PublicKeyAlgorithm: c.PublicKeyAlgorithm, - } -} - -func (c *CertificateRequest) GetLeafCertificate() *Certificate { - keyUsage := x509.KeyUsageDigitalSignature - if _, ok := c.PublicKey.(*rsa.PublicKey); ok { - keyUsage |= x509.KeyUsageKeyEncipherment - } - - cert := c.GetCertificate() - cert.KeyUsage = KeyUsage(keyUsage) - cert.ExtKeyUsage = ExtKeyUsage([]x509.ExtKeyUsage{ - x509.ExtKeyUsageServerAuth, - x509.ExtKeyUsageClientAuth, - }) - return cert -} diff --git a/x509util/certificate_request_test.go b/x509util/certificate_request_test.go deleted file mode 100644 index f09faabb..00000000 --- a/x509util/certificate_request_test.go +++ /dev/null @@ -1,212 +0,0 @@ -package x509util - -import ( - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "net" - "net/url" - "reflect" - "testing" -) - -func Test_newCertificateRequest(t *testing.T) { - - type args struct { - cr *x509.CertificateRequest - } - tests := []struct { - name string - args args - want *CertificateRequest - }{ - {"ok", args{&x509.CertificateRequest{}}, &CertificateRequest{}}, - {"complex", args{&x509.CertificateRequest{ - Extensions: []pkix.Extension{{Id: []int{1, 2, 3}, Critical: true, Value: []byte{3, 2, 1}}}, - Subject: pkix.Name{Province: []string{"CA"}, CommonName: "commonName"}, - DNSNames: []string{"foo"}, - PublicKey: []byte("publicKey"), - }}, &CertificateRequest{ - Extensions: []Extension{{ID: []int{1, 2, 3}, Critical: true, Value: []byte{3, 2, 1}}}, - Subject: Subject{Province: []string{"CA"}, CommonName: "commonName"}, - DNSNames: []string{"foo"}, - PublicKey: []byte("publicKey"), - }}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := newCertificateRequest(tt.args.cr); !reflect.DeepEqual(got, tt.want) { - t.Errorf("newCertificateRequest() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestCertificateRequest_GetCertificate(t *testing.T) { - type fields struct { - Version int - Subject Subject - DNSNames MultiString - EmailAddresses MultiString - IPAddresses MultiIP - URIs MultiURL - Extensions []Extension - PublicKey interface{} - PublicKeyAlgorithm x509.PublicKeyAlgorithm - Signature []byte - SignatureAlgorithm x509.SignatureAlgorithm - } - tests := []struct { - name string - fields fields - want *Certificate - }{ - {"ok", - fields{ - Version: 2, - Subject: Subject{CommonName: "foo"}, - DNSNames: []string{"foo"}, - EmailAddresses: []string{"foo@bar.com"}, - IPAddresses: []net.IP{net.ParseIP("::1")}, - URIs: []*url.URL{{Scheme: "https", Host: "foo.bar"}}, - Extensions: []Extension{{ID: []int{1, 2, 3}, Critical: true, Value: []byte{3, 2, 1}}}, - PublicKey: []byte("publicKey"), - PublicKeyAlgorithm: x509.Ed25519, - Signature: []byte("signature"), - SignatureAlgorithm: x509.PureEd25519, - }, - &Certificate{ - Subject: Subject{CommonName: "foo"}, - DNSNames: []string{"foo"}, - EmailAddresses: []string{"foo@bar.com"}, - IPAddresses: []net.IP{net.ParseIP("::1")}, - URIs: []*url.URL{{Scheme: "https", Host: "foo.bar"}}, - Extensions: []Extension{{ID: []int{1, 2, 3}, Critical: true, Value: []byte{3, 2, 1}}}, - PublicKey: []byte("publicKey"), - PublicKeyAlgorithm: x509.Ed25519, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &CertificateRequest{ - Version: tt.fields.Version, - Subject: tt.fields.Subject, - DNSNames: tt.fields.DNSNames, - EmailAddresses: tt.fields.EmailAddresses, - IPAddresses: tt.fields.IPAddresses, - URIs: tt.fields.URIs, - Extensions: tt.fields.Extensions, - PublicKey: tt.fields.PublicKey, - PublicKeyAlgorithm: tt.fields.PublicKeyAlgorithm, - Signature: tt.fields.Signature, - SignatureAlgorithm: tt.fields.SignatureAlgorithm, - } - if got := c.GetCertificate(); !reflect.DeepEqual(got, tt.want) { - t.Errorf("CertificateRequest.GetCertificate() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestCertificateRequest_GetLeafCertificate(t *testing.T) { - type fields struct { - Version int - Subject Subject - DNSNames MultiString - EmailAddresses MultiString - IPAddresses MultiIP - URIs MultiURL - Extensions []Extension - PublicKey interface{} - PublicKeyAlgorithm x509.PublicKeyAlgorithm - Signature []byte - SignatureAlgorithm x509.SignatureAlgorithm - } - tests := []struct { - name string - fields fields - want *Certificate - }{ - {"ok", - fields{ - Version: 2, - Subject: Subject{CommonName: "foo"}, - DNSNames: []string{"foo"}, - EmailAddresses: []string{"foo@bar.com"}, - IPAddresses: []net.IP{net.ParseIP("::1")}, - URIs: []*url.URL{{Scheme: "https", Host: "foo.bar"}}, - Extensions: []Extension{{ID: []int{1, 2, 3}, Critical: true, Value: []byte{3, 2, 1}}}, - PublicKey: []byte("publicKey"), - PublicKeyAlgorithm: x509.Ed25519, - Signature: []byte("signature"), - SignatureAlgorithm: x509.PureEd25519, - }, - &Certificate{ - Subject: Subject{CommonName: "foo"}, - DNSNames: []string{"foo"}, - EmailAddresses: []string{"foo@bar.com"}, - IPAddresses: []net.IP{net.ParseIP("::1")}, - URIs: []*url.URL{{Scheme: "https", Host: "foo.bar"}}, - Extensions: []Extension{{ID: []int{1, 2, 3}, Critical: true, Value: []byte{3, 2, 1}}}, - KeyUsage: KeyUsage(x509.KeyUsageDigitalSignature), - ExtKeyUsage: ExtKeyUsage([]x509.ExtKeyUsage{ - x509.ExtKeyUsageServerAuth, - x509.ExtKeyUsageClientAuth, - }), - PublicKey: []byte("publicKey"), - PublicKeyAlgorithm: x509.Ed25519, - }, - }, - {"rsa", - fields{ - Version: 2, - Subject: Subject{CommonName: "foo"}, - DNSNames: []string{"foo"}, - EmailAddresses: []string{"foo@bar.com"}, - IPAddresses: []net.IP{net.ParseIP("::1")}, - URIs: []*url.URL{{Scheme: "https", Host: "foo.bar"}}, - Extensions: []Extension{{ID: []int{1, 2, 3}, Critical: true, Value: []byte{3, 2, 1}}}, - PublicKey: &rsa.PublicKey{}, - PublicKeyAlgorithm: x509.RSA, - Signature: []byte("signature"), - SignatureAlgorithm: x509.SHA256WithRSA, - }, - &Certificate{ - Subject: Subject{CommonName: "foo"}, - DNSNames: []string{"foo"}, - EmailAddresses: []string{"foo@bar.com"}, - IPAddresses: []net.IP{net.ParseIP("::1")}, - URIs: []*url.URL{{Scheme: "https", Host: "foo.bar"}}, - Extensions: []Extension{{ID: []int{1, 2, 3}, Critical: true, Value: []byte{3, 2, 1}}}, - KeyUsage: KeyUsage(x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment), - ExtKeyUsage: ExtKeyUsage([]x509.ExtKeyUsage{ - x509.ExtKeyUsageServerAuth, - x509.ExtKeyUsageClientAuth, - }), - PublicKey: &rsa.PublicKey{}, - PublicKeyAlgorithm: x509.RSA, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &CertificateRequest{ - Version: tt.fields.Version, - Subject: tt.fields.Subject, - DNSNames: tt.fields.DNSNames, - EmailAddresses: tt.fields.EmailAddresses, - IPAddresses: tt.fields.IPAddresses, - URIs: tt.fields.URIs, - Extensions: tt.fields.Extensions, - PublicKey: tt.fields.PublicKey, - PublicKeyAlgorithm: tt.fields.PublicKeyAlgorithm, - Signature: tt.fields.Signature, - SignatureAlgorithm: tt.fields.SignatureAlgorithm, - } - if got := c.GetLeafCertificate(); !reflect.DeepEqual(got, tt.want) { - t.Errorf("CertificateRequest.GetLeafCertificate() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/x509util/certificate_test.go b/x509util/certificate_test.go deleted file mode 100644 index 695d6977..00000000 --- a/x509util/certificate_test.go +++ /dev/null @@ -1,432 +0,0 @@ -package x509util - -import ( - "crypto" - "crypto/ed25519" - "crypto/rand" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "fmt" - "io" - "math/big" - "net" - "net/url" - "reflect" - "testing" - "time" -) - -func createCertificateRequest(t *testing.T, commonName string, sans []string) (*x509.CertificateRequest, crypto.Signer) { - dnsNames, ips, emails, uris := SplitSANs(sans) - t.Helper() - _, priv, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - t.Fatal(err) - } - asn1Data, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{ - Subject: pkix.Name{CommonName: commonName}, - DNSNames: dnsNames, - IPAddresses: ips, - EmailAddresses: emails, - URIs: uris, - SignatureAlgorithm: x509.PureEd25519, - }, priv) - if err != nil { - t.Fatal(err) - } - cr, err := x509.ParseCertificateRequest(asn1Data) - if err != nil { - t.Fatal(err) - } - return cr, priv -} - -func createIssuerCertificate(t *testing.T, commonName string) (*x509.Certificate, crypto.Signer) { - t.Helper() - now := time.Now() - pub, priv, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - t.Fatal(err) - } - subjectKeyID, err := generateSubjectKeyID(pub) - if err != nil { - t.Fatal(err) - } - sn, err := generateSerialNumber() - if err != nil { - t.Fatal(err) - } - - template := &x509.Certificate{ - IsCA: true, - NotBefore: now, - NotAfter: now.Add(time.Hour), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - BasicConstraintsValid: true, - MaxPathLen: 0, - MaxPathLenZero: true, - Issuer: pkix.Name{CommonName: "issuer"}, - Subject: pkix.Name{CommonName: "issuer"}, - SerialNumber: sn, - SubjectKeyId: subjectKeyID, - } - asn1Data, err := x509.CreateCertificate(rand.Reader, template, template, pub, priv) - if err != nil { - t.Fatal(err) - } - crt, err := x509.ParseCertificate(asn1Data) - if err != nil { - t.Fatal(err) - } - return crt, priv -} - -type badSigner struct { - pub crypto.PublicKey -} - -func createBadSigner(t *testing.T) *badSigner { - pub, _, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - t.Fatal(err) - } - return &badSigner{ - pub: pub, - } -} - -func (b *badSigner) Public() crypto.PublicKey { - return b.pub -} - -func (b *badSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { - return nil, fmt.Errorf("💥") -} - -func TestNewCertificate(t *testing.T) { - cr, priv := createCertificateRequest(t, "commonName", []string{"foo.com", "root@foo.com"}) - crBadSignateure, _ := createCertificateRequest(t, "fail", []string{"foo.com"}) - crBadSignateure.PublicKey = priv.Public() - - ipNet := func(s string) *net.IPNet { - _, ipNet, err := net.ParseCIDR(s) - if err != nil { - t.Fatal(err) - } - return ipNet - } - - type args struct { - cr *x509.CertificateRequest - opts []Option - } - tests := []struct { - name string - args args - want *Certificate - wantErr bool - }{ - {"okSimple", args{cr, nil}, &Certificate{ - Subject: Subject{CommonName: "commonName"}, - DNSNames: []string{"foo.com"}, - EmailAddresses: []string{"root@foo.com"}, - KeyUsage: KeyUsage(x509.KeyUsageDigitalSignature), - ExtKeyUsage: ExtKeyUsage([]x509.ExtKeyUsage{ - x509.ExtKeyUsageServerAuth, - x509.ExtKeyUsageClientAuth, - }), - Extensions: newExtensions(cr.Extensions), - PublicKey: priv.Public(), - PublicKeyAlgorithm: x509.Ed25519, - }, false}, - {"okDefaultTemplate", args{cr, []Option{WithTemplate(DefaultLeafTemplate, CreateTemplateData("commonName", []string{"foo.com"}))}}, &Certificate{ - Subject: Subject{CommonName: "commonName"}, - SANs: []SubjectAlternativeName{{Type: DNSType, Value: "foo.com"}}, - KeyUsage: KeyUsage(x509.KeyUsageDigitalSignature), - ExtKeyUsage: ExtKeyUsage([]x509.ExtKeyUsage{ - x509.ExtKeyUsageServerAuth, - x509.ExtKeyUsageClientAuth, - }), - PublicKey: priv.Public(), - PublicKeyAlgorithm: x509.Ed25519, - }, false}, - {"okExample", args{cr, []Option{WithTemplateFile("./testdata/example.tpl", TemplateData{ - SANsKey: []SubjectAlternativeName{ - {Type: "dns", Value: "foo.com"}, - }, - TokenKey: map[string]interface{}{ - "iss": "https://iss", - "sub": "sub", - }, - })}}, &Certificate{ - Subject: Subject{CommonName: "commonName"}, - SANs: []SubjectAlternativeName{{Type: DNSType, Value: "foo.com"}}, - EmailAddresses: []string{"root@foo.com"}, - URIs: []*url.URL{{Scheme: "https", Host: "iss", Fragment: "sub"}}, - KeyUsage: KeyUsage(x509.KeyUsageDigitalSignature), - ExtKeyUsage: ExtKeyUsage([]x509.ExtKeyUsage{ - x509.ExtKeyUsageServerAuth, - x509.ExtKeyUsageClientAuth, - }), - PublicKey: priv.Public(), - PublicKeyAlgorithm: x509.Ed25519, - }, false}, - {"okFullSimple", args{cr, []Option{WithTemplateFile("./testdata/fullsimple.tpl", TemplateData{})}}, &Certificate{ - Version: 3, - Subject: Subject{CommonName: "subjectCommonName"}, - SerialNumber: SerialNumber{big.NewInt(78187493520)}, - Issuer: Issuer{CommonName: "issuerCommonName"}, - DNSNames: []string{"doe.com"}, - IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}, - EmailAddresses: []string{"jane@doe.com"}, - URIs: []*url.URL{{Scheme: "https", Host: "doe.com"}}, - SANs: []SubjectAlternativeName{{Type: DNSType, Value: "www.doe.com"}}, - Extensions: []Extension{{ID: []int{1, 2, 3, 4}, Critical: true, Value: []byte("extension")}}, - KeyUsage: KeyUsage(x509.KeyUsageDigitalSignature), - ExtKeyUsage: ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}), - SubjectKeyID: []byte("subjectKeyId"), - AuthorityKeyID: []byte("authorityKeyId"), - OCSPServer: []string{"https://ocsp.server"}, - IssuingCertificateURL: []string{"https://ca.com"}, - CRLDistributionPoints: []string{"https://ca.com/ca.crl"}, - PolicyIdentifiers: PolicyIdentifiers{[]int{5, 6, 7, 8, 9, 0}}, - BasicConstraints: &BasicConstraints{ - IsCA: false, - MaxPathLen: 0, - }, - NameConstraints: &NameConstraints{ - Critical: true, - PermittedDNSDomains: []string{"jane.doe.com"}, - ExcludedDNSDomains: []string{"john.doe.com"}, - PermittedIPRanges: []*net.IPNet{ipNet("127.0.0.1/32")}, - ExcludedIPRanges: []*net.IPNet{ipNet("0.0.0.0/0")}, - PermittedEmailAddresses: []string{"jane@doe.com"}, - ExcludedEmailAddresses: []string{"john@doe.com"}, - PermittedURIDomains: []string{"https://jane.doe.com"}, - ExcludedURIDomains: []string{"https://john.doe.com"}, - }, - SignatureAlgorithm: SignatureAlgorithm(x509.PureEd25519), - PublicKey: priv.Public(), - PublicKeyAlgorithm: x509.Ed25519, - }, - false}, - {"badSignature", args{crBadSignateure, nil}, nil, true}, - {"failTemplate", args{cr, []Option{WithTemplate(`{{ fail "fatal error }}`, CreateTemplateData("commonName", []string{"foo.com"}))}}, nil, true}, - {"missingTemplate", args{cr, []Option{WithTemplateFile("./testdata/missing.tpl", CreateTemplateData("commonName", []string{"foo.com"}))}}, nil, true}, - {"badJson", args{cr, []Option{WithTemplate(`"this is not a json object"`, CreateTemplateData("commonName", []string{"foo.com"}))}}, nil, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := NewCertificate(tt.args.cr, tt.args.opts...) - if (err != nil) != tt.wantErr { - t.Errorf("NewCertificate() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewCertificate() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestCertificate_GetCertificate(t *testing.T) { - type fields struct { - Version int - Subject Subject - Issuer Issuer - SerialNumber SerialNumber - DNSNames MultiString - EmailAddresses MultiString - IPAddresses MultiIP - URIs MultiURL - SANs []SubjectAlternativeName - Extensions []Extension - KeyUsage KeyUsage - ExtKeyUsage ExtKeyUsage - SubjectKeyID SubjectKeyID - AuthorityKeyID AuthorityKeyID - OCSPServer OCSPServer - IssuingCertificateURL IssuingCertificateURL - CRLDistributionPoints CRLDistributionPoints - PolicyIdentifiers PolicyIdentifiers - BasicConstraints *BasicConstraints - NameConstraints *NameConstraints - SignatureAlgorithm SignatureAlgorithm - PublicKeyAlgorithm x509.PublicKeyAlgorithm - PublicKey interface{} - } - tests := []struct { - name string - fields fields - want *x509.Certificate - }{ - {"ok", fields{ - Version: 3, - Subject: Subject{CommonName: "commonName", Organization: []string{"smallstep"}}, - Issuer: Issuer{CommonName: "issuer", Organization: []string{"smallstep"}}, - SerialNumber: SerialNumber{big.NewInt(123)}, - DNSNames: []string{"foo.bar"}, - EmailAddresses: []string{"root@foo.com"}, - IPAddresses: []net.IP{net.ParseIP("::1")}, - URIs: []*url.URL{{Scheme: "mailto", Opaque: "root@foo.com"}}, - SANs: []SubjectAlternativeName{ - {Type: DNSType, Value: "www.foo.bar"}, - {Type: IPType, Value: "127.0.0.1"}, - {Type: EmailType, Value: "admin@foo.com"}, - {Type: URIType, Value: "mailto:admin@foo.com"}, - }, - Extensions: []Extension{{ID: []int{1, 2, 3, 4}, Critical: true, Value: []byte("custom extension")}}, - KeyUsage: KeyUsage(x509.KeyUsageDigitalSignature), - ExtKeyUsage: ExtKeyUsage([]x509.ExtKeyUsage{ - x509.ExtKeyUsageServerAuth, - x509.ExtKeyUsageClientAuth, - }), - SubjectKeyID: []byte("subject-key-id"), - AuthorityKeyID: []byte("authority-key-id"), - OCSPServer: []string{"https://oscp.server"}, - IssuingCertificateURL: []string{"https://ca.com"}, - CRLDistributionPoints: []string{"https://ca.com/crl"}, - PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3, 4}}, - BasicConstraints: &BasicConstraints{IsCA: true, MaxPathLen: 0}, - NameConstraints: &NameConstraints{PermittedDNSDomains: []string{"foo.bar"}}, - SignatureAlgorithm: SignatureAlgorithm(x509.PureEd25519), - PublicKeyAlgorithm: x509.Ed25519, - PublicKey: ed25519.PublicKey("public key"), - }, &x509.Certificate{ - Version: 0, - Subject: pkix.Name{CommonName: "commonName", Organization: []string{"smallstep"}}, - Issuer: pkix.Name{}, - SerialNumber: big.NewInt(123), - DNSNames: []string{"foo.bar", "www.foo.bar"}, - EmailAddresses: []string{"root@foo.com", "admin@foo.com"}, - IPAddresses: []net.IP{net.ParseIP("::1"), net.ParseIP("127.0.0.1")}, - URIs: []*url.URL{{Scheme: "mailto", Opaque: "root@foo.com"}, {Scheme: "mailto", Opaque: "admin@foo.com"}}, - ExtraExtensions: []pkix.Extension{{Id: []int{1, 2, 3, 4}, Critical: true, Value: []byte("custom extension")}}, - KeyUsage: x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{ - x509.ExtKeyUsageServerAuth, - x509.ExtKeyUsageClientAuth, - }, - SubjectKeyId: []byte("subject-key-id"), - AuthorityKeyId: []byte("authority-key-id"), - OCSPServer: []string{"https://oscp.server"}, - IssuingCertificateURL: []string{"https://ca.com"}, - CRLDistributionPoints: []string{"https://ca.com/crl"}, - PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3, 4}}, - IsCA: true, - MaxPathLen: 0, - MaxPathLenZero: true, - BasicConstraintsValid: true, - PermittedDNSDomains: []string{"foo.bar"}, - SignatureAlgorithm: x509.PureEd25519, - PublicKeyAlgorithm: x509.Ed25519, - PublicKey: ed25519.PublicKey("public key"), - }}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := &Certificate{ - Version: tt.fields.Version, - Subject: tt.fields.Subject, - Issuer: tt.fields.Issuer, - SerialNumber: tt.fields.SerialNumber, - DNSNames: tt.fields.DNSNames, - EmailAddresses: tt.fields.EmailAddresses, - IPAddresses: tt.fields.IPAddresses, - URIs: tt.fields.URIs, - SANs: tt.fields.SANs, - Extensions: tt.fields.Extensions, - KeyUsage: tt.fields.KeyUsage, - ExtKeyUsage: tt.fields.ExtKeyUsage, - SubjectKeyID: tt.fields.SubjectKeyID, - AuthorityKeyID: tt.fields.AuthorityKeyID, - OCSPServer: tt.fields.OCSPServer, - IssuingCertificateURL: tt.fields.IssuingCertificateURL, - CRLDistributionPoints: tt.fields.CRLDistributionPoints, - PolicyIdentifiers: tt.fields.PolicyIdentifiers, - BasicConstraints: tt.fields.BasicConstraints, - NameConstraints: tt.fields.NameConstraints, - SignatureAlgorithm: tt.fields.SignatureAlgorithm, - PublicKeyAlgorithm: tt.fields.PublicKeyAlgorithm, - PublicKey: tt.fields.PublicKey, - } - if got := c.GetCertificate(); !reflect.DeepEqual(got, tt.want) { - t.Errorf("Certificate.GetCertificate() = \n%+v, want \n%+v", got, tt.want) - } - }) - } -} - -func TestCreateCertificate(t *testing.T) { - iss, issPriv := createIssuerCertificate(t, "issuer") - - mustSerialNumber := func() *big.Int { - sn, err := generateSerialNumber() - if err != nil { - t.Fatal(err) - } - return sn - } - mustSubjectKeyID := func(pub crypto.PublicKey) []byte { - b, err := generateSubjectKeyID(pub) - if err != nil { - t.Fatal(err) - } - return b - } - - cr1, priv1 := createCertificateRequest(t, "commonName", []string{"foo.com"}) - crt1 := newCertificateRequest(cr1).GetLeafCertificate().GetCertificate() - crt1.SerialNumber = mustSerialNumber() - crt1.SubjectKeyId = mustSubjectKeyID(priv1.Public()) - - cr2, priv2 := createCertificateRequest(t, "commonName", []string{"foo.com"}) - crt2 := newCertificateRequest(cr2).GetLeafCertificate().GetCertificate() - crt2.SerialNumber = mustSerialNumber() - - cr3, priv3 := createCertificateRequest(t, "commonName", []string{"foo.com"}) - crt3 := newCertificateRequest(cr3).GetLeafCertificate().GetCertificate() - crt3.SubjectKeyId = mustSubjectKeyID(priv1.Public()) - - cr4, priv4 := createCertificateRequest(t, "commonName", []string{"foo.com"}) - crt4 := newCertificateRequest(cr4).GetLeafCertificate().GetCertificate() - - cr5, _ := createCertificateRequest(t, "commonName", []string{"foo.com"}) - crt5 := newCertificateRequest(cr5).GetLeafCertificate().GetCertificate() - - badSigner := createBadSigner(t) - - type args struct { - template *x509.Certificate - parent *x509.Certificate - pub crypto.PublicKey - signer crypto.Signer - } - tests := []struct { - name string - args args - wantErr bool - }{ - {"ok", args{crt1, iss, priv1.Public(), issPriv}, false}, - {"okNoSubjectKeyID", args{crt2, iss, priv2.Public(), issPriv}, false}, - {"okNoSerialNumber", args{crt3, iss, priv3.Public(), issPriv}, false}, - {"okNothing", args{crt4, iss, priv4.Public(), issPriv}, false}, - {"failSubjectKeyID", args{crt5, iss, []byte("foo"), issPriv}, true}, - {"failSign", args{crt1, iss, priv1.Public(), badSigner}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := CreateCertificate(tt.args.template, tt.args.parent, tt.args.pub, tt.args.signer) - if (err != nil) != tt.wantErr { - t.Errorf("CreateCertificate() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !tt.wantErr { - if err := got.CheckSignatureFrom(iss); err != nil { - t.Errorf("Certificate.CheckSignatureFrom() error = %v", err) - } - } - }) - } -} diff --git a/x509util/extensions.go b/x509util/extensions.go deleted file mode 100644 index d6f71a73..00000000 --- a/x509util/extensions.go +++ /dev/null @@ -1,473 +0,0 @@ -package x509util - -import ( - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/json" - "fmt" - "math/big" - "net" - "net/url" - "strings" - - "github.com/pkg/errors" -) - -func convertName(s string) string { - return strings.ReplaceAll(strings.ToLower(s), "_", "") -} - -// Names used for key usages. -var ( - KeyUsageDigitalSignature = convertName("DigitalSignature") - KeyUsageContentCommitment = convertName("ContentCommitment") - KeyUsageKeyEncipherment = convertName("KeyEncipherment") - KeyUsageDataEncipherment = convertName("DataEncipherment") - KeyUsageKeyAgreement = convertName("KeyAgreement") - KeyUsageCertSign = convertName("CertSign") - KeyUsageCRLSign = convertName("CRLSign") - KeyUsageEncipherOnly = convertName("EncipherOnly") - KeyUsageDecipherOnly = convertName("DecipherOnly") -) - -// Names used for extended key usages. -var ( - ExtKeyUsageAny = convertName("Any") - ExtKeyUsageServerAuth = convertName("ServerAuth") - ExtKeyUsageClientAuth = convertName("ClientAuth") - ExtKeyUsageCodeSigning = convertName("CodeSigning") - ExtKeyUsageEmailProtection = convertName("EmailProtection") - ExtKeyUsageIPSECEndSystem = convertName("IPSECEndSystem") - ExtKeyUsageIPSECTunnel = convertName("IPSECTunnel") - ExtKeyUsageIPSECUser = convertName("IPSECUser") - ExtKeyUsageTimeStamping = convertName("TimeStamping") - ExtKeyUsageOCSPSigning = convertName("OCSPSigning") - ExtKeyUsageMicrosoftServerGatedCrypto = convertName("MicrosoftServerGatedCrypto") - ExtKeyUsageNetscapeServerGatedCrypto = convertName("NetscapeServerGatedCrypto") - ExtKeyUsageMicrosoftCommercialCodeSigning = convertName("MicrosoftCommercialCodeSigning") - ExtKeyUsageMicrosoftKernelCodeSigning = convertName("MicrosoftKernelCodeSigning") -) - -// Names used and SubjectAlternativeNames types. -const ( - AutoType = "auto" - DNSType = "dns" - EmailType = "email" - IPType = "ip" - URIType = "uri" -) - -// Extension is the JSON representation of a raw X.509 extensions. -type Extension struct { - ID ObjectIdentifier `json:"id"` - Critical bool `json:"critical"` - Value []byte `json:"value"` -} - -// newExtension creates an Extension from a standard pkix.Extension. -func newExtension(e pkix.Extension) Extension { - return Extension{ - ID: ObjectIdentifier(e.Id), - Critical: e.Critical, - Value: e.Value, - } -} - -// newExtensions creates a slice of Extension from a slice of pkix.Exntesion. -func newExtensions(extensions []pkix.Extension) []Extension { - if extensions == nil { - return nil - } - ret := make([]Extension, len(extensions)) - for i, e := range extensions { - ret[i] = newExtension(e) - } - return ret - -} - -// Set adds the extension to the given X509 certificate. -func (e Extension) Set(c *x509.Certificate) { - c.ExtraExtensions = append(c.ExtraExtensions, pkix.Extension{ - Id: asn1.ObjectIdentifier(e.ID), - Critical: e.Critical, - Value: e.Value, - }) -} - -// ObjectIdentifier represents a JSON strings that unmarshals into an ASN1 -// object identifier or OID. -type ObjectIdentifier asn1.ObjectIdentifier - -// MarshalJSON implements the json.Marshaler interface and returns the string -// version of the asn1.ObjectIdentifier. -func (o ObjectIdentifier) MarshalJSON() ([]byte, error) { - return json.Marshal(asn1.ObjectIdentifier(o).String()) -} - -// UnmarshalJSON implements the json.Unmarshaler interface and coverts a strings -// like "2.5.29.17" into an ASN1 object identifier. -func (o *ObjectIdentifier) UnmarshalJSON(data []byte) error { - s, err := unmarshalString(data) - if err != nil { - return err - } - - oid, err := parseObjectIdentifier(s) - if err != nil { - return err - } - *o = ObjectIdentifier(oid) - return nil -} - -type SubjectAlternativeName struct { - Type string `json:"type"` - Value string `json:"value"` -} - -func (s SubjectAlternativeName) Set(c *x509.Certificate) { - switch strings.ToLower(s.Type) { - case DNSType: - c.DNSNames = append(c.DNSNames, s.Value) - case EmailType: - c.EmailAddresses = append(c.EmailAddresses, s.Value) - case IPType: - // The validation of the IP would happen in the unmarshaling, but just - // to be sure we are only adding valid IPs. - if ip := net.ParseIP(s.Value); ip != nil { - c.IPAddresses = append(c.IPAddresses, ip) - } - case URIType: - if u, err := url.Parse(s.Value); err == nil { - c.URIs = append(c.URIs, u) - } - case "", AutoType: - dnsNames, ips, emails, uris := SplitSANs([]string{s.Value}) - c.DNSNames = append(c.DNSNames, dnsNames...) - c.IPAddresses = append(c.IPAddresses, ips...) - c.EmailAddresses = append(c.EmailAddresses, emails...) - c.URIs = append(c.URIs, uris...) - default: - panic(fmt.Sprintf("unsupported subject alternative name type %s", s.Type)) - } -} - -// KeyUsage type represents the JSON array used to represent the key usages of a -// X509 certificate. -type KeyUsage x509.KeyUsage - -// Set sets the key usage to the given certificate. -func (k KeyUsage) Set(c *x509.Certificate) { - c.KeyUsage = x509.KeyUsage(k) -} - -// UnmarshalJSON implements the json.Unmarshaler interface and coverts a string -// or a list of strings into a key usage. -func (k *KeyUsage) UnmarshalJSON(data []byte) error { - ms, err := unmarshalMultiString(data) - if err != nil { - return err - } - - *k = 0 - - for _, s := range ms { - var ku x509.KeyUsage - switch convertName(s) { - case KeyUsageDigitalSignature: - ku = x509.KeyUsageDigitalSignature - case KeyUsageContentCommitment: - ku = x509.KeyUsageContentCommitment - case KeyUsageKeyEncipherment: - ku = x509.KeyUsageKeyEncipherment - case KeyUsageDataEncipherment: - ku = x509.KeyUsageDataEncipherment - case KeyUsageKeyAgreement: - ku = x509.KeyUsageKeyAgreement - case KeyUsageCertSign: - ku = x509.KeyUsageCertSign - case KeyUsageCRLSign: - ku = x509.KeyUsageCRLSign - case KeyUsageEncipherOnly: - ku = x509.KeyUsageEncipherOnly - case KeyUsageDecipherOnly: - ku = x509.KeyUsageDecipherOnly - default: - return errors.Errorf("unsupported keyUsage %s", s) - } - *k |= KeyUsage(ku) - } - - return nil -} - -// ExtKeyUsage represents a JSON array of extended key usages. -type ExtKeyUsage []x509.ExtKeyUsage - -// Set sets the extended key usages in the given certificate. -func (k ExtKeyUsage) Set(c *x509.Certificate) { - c.ExtKeyUsage = []x509.ExtKeyUsage(k) -} - -// UnmarshalJSON implements the json.Unmarshaler interface and coverts a string -// or a list of strings into a list of extended key usages. -func (k *ExtKeyUsage) UnmarshalJSON(data []byte) error { - ms, err := unmarshalMultiString(data) - if err != nil { - return err - } - - eku := make([]x509.ExtKeyUsage, len(ms)) - for i, s := range ms { - var ku x509.ExtKeyUsage - switch convertName(s) { - case ExtKeyUsageAny: - ku = x509.ExtKeyUsageAny - case ExtKeyUsageServerAuth: - ku = x509.ExtKeyUsageServerAuth - case ExtKeyUsageClientAuth: - ku = x509.ExtKeyUsageClientAuth - case ExtKeyUsageCodeSigning: - ku = x509.ExtKeyUsageCodeSigning - case ExtKeyUsageEmailProtection: - ku = x509.ExtKeyUsageEmailProtection - case ExtKeyUsageIPSECEndSystem: - ku = x509.ExtKeyUsageIPSECEndSystem - case ExtKeyUsageIPSECTunnel: - ku = x509.ExtKeyUsageIPSECTunnel - case ExtKeyUsageIPSECUser: - ku = x509.ExtKeyUsageIPSECUser - case ExtKeyUsageTimeStamping: - ku = x509.ExtKeyUsageTimeStamping - case ExtKeyUsageOCSPSigning: - ku = x509.ExtKeyUsageOCSPSigning - case ExtKeyUsageMicrosoftServerGatedCrypto: - ku = x509.ExtKeyUsageMicrosoftServerGatedCrypto - case ExtKeyUsageNetscapeServerGatedCrypto: - ku = x509.ExtKeyUsageNetscapeServerGatedCrypto - case ExtKeyUsageMicrosoftCommercialCodeSigning: - ku = x509.ExtKeyUsageMicrosoftCommercialCodeSigning - case ExtKeyUsageMicrosoftKernelCodeSigning: - ku = x509.ExtKeyUsageMicrosoftKernelCodeSigning - default: - return errors.Errorf("unsupported extKeyUsage %s", s) - } - eku[i] = ku - } - - *k = ExtKeyUsage(eku) - return nil -} - -// SubjectKeyID represents the binary value of the subject key identifier -// extension, this should be the SHA-1 hash of the public key. In JSON this -// value should be a base64-encoded string, and in most cases it should not be -// set because it will be automatically generated. -type SubjectKeyID []byte - -// Set sets the subject key identifier to the given certificate. -func (id SubjectKeyID) Set(c *x509.Certificate) { - c.SubjectKeyId = id -} - -// AuthorityKeyID represents the binary value of the authority key identifier -// extension. It should be the subject key identifier of the parent certificate. -// In JSON this value should be a base64-encoded string, and in most cases it -// should not be set, as it will be automatically provided. -type AuthorityKeyID []byte - -// Set sets the authority key identifier to the given certificate. -func (id AuthorityKeyID) Set(c *x509.Certificate) { - c.AuthorityKeyId = id -} - -// OCSPServer contains the list of OSCP servers that will be encoded in the -// authority information access extension. -type OCSPServer MultiString - -// UnmarshalJSON implements the json.Unmarshaler interface in OCSPServer. -func (o *OCSPServer) UnmarshalJSON(data []byte) error { - ms, err := unmarshalMultiString(data) - if err != nil { - return err - } - *o = ms - return nil -} - -// Set sets the list of OSCP servers to the given certificate. -func (o OCSPServer) Set(c *x509.Certificate) { - c.OCSPServer = o -} - -// IssuingCertificateURL contains the list of the issuing certificate url that -// will be encoded in the authority information access extension. -type IssuingCertificateURL MultiString - -// UnmarshalJSON implements the json.Unmarshaler interface in IssuingCertificateURL. -func (u *IssuingCertificateURL) UnmarshalJSON(data []byte) error { - ms, err := unmarshalMultiString(data) - if err != nil { - return err - } - *u = ms - return nil -} - -// Set sets the list of issuing certificate urls to the given certificate. -func (u IssuingCertificateURL) Set(c *x509.Certificate) { - c.IssuingCertificateURL = u -} - -// CRLDistributionPoints contains the list of CRL distribution points that will -// be encoded in the CRL distribution points extension. -type CRLDistributionPoints MultiString - -// UnmarshalJSON implements the json.Unmarshaler interface in CRLDistributionPoints. -func (u *CRLDistributionPoints) UnmarshalJSON(data []byte) error { - ms, err := unmarshalMultiString(data) - if err != nil { - return err - } - *u = ms - return nil -} - -// Set sets the CRL distribution points to the given certificate. -func (u CRLDistributionPoints) Set(c *x509.Certificate) { - c.CRLDistributionPoints = u -} - -// PolicyIdentifiers represents the list of OIDs to set in the certificate -// policies extension. -type PolicyIdentifiers MultiObjectIdentifier - -// MarshalJSON implements the json.Marshaler interface in PolicyIdentifiers. -func (p PolicyIdentifiers) MarshalJSON() ([]byte, error) { - return MultiObjectIdentifier(p).MarshalJSON() -} - -// UnmarshalJSON implements the json.Unmarshaler interface in PolicyIdentifiers. -func (p *PolicyIdentifiers) UnmarshalJSON(data []byte) error { - var v MultiObjectIdentifier - if err := json.Unmarshal(data, &v); err != nil { - return errors.Wrap(err, "error unmarshaling json") - } - *p = PolicyIdentifiers(v) - return nil -} - -// Sets sets the policy identifiers to the given certificate. -func (p PolicyIdentifiers) Set(c *x509.Certificate) { - c.PolicyIdentifiers = p -} - -// BasicConstraints represents the X509 basic constraints extension and defines -// if a certificate is a CA and then maximum depth of valid certification paths -// that include the certificate. A MaxPathLen of zero indicates that no non- -// self-issued intermediate CA certificates may follow in a valid certification -// path. To do not impose a limit the MaxPathLen should be set to -1. -type BasicConstraints struct { - IsCA bool `json:"isCA"` - MaxPathLen int `json:"maxPathLen"` -} - -// Set sets the basic constraints to the given certificate. -func (b BasicConstraints) Set(c *x509.Certificate) { - c.IsCA = b.IsCA - if c.IsCA { - c.BasicConstraintsValid = true - switch { - case b.MaxPathLen == 0: - c.MaxPathLen = 0 - c.MaxPathLenZero = true - case b.MaxPathLen < 0: - c.MaxPathLen = -1 - c.MaxPathLenZero = false - default: - c.MaxPathLen = b.MaxPathLen - c.MaxPathLenZero = false - } - } else { - c.BasicConstraintsValid = false - c.MaxPathLen = 0 - c.MaxPathLenZero = false - } -} - -// NameConstraints represents the X509 Name constraints extension and defines a -// names space within which all subject names in subsequent certificates in a -// certificate path must be located. The name constraints extension must be used -// only in a CA. -type NameConstraints struct { - Critical bool `json:"critical"` - PermittedDNSDomains MultiString `json:"permittedDNSDomains"` - ExcludedDNSDomains MultiString `json:"excludedDNSDomains"` - PermittedIPRanges MultiIPNet `json:"permittedIPRanges"` - ExcludedIPRanges MultiIPNet `json:"excludedIPRanges"` - PermittedEmailAddresses MultiString `json:"permittedEmailAddresses"` - ExcludedEmailAddresses MultiString `json:"excludedEmailAddresses"` - PermittedURIDomains MultiString `json:"permittedURIDomains"` - ExcludedURIDomains MultiString `json:"excludedURIDomains"` -} - -// Sets sets the name constraints in the given certificate. -func (n NameConstraints) Set(c *x509.Certificate) { - c.PermittedDNSDomainsCritical = n.Critical - c.PermittedDNSDomains = n.PermittedDNSDomains - c.ExcludedDNSDomains = n.ExcludedDNSDomains - c.PermittedIPRanges = n.PermittedIPRanges - c.ExcludedIPRanges = n.ExcludedIPRanges - c.PermittedEmailAddresses = n.PermittedEmailAddresses - c.ExcludedEmailAddresses = n.ExcludedEmailAddresses - c.PermittedURIDomains = n.PermittedURIDomains - c.ExcludedURIDomains = n.ExcludedURIDomains -} - -// SerialNumber is the JSON representation of the X509 serial number. -type SerialNumber struct { - *big.Int -} - -// Set sets the serial number in the given certificate. -func (s SerialNumber) Set(c *x509.Certificate) { - c.SerialNumber = s.Int -} - -func (s *SerialNumber) MarshalJSON() ([]byte, error) { - if s == nil || s.Int == nil { - return []byte(`null`), nil - } - return s.Int.MarshalJSON() -} - -// UnmarshalJSON implements the json.Unmarshal interface and unmarshals an -// integer or a string into a serial number. If a string is used, a prefix of -// “0b” or “0B” selects base 2, “0”, “0o” or “0O” selects base 8, and “0x” or -// “0X” selects base 16. Otherwise, the selected base is 10 and no prefix is -// accepted. -func (s *SerialNumber) UnmarshalJSON(data []byte) error { - if sn, ok := maybeString(data); ok { - // Using base 0 to accept prefixes 0b, 0o, 0x but defaults as base 10. - b, ok := new(big.Int).SetString(sn, 0) - if !ok { - return errors.Errorf("error unmarshaling json: serialNumber %s is not valid", sn) - } - *s = SerialNumber{ - Int: b, - } - return nil - } - - // Assume a number. - var i int64 - if err := json.Unmarshal(data, &i); err != nil { - return errors.Wrap(err, "error unmarshaling json") - } - *s = SerialNumber{ - Int: new(big.Int).SetInt64(i), - } - return nil -} diff --git a/x509util/extensions_test.go b/x509util/extensions_test.go deleted file mode 100644 index fb8f23f0..00000000 --- a/x509util/extensions_test.go +++ /dev/null @@ -1,892 +0,0 @@ -package x509util - -import ( - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/json" - "math/big" - "net" - "net/url" - "reflect" - "testing" -) - -func Test_convertName(t *testing.T) { - type args struct { - s string - } - tests := []struct { - name string - args args - want string - }{ - {"lowerCase", args{"FooBAR"}, "foobar"}, - {"underscore", args{"foo_bar"}, "foobar"}, - {"mixed", args{"FOO_Bar"}, "foobar"}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := convertName(tt.args.s); got != tt.want { - t.Errorf("convertName() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_newExtension(t *testing.T) { - type args struct { - e pkix.Extension - } - tests := []struct { - name string - args args - want Extension - }{ - {"ok", args{pkix.Extension{Id: []int{1, 2, 3, 4}, Value: []byte("foo")}}, Extension{ID: []int{1, 2, 3, 4}, Critical: false, Value: []byte("foo")}}, - {"critical", args{pkix.Extension{Id: []int{1, 2, 3, 4}, Critical: true, Value: []byte("foo")}}, Extension{ID: []int{1, 2, 3, 4}, Critical: true, Value: []byte("foo")}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := newExtension(tt.args.e); !reflect.DeepEqual(got, tt.want) { - t.Errorf("newExtension() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_newExtensions(t *testing.T) { - ext1 := pkix.Extension{Id: []int{1, 2, 3, 4}, Value: []byte("foo")} - ext2 := pkix.Extension{Id: []int{4, 3, 2, 1}, Critical: true, Value: []byte("bar")} - - type args struct { - extensions []pkix.Extension - } - tests := []struct { - name string - args args - want []Extension - }{ - {"ok", args{[]pkix.Extension{ext1, ext2}}, []Extension{ - {ID: []int{1, 2, 3, 4}, Critical: false, Value: []byte("foo")}, - {ID: []int{4, 3, 2, 1}, Critical: true, Value: []byte("bar")}, - }}, - {"nil", args{}, nil}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := newExtensions(tt.args.extensions); !reflect.DeepEqual(got, tt.want) { - t.Errorf("newExtensions() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestExtension_Set(t *testing.T) { - type fields struct { - ID ObjectIdentifier - Critical bool - Value []byte - } - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - fields fields - args args - want *x509.Certificate - }{ - {"ok", fields{[]int{1, 2, 3, 4}, true, []byte("foo")}, args{&x509.Certificate{}}, &x509.Certificate{ - ExtraExtensions: []pkix.Extension{{Id: []int{1, 2, 3, 4}, Critical: true, Value: []byte("foo")}}, - }}, - {"existing", fields{[]int{1, 2, 3, 4}, true, []byte("foo")}, args{&x509.Certificate{ - ExtraExtensions: []pkix.Extension{ - {Id: []int{1, 1, 1, 1}, Critical: false, Value: []byte("foo")}, - }, - }}, &x509.Certificate{ - ExtraExtensions: []pkix.Extension{ - {Id: []int{1, 1, 1, 1}, Critical: false, Value: []byte("foo")}, - {Id: []int{1, 2, 3, 4}, Critical: true, Value: []byte("foo")}, - }, - }}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - e := Extension{ - ID: tt.fields.ID, - Critical: tt.fields.Critical, - Value: tt.fields.Value, - } - e.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("Extension.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} - -func TestObjectIdentifier_MarshalJSON(t *testing.T) { - tests := []struct { - name string - o ObjectIdentifier - want []byte - wantErr bool - }{ - {"ok", []int{1, 2, 3, 4}, []byte(`"1.2.3.4"`), false}, - {"empty", []int{}, []byte(`""`), false}, - {"nil", nil, []byte(`""`), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.o.MarshalJSON() - if (err != nil) != tt.wantErr { - t.Errorf("ObjectIdentifier.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("ObjectIdentifier.MarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestObjectIdentifier_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want ObjectIdentifier - wantErr bool - }{ - {"ok", args{[]byte(`"1.2.3.4"`)}, []int{1, 2, 3, 4}, false}, - {"empty", args{[]byte(`""`)}, []int{}, false}, - {"null", args{[]byte(`null`)}, []int{}, false}, - {"number", args{[]byte(`123`)}, nil, true}, - {"badFormat", args{[]byte(`"1.2.foo.4"`)}, nil, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got ObjectIdentifier - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("ObjectIdentifier.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("ObjectIdentifier.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestSubjectAlternativeName_Set(t *testing.T) { - panicCount := 0 - type fields struct { - Type string - Value string - } - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - fields fields - args args - want *x509.Certificate - }{ - {"dns", fields{"dns", "foo.com"}, args{&x509.Certificate{}}, &x509.Certificate{DNSNames: []string{"foo.com"}}}, - {"dnsAdd", fields{"DNS", "bar.com"}, args{&x509.Certificate{DNSNames: []string{"foo.com"}}}, &x509.Certificate{DNSNames: []string{"foo.com", "bar.com"}}}, - {"email", fields{"email", "john@doe.com"}, args{&x509.Certificate{}}, &x509.Certificate{EmailAddresses: []string{"john@doe.com"}}}, - {"emailAdd", fields{"EMAIL", "jane@doe.com"}, args{&x509.Certificate{EmailAddresses: []string{"john@doe.com"}}}, &x509.Certificate{EmailAddresses: []string{"john@doe.com", "jane@doe.com"}}}, - {"ip", fields{"ip", "127.0.0.1"}, args{&x509.Certificate{}}, &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}}}, - {"ipAdd", fields{"IP", "::1"}, args{&x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}}}, &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")}}}, - {"ipBad", fields{"IP", "fooo"}, args{&x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}}}, &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}}}, - {"uri", fields{"uri", "https://foo.com"}, args{&x509.Certificate{}}, &x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}}}}, - {"uriAdd", fields{"URI", "uri:foo:bar"}, args{&x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}}}}, &x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}, {Scheme: "uri", Opaque: "foo:bar"}}}}, - {"uriBad", fields{"URI", "::1"}, args{&x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}}}}, &x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}}}}, - {"AutoDNS", fields{"", "foo.com"}, args{&x509.Certificate{}}, &x509.Certificate{DNSNames: []string{"foo.com"}}}, - {"AutoDNSAdd", fields{"auto", "bar.com"}, args{&x509.Certificate{DNSNames: []string{"foo.com"}}}, &x509.Certificate{DNSNames: []string{"foo.com", "bar.com"}}}, - {"AutoEmail", fields{"AUTO", "john@doe.com"}, args{&x509.Certificate{}}, &x509.Certificate{EmailAddresses: []string{"john@doe.com"}}}, - {"AutoEmailAdd", fields{"", "jane@doe.com"}, args{&x509.Certificate{EmailAddresses: []string{"john@doe.com"}}}, &x509.Certificate{EmailAddresses: []string{"john@doe.com", "jane@doe.com"}}}, - {"IPAutoIP", fields{"AutO", "127.0.0.1"}, args{&x509.Certificate{}}, &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}}}, - {"AutoIPAdd", fields{"", "::1"}, args{&x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}}}, &x509.Certificate{IPAddresses: []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")}}}, - {"AutoURI", fields{"Auto", "https://foo.com"}, args{&x509.Certificate{}}, &x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}}}}, - {"AutoURIAdd", fields{"", "uri:foo:bar"}, args{&x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}}}}, &x509.Certificate{URIs: []*url.URL{{Scheme: "https", Host: "foo.com"}, {Scheme: "uri", Opaque: "foo:bar"}}}}, - {"panic", fields{"panic", "foo.com"}, args{&x509.Certificate{}}, &x509.Certificate{DNSNames: []string{"foo.com"}}}, - {"panicAdd", fields{"panic", "bar.com"}, args{&x509.Certificate{DNSNames: []string{"foo.com"}}}, &x509.Certificate{DNSNames: []string{"foo.com"}}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer func() { - if r := recover(); r != nil { - panicCount++ - } - }() - s := SubjectAlternativeName{ - Type: tt.fields.Type, - Value: tt.fields.Value, - } - s.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("SubjectAlternativeName.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } - - if panicCount != 2 { - t.Errorf("SubjectAlternativeName.Set() number of panics = %d, want 2", panicCount) - } -} - -func TestKeyUsage_Set(t *testing.T) { - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - k KeyUsage - args args - want *x509.Certificate - }{ - {"ok", KeyUsage(x509.KeyUsageDigitalSignature), args{&x509.Certificate{}}, &x509.Certificate{KeyUsage: x509.KeyUsageDigitalSignature}}, - {"overwrite", KeyUsage(x509.KeyUsageCRLSign | x509.KeyUsageCertSign), args{&x509.Certificate{KeyUsage: x509.KeyUsageDigitalSignature}}, &x509.Certificate{KeyUsage: x509.KeyUsageCRLSign | x509.KeyUsageCertSign}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.k.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("KeyUsage.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} - -func TestKeyUsage_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want KeyUsage - wantErr bool - }{ - // Normalized - {"DigitalSignature", args{[]byte(`"DigitalSignature"`)}, KeyUsage(x509.KeyUsageDigitalSignature), false}, - {"ContentCommitment", args{[]byte(`"ContentCommitment"`)}, KeyUsage(x509.KeyUsageContentCommitment), false}, - {"KeyEncipherment", args{[]byte(`"KeyEncipherment"`)}, KeyUsage(x509.KeyUsageKeyEncipherment), false}, - {"DataEncipherment", args{[]byte(`"DataEncipherment"`)}, KeyUsage(x509.KeyUsageDataEncipherment), false}, - {"KeyAgreement", args{[]byte(`"KeyAgreement"`)}, KeyUsage(x509.KeyUsageKeyAgreement), false}, - {"CertSign", args{[]byte(`"CertSign"`)}, KeyUsage(x509.KeyUsageCertSign), false}, - {"CRLSign", args{[]byte(`"CRLSign"`)}, KeyUsage(x509.KeyUsageCRLSign), false}, - {"EncipherOnly", args{[]byte(`"EncipherOnly"`)}, KeyUsage(x509.KeyUsageEncipherOnly), false}, - {"DecipherOnly", args{[]byte(`"DecipherOnly"`)}, KeyUsage(x509.KeyUsageDecipherOnly), false}, - // Snake case - {"digital_signature", args{[]byte(`"digital_signature"`)}, KeyUsage(x509.KeyUsageDigitalSignature), false}, - {"content_commitment", args{[]byte(`"content_commitment"`)}, KeyUsage(x509.KeyUsageContentCommitment), false}, - {"key_encipherment", args{[]byte(`"key_encipherment"`)}, KeyUsage(x509.KeyUsageKeyEncipherment), false}, - {"data_encipherment", args{[]byte(`"data_encipherment"`)}, KeyUsage(x509.KeyUsageDataEncipherment), false}, - {"key_agreement", args{[]byte(`"key_agreement"`)}, KeyUsage(x509.KeyUsageKeyAgreement), false}, - {"cert_sign", args{[]byte(`"cert_sign"`)}, KeyUsage(x509.KeyUsageCertSign), false}, - {"crl_sign", args{[]byte(`"crl_sign"`)}, KeyUsage(x509.KeyUsageCRLSign), false}, - {"encipher_only", args{[]byte(`"encipher_only"`)}, KeyUsage(x509.KeyUsageEncipherOnly), false}, - {"decipher_only", args{[]byte(`"decipher_only"`)}, KeyUsage(x509.KeyUsageDecipherOnly), false}, - // MultiString - {"DigitalSignatureAsArray", args{[]byte(`["digital_signature"]`)}, KeyUsage(x509.KeyUsageDigitalSignature), false}, - {"DigitalSignature|KeyEncipherment", args{[]byte(`["DigitalSignature", "key_encipherment"]`)}, KeyUsage(x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment), false}, - // Errors - {"invalid", args{[]byte(`"invalid"`)}, KeyUsage(0), true}, - {"number", args{[]byte(`123`)}, KeyUsage(0), true}, - {"object", args{[]byte(`{}`)}, KeyUsage(0), true}, - {"badJSON", args{[]byte(`{`)}, KeyUsage(0), true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got KeyUsage - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("KeyUsage.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("KeyUsage.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestExtKeyUsage_Set(t *testing.T) { - eku1 := []x509.ExtKeyUsage{ - x509.ExtKeyUsageClientAuth, - x509.ExtKeyUsageServerAuth, - } - eku2 := []x509.ExtKeyUsage{ - x509.ExtKeyUsageCodeSigning, - } - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - k ExtKeyUsage - args args - want *x509.Certificate - }{ - {"ok", ExtKeyUsage(eku1), args{&x509.Certificate{}}, &x509.Certificate{ExtKeyUsage: eku1}}, - {"overwrite", ExtKeyUsage(eku2), args{&x509.Certificate{ExtKeyUsage: eku1}}, &x509.Certificate{ExtKeyUsage: eku2}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.k.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("ExtKeyUsage.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} - -func TestExtKeyUsage_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want ExtKeyUsage - wantErr bool - }{ - // Normalized - {"Any", args{[]byte(`"Any"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageAny}), false}, - {"ServerAuth", args{[]byte(`"ServerAuth"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}), false}, - {"ClientAuth", args{[]byte(`"ClientAuth"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}), false}, - {"CodeSigning", args{[]byte(`"CodeSigning"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}), false}, - {"EmailProtection", args{[]byte(`"EmailProtection"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection}), false}, - {"IPSECEndSystem", args{[]byte(`"IPSECEndSystem"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageIPSECEndSystem}), false}, - {"IPSECTunnel", args{[]byte(`"IPSECTunnel"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageIPSECTunnel}), false}, - {"IPSECUser", args{[]byte(`"IPSECUser"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageIPSECUser}), false}, - {"TimeStamping", args{[]byte(`"TimeStamping"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping}), false}, - {"OCSPSigning", args{[]byte(`"OCSPSigning"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning}), false}, - {"MicrosoftServerGatedCrypto", args{[]byte(`"MicrosoftServerGatedCrypto"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageMicrosoftServerGatedCrypto}), false}, - {"NetscapeServerGatedCrypto", args{[]byte(`"NetscapeServerGatedCrypto"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageNetscapeServerGatedCrypto}), false}, - {"MicrosoftCommercialCodeSigning", args{[]byte(`"MicrosoftCommercialCodeSigning"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageMicrosoftCommercialCodeSigning}), false}, - {"MicrosoftKernelCodeSigning", args{[]byte(`"MicrosoftKernelCodeSigning"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageMicrosoftKernelCodeSigning}), false}, - // Snake case - {"any", args{[]byte(`"any"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageAny}), false}, - {"server_auth", args{[]byte(`"server_auth"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}), false}, - {"client_auth", args{[]byte(`"client_auth"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}), false}, - {"code_signing", args{[]byte(`"code_signing"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}), false}, - {"email_protection", args{[]byte(`"email_protection"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection}), false}, - {"ipsec_end_system", args{[]byte(`"ipsec_end_system"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageIPSECEndSystem}), false}, - {"ipsec_tunnel", args{[]byte(`"ipsec_tunnel"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageIPSECTunnel}), false}, - {"ipsec_user", args{[]byte(`"ipsec_user"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageIPSECUser}), false}, - {"time_stamping", args{[]byte(`"time_stamping"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping}), false}, - {"ocsp_signing", args{[]byte(`"ocsp_signing"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning}), false}, - {"microsoft_server_gated_crypto", args{[]byte(`"microsoft_server_gated_crypto"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageMicrosoftServerGatedCrypto}), false}, - {"netscape_server_gated_crypto", args{[]byte(`"netscape_server_gated_crypto"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageNetscapeServerGatedCrypto}), false}, - {"microsoft_commercial_code_signing", args{[]byte(`"microsoft_commercial_code_signing"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageMicrosoftCommercialCodeSigning}), false}, - {"microsoft_kernel_code_signing", args{[]byte(`"microsoft_kernel_code_signing"`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageMicrosoftKernelCodeSigning}), false}, - // Multistring - {"CodeSigningAsArray", args{[]byte(`["code_signing"]`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning}), false}, - {"ServerAuth+ClientAuth", args{[]byte(`["ServerAuth","client_auth"]`)}, ExtKeyUsage([]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}), false}, - // Errors - {"invalid", args{[]byte(`"invalid"`)}, nil, true}, - {"number", args{[]byte(`123`)}, nil, true}, - {"object", args{[]byte(`{}`)}, nil, true}, - {"badJSON", args{[]byte(`{`)}, nil, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got ExtKeyUsage - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("ExtKeyUsage.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("ExtKeyUsage.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestSubjectKeyID_Set(t *testing.T) { - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - id SubjectKeyID - args args - want *x509.Certificate - }{ - {"ok", []byte("subjectKeyID"), args{&x509.Certificate{}}, &x509.Certificate{SubjectKeyId: []byte("subjectKeyID")}}, - {"overwrite", []byte("newSubjectKeyID"), args{&x509.Certificate{SubjectKeyId: []byte("subjectKeyID")}}, &x509.Certificate{SubjectKeyId: []byte("newSubjectKeyID")}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.id.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("SubjectKeyID.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} - -func TestAuthorityKeyID_Set(t *testing.T) { - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - id AuthorityKeyID - args args - want *x509.Certificate - }{ - {"ok", []byte("authorityKeyID"), args{&x509.Certificate{}}, &x509.Certificate{AuthorityKeyId: []byte("authorityKeyID")}}, - {"overwrite", []byte("newAuthorityKeyID"), args{&x509.Certificate{AuthorityKeyId: []byte("authorityKeyID")}}, &x509.Certificate{AuthorityKeyId: []byte("newAuthorityKeyID")}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.id.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("AuthorityKeyID.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} - -func TestOCSPServer_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want OCSPServer - wantErr bool - }{ - {"string", args{[]byte(`"foo"`)}, []string{"foo"}, false}, - {"array", args{[]byte(`["foo", "bar", "zar"]`)}, []string{"foo", "bar", "zar"}, false}, - {"empty", args{[]byte(`[]`)}, []string{}, false}, - {"null", args{[]byte(`null`)}, nil, false}, - {"fail", args{[]byte(`["foo"`)}, nil, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got OCSPServer - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("OCSPServer.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("OCSPServer.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestOCSPServer_Set(t *testing.T) { - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - o OCSPServer - args args - want *x509.Certificate - }{ - {"ok", []string{"oscp.server"}, args{&x509.Certificate{}}, &x509.Certificate{OCSPServer: []string{"oscp.server"}}}, - {"overwrite", []string{"oscp.server", "oscp.com"}, args{&x509.Certificate{OCSPServer: []string{"oscp.server"}}}, &x509.Certificate{OCSPServer: []string{"oscp.server", "oscp.com"}}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.o.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("OCSPServer.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} - -func TestIssuingCertificateURL_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want IssuingCertificateURL - wantErr bool - }{ - {"string", args{[]byte(`"foo"`)}, []string{"foo"}, false}, - {"array", args{[]byte(`["foo", "bar", "zar"]`)}, []string{"foo", "bar", "zar"}, false}, - {"empty", args{[]byte(`[]`)}, []string{}, false}, - {"null", args{[]byte(`null`)}, nil, false}, - {"fail", args{[]byte(`["foo"`)}, nil, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got IssuingCertificateURL - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("IssuingCertificateURL.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("IssuingCertificateURL.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestIssuingCertificateURL_Set(t *testing.T) { - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - o IssuingCertificateURL - args args - want *x509.Certificate - }{ - {"ok", []string{"issuing.server"}, args{&x509.Certificate{}}, &x509.Certificate{IssuingCertificateURL: []string{"issuing.server"}}}, - {"overwrite", []string{"issuing.server", "issuing.com"}, args{&x509.Certificate{IssuingCertificateURL: []string{"issuing.server"}}}, &x509.Certificate{IssuingCertificateURL: []string{"issuing.server", "issuing.com"}}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.o.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("IssuingCertificateURL.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} - -func TestCRLDistributionPoints_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want CRLDistributionPoints - wantErr bool - }{ - {"string", args{[]byte(`"foo"`)}, []string{"foo"}, false}, - {"array", args{[]byte(`["foo", "bar", "zar"]`)}, []string{"foo", "bar", "zar"}, false}, - {"empty", args{[]byte(`[]`)}, []string{}, false}, - {"null", args{[]byte(`null`)}, nil, false}, - {"fail", args{[]byte(`["foo"`)}, nil, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got CRLDistributionPoints - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("CRLDistributionPoints.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("CRLDistributionPoints.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestCRLDistributionPoints_Set(t *testing.T) { - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - o CRLDistributionPoints - args args - want *x509.Certificate - }{ - {"ok", []string{"crl.server"}, args{&x509.Certificate{}}, &x509.Certificate{CRLDistributionPoints: []string{"crl.server"}}}, - {"overwrite", []string{"crl.server", "crl.com"}, args{&x509.Certificate{CRLDistributionPoints: []string{"crl.server"}}}, &x509.Certificate{CRLDistributionPoints: []string{"crl.server", "crl.com"}}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.o.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("CRLDistributionPoints.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} - -func TestPolicyIdentifiers_MarshalJSON(t *testing.T) { - tests := []struct { - name string - m PolicyIdentifiers - want []byte - wantErr bool - }{ - {"ok", []asn1.ObjectIdentifier{[]int{1, 2, 3, 4}, []int{5, 6, 7, 8, 9, 0}}, []byte(`["1.2.3.4","5.6.7.8.9.0"]`), false}, - {"empty", []asn1.ObjectIdentifier{}, []byte(`[]`), false}, - {"nil", nil, []byte(`null`), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := json.Marshal(tt.m) - if (err != nil) != tt.wantErr { - t.Errorf("PolicyIdentifiers.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("PolicyIdentifiers.MarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestPolicyIdentifiers_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want PolicyIdentifiers - wantErr bool - }{ - {"string", args{[]byte(`"1.2.3.4"`)}, []asn1.ObjectIdentifier{[]int{1, 2, 3, 4}}, false}, - {"array", args{[]byte(`["1.2.3.4", "5.6.7.8.9.0"]`)}, []asn1.ObjectIdentifier{[]int{1, 2, 3, 4}, []int{5, 6, 7, 8, 9, 0}}, false}, - {"empty", args{[]byte(`[]`)}, []asn1.ObjectIdentifier{}, false}, - {"null", args{[]byte(`null`)}, nil, false}, - {"fail", args{[]byte(`":foo:bar"`)}, nil, true}, - {"failJSON", args{[]byte(`["https://iss#sub"`)}, nil, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got PolicyIdentifiers - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("PolicyIdentifiers.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("PolicyIdentifiers.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestPolicyIdentifiers_Set(t *testing.T) { - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - o PolicyIdentifiers - args args - want *x509.Certificate - }{ - {"ok", []asn1.ObjectIdentifier{{1, 2, 3, 4}}, args{&x509.Certificate{}}, &x509.Certificate{PolicyIdentifiers: []asn1.ObjectIdentifier{{1, 2, 3, 4}}}}, - {"overwrite", []asn1.ObjectIdentifier{{1, 2, 3, 4}, {4, 3, 2, 1}}, args{&x509.Certificate{PolicyIdentifiers: []asn1.ObjectIdentifier{{1, 2, 3, 4}}}}, &x509.Certificate{PolicyIdentifiers: []asn1.ObjectIdentifier{{1, 2, 3, 4}, {4, 3, 2, 1}}}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.o.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("PolicyIdentifiers.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} - -func TestBasicConstraints_Set(t *testing.T) { - type fields struct { - IsCA bool - MaxPathLen int - } - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - fields fields - args args - want *x509.Certificate - }{ - {"IsCAFalse", fields{false, 0}, args{&x509.Certificate{}}, &x509.Certificate{}}, - {"IsCAFalseWithPathLen", fields{false, 1}, args{&x509.Certificate{}}, &x509.Certificate{}}, - {"IsCAFalseWithAnyPathLen", fields{false, -1}, args{&x509.Certificate{}}, &x509.Certificate{}}, - {"IsCATrue", fields{true, 0}, args{&x509.Certificate{}}, &x509.Certificate{IsCA: true, MaxPathLen: 0, MaxPathLenZero: true, BasicConstraintsValid: true}}, - {"IsCATrueWithPathLen", fields{true, 1}, args{&x509.Certificate{}}, &x509.Certificate{IsCA: true, MaxPathLen: 1, MaxPathLenZero: false, BasicConstraintsValid: true}}, - {"IsCATrueWithAnyPathLen", fields{true, -1}, args{&x509.Certificate{}}, &x509.Certificate{IsCA: true, MaxPathLen: -1, MaxPathLenZero: false, BasicConstraintsValid: true}}, - {"overwriteToFalse", fields{false, 0}, args{&x509.Certificate{IsCA: true, MaxPathLen: 0, MaxPathLenZero: true, BasicConstraintsValid: true}}, &x509.Certificate{}}, - {"overwriteToTrue", fields{true, -100}, args{&x509.Certificate{IsCA: true, MaxPathLen: 0, MaxPathLenZero: true, BasicConstraintsValid: true}}, &x509.Certificate{IsCA: true, MaxPathLen: -1, MaxPathLenZero: false, BasicConstraintsValid: true}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - b := BasicConstraints{ - IsCA: tt.fields.IsCA, - MaxPathLen: tt.fields.MaxPathLen, - } - b.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("BasicConstraints.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} - -func TestNameConstraints_Set(t *testing.T) { - ipNet := func(s string) *net.IPNet { - _, ipNet, err := net.ParseCIDR(s) - if err != nil { - t.Fatal(err) - } - return ipNet - } - type fields struct { - Critical bool - PermittedDNSDomains MultiString - ExcludedDNSDomains MultiString - PermittedIPRanges MultiIPNet - ExcludedIPRanges MultiIPNet - PermittedEmailAddresses MultiString - ExcludedEmailAddresses MultiString - PermittedURIDomains MultiString - ExcludedURIDomains MultiString - } - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - fields fields - args args - want *x509.Certificate - }{ - {"ok", fields{ - Critical: true, - PermittedDNSDomains: []string{"foo.com", "bar.com"}, - ExcludedDNSDomains: []string{"zar.com"}, - PermittedIPRanges: []*net.IPNet{ipNet("1.2.0.0/16"), ipNet("2.3.4.0/8")}, - ExcludedIPRanges: []*net.IPNet{ipNet("3.0.0.0/24")}, - PermittedEmailAddresses: []string{"root@foo.com"}, - ExcludedEmailAddresses: []string{"admin@foo.com", "root@bar.com", "admin@bar.com"}, - PermittedURIDomains: []string{".foo.com", ".bar.com"}, - ExcludedURIDomains: []string{".zar.com"}, - }, args{&x509.Certificate{}}, &x509.Certificate{ - PermittedDNSDomainsCritical: true, - PermittedDNSDomains: []string{"foo.com", "bar.com"}, - ExcludedDNSDomains: []string{"zar.com"}, - PermittedIPRanges: []*net.IPNet{ipNet("1.2.0.0/16"), ipNet("2.3.4.0/8")}, - ExcludedIPRanges: []*net.IPNet{ipNet("3.0.0.0/24")}, - PermittedEmailAddresses: []string{"root@foo.com"}, - ExcludedEmailAddresses: []string{"admin@foo.com", "root@bar.com", "admin@bar.com"}, - PermittedURIDomains: []string{".foo.com", ".bar.com"}, - ExcludedURIDomains: []string{".zar.com"}, - }}, - {"overwrite", fields{}, args{&x509.Certificate{ - PermittedDNSDomainsCritical: true, - PermittedDNSDomains: []string{"foo.com", "bar.com"}, - ExcludedDNSDomains: []string{"zar.com"}, - PermittedIPRanges: []*net.IPNet{ipNet("1.2.0.0/16"), ipNet("2.3.4.0/8")}, - ExcludedIPRanges: []*net.IPNet{ipNet("3.0.0.0/24")}, - PermittedEmailAddresses: []string{"root@foo.com"}, - ExcludedEmailAddresses: []string{"admin@foo.com", "root@bar.com", "admin@bar.com"}, - PermittedURIDomains: []string{".foo.com", ".bar.com"}, - ExcludedURIDomains: []string{".zar.com"}, - }}, &x509.Certificate{}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - n := NameConstraints{ - Critical: tt.fields.Critical, - PermittedDNSDomains: tt.fields.PermittedDNSDomains, - ExcludedDNSDomains: tt.fields.ExcludedDNSDomains, - PermittedIPRanges: tt.fields.PermittedIPRanges, - ExcludedIPRanges: tt.fields.ExcludedIPRanges, - PermittedEmailAddresses: tt.fields.PermittedEmailAddresses, - ExcludedEmailAddresses: tt.fields.ExcludedEmailAddresses, - PermittedURIDomains: tt.fields.PermittedURIDomains, - ExcludedURIDomains: tt.fields.ExcludedURIDomains, - } - n.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("NameConstraints.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} - -func TestSerialNumber_Set(t *testing.T) { - type fields struct { - Int *big.Int - } - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - fields fields - args args - want *x509.Certificate - }{ - {"ok", fields{big.NewInt(1234)}, args{&x509.Certificate{}}, &x509.Certificate{SerialNumber: big.NewInt(1234)}}, - {"overwrite", fields{big.NewInt(4321)}, args{&x509.Certificate{SerialNumber: big.NewInt(1234)}}, &x509.Certificate{SerialNumber: big.NewInt(4321)}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := SerialNumber{ - Int: tt.fields.Int, - } - s.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("SerialNumber.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} - -func TestSerialNumber_MarshalJSON(t *testing.T) { - tests := []struct { - name string - sn *SerialNumber - want []byte - wantErr bool - }{ - {"ok", &SerialNumber{big.NewInt(1234)}, []byte("1234"), false}, - {"nilStruct", nil, []byte("null"), false}, - {"nilBigInt", &SerialNumber{}, []byte("null"), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.sn.MarshalJSON() - if (err != nil) != tt.wantErr { - t.Errorf("SerialNumber.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("SerialNumber.MarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestSerialNumber_UnmarshalJSON(t *testing.T) { - expected := SerialNumber{big.NewInt(12345)} - - type args struct { - data []byte - } - tests := []struct { - name string - args args - want SerialNumber - wantErr bool - }{ - {"string", args{[]byte(`"12345"`)}, expected, false}, - {"stringHex", args{[]byte(`"0x3039"`)}, expected, false}, - {"number", args{[]byte(`12345`)}, expected, false}, - {"badString", args{[]byte(`"123s"`)}, SerialNumber{}, true}, - {"object", args{[]byte(`{}`)}, SerialNumber{}, true}, - {"badJSON", args{[]byte(`{`)}, SerialNumber{}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var s SerialNumber - if err := s.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("SerialNumber.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(s, tt.want) { - t.Errorf("SerialNumber.UnmarshalJSON() = %v, want %v", s, tt.want) - } - }) - } -} diff --git a/x509util/marshal_utils.go b/x509util/marshal_utils.go deleted file mode 100644 index 4408e41b..00000000 --- a/x509util/marshal_utils.go +++ /dev/null @@ -1,212 +0,0 @@ -package x509util - -import ( - "encoding/asn1" - "encoding/json" - "net" - "net/url" - "strconv" - "strings" - - "github.com/pkg/errors" -) - -// MultiString is a type used to unmarshal a JSON string or an array of strings -// into a []string. -type MultiString []string - -// UnmarshalJSON implements the json.Unmarshaler interface for MultiString. -func (m *MultiString) UnmarshalJSON(data []byte) error { - if s, ok := maybeString(data); ok { - *m = MultiString([]string{s}) - return nil - } - - var v []string - if err := json.Unmarshal(data, &v); err != nil { - return errors.Wrap(err, "error unmarshaling json") - } - *m = MultiString(v) - return nil -} - -// MultiIP is a type used to unmarshal a JSON string or an array of strings into -// a []net.IP. -type MultiIP []net.IP - -// UnmarshalJSON implements the json.Unmarshaler interface for MultiIP. -func (m *MultiIP) UnmarshalJSON(data []byte) error { - ms, err := unmarshalMultiString(data) - if err != nil { - return err - } - if ms != nil { - ips := make([]net.IP, len(ms)) - for i, s := range ms { - ip := net.ParseIP(s) - if ip == nil { - return errors.Errorf("error unmarshaling json: ip %s is not valid", s) - } - ips[i] = ip - } - - *m = MultiIP(ips) - } - return nil -} - -// MultiIPNet is a type used to unmarshal a JSON string or an array of strings -// into a []*net.IPNet. -type MultiIPNet []*net.IPNet - -// MarshalJSON implements the json.Marshaler interface for MultiIPNet. -func (m MultiIPNet) MarshalJSON() ([]byte, error) { - if m == nil { - return []byte("null"), nil - } - ipNets := make([]string, len(m)) - for i, v := range m { - ipNets[i] = v.String() - } - return json.Marshal(ipNets) -} - -// UnmarshalJSON implements the json.Unmarshaler interface for MultiIPNet. -func (m *MultiIPNet) UnmarshalJSON(data []byte) error { - ms, err := unmarshalMultiString(data) - if err != nil { - return err - } - if ms != nil { - ipNets := make([]*net.IPNet, len(ms)) - for i, s := range ms { - _, ipNet, err := net.ParseCIDR(s) - if err != nil { - return errors.Wrap(err, "error unmarshaling json") - } - ipNets[i] = ipNet - } - - *m = MultiIPNet(ipNets) - } - return nil -} - -// MultiURL is a type used to unmarshal a JSON string or an array of strings -// into a []*url.URL. -type MultiURL []*url.URL - -// MarshalJSON implements the json.Marshaler interface for MultiURL. -func (m MultiURL) MarshalJSON() ([]byte, error) { - if m == nil { - return []byte("null"), nil - } - urls := make([]string, len(m)) - for i, u := range m { - urls[i] = u.String() - } - return json.Marshal(urls) -} - -// UnmarshalJSON implements the json.Unmarshaler interface for MultiURL. -func (m *MultiURL) UnmarshalJSON(data []byte) error { - ms, err := unmarshalMultiString(data) - if err != nil { - return err - } - if ms != nil { - urls := make([]*url.URL, len(ms)) - for i, s := range ms { - u, err := url.Parse(s) - if err != nil { - return errors.Wrap(err, "error unmarshaling json") - } - urls[i] = u - } - - *m = MultiURL(urls) - } - return nil -} - -// MultiObjectIdentifier is a type used to unmarshal a JSON string or an array -// of strings into a []asn1.ObjectIdentifier. -type MultiObjectIdentifier []asn1.ObjectIdentifier - -// MarshalJSON implements the json.Marshaler interface for MultiObjectIdentifier. -func (m MultiObjectIdentifier) MarshalJSON() ([]byte, error) { - if m == nil { - return []byte("null"), nil - } - oids := make([]string, len(m)) - for i, u := range m { - oids[i] = u.String() - } - return json.Marshal(oids) -} - -// UnmarshalJSON implements the json.Unmarshaler interface for -// MultiObjectIdentifier. -func (m *MultiObjectIdentifier) UnmarshalJSON(data []byte) error { - ms, err := unmarshalMultiString(data) - if err != nil { - return err - } - if ms != nil { - oids := make([]asn1.ObjectIdentifier, len(ms)) - for i, s := range ms { - oid, err := parseObjectIdentifier(s) - if err != nil { - return err - } - oids[i] = oid - } - - *m = MultiObjectIdentifier(oids) - } - return nil -} - -func maybeString(data []byte) (string, bool) { - if len(data) > 0 && data[0] == '"' { - var v string - if err := json.Unmarshal(data, &v); err == nil { - return v, true - } - } - return "", false -} - -func unmarshalString(data []byte) (string, error) { - var v string - if err := json.Unmarshal(data, &v); err != nil { - return v, errors.Wrap(err, "error unmarshaling json") - } - return v, nil -} - -func unmarshalMultiString(data []byte) ([]string, error) { - var v MultiString - if err := json.Unmarshal(data, &v); err != nil { - return nil, errors.Wrap(err, "error unmarshaling json") - } - return []string(v), nil -} - -func parseObjectIdentifier(oid string) (asn1.ObjectIdentifier, error) { - if oid == "" { - return asn1.ObjectIdentifier{}, nil - } - - parts := strings.Split(oid, ".") - oids := make([]int, len(parts)) - - for i, s := range parts { - n, err := strconv.Atoi(s) - if err != nil { - return asn1.ObjectIdentifier{}, errors.Errorf("error unmarshaling json: %s is not an ASN1 object identifier", oid) - } - oids[i] = n - } - return asn1.ObjectIdentifier(oids), nil -} diff --git a/x509util/marshal_utils_test.go b/x509util/marshal_utils_test.go deleted file mode 100644 index b17f1f72..00000000 --- a/x509util/marshal_utils_test.go +++ /dev/null @@ -1,300 +0,0 @@ -package x509util - -import ( - "encoding/asn1" - "encoding/json" - "net" - "net/url" - "reflect" - "testing" -) - -func TestMultiString_MarshalJSON(t *testing.T) { - tests := []struct { - name string - m MultiString - want []byte - wantErr bool - }{ - {"ok", []string{"foo", "bar"}, []byte(`["foo","bar"]`), false}, - {"empty", []string{}, []byte(`[]`), false}, - {"nil", nil, []byte(`null`), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := json.Marshal(tt.m) - if (err != nil) != tt.wantErr { - t.Errorf("MultiIPNet.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MultiIPNet.MarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestMultiString_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want MultiString - wantErr bool - }{ - {"string", args{[]byte(`"foo"`)}, []string{"foo"}, false}, - {"array", args{[]byte(`["foo", "bar", "zar"]`)}, []string{"foo", "bar", "zar"}, false}, - {"empty", args{[]byte(`[]`)}, []string{}, false}, - {"null", args{[]byte(`null`)}, nil, false}, - {"fail", args{[]byte(`["foo"`)}, nil, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got MultiString - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("MultiString.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MultiString.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestMultiIP_MarshalJSON(t *testing.T) { - tests := []struct { - name string - m MultiIP - want []byte - wantErr bool - }{ - {"ok", []net.IP{net.ParseIP("::1"), net.ParseIP("1.2.3.4")}, []byte(`["::1","1.2.3.4"]`), false}, - {"empty", []net.IP{}, []byte(`[]`), false}, - {"nil", nil, []byte(`null`), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := json.Marshal(tt.m) - if (err != nil) != tt.wantErr { - t.Errorf("MultiIPNet.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MultiIPNet.MarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestMultiIP_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want MultiIP - wantErr bool - }{ - {"string", args{[]byte(`"::1"`)}, []net.IP{net.ParseIP("::1")}, false}, - {"array", args{[]byte(`["127.0.0.1", "::1"]`)}, []net.IP{net.ParseIP("127.0.0.1"), net.ParseIP("::1")}, false}, - {"empty", args{[]byte(`[]`)}, []net.IP{}, false}, - {"null", args{[]byte(`null`)}, nil, false}, - {"fail", args{[]byte(`"foo.bar"`)}, nil, true}, - {"failJSON", args{[]byte(`["::1"`)}, nil, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got MultiIP - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("MultiIP.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MultiIP.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestMultiIPNet_MarshalJSON(t *testing.T) { - ipNet := func(s string) *net.IPNet { - _, ipNet, err := net.ParseCIDR(s) - if err != nil { - t.Fatal(err) - } - return ipNet - } - - tests := []struct { - name string - m MultiIPNet - want []byte - wantErr bool - }{ - {"ok", []*net.IPNet{ipNet("1.1.0.0/16"), ipNet("2001:db8:8a2e:7334::/64")}, []byte(`["1.1.0.0/16","2001:db8:8a2e:7334::/64"]`), false}, - {"empty", []*net.IPNet{}, []byte(`[]`), false}, - {"nil", nil, []byte(`null`), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.m.MarshalJSON() - if (err != nil) != tt.wantErr { - t.Errorf("MultiIPNet.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MultiIPNet.MarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestMultiIPNet_UnmarshalJSON(t *testing.T) { - ipNet := func(s string) *net.IPNet { - _, ipNet, err := net.ParseCIDR(s) - if err != nil { - t.Fatal(err) - } - return ipNet - } - - type args struct { - data []byte - } - tests := []struct { - name string - args args - want MultiIPNet - wantErr bool - }{ - {"string", args{[]byte(`"1.1.0.0/16"`)}, []*net.IPNet{ipNet("1.1.0.0/16")}, false}, - {"array", args{[]byte(`["1.0.0.0/24", "2.1.0.0/16"]`)}, []*net.IPNet{ipNet("1.0.0.0/24"), ipNet("2.1.0.0/16")}, false}, - {"empty", args{[]byte(`[]`)}, []*net.IPNet{}, false}, - {"null", args{[]byte(`null`)}, nil, false}, - {"fail", args{[]byte(`"foo.bar/16"`)}, nil, true}, - {"failJSON", args{[]byte(`["1.0.0.0/24"`)}, nil, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got MultiIPNet - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("MultiIPNet.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MultiIPNet.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestMultiURL_MarshalJSON(t *testing.T) { - tests := []struct { - name string - m MultiURL - want []byte - wantErr bool - }{ - {"ok", []*url.URL{{Scheme: "https", Host: "iss", Fragment: "sub"}, {Scheme: "uri", Opaque: "foo:bar"}}, []byte(`["https://iss#sub","uri:foo:bar"]`), false}, - {"empty", []*url.URL{}, []byte(`[]`), false}, - {"nil", nil, []byte(`null`), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := tt.m.MarshalJSON() - if (err != nil) != tt.wantErr { - t.Errorf("MultiURL.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MultiURL.MarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestMultiURL_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want MultiURL - wantErr bool - }{ - {"string", args{[]byte(`"https://iss#sub"`)}, []*url.URL{{Scheme: "https", Host: "iss", Fragment: "sub"}}, false}, - {"array", args{[]byte(`["https://iss#sub", "uri:foo:bar"]`)}, []*url.URL{{Scheme: "https", Host: "iss", Fragment: "sub"}, {Scheme: "uri", Opaque: "foo:bar"}}, false}, - {"empty", args{[]byte(`[]`)}, []*url.URL{}, false}, - {"null", args{[]byte(`null`)}, nil, false}, - {"fail", args{[]byte(`":foo:bar"`)}, nil, true}, - {"failJSON", args{[]byte(`["https://iss#sub"`)}, nil, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got MultiURL - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("MultiURL.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MultiURL.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestMultiObjectIdentifier_MarshalJSON(t *testing.T) { - tests := []struct { - name string - m MultiObjectIdentifier - want []byte - wantErr bool - }{ - {"ok", []asn1.ObjectIdentifier{[]int{1, 2, 3, 4}, []int{5, 6, 7, 8, 9, 0}}, []byte(`["1.2.3.4","5.6.7.8.9.0"]`), false}, - {"empty", []asn1.ObjectIdentifier{}, []byte(`[]`), false}, - {"nil", nil, []byte(`null`), false}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := json.Marshal(tt.m) - if (err != nil) != tt.wantErr { - t.Errorf("MultiObjectIdentifier.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MultiObjectIdentifier.MarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestMultiObjectIdentifier_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want MultiObjectIdentifier - wantErr bool - }{ - {"string", args{[]byte(`"1.2.3.4"`)}, []asn1.ObjectIdentifier{[]int{1, 2, 3, 4}}, false}, - {"array", args{[]byte(`["1.2.3.4", "5.6.7.8.9.0"]`)}, []asn1.ObjectIdentifier{[]int{1, 2, 3, 4}, []int{5, 6, 7, 8, 9, 0}}, false}, - {"empty", args{[]byte(`[]`)}, []asn1.ObjectIdentifier{}, false}, - {"null", args{[]byte(`null`)}, nil, false}, - {"fail", args{[]byte(`":foo:bar"`)}, nil, true}, - {"failJSON", args{[]byte(`["https://iss#sub"`)}, nil, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got MultiObjectIdentifier - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("MultiObjectIdentifier.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("MultiObjectIdentifier.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/x509util/name.go b/x509util/name.go deleted file mode 100644 index f290de63..00000000 --- a/x509util/name.go +++ /dev/null @@ -1,127 +0,0 @@ -package x509util - -import ( - "crypto/x509" - "crypto/x509/pkix" - "encoding/json" - - "github.com/pkg/errors" -) - -// Name is the JSON representation of X.501 type Name, used in the X.509 subject -// and issuer fields. -type Name struct { - Country MultiString `json:"country,omitempty"` - Organization MultiString `json:"organization,omitempty"` - OrganizationalUnit MultiString `json:"organizationalUnit,omitempty"` - Locality MultiString `json:"locality,omitempty"` - Province MultiString `json:"province,omitempty"` - StreetAddress MultiString `json:"streetAddress,omitempty"` - PostalCode MultiString `json:"postalCode,omitempty"` - SerialNumber string `json:"serialNumber,omitempty"` - CommonName string `json:"commonName,omitempty"` -} - -// UnmarshalJSON implements the json.Unmarshal interface and unmarshals a JSON -// object in the Name struct or a string as just the subject common name. -func (n *Name) UnmarshalJSON(data []byte) error { - if cn, ok := maybeString(data); ok { - n.CommonName = cn - return nil - } - - type nameAlias Name - var nn nameAlias - if err := json.Unmarshal(data, &nn); err != nil { - return errors.Wrap(err, "error unmarshaling json") - } - *n = Name(nn) - return nil -} - -// Subject is the JSON representation of the X.509 subject field. -type Subject Name - -func newSubject(n pkix.Name) Subject { - return Subject{ - Country: n.Country, - Organization: n.Organization, - OrganizationalUnit: n.OrganizationalUnit, - Locality: n.Locality, - Province: n.Province, - StreetAddress: n.StreetAddress, - PostalCode: n.PostalCode, - SerialNumber: n.SerialNumber, - CommonName: n.CommonName, - } -} - -// UnmarshalJSON implements the json.Unmarshal interface and unmarshals a JSON -// object in the Subject struct or a string as just the subject common name. -func (s *Subject) UnmarshalJSON(data []byte) error { - var name Name - if err := name.UnmarshalJSON(data); err != nil { - return err - } - *s = Subject(name) - return nil -} - -// Set sets the subject in the given certificate. -func (s Subject) Set(c *x509.Certificate) { - c.Subject = pkix.Name{ - Country: s.Country, - Organization: s.Organization, - OrganizationalUnit: s.OrganizationalUnit, - Locality: s.Locality, - Province: s.Province, - StreetAddress: s.StreetAddress, - PostalCode: s.PostalCode, - SerialNumber: s.SerialNumber, - CommonName: s.CommonName, - } -} - -// Issuer is the JSON representation of the X.509 issuer field. -type Issuer Name - -// nolint:unused -func newIssuer(n pkix.Name) Issuer { - return Issuer{ - Country: n.Country, - Organization: n.Organization, - OrganizationalUnit: n.OrganizationalUnit, - Locality: n.Locality, - Province: n.Province, - StreetAddress: n.StreetAddress, - PostalCode: n.PostalCode, - SerialNumber: n.SerialNumber, - CommonName: n.CommonName, - } -} - -// UnmarshalJSON implements the json.Unmarshal interface and unmarshals a JSON -// object in the Issuer struct or a string as just the subject common name. -func (i *Issuer) UnmarshalJSON(data []byte) error { - var name Name - if err := name.UnmarshalJSON(data); err != nil { - return err - } - *i = Issuer(name) - return nil -} - -// Set sets the issuer in the given certificate. -func (i Issuer) Set(c *x509.Certificate) { - c.Issuer = pkix.Name{ - Country: i.Country, - Organization: i.Organization, - OrganizationalUnit: i.OrganizationalUnit, - Locality: i.Locality, - Province: i.Province, - StreetAddress: i.StreetAddress, - PostalCode: i.PostalCode, - SerialNumber: i.SerialNumber, - CommonName: i.CommonName, - } -} diff --git a/x509util/name_test.go b/x509util/name_test.go deleted file mode 100644 index e2c88a46..00000000 --- a/x509util/name_test.go +++ /dev/null @@ -1,384 +0,0 @@ -package x509util - -import ( - "crypto/x509" - "crypto/x509/pkix" - "reflect" - "testing" -) - -func TestName_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want Name - wantErr bool - }{ - {"null", args{[]byte("null")}, Name{}, false}, - {"empty", args{[]byte("{}")}, Name{}, false}, - {"commonName", args{[]byte(`"commonName"`)}, Name{CommonName: "commonName"}, false}, - {"object", args{[]byte(`{ - "country": "The country", - "organization": "The organization", - "organizationalUnit": ["The organizationalUnit 1", "The organizationalUnit 2"], - "locality": ["The locality 1", "The locality 2"], - "province": "The province", - "streetAddress": "The streetAddress", - "postalCode": "The postalCode", - "serialNumber": "The serialNumber", - "commonName": "The commonName" - }`)}, Name{ - Country: []string{"The country"}, - Organization: []string{"The organization"}, - OrganizationalUnit: []string{"The organizationalUnit 1", "The organizationalUnit 2"}, - Locality: []string{"The locality 1", "The locality 2"}, - Province: []string{"The province"}, - StreetAddress: []string{"The streetAddress"}, - PostalCode: []string{"The postalCode"}, - SerialNumber: "The serialNumber", - CommonName: "The commonName", - }, false}, - {"number", args{[]byte("1234")}, Name{}, true}, - {"badJSON", args{[]byte("'badJSON'")}, Name{}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got Name - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("Name.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Name.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func Test_newSubject(t *testing.T) { - type args struct { - n pkix.Name - } - tests := []struct { - name string - args args - want Subject - }{ - {"ok", args{pkix.Name{ - Country: []string{"The country"}, - Organization: []string{"The organization"}, - OrganizationalUnit: []string{"The organizationalUnit 1", "The organizationalUnit 2"}, - Locality: []string{"The locality 1", "The locality 2"}, - Province: []string{"The province"}, - StreetAddress: []string{"The streetAddress"}, - PostalCode: []string{"The postalCode"}, - SerialNumber: "The serialNumber", - CommonName: "The commonName", - }}, Subject{ - Country: []string{"The country"}, - Organization: []string{"The organization"}, - OrganizationalUnit: []string{"The organizationalUnit 1", "The organizationalUnit 2"}, - Locality: []string{"The locality 1", "The locality 2"}, - Province: []string{"The province"}, - StreetAddress: []string{"The streetAddress"}, - PostalCode: []string{"The postalCode"}, - SerialNumber: "The serialNumber", - CommonName: "The commonName", - }}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := newSubject(tt.args.n); !reflect.DeepEqual(got, tt.want) { - t.Errorf("newSubject() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestSubject_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want Subject - wantErr bool - }{ - {"null", args{[]byte("null")}, Subject{}, false}, - {"empty", args{[]byte("{}")}, Subject{}, false}, - {"commonName", args{[]byte(`"commonName"`)}, Subject{CommonName: "commonName"}, false}, - {"object", args{[]byte(`{ - "country": "The country", - "organization": "The organization", - "organizationalUnit": ["The organizationalUnit 1", "The organizationalUnit 2"], - "locality": ["The locality 1", "The locality 2"], - "province": "The province", - "streetAddress": "The streetAddress", - "postalCode": "The postalCode", - "serialNumber": "The serialNumber", - "commonName": "The commonName" - }`)}, Subject{ - Country: []string{"The country"}, - Organization: []string{"The organization"}, - OrganizationalUnit: []string{"The organizationalUnit 1", "The organizationalUnit 2"}, - Locality: []string{"The locality 1", "The locality 2"}, - Province: []string{"The province"}, - StreetAddress: []string{"The streetAddress"}, - PostalCode: []string{"The postalCode"}, - SerialNumber: "The serialNumber", - CommonName: "The commonName", - }, false}, - {"number", args{[]byte("1234")}, Subject{}, true}, - {"badJSON", args{[]byte("'badJSON'")}, Subject{}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got Subject - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("Subject.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Subject.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestSubject_Set(t *testing.T) { - type fields struct { - Country MultiString - Organization MultiString - OrganizationalUnit MultiString - Locality MultiString - Province MultiString - StreetAddress MultiString - PostalCode MultiString - SerialNumber string - CommonName string - } - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - fields fields - args args - want *x509.Certificate - }{ - {"ok", fields{ - Country: []string{"The country"}, - Organization: []string{"The organization"}, - OrganizationalUnit: []string{"The organizationalUnit 1", "The organizationalUnit 2"}, - Locality: []string{"The locality 1", "The locality 2"}, - Province: []string{"The province"}, - StreetAddress: []string{"The streetAddress"}, - PostalCode: []string{"The postalCode"}, - SerialNumber: "The serialNumber", - CommonName: "The commonName", - }, args{&x509.Certificate{}}, &x509.Certificate{ - Subject: pkix.Name{ - Country: []string{"The country"}, - Organization: []string{"The organization"}, - OrganizationalUnit: []string{"The organizationalUnit 1", "The organizationalUnit 2"}, - Locality: []string{"The locality 1", "The locality 2"}, - Province: []string{"The province"}, - StreetAddress: []string{"The streetAddress"}, - PostalCode: []string{"The postalCode"}, - SerialNumber: "The serialNumber", - CommonName: "The commonName", - }, - }}, - {"overwrite", fields{ - CommonName: "The commonName", - }, args{&x509.Certificate{}}, &x509.Certificate{ - Subject: pkix.Name{ - CommonName: "The commonName", - }, - }}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := Subject{ - Country: tt.fields.Country, - Organization: tt.fields.Organization, - OrganizationalUnit: tt.fields.OrganizationalUnit, - Locality: tt.fields.Locality, - Province: tt.fields.Province, - StreetAddress: tt.fields.StreetAddress, - PostalCode: tt.fields.PostalCode, - SerialNumber: tt.fields.SerialNumber, - CommonName: tt.fields.CommonName, - } - s.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("Subject.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} - -func Test_newIssuer(t *testing.T) { - type args struct { - n pkix.Name - } - tests := []struct { - name string - args args - want Issuer - }{ - {"ok", args{pkix.Name{ - Country: []string{"The country"}, - Organization: []string{"The organization"}, - OrganizationalUnit: []string{"The organizationalUnit 1", "The organizationalUnit 2"}, - Locality: []string{"The locality 1", "The locality 2"}, - Province: []string{"The province"}, - StreetAddress: []string{"The streetAddress"}, - PostalCode: []string{"The postalCode"}, - SerialNumber: "The serialNumber", - CommonName: "The commonName", - }}, Issuer{ - Country: []string{"The country"}, - Organization: []string{"The organization"}, - OrganizationalUnit: []string{"The organizationalUnit 1", "The organizationalUnit 2"}, - Locality: []string{"The locality 1", "The locality 2"}, - Province: []string{"The province"}, - StreetAddress: []string{"The streetAddress"}, - PostalCode: []string{"The postalCode"}, - SerialNumber: "The serialNumber", - CommonName: "The commonName", - }}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := newIssuer(tt.args.n); !reflect.DeepEqual(got, tt.want) { - t.Errorf("newIssuer() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestIssuer_UnmarshalJSON(t *testing.T) { - type args struct { - data []byte - } - tests := []struct { - name string - args args - want Issuer - wantErr bool - }{ - {"null", args{[]byte("null")}, Issuer{}, false}, - {"empty", args{[]byte("{}")}, Issuer{}, false}, - {"commonName", args{[]byte(`"commonName"`)}, Issuer{CommonName: "commonName"}, false}, - {"object", args{[]byte(`{ - "country": "The country", - "organization": "The organization", - "organizationalUnit": ["The organizationalUnit 1", "The organizationalUnit 2"], - "locality": ["The locality 1", "The locality 2"], - "province": "The province", - "streetAddress": "The streetAddress", - "postalCode": "The postalCode", - "serialNumber": "The serialNumber", - "commonName": "The commonName" - }`)}, Issuer{ - Country: []string{"The country"}, - Organization: []string{"The organization"}, - OrganizationalUnit: []string{"The organizationalUnit 1", "The organizationalUnit 2"}, - Locality: []string{"The locality 1", "The locality 2"}, - Province: []string{"The province"}, - StreetAddress: []string{"The streetAddress"}, - PostalCode: []string{"The postalCode"}, - SerialNumber: "The serialNumber", - CommonName: "The commonName", - }, false}, - {"number", args{[]byte("1234")}, Issuer{}, true}, - {"badJSON", args{[]byte("'badJSON'")}, Issuer{}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got Issuer - if err := got.UnmarshalJSON(tt.args.data); (err != nil) != tt.wantErr { - t.Errorf("Issuer.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Issuer.UnmarshalJSON() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestIssuer_Set(t *testing.T) { - type fields struct { - Country MultiString - Organization MultiString - OrganizationalUnit MultiString - Locality MultiString - Province MultiString - StreetAddress MultiString - PostalCode MultiString - SerialNumber string - CommonName string - } - type args struct { - c *x509.Certificate - } - tests := []struct { - name string - fields fields - args args - want *x509.Certificate - }{ - {"ok", fields{ - Country: []string{"The country"}, - Organization: []string{"The organization"}, - OrganizationalUnit: []string{"The organizationalUnit 1", "The organizationalUnit 2"}, - Locality: []string{"The locality 1", "The locality 2"}, - Province: []string{"The province"}, - StreetAddress: []string{"The streetAddress"}, - PostalCode: []string{"The postalCode"}, - SerialNumber: "The serialNumber", - CommonName: "The commonName", - }, args{&x509.Certificate{}}, &x509.Certificate{ - Issuer: pkix.Name{ - Country: []string{"The country"}, - Organization: []string{"The organization"}, - OrganizationalUnit: []string{"The organizationalUnit 1", "The organizationalUnit 2"}, - Locality: []string{"The locality 1", "The locality 2"}, - Province: []string{"The province"}, - StreetAddress: []string{"The streetAddress"}, - PostalCode: []string{"The postalCode"}, - SerialNumber: "The serialNumber", - CommonName: "The commonName", - }, - }}, - {"overwrite", fields{ - CommonName: "The commonName", - }, args{&x509.Certificate{}}, &x509.Certificate{ - Issuer: pkix.Name{ - CommonName: "The commonName", - }, - }}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - i := Issuer{ - Country: tt.fields.Country, - Organization: tt.fields.Organization, - OrganizationalUnit: tt.fields.OrganizationalUnit, - Locality: tt.fields.Locality, - Province: tt.fields.Province, - StreetAddress: tt.fields.StreetAddress, - PostalCode: tt.fields.PostalCode, - SerialNumber: tt.fields.SerialNumber, - CommonName: tt.fields.CommonName, - } - i.Set(tt.args.c) - if !reflect.DeepEqual(tt.args.c, tt.want) { - t.Errorf("Issuer.Set() = %v, want %v", tt.args.c, tt.want) - } - }) - } -} diff --git a/x509util/options.go b/x509util/options.go deleted file mode 100644 index 510ce972..00000000 --- a/x509util/options.go +++ /dev/null @@ -1,91 +0,0 @@ -package x509util - -import ( - "bytes" - "crypto/x509" - "encoding/base64" - "io/ioutil" - "text/template" - - "github.com/Masterminds/sprig/v3" - "github.com/pkg/errors" - "github.com/smallstep/cli/config" -) - -func getFuncMap(failMessage *string) template.FuncMap { - m := sprig.TxtFuncMap() - m["fail"] = func(msg string) (string, error) { - *failMessage = msg - return "", errors.New(msg) - } - return m -} - -// Options are the options that can be passed to NewCertificate. -type Options struct { - CertBuffer *bytes.Buffer -} - -func (o *Options) apply(cr *x509.CertificateRequest, opts []Option) (*Options, error) { - for _, fn := range opts { - if err := fn(cr, o); err != nil { - return o, err - } - } - return o, nil -} - -// Option is the type used as a variadic argument in NewCertificate. -type Option func(cr *x509.CertificateRequest, o *Options) error - -// WithTemplate is an options that executes the given template text with the -// given data. -func WithTemplate(text string, data TemplateData) Option { - return func(cr *x509.CertificateRequest, o *Options) error { - terr := new(TemplateError) - funcMap := getFuncMap(&terr.Message) - - tmpl, err := template.New("template").Funcs(funcMap).Parse(text) - if err != nil { - return errors.Wrapf(err, "error parsing template") - } - - buf := new(bytes.Buffer) - data.SetCertificateRequest(cr) - if err := tmpl.Execute(buf, data); err != nil { - if terr.Message != "" { - return terr - } - return errors.Wrapf(err, "error executing template") - } - o.CertBuffer = buf - return nil - } -} - -// WithTemplateBase64 is an options that executes the given template base64 -// string with the given data. -func WithTemplateBase64(s string, data TemplateData) Option { - return func(cr *x509.CertificateRequest, o *Options) error { - b, err := base64.StdEncoding.DecodeString(s) - if err != nil { - return errors.Wrap(err, "error decoding template") - } - fn := WithTemplate(string(b), data) - return fn(cr, o) - } -} - -// WithTemplateFile is an options that reads the template file and executes it -// with the given data. -func WithTemplateFile(path string, data TemplateData) Option { - return func(cr *x509.CertificateRequest, o *Options) error { - filename := config.StepAbs(path) - b, err := ioutil.ReadFile(filename) - if err != nil { - return errors.Wrapf(err, "error reading %s", path) - } - fn := WithTemplate(string(b), data) - return fn(cr, o) - } -} diff --git a/x509util/options_test.go b/x509util/options_test.go deleted file mode 100644 index 7d2ee725..00000000 --- a/x509util/options_test.go +++ /dev/null @@ -1,237 +0,0 @@ -package x509util - -import ( - "bytes" - "crypto" - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "crypto/x509/pkix" - "encoding/base64" - "reflect" - "testing" - - "github.com/pkg/errors" -) - -func createRSACertificateRequest(t *testing.T, bits int, commonName string, sans []string) (*x509.CertificateRequest, crypto.Signer) { - dnsNames, ips, emails, uris := SplitSANs(sans) - t.Helper() - priv, err := rsa.GenerateKey(rand.Reader, bits) - if err != nil { - t.Fatal(err) - } - asn1Data, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{ - Subject: pkix.Name{CommonName: commonName}, - DNSNames: dnsNames, - IPAddresses: ips, - EmailAddresses: emails, - URIs: uris, - SignatureAlgorithm: x509.SHA256WithRSAPSS, - }, priv) - if err != nil { - t.Fatal(err) - } - cr, err := x509.ParseCertificateRequest(asn1Data) - if err != nil { - t.Fatal(err) - } - return cr, priv -} - -func Test_getFuncMap_fail(t *testing.T) { - var failMesage string - fns := getFuncMap(&failMesage) - fail := fns["fail"].(func(s string) (string, error)) - s, err := fail("the fail message") - if err == nil { - t.Errorf("fail() error = %v, wantErr %v", err, errors.New("the fail message")) - } - if s != "" { - t.Errorf("fail() = \"%s\", want \"the fail message\"", s) - } - if failMesage != "the fail message" { - t.Errorf("fail() message = \"%s\", want \"the fail message\"", failMesage) - } -} - -func TestWithTemplate(t *testing.T) { - cr, _ := createCertificateRequest(t, "foo", []string{"foo.com", "foo@foo.com", "::1", "https://foo.com"}) - crRSA, _ := createRSACertificateRequest(t, 2048, "foo", []string{"foo.com", "foo@foo.com", "::1", "https://foo.com"}) - type args struct { - text string - data TemplateData - cr *x509.CertificateRequest - } - tests := []struct { - name string - args args - want Options - wantErr bool - }{ - {"leaf", args{DefaultLeafTemplate, TemplateData{ - SubjectKey: Subject{CommonName: "foo"}, - SANsKey: []SubjectAlternativeName{{Type: "dns", Value: "foo.com"}}, - }, cr}, Options{ - CertBuffer: bytes.NewBufferString(`{ - "subject": {"commonName":"foo"}, - "sans": [{"type":"dns","value":"foo.com"}], - "keyUsage": ["digitalSignature"], - "extKeyUsage": ["serverAuth", "clientAuth"] -}`), - }, false}, - {"leafRSA", args{DefaultLeafTemplate, TemplateData{ - SubjectKey: Subject{CommonName: "foo"}, - SANsKey: []SubjectAlternativeName{{Type: "dns", Value: "foo.com"}}, - }, crRSA}, Options{ - CertBuffer: bytes.NewBufferString(`{ - "subject": {"commonName":"foo"}, - "sans": [{"type":"dns","value":"foo.com"}], - "keyUsage": ["keyEncipherment", "digitalSignature"], - "extKeyUsage": ["serverAuth", "clientAuth"] -}`), - }, false}, - {"iid", args{DefaultIIDLeafTemplate, TemplateData{}, cr}, Options{ - CertBuffer: bytes.NewBufferString(`{ - "subject": {"commonName":"foo"}, - "dnsNames": ["foo.com"], - "emailAddresses": ["foo@foo.com"], - "ipAddresses": ["::1"], - "uris": ["https://foo.com"], - "keyUsage": ["digitalSignature"], - "extKeyUsage": ["serverAuth", "clientAuth"] -}`), - }, false}, - {"iidRSAAndEnforced", args{DefaultIIDLeafTemplate, TemplateData{ - SANsKey: []SubjectAlternativeName{{Type: "dns", Value: "foo.com"}}, - }, crRSA}, Options{ - CertBuffer: bytes.NewBufferString(`{ - "subject": {"commonName":"foo"}, - "sans": [{"type":"dns","value":"foo.com"}], - "keyUsage": ["keyEncipherment", "digitalSignature"], - "extKeyUsage": ["serverAuth", "clientAuth"] -}`), - }, false}, - {"fail", args{`{{ fail "a message" }}`, TemplateData{}, cr}, Options{}, true}, - {"error", args{`{{ mustHas 3 .Data }}`, TemplateData{ - "Data": 3, - }, cr}, Options{}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got Options - fn := WithTemplate(tt.args.text, tt.args.data) - if err := fn(tt.args.cr, &got); (err != nil) != tt.wantErr { - t.Errorf("WithTemplate() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("WithTemplate() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestWithTemplateBase64(t *testing.T) { - cr, _ := createCertificateRequest(t, "foo", []string{"foo.com", "foo@foo.com", "::1", "https://foo.com"}) - type args struct { - s string - data TemplateData - cr *x509.CertificateRequest - } - tests := []struct { - name string - args args - want Options - wantErr bool - }{ - {"leaf", args{base64.StdEncoding.EncodeToString([]byte(DefaultLeafTemplate)), TemplateData{ - SubjectKey: Subject{CommonName: "foo"}, - SANsKey: []SubjectAlternativeName{{Type: "dns", Value: "foo.com"}}, - }, cr}, Options{ - CertBuffer: bytes.NewBufferString(`{ - "subject": {"commonName":"foo"}, - "sans": [{"type":"dns","value":"foo.com"}], - "keyUsage": ["digitalSignature"], - "extKeyUsage": ["serverAuth", "clientAuth"] -}`), - }, false}, - {"badBase64", args{"foobar", TemplateData{}, cr}, Options{}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got Options - fn := WithTemplateBase64(tt.args.s, tt.args.data) - if err := fn(tt.args.cr, &got); (err != nil) != tt.wantErr { - t.Errorf("WithTemplateBase64() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("WithTemplateBase64() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestWithTemplateFile(t *testing.T) { - cr, _ := createCertificateRequest(t, "foo", []string{"foo.com", "foo@foo.com", "::1", "https://foo.com"}) - rsa2048, _ := createRSACertificateRequest(t, 2048, "foo", []string{"foo.com", "foo@foo.com", "::1", "https://foo.com"}) - rsa3072, _ := createRSACertificateRequest(t, 3072, "foo", []string{"foo.com", "foo@foo.com", "::1", "https://foo.com"}) - - data := TemplateData{ - SANsKey: []SubjectAlternativeName{ - {Type: "dns", Value: "foo.com"}, - {Type: "email", Value: "root@foo.com"}, - {Type: "ip", Value: "127.0.0.1"}, - {Type: "uri", Value: "uri:foo:bar"}, - }, - TokenKey: map[string]interface{}{ - "iss": "https://iss", - "sub": "sub", - }, - } - type args struct { - path string - data TemplateData - cr *x509.CertificateRequest - } - tests := []struct { - name string - args args - want Options - wantErr bool - }{ - {"example", args{"./testdata/example.tpl", data, cr}, Options{ - CertBuffer: bytes.NewBufferString(`{ - "subject": {"commonName":"foo"}, - "sans": [{"type":"dns","value":"foo.com"},{"type":"email","value":"root@foo.com"},{"type":"ip","value":"127.0.0.1"},{"type":"uri","value":"uri:foo:bar"}], - "emailAddresses": ["foo@foo.com"], - "uris": "https://iss#sub", - "keyUsage": ["digitalSignature"], - "extKeyUsage": ["serverAuth", "clientAuth"] -}`), - }, false}, - {"exampleRSA3072", args{"./testdata/example.tpl", data, rsa3072}, Options{ - CertBuffer: bytes.NewBufferString(`{ - "subject": {"commonName":"foo"}, - "sans": [{"type":"dns","value":"foo.com"},{"type":"email","value":"root@foo.com"},{"type":"ip","value":"127.0.0.1"},{"type":"uri","value":"uri:foo:bar"}], - "emailAddresses": ["foo@foo.com"], - "uris": "https://iss#sub", - "keyUsage": ["keyEncipherment", "digitalSignature"], - "extKeyUsage": ["serverAuth", "clientAuth"] -}`), - }, false}, - {"exampleRSA2048", args{"./testdata/example.tpl", data, rsa2048}, Options{}, true}, - {"missing", args{"./testdata/missing.tpl", data, cr}, Options{}, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - var got Options - fn := WithTemplateFile(tt.args.path, tt.args.data) - if err := fn(tt.args.cr, &got); (err != nil) != tt.wantErr { - t.Errorf("WithTemplateFile() error = %v, wantErr %v", err, tt.wantErr) - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("WithTemplateFile() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/x509util/templates.go b/x509util/templates.go deleted file mode 100644 index b252e832..00000000 --- a/x509util/templates.go +++ /dev/null @@ -1,156 +0,0 @@ -package x509util - -import ( - "crypto/x509" -) - -const ( - SubjectKey = "Subject" - SANsKey = "SANs" - TokenKey = "Token" - InsecureKey = "Insecure" - UserKey = "User" - CertificateRequestKey = "CR" -) - -// TemplateError represents an error in a template produced by the fail -// function. -type TemplateError struct { - Message string -} - -// Error implements the error interface and returns the error string when a -// template executes the `fail "message"` function. -func (e *TemplateError) Error() string { - return e.Message -} - -// TemplateData is an alias for map[string]interface{}. It represents the data -// passed to the templates. -type TemplateData map[string]interface{} - -// NewTemplateData creates a new map for templates data. -func NewTemplateData() TemplateData { - return TemplateData{} -} - -// CreateTemplateData creates a new TemplateData with the given common name and SANs. -func CreateTemplateData(commonName string, sans []string) TemplateData { - return TemplateData{ - SubjectKey: Subject{ - CommonName: commonName, - }, - SANsKey: CreateSANs(sans), - } -} - -// Set sets a key-value pair in the template data. -func (t TemplateData) Set(key string, v interface{}) { - t[key] = v -} - -// SetInsecure sets a key-value pair in the insecure template data. -func (t TemplateData) SetInsecure(key string, v interface{}) { - if m, ok := t[InsecureKey].(TemplateData); ok { - m[key] = v - } else { - t[InsecureKey] = TemplateData{key: v} - } -} - -// SetSubject sets the given subject in the template data. -func (t TemplateData) SetSubject(v Subject) { - t.Set(SubjectKey, v) -} - -// SetCommonName sets the given common name in the subject in the template data. -func (t TemplateData) SetCommonName(cn string) { - s, _ := t[SubjectKey].(Subject) - s.CommonName = cn - t[SubjectKey] = s -} - -// SetSANs sets the given SANs in the template data. -func (t TemplateData) SetSANs(sans []string) { - t.Set(SANsKey, CreateSANs(sans)) -} - -// SetToken sets the given token in the template data. -func (t TemplateData) SetToken(v interface{}) { - t.Set(TokenKey, v) -} - -// SetUserData sets the given user provided object in the insecure template -// data. -func (t TemplateData) SetUserData(v interface{}) { - t.SetInsecure(UserKey, v) -} - -// SetCertificateRequest sets the given certificate request in the insecure -// template data. -func (t TemplateData) SetCertificateRequest(cr *x509.CertificateRequest) { - t.SetInsecure(CertificateRequestKey, newCertificateRequest(cr)) -} - -// DefaultLeafTemplate is the default template used to generate a leaf -// certificate. -const DefaultLeafTemplate = `{ - "subject": {{ toJson .Subject }}, - "sans": {{ toJson .SANs }}, -{{- if typeIs "*rsa.PublicKey" .Insecure.CR.PublicKey }} - "keyUsage": ["keyEncipherment", "digitalSignature"], -{{- else }} - "keyUsage": ["digitalSignature"], -{{- end }} - "extKeyUsage": ["serverAuth", "clientAuth"] -}` - -// DefaultIIDLeafTemplate is the template used by default on instance identity -// provisioners like AWS, GCP or Azure. By default, those provisioners allow the -// SANs provided in the certificate request, but the option `DisableCustomSANs` -// can be provided to force only the verified domains, if the option is true -// `.SANs` will be set with the verified domains. -const DefaultIIDLeafTemplate = `{ - "subject": {"commonName":"{{ .Insecure.CR.Subject.CommonName }}"}, -{{- if .SANs }} - "sans": {{ toJson .SANs }}, -{{- else }} - "dnsNames": {{ toJson .Insecure.CR.DNSNames }}, - "emailAddresses": {{ toJson .Insecure.CR.EmailAddresses }}, - "ipAddresses": {{ toJson .Insecure.CR.IPAddresses }}, - "uris": {{ toJson .Insecure.CR.URIs }}, -{{- end }} -{{- if typeIs "*rsa.PublicKey" .Insecure.CR.PublicKey }} - "keyUsage": ["keyEncipherment", "digitalSignature"], -{{- else }} - "keyUsage": ["digitalSignature"], -{{- end }} - "extKeyUsage": ["serverAuth", "clientAuth"] -}` - -// DefaultIntermediateTemplate is a template that can be used to generate an -// intermediate certificate. -const DefaultIntermediateTemplate = `{ - "subject": {{ toJson .Subject }}, - "keyUsage": ["certSign", "crlSign"], - "basicConstraints": { - "isCA": true, - "maxPathLen": 0 - } -}` - -// DefaultRootTemplate is a template that can be used to generate a root -// certificate. -const DefaultRootTemplate = `{ - "subject": {{ toJson .Subject }}, - "issuer": {{ toJson .Subject }}, - "keyUsage": ["certSign", "crlSign"], - "basicConstraints": { - "isCA": true, - "maxPathLen": 1 - } -}` - -// CertificateRequestTemplate is a template that will sign the given certificate -// request. -const CertificateRequestTemplate = `{{ toJson .Insecure.CR }}` diff --git a/x509util/templates_test.go b/x509util/templates_test.go deleted file mode 100644 index c5f66a97..00000000 --- a/x509util/templates_test.go +++ /dev/null @@ -1,260 +0,0 @@ -package x509util - -import ( - "crypto/x509" - "reflect" - "testing" -) - -func TestTemplateError_Error(t *testing.T) { - type fields struct { - Message string - } - tests := []struct { - name string - fields fields - want string - }{ - {"ok", fields{"an error"}, "an error"}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - e := &TemplateError{ - Message: tt.fields.Message, - } - if got := e.Error(); got != tt.want { - t.Errorf("TemplateError.Error() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestNewTemplateData(t *testing.T) { - tests := []struct { - name string - want TemplateData - }{ - {"ok", TemplateData{}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := NewTemplateData(); !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewTemplateData() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestCreateTemplateData(t *testing.T) { - type args struct { - commonName string - sans []string - } - tests := []struct { - name string - args args - want TemplateData - }{ - {"ok", args{"jane.doe.com", []string{"jane.doe.com", "jane@doe.com", "1.1.1.1", "mailto:jane@doe.com"}}, TemplateData{ - SubjectKey: Subject{CommonName: "jane.doe.com"}, - SANsKey: []SubjectAlternativeName{ - {Type: DNSType, Value: "jane.doe.com"}, - {Type: IPType, Value: "1.1.1.1"}, - {Type: EmailType, Value: "jane@doe.com"}, - {Type: URIType, Value: "mailto:jane@doe.com"}, - }, - }}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := CreateTemplateData(tt.args.commonName, tt.args.sans); !reflect.DeepEqual(got, tt.want) { - t.Errorf("CreateTemplateData() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestTemplateData_SetInsecure(t *testing.T) { - type args struct { - key string - v interface{} - } - tests := []struct { - name string - td TemplateData - args args - want TemplateData - }{ - {"empty", TemplateData{}, args{"foo", "bar"}, TemplateData{InsecureKey: TemplateData{"foo": "bar"}}}, - {"overwrite", TemplateData{InsecureKey: TemplateData{"foo": "bar"}}, args{"foo", "zar"}, TemplateData{InsecureKey: TemplateData{"foo": "zar"}}}, - {"existing", TemplateData{InsecureKey: TemplateData{"foo": "bar"}}, args{"bar", "foo"}, TemplateData{InsecureKey: TemplateData{"foo": "bar", "bar": "foo"}}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.td.SetInsecure(tt.args.key, tt.args.v) - if !reflect.DeepEqual(tt.td, tt.want) { - t.Errorf("TemplateData.SetInsecure() = %v, want %v", tt.td, tt.want) - } - }) - } -} - -func TestTemplateData_SetSubject(t *testing.T) { - type args struct { - v Subject - } - tests := []struct { - name string - td TemplateData - args args - want TemplateData - }{ - {"ok", TemplateData{}, args{Subject{CommonName: "foo"}}, TemplateData{SubjectKey: Subject{CommonName: "foo"}}}, - {"overwrite", TemplateData{SubjectKey: Subject{CommonName: "foo"}}, args{Subject{Province: []string{"CA"}}}, TemplateData{SubjectKey: Subject{Province: []string{"CA"}}}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.td.SetSubject(tt.args.v) - if !reflect.DeepEqual(tt.td, tt.want) { - t.Errorf("TemplateData.SetSubject() = %v, want %v", tt.td, tt.want) - } - }) - } -} - -func TestTemplateData_SetCommonName(t *testing.T) { - type args struct { - cn string - } - tests := []struct { - name string - td TemplateData - args args - want TemplateData - }{ - {"ok", TemplateData{}, args{"commonName"}, TemplateData{SubjectKey: Subject{CommonName: "commonName"}}}, - {"overwrite", TemplateData{SubjectKey: Subject{CommonName: "foo", Province: []string{"CA"}}}, args{"commonName"}, TemplateData{SubjectKey: Subject{CommonName: "commonName", Province: []string{"CA"}}}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.td.SetCommonName(tt.args.cn) - if !reflect.DeepEqual(tt.td, tt.want) { - t.Errorf("TemplateData.SetCommonName() = %v, want %v", tt.td, tt.want) - } - }) - } -} - -func TestTemplateData_SetSANs(t *testing.T) { - type args struct { - sans []string - } - tests := []struct { - name string - td TemplateData - args args - want TemplateData - }{ - {"ok", TemplateData{}, args{[]string{"jane.doe.com", "jane@doe.com", "1.1.1.1", "mailto:jane@doe.com"}}, TemplateData{ - SANsKey: []SubjectAlternativeName{ - {Type: DNSType, Value: "jane.doe.com"}, - {Type: IPType, Value: "1.1.1.1"}, - {Type: EmailType, Value: "jane@doe.com"}, - {Type: URIType, Value: "mailto:jane@doe.com"}, - }}, - }, - {"overwrite", TemplateData{}, args{[]string{"jane.doe.com"}}, TemplateData{ - SANsKey: []SubjectAlternativeName{ - {Type: DNSType, Value: "jane.doe.com"}, - }}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.td.SetSANs(tt.args.sans) - if !reflect.DeepEqual(tt.td, tt.want) { - t.Errorf("TemplateData.SetSANs() = %v, want %v", tt.td, tt.want) - } - }) - } -} - -func TestTemplateData_SetToken(t *testing.T) { - type args struct { - v interface{} - } - tests := []struct { - name string - td TemplateData - args args - want TemplateData - }{ - {"ok", TemplateData{}, args{"token"}, TemplateData{TokenKey: "token"}}, - {"overwrite", TemplateData{TokenKey: "foo"}, args{"token"}, TemplateData{TokenKey: "token"}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.td.SetToken(tt.args.v) - if !reflect.DeepEqual(tt.td, tt.want) { - t.Errorf("TemplateData.SetToken() = %v, want %v", tt.td, tt.want) - } - }) - } -} - -func TestTemplateData_SetUserData(t *testing.T) { - type args struct { - v interface{} - } - tests := []struct { - name string - td TemplateData - args args - want TemplateData - }{ - {"ok", TemplateData{}, args{"userData"}, TemplateData{InsecureKey: TemplateData{UserKey: "userData"}}}, - {"overwrite", TemplateData{InsecureKey: TemplateData{UserKey: "foo"}}, args{"userData"}, TemplateData{InsecureKey: TemplateData{UserKey: "userData"}}}, - {"existing", TemplateData{InsecureKey: TemplateData{"foo": "bar"}}, args{"userData"}, TemplateData{InsecureKey: TemplateData{"foo": "bar", UserKey: "userData"}}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.td.SetUserData(tt.args.v) - if !reflect.DeepEqual(tt.td, tt.want) { - t.Errorf("TemplateData.SetUserData() = %v, want %v", tt.td, tt.want) - } - }) - } -} - -func TestTemplateData_SetCertificateRequest(t *testing.T) { - cr := &x509.CertificateRequest{ - DNSNames: []string{"foo", "bar"}, - } - cr1 := &CertificateRequest{ - DNSNames: []string{"foo", "bar"}, - } - cr2 := &CertificateRequest{ - EmailAddresses: []string{"foo@bar.com"}, - } - type args struct { - cr *x509.CertificateRequest - } - tests := []struct { - name string - td TemplateData - args args - want TemplateData - }{ - {"ok", TemplateData{}, args{cr}, TemplateData{InsecureKey: TemplateData{CertificateRequestKey: cr1}}}, - {"overwrite", TemplateData{InsecureKey: TemplateData{CertificateRequestKey: cr2}}, args{cr}, TemplateData{InsecureKey: TemplateData{CertificateRequestKey: cr1}}}, - {"existing", TemplateData{InsecureKey: TemplateData{"foo": "bar"}}, args{cr}, TemplateData{InsecureKey: TemplateData{"foo": "bar", CertificateRequestKey: cr1}}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.td.SetCertificateRequest(tt.args.cr) - if !reflect.DeepEqual(tt.td, tt.want) { - t.Errorf("TemplateData.SetCertificateRequest() = %v, want %v", tt.td, tt.want) - } - }) - } -} diff --git a/x509util/testdata/ed25519.crt b/x509util/testdata/ed25519.crt deleted file mode 100644 index ef83f0dd..00000000 --- a/x509util/testdata/ed25519.crt +++ /dev/null @@ -1,10 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBWzCCAQ2gAwIBAgIUDIPYISuCyyOYI2Pi95eKQ1vzvZIwBQYDK2VwMCMxITAf -BgNVBAMMGEVkMjU1MTkgdGVzdCBjZXJ0aWZpY2F0ZTAeFw0xOTA1MDYxNzI3MTZa -Fw0xOTA2MDUxNzI3MTZaMCMxITAfBgNVBAMMGEVkMjU1MTkgdGVzdCBjZXJ0aWZp -Y2F0ZTAqMAUGAytlcAMhADYpxWwNTxRsgdD/ddNqcF9pzQ9NZtXamH6CSYmjijz6 -o1MwUTAdBgNVHQ4EFgQUCTs6nUop2JX/aL57Q1Ry4K2i464wHwYDVR0jBBgwFoAU -CTs6nUop2JX/aL57Q1Ry4K2i464wDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQBT -pVgcLDsqnqydTqUdX11tprUI3hKC85cgrvrYmPQagzJrkfUkHcQgfyziTdoTO21U -GtKoKNxgudT0eEs8HJEA ------END CERTIFICATE----- \ No newline at end of file diff --git a/x509util/testdata/example.tpl b/x509util/testdata/example.tpl deleted file mode 100644 index 286edb5c..00000000 --- a/x509util/testdata/example.tpl +++ /dev/null @@ -1,21 +0,0 @@ -{ - "subject": {{ toJson .Insecure.CR.Subject }}, - "sans": {{ toJson .SANs }}, -{{- if .Insecure.CR.EmailAddresses }} - "emailAddresses": {{ toJson .Insecure.CR.EmailAddresses }}, -{{- end }} -{{- if .Token }} - "uris": "{{ .Token.iss }}#{{ .Token.sub }}", -{{- end }} -{{- if typeIs "*rsa.PublicKey" .Insecure.CR.PublicKey }} - {{- if lt .Insecure.CR.PublicKey.Size 384 }} - {{ fail "Key length must be at least 3072 bits" }} - {{- end }} -{{- end }} -{{- if typeIs "*rsa.PublicKey" .Insecure.CR.PublicKey }} - "keyUsage": ["keyEncipherment", "digitalSignature"], -{{- else }} - "keyUsage": ["digitalSignature"], -{{- end }} - "extKeyUsage": ["serverAuth", "clientAuth"] -} \ No newline at end of file diff --git a/x509util/testdata/fullsimple.tpl b/x509util/testdata/fullsimple.tpl deleted file mode 100644 index 0fbf16e6..00000000 --- a/x509util/testdata/fullsimple.tpl +++ /dev/null @@ -1,36 +0,0 @@ -{ - "version": 3, - "subject": "subjectCommonName", - "issuer": "issuerCommonName", - "serialNumber": "0x1234567890", - "dnsNames": "doe.com", - "emailAddresses": "jane@doe.com", - "ipAddresses": "127.0.0.1", - "uris": "https://doe.com", - "sans": [{"type":"dns", "value":"www.doe.com"}], - "extensions": [{"id":"1.2.3.4","critical":true,"value":"ZXh0ZW5zaW9u"}], - "keyUsage": ["digitalSignature"], - "extKeyUsage": ["serverAuth"], - "subjectKeyId": "c3ViamVjdEtleUlk", - "authorityKeyId": "YXV0aG9yaXR5S2V5SWQ=", - "ocspServer": "https://ocsp.server", - "issuingCertificateURL": "https://ca.com", - "crlDistributionPoints": "https://ca.com/ca.crl", - "policyIdentifiers": "5.6.7.8.9.0", - "basicConstraints": { - "isCA": false, - "maxPathLen": 0 - }, - "nameConstraints": { - "critical": true, - "permittedDNSDomains": "jane.doe.com", - "excludedDNSDomains": "john.doe.com", - "permittedIPRanges": "127.0.0.1/32", - "excludedIPRanges": "0.0.0.0/0", - "permittedEmailAddresses": "jane@doe.com", - "excludedEmailAddresses": "john@doe.com", - "permittedURIDomains": "https://jane.doe.com", - "excludedURIDomains": "https://john.doe.com" - }, - "signatureAlgorithm": "Ed25519" -} \ No newline at end of file diff --git a/x509util/testdata/google.crt b/x509util/testdata/google.crt deleted file mode 100644 index c7465328..00000000 --- a/x509util/testdata/google.crt +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIExjCCA66gAwIBAgIRAP1vPiSYwlsdCAAAAABH8DMwDQYJKoZIhvcNAQELBQAw -QjELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczET -MBEGA1UEAxMKR1RTIENBIDFPMTAeFw0yMDA2MTcxNDMxMjJaFw0yMDA5MDkxNDMx -MjJaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH -Ew1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMRcwFQYDVQQDEw53 -d3cuZ29vZ2xlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOFYzxk3Ghpj -cPvTNffIAqyY4p62NFaISj/X66RMGO7rs0RyM2I4ch1hiEP/alWb/+81BzA+R1nK -w0ZKwy86Kh6jggJaMIICVjAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYB -BQUHAwEwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUjqNsRxKnCgdblFHWKj9y+TUG -RSwwHwYDVR0jBBgwFoAUmNH4bhDrz5vsYJ8YkBug630J/SswaAYIKwYBBQUHAQEE -XDBaMCsGCCsGAQUFBzABhh9odHRwOi8vb2NzcC5wa2kuZ29vZy9ndHMxbzFjb3Jl -MCsGCCsGAQUFBzAChh9odHRwOi8vcGtpLmdvb2cvZ3NyMi9HVFMxTzEuY3J0MBkG -A1UdEQQSMBCCDnd3dy5nb29nbGUuY29tMCEGA1UdIAQaMBgwCAYGZ4EMAQICMAwG -CisGAQQB1nkCBQMwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2NybC5wa2kuZ29v -Zy9HVFMxTzFjb3JlLmNybDCCAQIGCisGAQQB1nkCBAIEgfMEgfAA7gB1ALIeBcyL -os2KIE6HZvkruYolIGdr2vpw57JJUy3vi5BeAAABcsLn/z4AAAQDAEYwRAIgTB4w -h0IyoPg5FFwxva6DkCumsXIIhKmHO1xk6HrOpDECIA0c5Y7Yq2mgGu03QPdbPRLb -dBF5ISGltoW1ni2LkLIsAHUAXqdz+d9WwOe1Nkh90EngMnqRmgyEoRIShBh1loFx -RVgAAAFywuf/bgAABAMARjBEAiBUcGdSwAP/13zLGH4xcJ5l7rYoif6lb7Ymv/4u -intIHQIga1qNo582mt6FkPUYcKNq77MR9MPQYjJj+SQg266bbacwDQYJKoZIhvcN -AQELBQADggEBAFKf5klhZEz9FwJlsu6/vnAn9lUKM2hKVKxO4B8T4f6PuY6dlZ3T -g6mvdtsiLjAZ8v6WL6Bn0v15kTh9RhrKwEwVbLLfoXZbQBygV9B/k+HOCgMEb8U3 -Nn7chIP1kTJcyy3BW7TSU0WODmNuEusX0MQcs5TNl/omwdKkzpB6NjXWhnwgzCeD -1nT33vvq1pwgg28ncIBJkqUpDca7hBDQjx1HYEq00euF5/zDWLV98mH8ORPSC2i2 -N7e0OMNT3q2HJRtHor5USuzehR86u3Aie90Z6jBvG/kNsGSgjUAdj/PAlVBj9GcF -KtLwFt/ee1wps0/VGR+gqkoJps0BW8Tv+1k= ------END CERTIFICATE----- \ No newline at end of file diff --git a/x509util/testdata/smallstep.crt b/x509util/testdata/smallstep.crt deleted file mode 100644 index 7cc4b49f..00000000 --- a/x509util/testdata/smallstep.crt +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFZTCCBE2gAwIBAgISBPRFhEdtcBsNSxHgkoSkHpU/MA0GCSqGSIb3DQEBCwUA -MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD -ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0yMDA2MTYwNzE2NTZaFw0y -MDA5MTQwNzE2NTZaMBoxGDAWBgNVBAMMDyouc21hbGxzdGVwLmNvbTCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMgKbXAbD84RScmR3QGVlT27U09ihM6X -XkVp4Ht4fbfm2SvpmOm7kLt5EB3f+0/I3enVsupCoULisUOQpEaoY9wXTjSHRSKl -X3QPzyb8eJrwltxeo0qC5SidaVl2lXcSpKrKMvbp5qfd4hLhAJudEldmTFMQjzou -/FouhzDpP4UDzf8Kc9b8Px27Qw7hg/888lJwCIcTAIVgmHUOxZKmjsHe0rHTNTYi -mE+76udBPyG/Kis+ld2nuufqI/gPrD6gkm5pqSekt057zbk9oJTdBZTuAtaPGlER -bH4G4fbkoT6j0tD7qafeebKATGBtzsK8gqwTZ2Uh4jzd5Ppukg2x4gkCAwEAAaOC -AnMwggJvMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB -BQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUFlxnRkxa25R8P1zsLqtVMHBC -MlUwHwYDVR0jBBgwFoAUqEpqYwR93brm0Tm3pkVl7/Oo7KEwbwYIKwYBBQUHAQEE -YzBhMC4GCCsGAQUFBzABhiJodHRwOi8vb2NzcC5pbnQteDMubGV0c2VuY3J5cHQu -b3JnMC8GCCsGAQUFBzAChiNodHRwOi8vY2VydC5pbnQteDMubGV0c2VuY3J5cHQu -b3JnLzApBgNVHREEIjAggg8qLnNtYWxsc3RlcC5jb22CDXNtYWxsc3RlcC5jb20w -TAYDVR0gBEUwQzAIBgZngQwBAgEwNwYLKwYBBAGC3xMBAQEwKDAmBggrBgEFBQcC -ARYaaHR0cDovL2Nwcy5sZXRzZW5jcnlwdC5vcmcwggEEBgorBgEEAdZ5AgQCBIH1 -BIHyAPAAdgDwlaRZ8gDRgkAQLS+TiI6tS/4dR+OZ4dA0prCoqo6ycwAAAXK8M+PT -AAAEAwBHMEUCIFjO361bM1BiXp9Nexw1KxJX34bI98JkBsHkSz3S+nSZAiEAuBy6 -KBUpYNLT71aPoVWtprZnxThUmKq2gm2Jltp5gMgAdgCyHgXMi6LNiiBOh2b5K7mK -JSBna9r6cOeySVMt74uQXgAAAXK8M+PAAAAEAwBHMEUCIQCIqfGGfeGQHtYnVO4J -UPTqIbNia+Lrbfw9HARElQw4iAIgSspUzNwYHY4cU/BdpfHRaTGzddrTFXhEoCQt -0pbaG0wwDQYJKoZIhvcNAQELBQADggEBAJJgEB70PUChWsbLUyFk1udDPePanU5O -zzSViB5+fesWppG9IDnYnL6KSI+l1jP9jl5Zym79SQ2gSC3xwkswpT8X+Drzup/7 -UV7T5NbHpzkZfg2itsx407yuxoW2L7zihZ4CWm1bodUbPKWNYJAZFrcxqcydwmFn -pvZMoJDP3PW0hejz+gsLe9ZrtXb8q1BLFupHfx+bTx476qhRyL+e2fDi5wQz1Qs2 -jVZGsvv/cljvlRSLZ0NVPeKcRN50f7UQ7OYw+JLR2K5+K1H7oHiu+iUX4F2/PGGm -Tb4HBKgzkJSsS5b3pEp+2U77m9KOBhsiZOVvQ2ECgJ9/eDiy0QCqEyI= ------END CERTIFICATE----- \ No newline at end of file diff --git a/x509util/utils.go b/x509util/utils.go deleted file mode 100644 index ee1caf23..00000000 --- a/x509util/utils.go +++ /dev/null @@ -1,76 +0,0 @@ -package x509util - -import ( - "crypto" - "crypto/rand" - "crypto/sha1" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "math/big" - "net" - "net/url" - - "github.com/pkg/errors" - "github.com/smallstep/cli/crypto/x509util" -) - -// SplitSANs splits a slice of Subject Alternative Names into slices of -// IP Addresses and DNS Names. If an element is not an IP address, then it -// is bucketed as a DNS Name. -func SplitSANs(sans []string) (dnsNames []string, ips []net.IP, emails []string, uris []*url.URL) { - return x509util.SplitSANs(sans) -} - -func CreateSANs(sans []string) []SubjectAlternativeName { - dnsNames, ips, emails, uris := SplitSANs(sans) - sanTypes := make([]SubjectAlternativeName, 0, len(sans)) - for _, v := range dnsNames { - sanTypes = append(sanTypes, SubjectAlternativeName{Type: "dns", Value: v}) - } - for _, v := range ips { - sanTypes = append(sanTypes, SubjectAlternativeName{Type: "ip", Value: v.String()}) - } - for _, v := range emails { - sanTypes = append(sanTypes, SubjectAlternativeName{Type: "email", Value: v}) - } - for _, v := range uris { - sanTypes = append(sanTypes, SubjectAlternativeName{Type: "uri", Value: v.String()}) - } - return sanTypes -} - -// generateSerialNumber returns a random serial number. -func generateSerialNumber() (*big.Int, error) { - limit := new(big.Int).Lsh(big.NewInt(1), 128) - sn, err := rand.Int(rand.Reader, limit) - if err != nil { - return nil, errors.Wrap(err, "error generating serial number") - } - return sn, nil -} - -// subjectPublicKeyInfo is a PKIX public key structure defined in RFC 5280. -type subjectPublicKeyInfo struct { - Algorithm pkix.AlgorithmIdentifier - SubjectPublicKey asn1.BitString -} - -// generateSubjectKeyID generates the key identifier according the the RFC 5280 -// section 4.2.1.2. -// -// The keyIdentifier is composed of the 160-bit SHA-1 hash of the value of the -// BIT STRING subjectPublicKey (excluding the tag, length, and number of unused -// bits). -func generateSubjectKeyID(pub crypto.PublicKey) ([]byte, error) { - b, err := x509.MarshalPKIXPublicKey(pub) - if err != nil { - return nil, errors.Wrap(err, "error marshaling public key") - } - var info subjectPublicKeyInfo - if _, err = asn1.Unmarshal(b, &info); err != nil { - return nil, errors.Wrap(err, "error unmarshaling public key") - } - hash := sha1.Sum(info.SubjectPublicKey.Bytes) - return hash[:], nil -} diff --git a/x509util/utils_test.go b/x509util/utils_test.go deleted file mode 100644 index 7965fe59..00000000 --- a/x509util/utils_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package x509util - -import ( - "crypto" - "crypto/x509" - "encoding/pem" - "io/ioutil" - "reflect" - "testing" -) - -func decodeCertificateFile(t *testing.T, filename string) *x509.Certificate { - t.Helper() - b, err := ioutil.ReadFile(filename) - if err != nil { - t.Fatal(err) - } - block, _ := pem.Decode(b) - if block == nil { - t.Fatal("error decoding pem") - } - crt, err := x509.ParseCertificate(block.Bytes) - if err != nil { - t.Fatal(err) - } - return crt -} - -func Test_generateSubjectKeyID(t *testing.T) { - ecdsaCrt := decodeCertificateFile(t, "testdata/google.crt") - rsaCrt := decodeCertificateFile(t, "testdata/smallstep.crt") - ed25519Crt := decodeCertificateFile(t, "testdata/ed25519.crt") - - type args struct { - pub crypto.PublicKey - } - tests := []struct { - name string - args args - want []byte - wantErr bool - }{ - {"ecdsa", args{ecdsaCrt.PublicKey}, ecdsaCrt.SubjectKeyId, false}, - {"rsa", args{rsaCrt.PublicKey}, rsaCrt.SubjectKeyId, false}, - {"ed25519", args{ed25519Crt.PublicKey}, ed25519Crt.SubjectKeyId, false}, - {"fail", args{[]byte("fail")}, nil, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := generateSubjectKeyID(tt.args.pub) - if (err != nil) != tt.wantErr { - t.Errorf("generateSubjectKeyID() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("generateSubjectKeyID() = %v, want %v", got, tt.want) - } - }) - } -}