forked from TrueCloudLab/certificates
Merge branch 'cert-templates' into ssh-cert-templates
This commit is contained in:
commit
f437b86a7b
37 changed files with 31 additions and 4423 deletions
|
@ -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
|
||||||
|
@ -301,8 +301,8 @@ func (o *order) finalize(db nosql.DB, csr *x509.CertificateRequest, auth SignAut
|
||||||
|
|
||||||
// Validate identifier names against CSR alternative names.
|
// Validate identifier names against CSR alternative names.
|
||||||
//
|
//
|
||||||
// Note that with certificate templates we are not going to check for the no
|
// Note that with certificate templates we are not going to check for the
|
||||||
// presence of other SANs as they will only be set if the templates allows
|
// absence of other SANs as they will only be set if the templates allows
|
||||||
// them.
|
// them.
|
||||||
if len(csr.DNSNames) != len(orderNames) {
|
if len(csr.DNSNames) != len(orderNames) {
|
||||||
return nil, BadCSRErr(errors.Errorf("CSR names do not match identifiers exactly: CSR names = %v, Order names = %v", csr.DNSNames, orderNames))
|
return nil, BadCSRErr(errors.Errorf("CSR names do not match identifiers exactly: CSR names = %v, Order names = %v", csr.DNSNames, orderNames))
|
||||||
|
|
|
@ -18,8 +18,8 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/errs"
|
"github.com/smallstep/certificates/errs"
|
||||||
"github.com/smallstep/certificates/sshutil"
|
"github.com/smallstep/certificates/sshutil"
|
||||||
"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.
|
||||||
|
|
|
@ -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/sshutil"
|
"github.com/smallstep/certificates/sshutil"
|
||||||
"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.
|
||||||
|
|
|
@ -16,8 +16,8 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/errs"
|
"github.com/smallstep/certificates/errs"
|
||||||
"github.com/smallstep/certificates/sshutil"
|
"github.com/smallstep/certificates/sshutil"
|
||||||
"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.
|
||||||
|
|
|
@ -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/sshutil"
|
"github.com/smallstep/certificates/sshutil"
|
||||||
"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.
|
||||||
|
|
|
@ -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"
|
||||||
|
@ -11,10 +12,9 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/errs"
|
"github.com/smallstep/certificates/errs"
|
||||||
"github.com/smallstep/certificates/sshutil"
|
"github.com/smallstep/certificates/sshutil"
|
||||||
"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
|
||||||
|
|
|
@ -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/sshutil"
|
"github.com/smallstep/certificates/sshutil"
|
||||||
"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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/errs"
|
"github.com/smallstep/certificates/errs"
|
||||||
"github.com/smallstep/certificates/sshutil"
|
"github.com/smallstep/certificates/sshutil"
|
||||||
"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.
|
||||||
|
|
|
@ -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
5
go.mod
|
@ -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
12
go.sum
|
@ -28,8 +28,12 @@ github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITg
|
||||||
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
github.com/Masterminds/semver 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=
|
||||||
|
|
|
@ -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 a JSON
|
|
||||||
// object in the Subject struct or a string as just the subject common name.
|
|
||||||
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
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// newExtensions 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
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
127
x509util/name.go
127
x509util/name.go
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 }}`
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
10
x509util/testdata/ed25519.crt
vendored
10
x509util/testdata/ed25519.crt
vendored
|
@ -1,10 +0,0 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIBWzCCAQ2gAwIBAgIUDIPYISuCyyOYI2Pi95eKQ1vzvZIwBQYDK2VwMCMxITAf
|
|
||||||
BgNVBAMMGEVkMjU1MTkgdGVzdCBjZXJ0aWZpY2F0ZTAeFw0xOTA1MDYxNzI3MTZa
|
|
||||||
Fw0xOTA2MDUxNzI3MTZaMCMxITAfBgNVBAMMGEVkMjU1MTkgdGVzdCBjZXJ0aWZp
|
|
||||||
Y2F0ZTAqMAUGAytlcAMhADYpxWwNTxRsgdD/ddNqcF9pzQ9NZtXamH6CSYmjijz6
|
|
||||||
o1MwUTAdBgNVHQ4EFgQUCTs6nUop2JX/aL57Q1Ry4K2i464wHwYDVR0jBBgwFoAU
|
|
||||||
CTs6nUop2JX/aL57Q1Ry4K2i464wDwYDVR0TAQH/BAUwAwEB/zAFBgMrZXADQQBT
|
|
||||||
pVgcLDsqnqydTqUdX11tprUI3hKC85cgrvrYmPQagzJrkfUkHcQgfyziTdoTO21U
|
|
||||||
GtKoKNxgudT0eEs8HJEA
|
|
||||||
-----END CERTIFICATE-----
|
|
21
x509util/testdata/example.tpl
vendored
21
x509util/testdata/example.tpl
vendored
|
@ -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"]
|
|
||||||
}
|
|
36
x509util/testdata/fullsimple.tpl
vendored
36
x509util/testdata/fullsimple.tpl
vendored
|
@ -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"
|
|
||||||
}
|
|
28
x509util/testdata/google.crt
vendored
28
x509util/testdata/google.crt
vendored
|
@ -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-----
|
|
31
x509util/testdata/smallstep.crt
vendored
31
x509util/testdata/smallstep.crt
vendored
|
@ -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-----
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue