Add identity client and move identity to a new package.
This commit is contained in:
parent
89b216c21e
commit
0d9a9e083e
19 changed files with 667 additions and 86 deletions
36
ca/client.go
36
ca/client.go
|
@ -25,13 +25,18 @@ import (
|
||||||
"github.com/smallstep/certificates/api"
|
"github.com/smallstep/certificates/api"
|
||||||
"github.com/smallstep/certificates/authority"
|
"github.com/smallstep/certificates/authority"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
|
"github.com/smallstep/certificates/ca/identity"
|
||||||
"github.com/smallstep/cli/config"
|
"github.com/smallstep/cli/config"
|
||||||
"github.com/smallstep/cli/crypto/keys"
|
"github.com/smallstep/cli/crypto/keys"
|
||||||
|
"github.com/smallstep/cli/crypto/pemutil"
|
||||||
"github.com/smallstep/cli/crypto/x509util"
|
"github.com/smallstep/cli/crypto/x509util"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"gopkg.in/square/go-jose.v2/jwt"
|
"gopkg.in/square/go-jose.v2/jwt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DisableIdentity is a global variable to disable the identity.
|
||||||
|
var DisableIdentity = false
|
||||||
|
|
||||||
// UserAgent will set the User-Agent header in the client requests.
|
// UserAgent will set the User-Agent header in the client requests.
|
||||||
var UserAgent = "step-http-client/1.0"
|
var UserAgent = "step-http-client/1.0"
|
||||||
|
|
||||||
|
@ -120,26 +125,18 @@ func (o *clientOptions) applyDefaultIdentity() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not load an identity if something fails
|
// Do not load an identity if something fails
|
||||||
b, err := ioutil.ReadFile(IdentityFile)
|
i, err := identity.LoadDefaultIdentity()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var identity Identity
|
if err := i.Validate(); err != nil {
|
||||||
if err := json.Unmarshal(b, &identity); err != nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := identity.Validate(); err != nil {
|
crt, err := i.TLSCertificate()
|
||||||
return nil
|
|
||||||
}
|
|
||||||
opts, err := identity.Options()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, fn := range opts {
|
o.certificate = crt
|
||||||
if err := fn(o); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1111,6 +1108,21 @@ func CreateCertificateRequest(commonName string, sans ...string) (*api.Certifica
|
||||||
return createCertificateRequest(commonName, sans, key)
|
return createCertificateRequest(commonName, sans, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateIdentityRequest returns a new CSR to create the identity. If an
|
||||||
|
// identity was already present it reuses the private key.
|
||||||
|
func CreateIdentityRequest(commonName string, sans ...string) (*api.CertificateRequest, crypto.PrivateKey, error) {
|
||||||
|
var identityKey crypto.PrivateKey
|
||||||
|
if i, err := identity.LoadDefaultIdentity(); err == nil && i.Key != "" {
|
||||||
|
if k, err := pemutil.Read(i.Key); err == nil {
|
||||||
|
identityKey = k
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if identityKey == nil {
|
||||||
|
return CreateCertificateRequest(commonName, sans...)
|
||||||
|
}
|
||||||
|
return createCertificateRequest(commonName, sans, identityKey)
|
||||||
|
}
|
||||||
|
|
||||||
func createCertificateRequest(commonName string, sans []string, key crypto.PrivateKey) (*api.CertificateRequest, crypto.PrivateKey, error) {
|
func createCertificateRequest(commonName string, sans []string, key crypto.PrivateKey) (*api.CertificateRequest, crypto.PrivateKey, error) {
|
||||||
if len(sans) == 0 {
|
if len(sans) == 0 {
|
||||||
sans = []string{commonName}
|
sans = []string{commonName}
|
||||||
|
|
105
ca/identity/client.go
Normal file
105
ca/identity/client.go
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
package identity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client wraps http.Client with a transport using the step root and identity.
|
||||||
|
type Client struct {
|
||||||
|
CaURL *url.URL
|
||||||
|
*http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResolveReference resolves the given reference from the CaURL.
|
||||||
|
func (c *Client) ResolveReference(ref *url.URL) *url.URL {
|
||||||
|
return c.CaURL.ResolveReference(ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadStepClient configures an http.Client with the root in
|
||||||
|
// $STEPPATH/config/defaults.json and the identity defined in
|
||||||
|
// $STEPPATH/config/identity.json
|
||||||
|
func LoadClient() (*Client, error) {
|
||||||
|
b, err := ioutil.ReadFile(DefaultsFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error reading %s", DefaultsFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaults defaultsConfig
|
||||||
|
if err := json.Unmarshal(b, &defaults); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error unmarshaling %s", DefaultsFile)
|
||||||
|
}
|
||||||
|
if err := defaults.Validate(); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error validating %s", DefaultsFile)
|
||||||
|
}
|
||||||
|
caURL, err := url.Parse(defaults.CaURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error validating %s", DefaultsFile)
|
||||||
|
}
|
||||||
|
if caURL.Scheme == "" {
|
||||||
|
caURL.Scheme = "https"
|
||||||
|
}
|
||||||
|
|
||||||
|
identity, err := LoadDefaultIdentity()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := identity.Validate(); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error validating %s", IdentityFile)
|
||||||
|
}
|
||||||
|
if kind := identity.Kind(); kind != MutualTLS {
|
||||||
|
return nil, errors.Errorf("unsupported identity %s: only mTLS is currently supported", kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare transport with information in defaults.json and identity.json
|
||||||
|
tr := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
|
tr.TLSClientConfig = &tls.Config{}
|
||||||
|
|
||||||
|
// RootCAs
|
||||||
|
b, err = ioutil.ReadFile(defaults.Root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "error loading %s", defaults.Root)
|
||||||
|
}
|
||||||
|
pool := x509.NewCertPool()
|
||||||
|
if pool.AppendCertsFromPEM(b) {
|
||||||
|
tr.TLSClientConfig.RootCAs = pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Certificate
|
||||||
|
crt, err := tls.LoadX509KeyPair(identity.Certificate, identity.Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error loading certificate: %v", err)
|
||||||
|
}
|
||||||
|
tr.TLSClientConfig.Certificates = []tls.Certificate{crt}
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
CaURL: caURL,
|
||||||
|
Client: &http.Client{
|
||||||
|
Transport: tr,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultsConfig struct {
|
||||||
|
CaURL string `json:"ca-url"`
|
||||||
|
Root string `json:"root"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *defaultsConfig) Validate() error {
|
||||||
|
switch {
|
||||||
|
case c.CaURL == "":
|
||||||
|
return fmt.Errorf("missing or invalid `ca-url` property")
|
||||||
|
case c.Root == "":
|
||||||
|
return fmt.Errorf("missing or invalid `root` property")
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
133
ca/identity/client_test.go
Normal file
133
ca/identity/client_test.go
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
package identity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClient_ResolveReference(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
CaURL *url.URL
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
ref *url.URL
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want *url.URL
|
||||||
|
}{
|
||||||
|
{"ok", fields{&url.URL{Scheme: "https", Host: "localhost"}}, args{&url.URL{Path: "/foo"}}, &url.URL{Scheme: "https", Host: "localhost", Path: "/foo"}},
|
||||||
|
{"ok", fields{&url.URL{Scheme: "https", Host: "localhost", Path: "/bar"}}, args{&url.URL{Path: "/foo"}}, &url.URL{Scheme: "https", Host: "localhost", Path: "/foo"}},
|
||||||
|
{"ok", fields{&url.URL{Scheme: "https", Host: "localhost"}}, args{&url.URL{Path: "/foo", RawQuery: "foo=bar"}}, &url.URL{Scheme: "https", Host: "localhost", Path: "/foo", RawQuery: "foo=bar"}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
c := &Client{
|
||||||
|
CaURL: tt.fields.CaURL,
|
||||||
|
}
|
||||||
|
if got := c.ResolveReference(tt.args.ref); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("Client.ResolveReference() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadClient(t *testing.T) {
|
||||||
|
oldIdentityFile := IdentityFile
|
||||||
|
oldDefaultsFile := DefaultsFile
|
||||||
|
defer func() {
|
||||||
|
IdentityFile = oldIdentityFile
|
||||||
|
DefaultsFile = oldDefaultsFile
|
||||||
|
}()
|
||||||
|
|
||||||
|
crt, err := tls.LoadX509KeyPair("testdata/identity/identity.crt", "testdata/identity/identity_key")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadFile("testdata/certs/root_ca.crt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pool := x509.NewCertPool()
|
||||||
|
pool.AppendCertsFromPEM(b)
|
||||||
|
|
||||||
|
tr := http.DefaultTransport.(*http.Transport).Clone()
|
||||||
|
tr.TLSClientConfig = &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{crt},
|
||||||
|
RootCAs: pool,
|
||||||
|
}
|
||||||
|
expected := &Client{
|
||||||
|
CaURL: &url.URL{Scheme: "https", Host: "127.0.0.1"},
|
||||||
|
Client: &http.Client{
|
||||||
|
Transport: tr,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
prepare func()
|
||||||
|
want *Client
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"ok", func() { IdentityFile = "testdata/config/identity.json"; DefaultsFile = "testdata/config/defaults.json" }, expected, false},
|
||||||
|
{"fail identity", func() { IdentityFile = "testdata/config/missing.json"; DefaultsFile = "testdata/config/defaults.json" }, nil, true},
|
||||||
|
{"fail identity", func() { IdentityFile = "testdata/config/fail.json"; DefaultsFile = "testdata/config/defaults.json" }, nil, true},
|
||||||
|
{"fail defaults", func() { IdentityFile = "testdata/config/identity.json"; DefaultsFile = "testdata/config/missing.json" }, nil, true},
|
||||||
|
{"fail defaults", func() { IdentityFile = "testdata/config/identity.json"; DefaultsFile = "testdata/config/fail.json" }, nil, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tt.prepare()
|
||||||
|
got, err := LoadClient()
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("LoadClient() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tt.want == nil {
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("LoadClient() = %#v, want %#v", got, tt.want)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !reflect.DeepEqual(got.CaURL, tt.want.CaURL) ||
|
||||||
|
!reflect.DeepEqual(got.Client.Transport.(*http.Transport).TLSClientConfig.RootCAs, tt.want.Client.Transport.(*http.Transport).TLSClientConfig.RootCAs) ||
|
||||||
|
!reflect.DeepEqual(got.Client.Transport.(*http.Transport).TLSClientConfig.Certificates, tt.want.Client.Transport.(*http.Transport).TLSClientConfig.Certificates) {
|
||||||
|
t.Errorf("LoadClient() = %#v, want %#v", got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_defaultsConfig_Validate(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
CaURL string
|
||||||
|
Root string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"ok", fields{"https://127.0.0.1", "root_ca.crt"}, false},
|
||||||
|
{"fail ca-url", fields{"", "root_ca.crt"}, true},
|
||||||
|
{"fail root", fields{"https://127.0.0.1", ""}, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
c := &defaultsConfig{
|
||||||
|
CaURL: tt.fields.CaURL,
|
||||||
|
Root: tt.fields.Root,
|
||||||
|
}
|
||||||
|
if err := c.Validate(); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("defaultsConfig.Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package ca
|
package identity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -8,7 +8,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -20,17 +19,14 @@ import (
|
||||||
"github.com/smallstep/cli/crypto/pemutil"
|
"github.com/smallstep/cli/crypto/pemutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IdentityType represents the different types of identity files.
|
// Type represents the different types of identity files.
|
||||||
type IdentityType string
|
type Type string
|
||||||
|
|
||||||
// DisableIdentity is a global variable to disable the identity.
|
|
||||||
var DisableIdentity = false
|
|
||||||
|
|
||||||
// Disabled represents a disabled identity type
|
// Disabled represents a disabled identity type
|
||||||
const Disabled IdentityType = ""
|
const Disabled Type = ""
|
||||||
|
|
||||||
// MutualTLS represents the identity using mTLS
|
// MutualTLS represents the identity using mTLS
|
||||||
const MutualTLS IdentityType = "mTLS"
|
const MutualTLS Type = "mTLS"
|
||||||
|
|
||||||
// DefaultLeeway is the duration for matching not before claims.
|
// DefaultLeeway is the duration for matching not before claims.
|
||||||
const DefaultLeeway = 1 * time.Minute
|
const DefaultLeeway = 1 * time.Minute
|
||||||
|
@ -38,6 +34,9 @@ const DefaultLeeway = 1 * time.Minute
|
||||||
// IdentityFile contains the location of the identity file.
|
// IdentityFile contains the location of the identity file.
|
||||||
var IdentityFile = filepath.Join(config.StepPath(), "config", "identity.json")
|
var IdentityFile = filepath.Join(config.StepPath(), "config", "identity.json")
|
||||||
|
|
||||||
|
// DefaultsFile contains the location of the defaults file.
|
||||||
|
var DefaultsFile = filepath.Join(config.StepPath(), "config", "defaults.json")
|
||||||
|
|
||||||
// Identity represents the identity file that can be used to authenticate with
|
// Identity represents the identity file that can be used to authenticate with
|
||||||
// the CA.
|
// the CA.
|
||||||
type Identity struct {
|
type Identity struct {
|
||||||
|
@ -46,26 +45,11 @@ type Identity struct {
|
||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIdentityRequest returns a new CSR to create the identity. If an identity
|
|
||||||
// was already present it reuses the private key.
|
|
||||||
func NewIdentityRequest(commonName string, sans ...string) (*api.CertificateRequest, crypto.PrivateKey, error) {
|
|
||||||
var identityKey crypto.PrivateKey
|
|
||||||
if i, err := LoadDefaultIdentity(); err == nil && i.Key != "" {
|
|
||||||
if k, err := pemutil.Read(i.Key); err == nil {
|
|
||||||
identityKey = k
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if identityKey == nil {
|
|
||||||
return CreateCertificateRequest(commonName, sans...)
|
|
||||||
}
|
|
||||||
return createCertificateRequest(commonName, sans, identityKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadDefaultIdentity loads the default identity.
|
// LoadDefaultIdentity loads the default identity.
|
||||||
func LoadDefaultIdentity() (*Identity, error) {
|
func LoadDefaultIdentity() (*Identity, error) {
|
||||||
b, err := ioutil.ReadFile(IdentityFile)
|
b, err := ioutil.ReadFile(IdentityFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error reading identity json")
|
return nil, errors.Wrapf(err, "error reading %s", IdentityFile)
|
||||||
}
|
}
|
||||||
identity := new(Identity)
|
identity := new(Identity)
|
||||||
if err := json.Unmarshal(b, &identity); err != nil {
|
if err := json.Unmarshal(b, &identity); err != nil {
|
||||||
|
@ -137,14 +121,14 @@ func WriteDefaultIdentity(certChain []api.Certificate, key crypto.PrivateKey) er
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kind returns the type for the given identity.
|
// Kind returns the type for the given identity.
|
||||||
func (i *Identity) Kind() IdentityType {
|
func (i *Identity) Kind() Type {
|
||||||
switch strings.ToLower(i.Type) {
|
switch strings.ToLower(i.Type) {
|
||||||
case "":
|
case "":
|
||||||
return Disabled
|
return Disabled
|
||||||
case "mtls":
|
case "mtls":
|
||||||
return MutualTLS
|
return MutualTLS
|
||||||
default:
|
default:
|
||||||
return IdentityType(i.Type)
|
return Type(i.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,74 +144,55 @@ func (i *Identity) Validate() error {
|
||||||
if i.Key == "" {
|
if i.Key == "" {
|
||||||
return errors.New("identity.key cannot be empty")
|
return errors.New("identity.key cannot be empty")
|
||||||
}
|
}
|
||||||
|
if err := fileExists(i.Certificate); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := fileExists(i.Key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("unsupported identity type %s", i.Type)
|
return errors.Errorf("unsupported identity type %s", i.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options returns the ClientOptions used for the given identity.
|
// TLSCertificate returns a tls.Certificate for the identity.
|
||||||
func (i *Identity) Options() ([]ClientOption, error) {
|
func (i *Identity) TLSCertificate() (tls.Certificate, error) {
|
||||||
|
fail := func(err error) (tls.Certificate, error) { return tls.Certificate{}, err }
|
||||||
switch i.Kind() {
|
switch i.Kind() {
|
||||||
case Disabled:
|
case Disabled:
|
||||||
return nil, nil
|
return tls.Certificate{}, nil
|
||||||
case MutualTLS:
|
case MutualTLS:
|
||||||
crt, err := tls.LoadX509KeyPair(i.Certificate, i.Key)
|
crt, err := tls.LoadX509KeyPair(i.Certificate, i.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error creating identity certificate")
|
return fail(errors.Wrap(err, "error creating identity certificate"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if certificate is expired.
|
// Check if certificate is expired.
|
||||||
// Do not return any options if expired.
|
|
||||||
x509Cert, err := x509.ParseCertificate(crt.Certificate[0])
|
x509Cert, err := x509.ParseCertificate(crt.Certificate[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error creating identity certificate")
|
return fail(errors.Wrap(err, "error creating identity certificate"))
|
||||||
}
|
}
|
||||||
now := time.Now().Truncate(time.Second)
|
now := time.Now().Truncate(time.Second)
|
||||||
if now.Add(DefaultLeeway).Before(x509Cert.NotBefore) || now.After(x509Cert.NotAfter) {
|
if now.Add(DefaultLeeway).Before(x509Cert.NotBefore) {
|
||||||
return nil, nil
|
return fail(errors.New("certificate is not yet valid"))
|
||||||
}
|
}
|
||||||
return []ClientOption{WithCertificate(crt)}, nil
|
if now.After(x509Cert.NotAfter) {
|
||||||
|
return fail(errors.New("certificate is already expired"))
|
||||||
|
}
|
||||||
|
return crt, nil
|
||||||
default:
|
default:
|
||||||
return nil, errors.Errorf("unsupported identity type %s", i.Type)
|
return fail(errors.Errorf("unsupported identity type %s", i.Type))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renew renews the identity certificate using the given client.
|
func fileExists(filename string) error {
|
||||||
func (i *Identity) Renew(client *Client) error {
|
info, err := os.Stat(filename)
|
||||||
switch i.Kind() {
|
if err != nil {
|
||||||
case Disabled:
|
return errors.Wrapf(err, "error reading %s", filename)
|
||||||
return nil
|
|
||||||
case MutualTLS:
|
|
||||||
cert, err := tls.LoadX509KeyPair(i.Certificate, i.Key)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "error creating identity certificate")
|
|
||||||
}
|
|
||||||
tr := &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{
|
|
||||||
Certificates: []tls.Certificate{cert},
|
|
||||||
RootCAs: client.GetRootCAs(),
|
|
||||||
PreferServerCipherSuites: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
resp, err := client.Renew(tr)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
for _, crt := range resp.CertChainPEM {
|
|
||||||
block := &pem.Block{
|
|
||||||
Type: "CERTIFICATE",
|
|
||||||
Bytes: crt.Raw,
|
|
||||||
}
|
|
||||||
if err := pem.Encode(buf, block); err != nil {
|
|
||||||
return errors.Wrap(err, "error encoding identity certificate")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := ioutil.WriteFile(i.Certificate, buf.Bytes(), 0600); err != nil {
|
|
||||||
return errors.Wrap(err, "error writing identity certificate")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return errors.Errorf("unsupported identity type %s", i.Type)
|
|
||||||
}
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
return errors.Errorf("error reading %s: file is a directory", filename)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
166
ca/identity/identity_test.go
Normal file
166
ca/identity/identity_test.go
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
package identity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadDefaultIdentity(t *testing.T) {
|
||||||
|
oldFile := IdentityFile
|
||||||
|
defer func() {
|
||||||
|
IdentityFile = oldFile
|
||||||
|
}()
|
||||||
|
|
||||||
|
expected := &Identity{
|
||||||
|
Type: "mTLS",
|
||||||
|
Certificate: "testdata/identity/identity.crt",
|
||||||
|
Key: "testdata/identity/identity_key",
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
prepare func()
|
||||||
|
want *Identity
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"ok", func() { IdentityFile = "testdata/config/identity.json" }, expected, false},
|
||||||
|
{"fail read", func() { IdentityFile = "testdata/config/missing.json" }, nil, true},
|
||||||
|
{"fail unmarshal", func() { IdentityFile = "testdata/config/fail.json" }, nil, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tt.prepare()
|
||||||
|
got, err := LoadDefaultIdentity()
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("LoadDefaultIdentity() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("LoadDefaultIdentity() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIdentity_Kind(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
want Type
|
||||||
|
}{
|
||||||
|
{"disabled", fields{""}, Disabled},
|
||||||
|
{"mutualTLS", fields{"mTLS"}, MutualTLS},
|
||||||
|
{"unknown", fields{"unknown"}, Type("unknown")},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
i := &Identity{
|
||||||
|
Type: tt.fields.Type,
|
||||||
|
}
|
||||||
|
if got := i.Kind(); got != tt.want {
|
||||||
|
t.Errorf("Identity.Kind() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIdentity_Validate(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
Type string
|
||||||
|
Certificate string
|
||||||
|
Key string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"ok", fields{"mTLS", "testdata/identity/identity.crt", "testdata/identity/identity_key"}, false},
|
||||||
|
{"ok disabled", fields{}, false},
|
||||||
|
{"fail type", fields{"foo", "testdata/identity/identity.crt", "testdata/identity/identity_key"}, true},
|
||||||
|
{"fail certificate", fields{"mTLS", "", "testdata/identity/identity_key"}, true},
|
||||||
|
{"fail key", fields{"mTLS", "testdata/identity/identity.crt", ""}, true},
|
||||||
|
{"fail missing certificate", fields{"mTLS", "testdata/identity/missing.crt", "testdata/identity/identity_key"}, true},
|
||||||
|
{"fail missing key", fields{"mTLS", "testdata/identity/identity.crt", "testdata/identity/missing_key"}, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
i := &Identity{
|
||||||
|
Type: tt.fields.Type,
|
||||||
|
Certificate: tt.fields.Certificate,
|
||||||
|
Key: tt.fields.Key,
|
||||||
|
}
|
||||||
|
if err := i.Validate(); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("Identity.Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIdentity_TLSCertificate(t *testing.T) {
|
||||||
|
expected, err := tls.LoadX509KeyPair("testdata/identity/identity.crt", "testdata/identity/identity_key")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type fields struct {
|
||||||
|
Type string
|
||||||
|
Certificate string
|
||||||
|
Key string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
want tls.Certificate
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"ok", fields{"mTLS", "testdata/identity/identity.crt", "testdata/identity/identity_key"}, expected, false},
|
||||||
|
{"ok disabled", fields{}, tls.Certificate{}, false},
|
||||||
|
{"fail type", fields{"foo", "testdata/identity/identity.crt", "testdata/identity/identity_key"}, tls.Certificate{}, true},
|
||||||
|
{"fail certificate", fields{"mTLS", "testdata/certs/server.crt", "testdata/identity/identity_key"}, tls.Certificate{}, true},
|
||||||
|
{"fail not after", fields{"mTLS", "testdata/identity/expired.crt", "testdata/identity/identity_key"}, tls.Certificate{}, true},
|
||||||
|
{"fail not before", fields{"mTLS", "testdata/identity/not_before.crt", "testdata/identity/identity_key"}, tls.Certificate{}, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
i := &Identity{
|
||||||
|
Type: tt.fields.Type,
|
||||||
|
Certificate: tt.fields.Certificate,
|
||||||
|
Key: tt.fields.Key,
|
||||||
|
}
|
||||||
|
got, err := i.TLSCertificate()
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("Identity.TLSCertificate() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("Identity.TLSCertificate() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_fileExists(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
filename string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{"ok", args{"testdata/identity/identity.crt"}, false},
|
||||||
|
{"missing", args{"testdata/identity/missing.crt"}, true},
|
||||||
|
{"directory", args{"testdata/identity"}, true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := fileExists(tt.args.filename); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("fileExists() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
11
ca/identity/testdata/certs/intermediate_ca.crt
vendored
Normal file
11
ca/identity/testdata/certs/intermediate_ca.crt
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBozCCAUqgAwIBAgIQF4UYp5uEiuq/BO0cOWTq9DAKBggqhkjOPQQDAjAcMRow
|
||||||
|
GAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0xOTEyMTIwMjQ1MThaFw0yOTEy
|
||||||
|
MDkwMjQ1MThaMCQxIjAgBgNVBAMTGVNtYWxsc3RlcCBJbnRlcm1lZGlhdGUgQ0Ew
|
||||||
|
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQGECLvDj+ZSqW78DRmUaugh0EU4NQ5
|
||||||
|
PoZxsLpB0gUsvNDGE0V5/2Q85GmsYzlBjBuoM+RfvF2fSP+dDTs3Hwjgo2YwZDAO
|
||||||
|
BgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU95Au
|
||||||
|
B82vrt2UJyDTNBQH3B8sePUwHwYDVR0jBBgwFoAUgwZucvb+H/1chTPLQ1GYTJwK
|
||||||
|
CXQwCgYIKoZIzj0EAwIDRwAwRAIgSaHuI61rNsFf1ke5WSUyuqy51DIE/ONCSWKT
|
||||||
|
VQgTVJMCIAMsE+Eibk43hL4qQi5vBJiFLfGQDDN/9HUi6w4w5EZ7
|
||||||
|
-----END CERTIFICATE-----
|
10
ca/identity/testdata/certs/root_ca.crt
vendored
Normal file
10
ca/identity/testdata/certs/root_ca.crt
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBfDCCASGgAwIBAgIQE8W0gyMruWxRDfegdPHrdDAKBggqhkjOPQQDAjAcMRow
|
||||||
|
GAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0xOTEyMTIwMjQ1MThaFw0yOTEy
|
||||||
|
MDkwMjQ1MThaMBwxGjAYBgNVBAMTEVNtYWxsc3RlcCBSb290IENBMFkwEwYHKoZI
|
||||||
|
zj0CAQYIKoZIzj0DAQcDQgAEgd74QbUDcEj3aV5Oxv5eAMzwnejj7S/iDFAp89t9
|
||||||
|
kEb+Ux4NZC3Pay+92yRL//dBUI5WOopLXBniYomH4SFJg6NFMEMwDgYDVR0PAQH/
|
||||||
|
BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFIMGbnL2/h/9XIUz
|
||||||
|
y0NRmEycCgl0MAoGCCqGSM49BAMCA0kAMEYCIQD3/IUBL5/9Hpdp2+t4XnA42cwQ
|
||||||
|
j5WkGY5hJIhdQ5P8qgIhAMf19nAIUlSbXKPf21Gv6eYEoNuuLfpcqnfBt5NJX64M
|
||||||
|
-----END CERTIFICATE-----
|
25
ca/identity/testdata/certs/server.crt
vendored
Normal file
25
ca/identity/testdata/certs/server.crt
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICHDCCAcKgAwIBAgIQQ4n25nGGKm6uGyVQ4cDNCTAKBggqhkjOPQQDAjAkMSIw
|
||||||
|
IAYDVQQDExlTbWFsbHN0ZXAgSW50ZXJtZWRpYXRlIENBMB4XDTE5MTIxMjAyNTAz
|
||||||
|
OVoXDTI5MTIwOTAyNTAzOVowFjEUMBIGA1UEAxMLdGVzdCBzZXJ2ZXIwWTATBgcq
|
||||||
|
hkjOPQIBBggqhkjOPQMBBwNCAATmQRMCzRP1hBcYhAXlbiyR9QtsQosQfCZTS+en
|
||||||
|
g6TtL9VjWsQXqd1SSStfi0grPyiTQLIPhPbSho/VJzSpf59Do4HjMIHgMA4GA1Ud
|
||||||
|
DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0O
|
||||||
|
BBYEFBvz34jDFrb3G4qiGkZZj99BnabAMB8GA1UdIwQYMBaAFPeQLgfNr67dlCcg
|
||||||
|
0zQUB9wfLHj1MBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATBTBgwrBgEEAYKk
|
||||||
|
ZMYoQAEEQzBBAgEBBA9qb2VAZXhhbXBsZS5jb20EKzJ3U05fQ21leFhXaWdfRG5w
|
||||||
|
VlpzWUZkTUgxU3RjODZCSUJ6TjBydDVpcEUwCgYIKoZIzj0EAwIDSAAwRQIhAOt6
|
||||||
|
/x9LWQyBtx3RcyyALF2//OCfGjAx0zLGmUsXIHGIAiAZGVwTxbhxiYU95AXncS3F
|
||||||
|
3tXNaaIJyyO7atiVPhCR1A==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBozCCAUqgAwIBAgIQF4UYp5uEiuq/BO0cOWTq9DAKBggqhkjOPQQDAjAcMRow
|
||||||
|
GAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0xOTEyMTIwMjQ1MThaFw0yOTEy
|
||||||
|
MDkwMjQ1MThaMCQxIjAgBgNVBAMTGVNtYWxsc3RlcCBJbnRlcm1lZGlhdGUgQ0Ew
|
||||||
|
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQGECLvDj+ZSqW78DRmUaugh0EU4NQ5
|
||||||
|
PoZxsLpB0gUsvNDGE0V5/2Q85GmsYzlBjBuoM+RfvF2fSP+dDTs3Hwjgo2YwZDAO
|
||||||
|
BgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU95Au
|
||||||
|
B82vrt2UJyDTNBQH3B8sePUwHwYDVR0jBBgwFoAUgwZucvb+H/1chTPLQ1GYTJwK
|
||||||
|
CXQwCgYIKoZIzj0EAwIDRwAwRAIgSaHuI61rNsFf1ke5WSUyuqy51DIE/ONCSWKT
|
||||||
|
VQgTVJMCIAMsE+Eibk43hL4qQi5vBJiFLfGQDDN/9HUi6w4w5EZ7
|
||||||
|
-----END CERTIFICATE-----
|
41
ca/identity/testdata/config/ca.json
vendored
Normal file
41
ca/identity/testdata/config/ca.json
vendored
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"root": "testdata/certs/root_ca.crt",
|
||||||
|
"federatedRoots": [],
|
||||||
|
"crt": "testdata/certs/intermediate_ca.crt",
|
||||||
|
"key": "testdata/secrets/intermediate_ca_key",
|
||||||
|
"address": ":443",
|
||||||
|
"dnsNames": [
|
||||||
|
"127.0.0.1",
|
||||||
|
"localhost"
|
||||||
|
],
|
||||||
|
"logger": {
|
||||||
|
"format": "text"
|
||||||
|
},
|
||||||
|
"authority": {
|
||||||
|
"provisioners": [
|
||||||
|
{
|
||||||
|
"type": "jwk",
|
||||||
|
"name": "joe@example.com",
|
||||||
|
"key": {
|
||||||
|
"use": "sig",
|
||||||
|
"kty": "EC",
|
||||||
|
"kid": "2wSN_CmexXWig_DnpVZsYFdMH1Stc86BIBzN0rt5ipE",
|
||||||
|
"crv": "P-256",
|
||||||
|
"alg": "ES256",
|
||||||
|
"x": "QqYaIULUQqP0EOmogorCcQIxEtI7-zCRcUVFxyNwq4Q",
|
||||||
|
"y": "YeIMipM7uMHjlxpFIUbfCBC1xEXczXNYRzJCMyrGcH0"
|
||||||
|
},
|
||||||
|
"encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiSVQ3MVNUMTNNMTd1S3Y4VHRDczYyUSJ9.TXShNLPcITS0bFvQeMjjCDhQLICQs1ShECkgUkUsAm9ZWpSq6Yu03w.SWxtxscivS3L5Yo5.O-XY9YKK8wEJgVs7X1-FxiM_6w4s7iJQNXRD2JrZRsXtDqUz7diPfXuBOFPUFsNzykvob1qCsU4B23Ek2nbaS2HqPrIOGbOvOsR8Pt6kNoraH1QDp3Hyzkv0S-VGM0MCGYDDmmH33PZmsdS36Aw8v9xBnDHlwlMg4NjTskxpqggfQl01433B0lCJqJdrmeBeGL1ZCKixvc-wAQxU8GH5iiD925ViLY7RlVo-tmIBXpxRgheLgKiuMxmgPvf15qCdgU5TRqeuJbYJLzvPpoai0W4WHjpM1zLjjmp5OYRFW4m4ZRZf5g1Cm4lstFPUlTn85fkMZFdBh4_bFbjAv7k.epXp8DZKHj_dxP9EohwDIg"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tls": {
|
||||||
|
"cipherSuites": [
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||||
|
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
|
||||||
|
],
|
||||||
|
"minVersion": 1.2,
|
||||||
|
"maxVersion": 1.2,
|
||||||
|
"renegotiation": false
|
||||||
|
}
|
||||||
|
}
|
6
ca/identity/testdata/config/defaults.json
vendored
Normal file
6
ca/identity/testdata/config/defaults.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"ca-url": "https://127.0.0.1",
|
||||||
|
"ca-config": "testdata/config/ca.json",
|
||||||
|
"fingerprint": "9dc35eef23a234b2520516a3169090d7ec2fc61323bdd6e4fde08bcfec5d0931",
|
||||||
|
"root": "testdata/certs/root_ca.crt"
|
||||||
|
}
|
1
ca/identity/testdata/config/fail.json
vendored
Normal file
1
ca/identity/testdata/config/fail.json
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
This is not a json file
|
5
ca/identity/testdata/config/identity.json
vendored
Normal file
5
ca/identity/testdata/config/identity.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"type": "mTLS",
|
||||||
|
"crt": "testdata/identity/identity.crt",
|
||||||
|
"key": "testdata/identity/identity_key"
|
||||||
|
}
|
25
ca/identity/testdata/identity/expired.crt
vendored
Normal file
25
ca/identity/testdata/identity/expired.crt
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICIDCCAcegAwIBAgIRAM1GK1TLmvWLVOjP0dqVCiEwCgYIKoZIzj0EAwIwJDEi
|
||||||
|
MCAGA1UEAxMZU21hbGxzdGVwIEludGVybWVkaWF0ZSBDQTAeFw0xODEyMTIwMzI2
|
||||||
|
MzZaFw0xODEyMTMwMzI2MzZaMBoxGDAWBgNVBAMMD2pvZUBleGFtcGxlLmNvbTBZ
|
||||||
|
MBMGByqGSM49AgEGCCqGSM49AwEHA0IABI0+NSjg3+vGhAeZGrxPksrXFqq0AIUB
|
||||||
|
D3nQPmGPuUWIEmbt6qp3EVF/o+KwzWgDv5fzBmDlBkdBRz9xc3XIcQ2jgeMwgeAw
|
||||||
|
HwYDVR0jBBgwFoAU95AuB82vrt2UJyDTNBQH3B8sePUwDgYDVR0PAQH/BAQDAgWg
|
||||||
|
MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQU1Ht6zX2M
|
||||||
|
eVXcnxhM4hxU0RCblNowGgYDVR0RBBMwEYEPam9lQGV4YW1wbGUuY29tMFMGDCsG
|
||||||
|
AQQBgqRkxihAAQRDMEECAQEED2pvZUBleGFtcGxlLmNvbQQrMndTTl9DbWV4WFdp
|
||||||
|
Z19EbnBWWnNZRmRNSDFTdGM4NkJJQnpOMHJ0NWlwRTAKBggqhkjOPQQDAgNHADBE
|
||||||
|
AiBgoPACCRJ6s+C5Yz3BWeyM6VnWewctnaMsVJKyPdb98AIgV/7HRZsc5Xgi8iVt
|
||||||
|
D4XxVOZDu/y1V4VIH5W4INfg6JA=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBozCCAUqgAwIBAgIQF4UYp5uEiuq/BO0cOWTq9DAKBggqhkjOPQQDAjAcMRow
|
||||||
|
GAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0xOTEyMTIwMjQ1MThaFw0yOTEy
|
||||||
|
MDkwMjQ1MThaMCQxIjAgBgNVBAMTGVNtYWxsc3RlcCBJbnRlcm1lZGlhdGUgQ0Ew
|
||||||
|
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQGECLvDj+ZSqW78DRmUaugh0EU4NQ5
|
||||||
|
PoZxsLpB0gUsvNDGE0V5/2Q85GmsYzlBjBuoM+RfvF2fSP+dDTs3Hwjgo2YwZDAO
|
||||||
|
BgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU95Au
|
||||||
|
B82vrt2UJyDTNBQH3B8sePUwHwYDVR0jBBgwFoAUgwZucvb+H/1chTPLQ1GYTJwK
|
||||||
|
CXQwCgYIKoZIzj0EAwIDRwAwRAIgSaHuI61rNsFf1ke5WSUyuqy51DIE/ONCSWKT
|
||||||
|
VQgTVJMCIAMsE+Eibk43hL4qQi5vBJiFLfGQDDN/9HUi6w4w5EZ7
|
||||||
|
-----END CERTIFICATE-----
|
25
ca/identity/testdata/identity/identity.crt
vendored
Normal file
25
ca/identity/testdata/identity/identity.crt
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICHzCCAcagAwIBAgIQfVgJ4dZ2AhS88uthvlIzyjAKBggqhkjOPQQDAjAkMSIw
|
||||||
|
IAYDVQQDExlTbWFsbHN0ZXAgSW50ZXJtZWRpYXRlIENBMB4XDTE5MTIxMjAyNDgy
|
||||||
|
MVoXDTI5MTIwOTAyNDgyMVowGjEYMBYGA1UEAwwPam9lQGV4YW1wbGUuY29tMFkw
|
||||||
|
EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjT41KODf68aEB5kavE+SytcWqrQAhQEP
|
||||||
|
edA+YY+5RYgSZu3qqncRUX+j4rDNaAO/l/MGYOUGR0FHP3FzdchxDaOB4zCB4DAO
|
||||||
|
BgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0G
|
||||||
|
A1UdDgQWBBTUe3rNfYx5VdyfGEziHFTREJuU2jAfBgNVHSMEGDAWgBT3kC4Hza+u
|
||||||
|
3ZQnINM0FAfcHyx49TAaBgNVHREEEzARgQ9qb2VAZXhhbXBsZS5jb20wUwYMKwYB
|
||||||
|
BAGCpGTGKEABBEMwQQIBAQQPam9lQGV4YW1wbGUuY29tBCsyd1NOX0NtZXhYV2ln
|
||||||
|
X0RucFZac1lGZE1IMVN0Yzg2QklCek4wcnQ1aXBFMAoGCCqGSM49BAMCA0cAMEQC
|
||||||
|
IHkYnKUBrXc/GIosKgnhHqVeRMi2O1JhnZdTE1uoy2C0AiA9ZrmGqPvpQ86f5yq5
|
||||||
|
llsieqBTzIum6A45q0/4XeN3QA==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBozCCAUqgAwIBAgIQF4UYp5uEiuq/BO0cOWTq9DAKBggqhkjOPQQDAjAcMRow
|
||||||
|
GAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0xOTEyMTIwMjQ1MThaFw0yOTEy
|
||||||
|
MDkwMjQ1MThaMCQxIjAgBgNVBAMTGVNtYWxsc3RlcCBJbnRlcm1lZGlhdGUgQ0Ew
|
||||||
|
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQGECLvDj+ZSqW78DRmUaugh0EU4NQ5
|
||||||
|
PoZxsLpB0gUsvNDGE0V5/2Q85GmsYzlBjBuoM+RfvF2fSP+dDTs3Hwjgo2YwZDAO
|
||||||
|
BgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU95Au
|
||||||
|
B82vrt2UJyDTNBQH3B8sePUwHwYDVR0jBBgwFoAUgwZucvb+H/1chTPLQ1GYTJwK
|
||||||
|
CXQwCgYIKoZIzj0EAwIDRwAwRAIgSaHuI61rNsFf1ke5WSUyuqy51DIE/ONCSWKT
|
||||||
|
VQgTVJMCIAMsE+Eibk43hL4qQi5vBJiFLfGQDDN/9HUi6w4w5EZ7
|
||||||
|
-----END CERTIFICATE-----
|
5
ca/identity/testdata/identity/identity_key
vendored
Normal file
5
ca/identity/testdata/identity/identity_key
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIJ4A5QcJioS5I89uT/hkuWPy/nlW5qy8vM8Tm2sgUCDyoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEjT41KODf68aEB5kavE+SytcWqrQAhQEPedA+YY+5RYgSZu3qqncR
|
||||||
|
UX+j4rDNaAO/l/MGYOUGR0FHP3FzdchxDQ==
|
||||||
|
-----END EC PRIVATE KEY-----
|
25
ca/identity/testdata/identity/not_before.crt
vendored
Normal file
25
ca/identity/testdata/identity/not_before.crt
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICIDCCAcagAwIBAgIQHRUI8eJv55I9/5IHi1mpmjAKBggqhkjOPQQDAjAkMSIw
|
||||||
|
IAYDVQQDExlTbWFsbHN0ZXAgSW50ZXJtZWRpYXRlIENBMB4XDTI5MTIwOTAzMzAx
|
||||||
|
NFoXDTI5MTIxMDAzMzAxNFowGjEYMBYGA1UEAwwPam9lQGV4YW1wbGUuY29tMFkw
|
||||||
|
EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjT41KODf68aEB5kavE+SytcWqrQAhQEP
|
||||||
|
edA+YY+5RYgSZu3qqncRUX+j4rDNaAO/l/MGYOUGR0FHP3FzdchxDaOB4zCB4DAf
|
||||||
|
BgNVHSMEGDAWgBT3kC4Hza+u3ZQnINM0FAfcHyx49TAOBgNVHQ8BAf8EBAMCBaAw
|
||||||
|
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBTUe3rNfYx5
|
||||||
|
VdyfGEziHFTREJuU2jAaBgNVHREEEzARgQ9qb2VAZXhhbXBsZS5jb20wUwYMKwYB
|
||||||
|
BAGCpGTGKEABBEMwQQIBAQQPam9lQGV4YW1wbGUuY29tBCsyd1NOX0NtZXhYV2ln
|
||||||
|
X0RucFZac1lGZE1IMVN0Yzg2QklCek4wcnQ1aXBFMAoGCCqGSM49BAMCA0gAMEUC
|
||||||
|
IQDJVzxQ0lY9+haZLs5qxhbaWoTmXwCbYdkwhThDfM/izwIgRZCmshc1flfimIPO
|
||||||
|
eblT85Gk16ND/diV6pmtUaMT73I=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBozCCAUqgAwIBAgIQF4UYp5uEiuq/BO0cOWTq9DAKBggqhkjOPQQDAjAcMRow
|
||||||
|
GAYDVQQDExFTbWFsbHN0ZXAgUm9vdCBDQTAeFw0xOTEyMTIwMjQ1MThaFw0yOTEy
|
||||||
|
MDkwMjQ1MThaMCQxIjAgBgNVBAMTGVNtYWxsc3RlcCBJbnRlcm1lZGlhdGUgQ0Ew
|
||||||
|
WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQGECLvDj+ZSqW78DRmUaugh0EU4NQ5
|
||||||
|
PoZxsLpB0gUsvNDGE0V5/2Q85GmsYzlBjBuoM+RfvF2fSP+dDTs3Hwjgo2YwZDAO
|
||||||
|
BgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU95Au
|
||||||
|
B82vrt2UJyDTNBQH3B8sePUwHwYDVR0jBBgwFoAUgwZucvb+H/1chTPLQ1GYTJwK
|
||||||
|
CXQwCgYIKoZIzj0EAwIDRwAwRAIgSaHuI61rNsFf1ke5WSUyuqy51DIE/ONCSWKT
|
||||||
|
VQgTVJMCIAMsE+Eibk43hL4qQi5vBJiFLfGQDDN/9HUi6w4w5EZ7
|
||||||
|
-----END CERTIFICATE-----
|
8
ca/identity/testdata/secrets/intermediate_ca_key
vendored
Normal file
8
ca/identity/testdata/secrets/intermediate_ca_key
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: AES-256-CBC,37e3019a1aa420225bbd4f342a3ce330
|
||||||
|
|
||||||
|
3SNIIXzE11cGKTPnErv8S1HIrd2lbQo+lsMT9GrU33GAi/MTvp0hx0txy7E3CsrU
|
||||||
|
DbuPXs3zLCjgoNLOeyAWLqGjPLRt4YNnZGVDi3F/dFUAWxgXH8gZQ2d9ZqAXwxdd
|
||||||
|
bhT4ZcRFgFzCPlHExtxBrJe+Tmeuq1HqD+8gpOSYbt0=
|
||||||
|
-----END EC PRIVATE KEY-----
|
8
ca/identity/testdata/secrets/root_ca_key
vendored
Normal file
8
ca/identity/testdata/secrets/root_ca_key
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: AES-256-CBC,48fc92ab6885b2377d8bbac5b035bde2
|
||||||
|
|
||||||
|
BE07EXlLmJbAfjt2c9GwQoTT07DzjLWgiGWqxMKC0bOLQdmHe2pFudeQldDhTOme
|
||||||
|
xnr9rRj9h+GRWV+sIzp+ilGd4/F6lfzWMl44GA5y7uBNWKhnI1uB9m9oo69hBNRg
|
||||||
|
dQuDmAx5EWXvg7Mgg1MQZIPY8539RXWJdAs+uRSI12g=
|
||||||
|
-----END EC PRIVATE KEY-----
|
5
ca/identity/testdata/secrets/server_key
vendored
Normal file
5
ca/identity/testdata/secrets/server_key
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIIGgfuMfx7h1VaCYzzEPZhrbTLsAr6dtyuQ2RLl6jKqBoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAE5kETAs0T9YQXGIQF5W4skfULbEKLEHwmU0vnp4Ok7S/VY1rEF6nd
|
||||||
|
UkkrX4tIKz8ok0CyD4T20oaP1Sc0qX+fQw==
|
||||||
|
-----END EC PRIVATE KEY-----
|
Loading…
Reference in a new issue