Use x509util from go.step.sm/crypto/x509util

This commit is contained in:
Mariano Cano 2020-08-05 16:02:46 -07:00
parent 53eea843bc
commit c8d225a763
37 changed files with 29 additions and 4421 deletions

View file

@ -10,8 +10,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/x509util"
"github.com/smallstep/nosql" "github.com/smallstep/nosql"
"go.step.sm/crypto/x509util"
) )
var defaultOrderExpiry = time.Hour * 24 var defaultOrderExpiry = time.Hour * 24

View file

@ -17,8 +17,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/errs"
"github.com/smallstep/certificates/x509util"
"github.com/smallstep/cli/jose" "github.com/smallstep/cli/jose"
"go.step.sm/crypto/x509util"
) )
// awsIssuer is the string used as issuer in the generated tokens. // awsIssuer is the string used as issuer in the generated tokens.

View file

@ -14,8 +14,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/errs"
"github.com/smallstep/certificates/x509util"
"github.com/smallstep/cli/jose" "github.com/smallstep/cli/jose"
"go.step.sm/crypto/x509util"
) )
// azureOIDCBaseURL is the base discovery url for Microsoft Azure tokens. // azureOIDCBaseURL is the base discovery url for Microsoft Azure tokens.

View file

@ -15,8 +15,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/errs"
"github.com/smallstep/certificates/x509util"
"github.com/smallstep/cli/jose" "github.com/smallstep/cli/jose"
"go.step.sm/crypto/x509util"
) )
// gcpCertsURL is the url that serves Google OAuth2 public keys. // gcpCertsURL is the url that serves Google OAuth2 public keys.

View file

@ -8,8 +8,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/errs"
"github.com/smallstep/certificates/x509util"
"github.com/smallstep/cli/jose" "github.com/smallstep/cli/jose"
"go.step.sm/crypto/x509util"
) )
// jwtPayload extends jwt.Claims with step attributes. // jwtPayload extends jwt.Claims with step attributes.

View file

@ -3,6 +3,7 @@ package provisioner
import ( import (
"context" "context"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
@ -10,10 +11,9 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/errs"
"github.com/smallstep/certificates/x509util"
"github.com/smallstep/cli/crypto/pemutil" "github.com/smallstep/cli/crypto/pemutil"
"github.com/smallstep/cli/jose" "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 // NOTE: There can be at most one kubernetes service account provisioner configured

View file

@ -13,8 +13,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/errs"
"github.com/smallstep/certificates/x509util"
"github.com/smallstep/cli/jose" "github.com/smallstep/cli/jose"
"go.step.sm/crypto/x509util"
) )
// openIDConfiguration contains the necessary properties in the // openIDConfiguration contains the necessary properties in the

View file

@ -5,8 +5,8 @@ import (
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/x509util"
"github.com/smallstep/cli/jose" "github.com/smallstep/cli/jose"
"go.step.sm/crypto/x509util"
) )
// CertificateOptions is an interface that returns a list of options passed when // CertificateOptions is an interface that returns a list of options passed when

View file

@ -7,8 +7,8 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/smallstep/certificates/x509util"
"github.com/smallstep/cli/crypto/pemutil" "github.com/smallstep/cli/crypto/pemutil"
"go.step.sm/crypto/x509util"
) )
func parseCertificateRequest(t *testing.T, filename string) *x509.CertificateRequest { func parseCertificateRequest(t *testing.T, filename string) *x509.CertificateRequest {

View file

@ -2,6 +2,7 @@ package provisioner
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
@ -13,8 +14,7 @@ import (
"time" "time"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/x509util" "go.step.sm/crypto/x509util"
"golang.org/x/crypto/ed25519"
) )
// DefaultCertValidity is the default validity for a certificate if none is specified. // DefaultCertValidity is the default validity for a certificate if none is specified.

View file

@ -9,8 +9,8 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/errs"
"github.com/smallstep/certificates/x509util"
"github.com/smallstep/cli/jose" "github.com/smallstep/cli/jose"
"go.step.sm/crypto/x509util"
) )
// x5cPayload extends jwt.Claims with step attributes. // x5cPayload extends jwt.Claims with step attributes.

View file

@ -16,11 +16,11 @@ import (
"github.com/smallstep/certificates/authority/provisioner" "github.com/smallstep/certificates/authority/provisioner"
"github.com/smallstep/certificates/db" "github.com/smallstep/certificates/db"
"github.com/smallstep/certificates/errs" "github.com/smallstep/certificates/errs"
"github.com/smallstep/certificates/x509util"
"github.com/smallstep/cli/crypto/pemutil" "github.com/smallstep/cli/crypto/pemutil"
"github.com/smallstep/cli/crypto/tlsutil" "github.com/smallstep/cli/crypto/tlsutil"
x509legacy "github.com/smallstep/cli/crypto/x509util" x509legacy "github.com/smallstep/cli/crypto/x509util"
"github.com/smallstep/cli/jose" "github.com/smallstep/cli/jose"
"go.step.sm/crypto/x509util"
) )
// GetTLSOptions returns the tls options configured. // GetTLSOptions returns the tls options configured.

5
go.mod
View file

@ -4,7 +4,7 @@ go 1.13
require ( require (
cloud.google.com/go v0.51.0 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/aws/aws-sdk-go v1.30.29
github.com/go-chi/chi v4.0.2+incompatible github.com/go-chi/chi v4.0.2+incompatible
github.com/go-piv/piv-go v1.5.0 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/cli v0.14.7-rc.1.0.20200721180458-731b7c4c8c95
github.com/smallstep/nosql v0.3.0 github.com/smallstep/nosql v0.3.0
github.com/urfave/cli v1.22.2 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 golang.org/x/net v0.0.0-20200202094626-16171245cfb2
google.golang.org/api v0.15.0 google.golang.org/api v0.15.0
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb

12
go.sum
View file

@ -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 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 h1:2kKm5lb7dKVrt5TYUiAavE6oFc1cFT0057UVGT+JqLk=
github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 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 h1:KSQz7Nb08/3VU9E4ns29dDxcczhOD1q7O1UfM4G3t3g=
github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U= 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/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 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= 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/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 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= 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/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/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 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= 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 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 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.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 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.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.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM= 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-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 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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-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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=

View file

@ -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
}

View file

@ -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)
}
})
}
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
}
})
}
}

View file

@ -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)
}
}
})
}
}

View file

@ -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
}

View file

@ -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)
}
})
}
}

View file

@ -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
}

View file

@ -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)
}
})
}
}

View file

@ -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,
}
}

View file

@ -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)
}
})
}
}

View file

@ -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)
}
}

View file

@ -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)
}
})
}
}

View file

@ -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 }}`

View file

@ -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)
}
})
}
}

View file

@ -1,10 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIBWzCCAQ2gAwIBAgIUDIPYISuCyyOYI2Pi95eKQ1vzvZIwBQYDK2VwMCMxITAf
BgNVBAMMGEVkMjU1MTkgdGVzdCBjZXJ0aWZpY2F0ZTAeFw0xOTA1MDYxNzI3MTZa
Fw0xOTA2MDUxNzI3MTZaMCMxITAfBgNVBAMMGEVkMjU1MTkgdGVzdCBjZXJ0aWZp
Y2F0ZTAqMAUGAytlcAMhADYpxWwNTxRsgdD/ddNqcF9pzQ9NZtXamH6CSYmjijz6
o1MwUTAdBgNVHQ4EFgQUCTs6nUop2JX/aL57Q1Ry4K2i464wHwYDVR0jBBgwFoAU
CTs6nUop2JX/aL57Q1Ry4K2i464wDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQBT
pVgcLDsqnqydTqUdX11tprUI3hKC85cgrvrYmPQagzJrkfUkHcQgfyziTdoTO21U
GtKoKNxgudT0eEs8HJEA
-----END CERTIFICATE-----

View file

@ -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"]
}

View file

@ -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"
}

View file

@ -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-----

View file

@ -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-----

View file

@ -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
}

View file

@ -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)
}
})
}
}