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/smallstep/certificates/authority/provisioner"
|
||||
"github.com/smallstep/certificates/x509util"
|
||||
"github.com/smallstep/nosql"
|
||||
"go.step.sm/crypto/x509util"
|
||||
)
|
||||
|
||||
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.
|
||||
//
|
||||
// Note that with certificate templates we are not going to check for the no
|
||||
// presence of other SANs as they will only be set if the templates allows
|
||||
// Note that with certificate templates we are not going to check for the
|
||||
// absence of other SANs as they will only be set if the templates allows
|
||||
// them.
|
||||
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))
|
||||
|
|
|
@ -18,8 +18,8 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/errs"
|
||||
"github.com/smallstep/certificates/sshutil"
|
||||
"github.com/smallstep/certificates/x509util"
|
||||
"github.com/smallstep/cli/jose"
|
||||
"go.step.sm/crypto/x509util"
|
||||
)
|
||||
|
||||
// awsIssuer is the string used as issuer in the generated tokens.
|
||||
|
|
|
@ -15,8 +15,8 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/errs"
|
||||
"github.com/smallstep/certificates/sshutil"
|
||||
"github.com/smallstep/certificates/x509util"
|
||||
"github.com/smallstep/cli/jose"
|
||||
"go.step.sm/crypto/x509util"
|
||||
)
|
||||
|
||||
// azureOIDCBaseURL is the base discovery url for Microsoft Azure tokens.
|
||||
|
|
|
@ -16,8 +16,8 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/errs"
|
||||
"github.com/smallstep/certificates/sshutil"
|
||||
"github.com/smallstep/certificates/x509util"
|
||||
"github.com/smallstep/cli/jose"
|
||||
"go.step.sm/crypto/x509util"
|
||||
)
|
||||
|
||||
// gcpCertsURL is the url that serves Google OAuth2 public keys.
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/errs"
|
||||
"github.com/smallstep/certificates/sshutil"
|
||||
"github.com/smallstep/certificates/x509util"
|
||||
"github.com/smallstep/cli/jose"
|
||||
"go.step.sm/crypto/x509util"
|
||||
)
|
||||
|
||||
// jwtPayload extends jwt.Claims with step attributes.
|
||||
|
|
|
@ -3,6 +3,7 @@ package provisioner
|
|||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
|
@ -11,10 +12,9 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/errs"
|
||||
"github.com/smallstep/certificates/sshutil"
|
||||
"github.com/smallstep/certificates/x509util"
|
||||
"github.com/smallstep/cli/crypto/pemutil"
|
||||
"github.com/smallstep/cli/jose"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"go.step.sm/crypto/x509util"
|
||||
)
|
||||
|
||||
// NOTE: There can be at most one kubernetes service account provisioner configured
|
||||
|
|
|
@ -14,8 +14,8 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/errs"
|
||||
"github.com/smallstep/certificates/sshutil"
|
||||
"github.com/smallstep/certificates/x509util"
|
||||
"github.com/smallstep/cli/jose"
|
||||
"go.step.sm/crypto/x509util"
|
||||
)
|
||||
|
||||
// openIDConfiguration contains the necessary properties in the
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/x509util"
|
||||
"github.com/smallstep/cli/jose"
|
||||
"go.step.sm/crypto/x509util"
|
||||
)
|
||||
|
||||
// CertificateOptions is an interface that returns a list of options passed when
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/smallstep/certificates/x509util"
|
||||
"github.com/smallstep/cli/crypto/pemutil"
|
||||
"go.step.sm/crypto/x509util"
|
||||
)
|
||||
|
||||
func parseCertificateRequest(t *testing.T, filename string) *x509.CertificateRequest {
|
||||
|
|
|
@ -2,6 +2,7 @@ package provisioner
|
|||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
|
@ -13,8 +14,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/x509util"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"go.step.sm/crypto/x509util"
|
||||
)
|
||||
|
||||
// DefaultCertValidity is the default validity for a certificate if none is specified.
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/errs"
|
||||
"github.com/smallstep/certificates/sshutil"
|
||||
"github.com/smallstep/certificates/x509util"
|
||||
"github.com/smallstep/cli/jose"
|
||||
"go.step.sm/crypto/x509util"
|
||||
)
|
||||
|
||||
// x5cPayload extends jwt.Claims with step attributes.
|
||||
|
|
|
@ -16,11 +16,11 @@ import (
|
|||
"github.com/smallstep/certificates/authority/provisioner"
|
||||
"github.com/smallstep/certificates/db"
|
||||
"github.com/smallstep/certificates/errs"
|
||||
"github.com/smallstep/certificates/x509util"
|
||||
"github.com/smallstep/cli/crypto/pemutil"
|
||||
"github.com/smallstep/cli/crypto/tlsutil"
|
||||
x509legacy "github.com/smallstep/cli/crypto/x509util"
|
||||
"github.com/smallstep/cli/jose"
|
||||
"go.step.sm/crypto/x509util"
|
||||
)
|
||||
|
||||
// GetTLSOptions returns the tls options configured.
|
||||
|
|
5
go.mod
5
go.mod
|
@ -4,7 +4,7 @@ go 1.13
|
|||
|
||||
require (
|
||||
cloud.google.com/go v0.51.0
|
||||
github.com/Masterminds/sprig/v3 v3.0.0
|
||||
github.com/Masterminds/sprig/v3 v3.1.0
|
||||
github.com/aws/aws-sdk-go v1.30.29
|
||||
github.com/go-chi/chi v4.0.2+incompatible
|
||||
github.com/go-piv/piv-go v1.5.0
|
||||
|
@ -17,7 +17,8 @@ require (
|
|||
github.com/smallstep/cli v0.14.7-rc.1.0.20200721180458-731b7c4c8c95
|
||||
github.com/smallstep/nosql v0.3.0
|
||||
github.com/urfave/cli v1.22.2
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59
|
||||
go.step.sm/crypto v0.0.0-20200805202904-ec18b6df3cf0
|
||||
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2
|
||||
google.golang.org/api v0.15.0
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb
|
||||
|
|
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/v3 v3.0.1 h1:2kKm5lb7dKVrt5TYUiAavE6oFc1cFT0057UVGT+JqLk=
|
||||
github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/semver/v3 v3.1.0 h1:Y2lUDsFKVRSYGojLJ1yLxSXdMmMYTYls0rCvoqmMUQk=
|
||||
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/sprig/v3 v3.0.0 h1:KSQz7Nb08/3VU9E4ns29dDxcczhOD1q7O1UfM4G3t3g=
|
||||
github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U=
|
||||
github.com/Masterminds/sprig/v3 v3.1.0 h1:j7GpgZ7PdFqNsmncycTHsLmVPf5/3wJtlgW9TNDYD9Y=
|
||||
github.com/Masterminds/sprig/v3 v3.1.0/go.mod h1:ONGMf7UfYGAbMXCZmQLy8x3lCDIPrEZE/rU8pmrbihA=
|
||||
github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
|
||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
|
@ -274,10 +278,14 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
|
|||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0=
|
||||
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
||||
github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs=
|
||||
github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428/go.mod h1:uhpZMVGznybq1itEKXj6RYw9I71qK4kH+OGMjRC4KEo=
|
||||
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
|
||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
|
@ -569,6 +577,8 @@ go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
|||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.step.sm/crypto v0.0.0-20200805202904-ec18b6df3cf0 h1:FymMl8TrXGxFf80BWpO0CnkSfLnw0BkDdRrhbMGf5zE=
|
||||
go.step.sm/crypto v0.0.0-20200805202904-ec18b6df3cf0/go.mod h1:8VYxmvSKt5yOTBx3MGsD2Gk4F1Es/3FIxrjnfeYWE8U=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM=
|
||||
|
@ -597,6 +607,8 @@ golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90
|
|||
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8=
|
||||
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
|
|
@ -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