forked from TrueCloudLab/certificates
Complete cloudcas using CAS v1beta1.
This commit is contained in:
parent
1b1f73dec6
commit
c8d9cb0a1d
8 changed files with 318 additions and 56 deletions
57
cas/apiv1/extension.go
Normal file
57
cas/apiv1/extension.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package apiv1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CertificateAuthorityExtension is type used to encode the certificate
|
||||||
|
// authority extension.
|
||||||
|
type CertificateAuthorityExtension struct {
|
||||||
|
Type string
|
||||||
|
CertificateID string `asn1:"optional,omitempty"`
|
||||||
|
KeyValuePairs []string `asn1:"optional,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCertificateAuthorityExtension returns a X.509 extension that shows the
|
||||||
|
// CAS type, id and a list of optional key value pairs.
|
||||||
|
func CreateCertificateAuthorityExtension(typ Type, certificateID string, keyValuePairs ...string) (pkix.Extension, error) {
|
||||||
|
b, err := asn1.Marshal(CertificateAuthorityExtension{
|
||||||
|
Type: typ.String(),
|
||||||
|
CertificateID: certificateID,
|
||||||
|
KeyValuePairs: keyValuePairs,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return pkix.Extension{}, errors.Wrapf(err, "error marshaling certificate id extension")
|
||||||
|
}
|
||||||
|
return pkix.Extension{
|
||||||
|
Id: oidStepCertificateAuthority,
|
||||||
|
Critical: false,
|
||||||
|
Value: b,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindCertificateAuthorityExtension returns the certificate authority extension
|
||||||
|
// from a signed certificate.
|
||||||
|
func FindCertificateAuthorityExtension(cert *x509.Certificate) (pkix.Extension, bool) {
|
||||||
|
for _, ext := range cert.Extensions {
|
||||||
|
if ext.Id.Equal(oidStepCertificateAuthority) {
|
||||||
|
return ext, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pkix.Extension{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveCertificateAuthorityExtension removes the certificate authority
|
||||||
|
// extension from a certificate template.
|
||||||
|
func RemoveCertificateAuthorityExtension(cert *x509.Certificate) {
|
||||||
|
for i, ext := range cert.ExtraExtensions {
|
||||||
|
if ext.Id.Equal(oidStepCertificateAuthority) {
|
||||||
|
cert.ExtraExtensions = append(cert.ExtraExtensions[:i], cert.ExtraExtensions[i+1:]...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,3 +30,11 @@ func (o *Options) Validate() error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasType returns if the options have the given type.
|
||||||
|
func (o *Options) HasType(t Type) bool {
|
||||||
|
if o == nil {
|
||||||
|
return SoftCAS == t.String()
|
||||||
|
}
|
||||||
|
return Type(o.Type).String() == t.String()
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ type CreateCertificateRequest struct {
|
||||||
Issuer *x509.Certificate
|
Issuer *x509.Certificate
|
||||||
Signer crypto.Signer
|
Signer crypto.Signer
|
||||||
Lifetime time.Duration
|
Lifetime time.Duration
|
||||||
|
Backdate time.Duration
|
||||||
RequestID string
|
RequestID string
|
||||||
}
|
}
|
||||||
type CreateCertificateResponse struct {
|
type CreateCertificateResponse struct {
|
||||||
|
@ -19,8 +19,24 @@ type CreateCertificateResponse struct {
|
||||||
CertificateChain []*x509.Certificate
|
CertificateChain []*x509.Certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
type RenewCertificateRequest struct{}
|
type RenewCertificateRequest struct {
|
||||||
type RenewCertificateResponse struct{}
|
Template *x509.Certificate
|
||||||
|
Issuer *x509.Certificate
|
||||||
|
Signer crypto.Signer
|
||||||
|
Lifetime time.Duration
|
||||||
|
Backdate time.Duration
|
||||||
|
RequestID string
|
||||||
|
}
|
||||||
|
type RenewCertificateResponse struct {
|
||||||
|
Certificate *x509.Certificate
|
||||||
|
CertificateChain []*x509.Certificate
|
||||||
|
}
|
||||||
|
|
||||||
type RevokeCertificateRequest struct{}
|
// RevokeCertificateRequest is the request used to revoke a certificate.
|
||||||
type RevokeCertificateResponse struct{}
|
type RevokeCertificateRequest struct {
|
||||||
|
Certificate *x509.Certificate
|
||||||
|
}
|
||||||
|
type RevokeCertificateResponse struct {
|
||||||
|
Certificate *x509.Certificate
|
||||||
|
CertificateChain []*x509.Certificate
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
package apiv1
|
package apiv1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/asn1"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
oidStepRoot = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 37476, 9000, 64}
|
||||||
|
oidStepCertificateAuthority = append(asn1.ObjectIdentifier(nil), append(oidStepRoot, 2)...)
|
||||||
|
)
|
||||||
|
|
||||||
// CertificateAuthorityService is the interface implemented to support external
|
// CertificateAuthorityService is the interface implemented to support external
|
||||||
// certificate authorities.
|
// certificate authorities.
|
||||||
type CertificateAuthorityService interface {
|
type CertificateAuthorityService interface {
|
||||||
|
@ -11,12 +21,24 @@ type CertificateAuthorityService interface {
|
||||||
// Type represents the KMS type used.
|
// Type represents the KMS type used.
|
||||||
type Type string
|
type Type string
|
||||||
|
|
||||||
//
|
|
||||||
const (
|
const (
|
||||||
// DefaultCAS is a CertificateAuthorityService using software.
|
// DefaultCAS is a CertificateAuthorityService using software.
|
||||||
DefaultCAS = ""
|
DefaultCAS = ""
|
||||||
// SoftCAS is a CertificateAuthorityService using software.
|
// SoftCAS is a CertificateAuthorityService using software.
|
||||||
SoftCAS = "softcas"
|
SoftCAS = "SoftCAS"
|
||||||
// CloudCAS is a CertificateAuthorityService using Google Cloud CAS.
|
// CloudCAS is a CertificateAuthorityService using Google Cloud CAS.
|
||||||
CloudCAS = "cloudcas"
|
CloudCAS = "CloudCAS"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// String returns the given type as a string. All the letters will be lowercase.
|
||||||
|
func (t Type) String() string {
|
||||||
|
if t == "" {
|
||||||
|
return SoftCAS
|
||||||
|
}
|
||||||
|
for _, s := range []string{SoftCAS, CloudCAS} {
|
||||||
|
if strings.EqualFold(s, string(t)) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string(t)
|
||||||
|
}
|
||||||
|
|
|
@ -63,7 +63,6 @@ func createCertificateConfig(tpl *x509.Certificate) (*pb.Certificate_Config, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func createPublicKey(key crypto.PublicKey) (*pb.PublicKey, error) {
|
func createPublicKey(key crypto.PublicKey) (*pb.PublicKey, error) {
|
||||||
pk := new(pb.PublicKey)
|
|
||||||
switch key := key.(type) {
|
switch key := key.(type) {
|
||||||
case *ecdsa.PublicKey:
|
case *ecdsa.PublicKey:
|
||||||
asn1Bytes, err := x509.MarshalPKIXPublicKey(key)
|
asn1Bytes, err := x509.MarshalPKIXPublicKey(key)
|
||||||
|
@ -88,8 +87,6 @@ func createPublicKey(key crypto.PublicKey) (*pb.PublicKey, error) {
|
||||||
default:
|
default:
|
||||||
return nil, errors.Errorf("unsupported public key type: %T", key)
|
return nil, errors.Errorf("unsupported public key type: %T", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pk, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createSubject(cert *x509.Certificate) *pb.Subject {
|
func createSubject(cert *x509.Certificate) *pb.Subject {
|
||||||
|
@ -138,12 +135,43 @@ func createSubjectAlternativeNames(cert *x509.Certificate) *pb.SubjectAltNames {
|
||||||
|
|
||||||
// Add extra SANs coming from the extensions
|
// Add extra SANs coming from the extensions
|
||||||
if ext, ok := findExtraExtension(cert, oidExtensionSubjectAltName); ok {
|
if ext, ok := findExtraExtension(cert, oidExtensionSubjectAltName); ok {
|
||||||
|
var rawValues []asn1.RawValue
|
||||||
|
if _, err := asn1.Unmarshal(ext.Value, &rawValues); err == nil {
|
||||||
|
var newValues []asn1.RawValue
|
||||||
|
for _, v := range rawValues {
|
||||||
|
switch v.Tag {
|
||||||
|
case nameTypeDNS:
|
||||||
|
if len(ret.DnsNames) == 0 {
|
||||||
|
newValues = append(newValues, v)
|
||||||
|
}
|
||||||
|
case nameTypeEmail:
|
||||||
|
if len(ret.EmailAddresses) == 0 {
|
||||||
|
newValues = append(newValues, v)
|
||||||
|
}
|
||||||
|
case nameTypeIP:
|
||||||
|
if len(ret.IpAddresses) == 0 {
|
||||||
|
newValues = append(newValues, v)
|
||||||
|
}
|
||||||
|
case nameTypeURI:
|
||||||
|
if len(ret.Uris) == 0 {
|
||||||
|
newValues = append(newValues, v)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
newValues = append(newValues, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(newValues) > 0 {
|
||||||
|
if b, err := asn1.Marshal(newValues); err == nil {
|
||||||
ret.CustomSans = []*pb.X509Extension{{
|
ret.CustomSans = []*pb.X509Extension{{
|
||||||
ObjectId: createObjectID(ext.Id),
|
ObjectId: createObjectID(ext.Id),
|
||||||
Critical: ext.Critical,
|
Critical: ext.Critical,
|
||||||
Value: ext.Value,
|
Value: b,
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,12 +238,14 @@ func createReusableConfig(cert *x509.Certificate) *pb.ReusableConfigWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extraExtensions := make([]*pb.X509Extension, len(cert.ExtraExtensions))
|
var extraExtensions []*pb.X509Extension
|
||||||
for i, ext := range cert.ExtraExtensions {
|
for _, ext := range cert.ExtraExtensions {
|
||||||
extraExtensions[i] = &pb.X509Extension{
|
if !ext.Id.Equal(oidExtensionSubjectAltName) {
|
||||||
|
extraExtensions = append(extraExtensions, &pb.X509Extension{
|
||||||
ObjectId: createObjectID(ext.Id),
|
ObjectId: createObjectID(ext.Id),
|
||||||
Critical: ext.Critical,
|
Critical: ext.Critical,
|
||||||
Value: ext.Value,
|
Value: ext.Value,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,18 @@ package cloudcas
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/asn1"
|
||||||
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
privateca "cloud.google.com/go/security/privateca/apiv1beta1"
|
privateca "cloud.google.com/go/security/privateca/apiv1beta1"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/cas/apiv1"
|
"github.com/smallstep/certificates/cas/apiv1"
|
||||||
privatecapb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1beta1"
|
"google.golang.org/api/option"
|
||||||
|
pb "google.golang.org/genproto/googleapis/cloud/security/privateca/v1beta1"
|
||||||
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,6 +24,16 @@ func init() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func debug(v interface{}) {
|
||||||
|
b, _ := json.MarshalIndent(v, "", " ")
|
||||||
|
fmt.Println(string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
stepOIDRoot = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 37476, 9000, 64}
|
||||||
|
stepOIDCertificateAuthority = append(asn1.ObjectIdentifier(nil), append(stepOIDRoot, 2)...)
|
||||||
|
)
|
||||||
|
|
||||||
// CloudCAS implements a Certificate Authority Service using Google Cloud CAS.
|
// CloudCAS implements a Certificate Authority Service using Google Cloud CAS.
|
||||||
type CloudCAS struct {
|
type CloudCAS struct {
|
||||||
client *privateca.CertificateAuthorityClient
|
client *privateca.CertificateAuthorityClient
|
||||||
|
@ -31,14 +45,19 @@ type caClient interface{}
|
||||||
// New creates a new CertificateAuthorityService implementation using Google
|
// New creates a new CertificateAuthorityService implementation using Google
|
||||||
// Cloud CAS.
|
// Cloud CAS.
|
||||||
func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) {
|
func New(ctx context.Context, opts apiv1.Options) (*CloudCAS, error) {
|
||||||
client, err := privateca.NewCertificateAuthorityClient(ctx)
|
var cloudOpts []option.ClientOption
|
||||||
|
if opts.CredentialsFile != "" {
|
||||||
|
cloudOpts = append(cloudOpts, option.WithCredentialsFile(opts.CredentialsFile))
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := privateca.NewCertificateAuthorityClient(ctx, cloudOpts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "error creating client")
|
return nil, errors.Wrap(err, "error creating client")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &CloudCAS{
|
return &CloudCAS{
|
||||||
client: client,
|
client: client,
|
||||||
certificateAuthority: "",
|
certificateAuthority: "projects/smallstep-cas-test/locations/us-west1/certificateAuthorities/Smallstep-Test-Intermediate-CA",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,52 +70,131 @@ func (c *CloudCAS) CreateCertificate(req *apiv1.CreateCertificateRequest) (*apiv
|
||||||
return nil, errors.New("createCertificateRequest `lifetime` cannot be 0")
|
return nil, errors.New("createCertificateRequest `lifetime` cannot be 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
certConfig, err := createCertificateConfig(req.Template)
|
cert, chain, err := c.createCertificate(req.Template, req.Lifetime, req.RequestID)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := defaultContext()
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
certpb, err := c.client.CreateCertificate(ctx, &privatecapb.CreateCertificateRequest{
|
|
||||||
Parent: c.certificateAuthority,
|
|
||||||
CertificateId: "",
|
|
||||||
Certificate: &privatecapb.Certificate{
|
|
||||||
CertificateConfig: certConfig,
|
|
||||||
Lifetime: durationpb.New(req.Lifetime),
|
|
||||||
Labels: map[string]string{},
|
|
||||||
},
|
|
||||||
RequestId: req.RequestID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "cloudCAS CreateCertificate failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
cert, err := parseCertificate(certpb.PemCertificate)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &apiv1.CreateCertificateResponse{
|
return &apiv1.CreateCertificateResponse{
|
||||||
Certificate: cert,
|
Certificate: cert,
|
||||||
|
CertificateChain: chain,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RenewCertificate renews the given certificate using Google Cloud CAS.
|
||||||
|
// Google's CAS does not support the renew operation, so this method uses
|
||||||
|
// CreateCertificate.
|
||||||
func (c *CloudCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) {
|
func (c *CloudCAS) RenewCertificate(req *apiv1.RenewCertificateRequest) (*apiv1.RenewCertificateResponse, error) {
|
||||||
return nil, fmt.Errorf("not implemented")
|
switch {
|
||||||
|
case req.Template == nil:
|
||||||
|
return nil, errors.New("renewCertificate `template` cannot be nil")
|
||||||
|
case req.Lifetime == 0:
|
||||||
|
return nil, errors.New("renewCertificate `lifetime` cannot be 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, chain, err := c.createCertificate(req.Template, req.Lifetime, req.RequestID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &apiv1.RenewCertificateResponse{
|
||||||
|
Certificate: cert,
|
||||||
|
CertificateChain: chain,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RevokeCertificate a certificate using Google Cloud CAS.
|
// RevokeCertificate a certificate using Google Cloud CAS.
|
||||||
func (c *CloudCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) {
|
func (c *CloudCAS) RevokeCertificate(req *apiv1.RevokeCertificateRequest) (*apiv1.RevokeCertificateResponse, error) {
|
||||||
|
if req.Certificate == nil {
|
||||||
|
return nil, errors.New("revokeCertificate `certificate` cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("not implemented")
|
ext, ok := apiv1.FindCertificateAuthorityExtension(req.Certificate)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("error revoking certificate: certificate authority extension was not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
var cae apiv1.CertificateAuthorityExtension
|
||||||
|
if _, err := asn1.Unmarshal(ext.Value, &ext); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error unmarshaling certificate authority extension")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := defaultContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
certpb, err := c.client.RevokeCertificate(ctx, &pb.RevokeCertificateRequest{
|
||||||
|
Name: c.certificateAuthority + "/certificates/" + cae.CertificateID,
|
||||||
|
Reason: pb.RevocationReason_REVOCATION_REASON_UNSPECIFIED,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "cloudCAS RevokeCertificate failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, chain, err := getCertificateAndChain(certpb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &apiv1.RevokeCertificateResponse{
|
||||||
|
Certificate: cert,
|
||||||
|
CertificateChain: chain,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CloudCAS) createCertificate(tpl *x509.Certificate, lifetime time.Duration, requestID string) (*x509.Certificate, []*x509.Certificate, error) {
|
||||||
|
// Removes the CAS extension if it exists.
|
||||||
|
apiv1.RemoveCertificateAuthorityExtension(tpl)
|
||||||
|
|
||||||
|
// Create new CAS extension with the certificate id.
|
||||||
|
id, err := createCertificateID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
casExtension, err := apiv1.CreateCertificateAuthorityExtension(apiv1.CloudCAS, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
tpl.ExtraExtensions = append(tpl.ExtraExtensions, casExtension)
|
||||||
|
|
||||||
|
// Create and submit certificate
|
||||||
|
certConfig, err := createCertificateConfig(tpl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := defaultContext()
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cert, err := c.client.CreateCertificate(ctx, &pb.CreateCertificateRequest{
|
||||||
|
Parent: c.certificateAuthority,
|
||||||
|
CertificateId: id,
|
||||||
|
Certificate: &pb.Certificate{
|
||||||
|
CertificateConfig: certConfig,
|
||||||
|
Lifetime: durationpb.New(lifetime),
|
||||||
|
Labels: map[string]string{},
|
||||||
|
},
|
||||||
|
RequestId: requestID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrap(err, "cloudCAS CreateCertificate failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return certificate and certificate chain
|
||||||
|
return getCertificateAndChain(cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultContext() (context.Context, context.CancelFunc) {
|
func defaultContext() (context.Context, context.CancelFunc) {
|
||||||
return context.WithTimeout(context.Background(), 15*time.Second)
|
return context.WithTimeout(context.Background(), 15*time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createCertificateID() (string, error) {
|
||||||
|
id, err := uuid.NewRandom()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(err, "error creating certificate id")
|
||||||
|
}
|
||||||
|
return id.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func parseCertificate(pemCert string) (*x509.Certificate, error) {
|
func parseCertificate(pemCert string) (*x509.Certificate, error) {
|
||||||
block, _ := pem.Decode([]byte(pemCert))
|
block, _ := pem.Decode([]byte(pemCert))
|
||||||
if block == nil {
|
if block == nil {
|
||||||
|
@ -108,3 +206,22 @@ func parseCertificate(pemCert string) (*x509.Certificate, error) {
|
||||||
}
|
}
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCertificateAndChain(certpb *pb.Certificate) (*x509.Certificate, []*x509.Certificate, error) {
|
||||||
|
cert, err := parseCertificate(certpb.PemCertificate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pemChain := certpb.PemCertificateChain[:len(certpb.PemCertificateChain)-1]
|
||||||
|
chain := make([]*x509.Certificate, len(pemChain))
|
||||||
|
for i := range pemChain {
|
||||||
|
chain[i], err = parseCertificate(pemChain[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cert, chain, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
9
go.mod
9
go.mod
|
@ -4,10 +4,12 @@ go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.65.1-0.20200904011802-3c2db50b5678
|
cloud.google.com/go v0.65.1-0.20200904011802-3c2db50b5678
|
||||||
|
|
||||||
github.com/Masterminds/sprig/v3 v3.1.0
|
github.com/Masterminds/sprig/v3 v3.1.0
|
||||||
github.com/aws/aws-sdk-go v1.30.29
|
github.com/aws/aws-sdk-go v1.30.29
|
||||||
github.com/go-chi/chi v4.0.2+incompatible
|
github.com/go-chi/chi v4.0.2+incompatible
|
||||||
github.com/go-piv/piv-go v1.5.0
|
github.com/go-piv/piv-go v1.5.0
|
||||||
|
github.com/google/uuid v1.1.2
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5
|
github.com/googleapis/gax-go/v2 v2.0.5
|
||||||
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a // indirect
|
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a // indirect
|
||||||
github.com/lunixbochs/vtclean v1.0.0 // indirect
|
github.com/lunixbochs/vtclean v1.0.0 // indirect
|
||||||
|
@ -24,11 +26,16 @@ require (
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
golang.org/x/net v0.0.0-20200822124328-c89045814202
|
||||||
google.golang.org/api v0.31.0
|
google.golang.org/api v0.31.0
|
||||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d
|
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d
|
||||||
google.golang.org/grpc v1.31.1
|
google.golang.org/grpc v1.32.0
|
||||||
google.golang.org/protobuf v1.25.0
|
google.golang.org/protobuf v1.25.0
|
||||||
gopkg.in/square/go-jose.v2 v2.5.1
|
gopkg.in/square/go-jose.v2 v2.5.1
|
||||||
|
// cloud.google.com/go/security/privateca/apiv1alpha1 v0.0.0
|
||||||
|
// google.golang.org/genproto/googleapis/cloud/security/privateca/v1alpha1 v0.0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
// replace github.com/smallstep/cli => ../cli
|
// replace github.com/smallstep/cli => ../cli
|
||||||
// replace github.com/smallstep/nosql => ../nosql
|
// replace github.com/smallstep/nosql => ../nosql
|
||||||
// replace go.step.sm/crypto => ../crypto
|
// replace go.step.sm/crypto => ../crypto
|
||||||
|
|
||||||
|
// replace cloud.google.com/go/security/privateca/apiv1alpha1 => ./pkg/cloud.google.com/go/security/privateca/apiv1alpha1
|
||||||
|
// replace google.golang.org/genproto/googleapis/cloud/security/privateca/v1alpha1 => ./pkg/google.golang.org/genproto/googleapis/cloud/security/privateca/v1alpha1
|
||||||
|
|
5
go.sum
5
go.sum
|
@ -298,6 +298,8 @@ github.com/google/trillian-examples v0.0.0-20190603134952-4e75ba15216c/go.mod h1
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
|
@ -911,6 +913,7 @@ google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6D
|
||||||
google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d h1:92D1fum1bJLKSdr11OJ+54YeCMCGYIygTA7R/YZxH5M=
|
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d h1:92D1fum1bJLKSdr11OJ+54YeCMCGYIygTA7R/YZxH5M=
|
||||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
|
google.golang.org/genproto v0.0.0-20200910191746-8ad3c7ee2cd1 h1:Oi/dETbxPPblvoi4hgkzJun62A4dwuBsTM0UcZYpN3U=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
@ -929,6 +932,8 @@ google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI=
|
||||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs=
|
google.golang.org/grpc v1.31.1 h1:SfXqXS5hkufcdZ/mHtYCh53P2b+92WQq/DZcKLgsFRs=
|
||||||
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
|
google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
|
||||||
|
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
|
Loading…
Reference in a new issue