mirror of https://github.com/docker/docs.git
Added trustmanager package and simple CLI
This commit is contained in:
parent
00a4ef9d15
commit
6ffe6df102
8
Makefile
8
Makefile
|
@ -19,6 +19,10 @@ ${PREFIX}/bin/vetinari-server: version/version.go $(shell find . -type f -name '
|
|||
@echo "+ $@"
|
||||
@go build -o $@ ${GO_LDFLAGS} ./cmd/vetinari-server
|
||||
|
||||
${PREFIX}/bin/trustmanager: version/version.go $(shell find . -type f -name '*.go')
|
||||
@echo "+ $@"
|
||||
@go build -o $@ ${GO_LDFLAGS} ./cmd/trustmanager
|
||||
|
||||
vet:
|
||||
@echo "+ $@"
|
||||
@test -z "$$(go tool vet -printf=false . 2>&1 | grep -v Godeps/_workspace/src/ | tee /dev/stderr)"
|
||||
|
@ -49,9 +53,9 @@ protos:
|
|||
clean-protos:
|
||||
@rm proto/*.pb.go
|
||||
|
||||
binaries: ${PREFIX}/bin/vetinari-server
|
||||
binaries: ${PREFIX}/bin/vetinari-server ${PREFIX}/bin/trustmanager
|
||||
@echo "+ $@"
|
||||
|
||||
clean:
|
||||
@echo "+ $@"
|
||||
@rm -rf "${PREFIX}/bin/vetinari-server"
|
||||
@rm -rf "${PREFIX}/bin/vetinari-server" "${PREFIX}/bin/trustmanager"
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
commandList = cli.Command{
|
||||
Name: "list",
|
||||
Usage: `List the currently trusted certificate authorities.`,
|
||||
Description: `List the currently trusted certificate authorities.`,
|
||||
Action: list,
|
||||
}
|
||||
)
|
||||
|
||||
func list(ctx *cli.Context) {
|
||||
// Load all the certificates
|
||||
trustedCAs := caStore.GetCertificates()
|
||||
trustedRepos := repoStore.GetCertificates()
|
||||
|
||||
fmt.Println("CAs Loaded:")
|
||||
for _, c := range trustedCAs {
|
||||
print_cert(c)
|
||||
}
|
||||
|
||||
fmt.Println("Repos Loaded:")
|
||||
for _, c := range trustedRepos {
|
||||
print_cert(c)
|
||||
}
|
||||
}
|
||||
|
||||
func print_cert(cert *x509.Certificate) {
|
||||
timeDifference := cert.NotAfter.Sub(time.Now())
|
||||
fmt.Printf("Certificate: %s ; Expires in: %v days; SKID: %s\n", printPkix(cert.Subject), math.Floor(timeDifference.Hours()/24), hex.EncodeToString(cert.SubjectKeyId[:]))
|
||||
}
|
||||
|
||||
func printPkix(pkixName pkix.Name) string {
|
||||
return fmt.Sprintf("%s - %s", pkixName.CommonName, pkixName.Organization)
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/vetinari/trustmanager"
|
||||
)
|
||||
|
||||
const caDir string = ".docker/trust/certificate_authorities/"
|
||||
const repoDir string = ".docker/trust/repositories/"
|
||||
|
||||
var caStore trustmanager.X509Store
|
||||
var repoStore trustmanager.X509Store
|
||||
|
||||
func init() {
|
||||
|
||||
// Retrieve current user to get home directory
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
errorf("cannot get current user: %v", err)
|
||||
}
|
||||
|
||||
// Get home directory for current user
|
||||
homeDir := usr.HomeDir
|
||||
if homeDir == "" {
|
||||
errorf("cannot get current user home directory")
|
||||
}
|
||||
|
||||
// Ensure the existence of the CAs directory
|
||||
fullCaDir, fullRepoDir := setupDefaultDirectories(homeDir)
|
||||
|
||||
// TODO(diogo): inspect permissions of the directories/files. Warn.
|
||||
caStore = trustmanager.NewX509FilteredFileStore(fullCaDir, func(cert *x509.Certificate) bool {
|
||||
return cert.IsCA
|
||||
})
|
||||
repoStore = trustmanager.NewX509FileStore(fullRepoDir)
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "keymanager"
|
||||
app.Usage = "trust keymanager"
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
commandTrust,
|
||||
commandList,
|
||||
commandUntrust,
|
||||
}
|
||||
|
||||
app.RunAndExitOnError()
|
||||
}
|
||||
|
||||
func errorf(format string, args ...interface{}) {
|
||||
fmt.Printf("* fatal: "+format+"\n", args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func setupDefaultDirectories(homeDir string) (string, string) {
|
||||
fullCaDir := path.Join(homeDir, path.Dir(caDir))
|
||||
if err := os.MkdirAll(fullCaDir, 0700); err != nil {
|
||||
errorf("cannot create directory: %v", err)
|
||||
}
|
||||
|
||||
// Ensure the existence of the repositories directory
|
||||
fullRepoDir := path.Join(homeDir, path.Dir(repoDir))
|
||||
if err := os.MkdirAll(fullRepoDir, 0700); err != nil {
|
||||
errorf("cannot create directory: %v", err)
|
||||
}
|
||||
|
||||
return fullCaDir, fullRepoDir
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
commandTrust = cli.Command{
|
||||
Name: "trust",
|
||||
Usage: "Add an entry to the trusted certificate authority list.",
|
||||
Description: "Add an entry to the trusted certificate authority list.",
|
||||
Action: add,
|
||||
}
|
||||
)
|
||||
|
||||
func add(ctx *cli.Context) {
|
||||
args := []string(ctx.Args())
|
||||
|
||||
if len(args) < 1 {
|
||||
cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||
errorf("must specify a URL or file.")
|
||||
}
|
||||
|
||||
// Verify if argument is a valid URL
|
||||
url, err := url.Parse(args[0])
|
||||
if err == nil && url.Scheme != "" {
|
||||
err = caStore.AddCertFromURL(args[0])
|
||||
if err != nil {
|
||||
errorf("error adding certificate to CA Store: %v", err)
|
||||
}
|
||||
// Verify is argument is a valid file
|
||||
} else if _, err := os.Stat(args[0]); err == nil {
|
||||
if err := caStore.AddCertFromFile(args[0]); err != nil {
|
||||
errorf("error adding certificate from file: %v", err)
|
||||
}
|
||||
} else {
|
||||
errorf("please provide a file location or URL for CA certificate.")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
commandUntrust = cli.Command{
|
||||
Name: "untrust",
|
||||
Usage: "remove trust from a specifice certificate authority",
|
||||
Description: "remove trust from a specifice certificate authority.",
|
||||
Action: untrust,
|
||||
}
|
||||
)
|
||||
|
||||
func untrust(ctx *cli.Context) {
|
||||
args := []string(ctx.Args())
|
||||
|
||||
if len(args) < 1 {
|
||||
cli.ShowCommandHelp(ctx, ctx.Command.Name)
|
||||
errorf("must specify a SHA256 SubjectKeyID of the certificate")
|
||||
}
|
||||
|
||||
cert, err := caStore.GetCertificateBySKID(args[0])
|
||||
if err != nil {
|
||||
errorf("certificate not found")
|
||||
}
|
||||
|
||||
fmt.Printf("Removing: ")
|
||||
print_cert(cert)
|
||||
|
||||
err = caStore.RemoveCert(cert)
|
||||
if err != nil {
|
||||
errorf("failed to remove certificate for Key Store")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
package trustmanager
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
// X509FileStore implements X509Store that persists on disk
|
||||
type X509FileStore struct {
|
||||
baseDir string
|
||||
validate Validator
|
||||
fileMap map[ID]string
|
||||
fingerprintMap map[ID]*x509.Certificate
|
||||
nameMap map[string][]ID
|
||||
}
|
||||
|
||||
// NewX509FileStore returns a new X509FileStore.
|
||||
func NewX509FileStore(directory string) *X509FileStore {
|
||||
validate := ValidatorFunc(func(cert *x509.Certificate) bool { return true })
|
||||
|
||||
s := &X509FileStore{
|
||||
|
||||
baseDir: directory,
|
||||
validate: validate,
|
||||
fileMap: make(map[ID]string),
|
||||
fingerprintMap: make(map[ID]*x509.Certificate),
|
||||
nameMap: make(map[string][]ID),
|
||||
}
|
||||
|
||||
loadCertsFromDir(s, directory)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// NewX509FilteredFileStore returns a new X509FileStore that validates certificates
|
||||
// that are added.
|
||||
func NewX509FilteredFileStore(directory string, validate func(*x509.Certificate) bool) *X509FileStore {
|
||||
s := &X509FileStore{
|
||||
|
||||
baseDir: directory,
|
||||
validate: ValidatorFunc(validate),
|
||||
fileMap: make(map[ID]string),
|
||||
fingerprintMap: make(map[ID]*x509.Certificate),
|
||||
nameMap: make(map[string][]ID),
|
||||
}
|
||||
|
||||
loadCertsFromDir(s, directory)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// AddCert creates a filename for a given cert and adds a certificate with that name
|
||||
func (s X509FileStore) AddCert(cert *x509.Certificate) error {
|
||||
if cert == nil {
|
||||
return errors.New("adding nil Certificate to X509Store")
|
||||
}
|
||||
|
||||
fingerprint := fingerprintCert(cert)
|
||||
filename := path.Join(s.baseDir, string(fingerprint)+certExtension)
|
||||
if err := s.addNamedCert(cert, filename); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// addNamedCert allows adding a certificate while controling the filename it gets
|
||||
// stored under. If the file does not exist on disk, saves it.
|
||||
func (s X509FileStore) addNamedCert(cert *x509.Certificate, filename string) error {
|
||||
if cert == nil {
|
||||
return errors.New("adding nil Certificate to X509Store")
|
||||
}
|
||||
|
||||
fingerprint := fingerprintCert(cert)
|
||||
|
||||
// Validate if we already loaded this certificate before
|
||||
if _, ok := s.fingerprintMap[fingerprint]; ok {
|
||||
return errors.New("certificate already in the store")
|
||||
}
|
||||
|
||||
// Check if this certificate meets our validation criteria
|
||||
if !s.validate.Validate(cert) {
|
||||
return errors.New("certificate validation failed")
|
||||
}
|
||||
|
||||
// Overwrite every certificate SubjectKeyID with a SHA256 version.
|
||||
subjectKeyID := sha256.Sum256(cert.Raw)
|
||||
cert.SubjectKeyId = subjectKeyID[:]
|
||||
|
||||
// Add the certificate to our in-memory storage
|
||||
s.fingerprintMap[fingerprint] = cert
|
||||
s.fileMap[fingerprint] = filename
|
||||
|
||||
name := string(cert.RawSubject)
|
||||
s.nameMap[name] = append(s.nameMap[name], fingerprint)
|
||||
|
||||
// Save the file to disk if not already there.
|
||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
return saveCertificate(cert, filename)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveCert removes a certificate from a X509FileStore.
|
||||
func (s X509FileStore) RemoveCert(cert *x509.Certificate) error {
|
||||
if cert == nil {
|
||||
return errors.New("removing nil Certificate from X509Store")
|
||||
}
|
||||
|
||||
fingerprint := fingerprintCert(cert)
|
||||
delete(s.fingerprintMap, fingerprint)
|
||||
filename := s.fileMap[fingerprint]
|
||||
delete(s.fileMap, fingerprint)
|
||||
|
||||
name := string(cert.RawSubject)
|
||||
|
||||
// Filter the fingerprint out of this name entry
|
||||
fpList := s.nameMap[name]
|
||||
newfpList := fpList[:0]
|
||||
for _, x := range fpList {
|
||||
if x != fingerprint {
|
||||
newfpList = append(newfpList, x)
|
||||
}
|
||||
}
|
||||
|
||||
s.nameMap[name] = newfpList
|
||||
|
||||
if err := os.Remove(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddCertFromPEM adds the first certificate that it finds in the byte[], returning
|
||||
// an error if no Certificates are found
|
||||
func (s X509FileStore) AddCertFromPEM(pemBytes []byte) error {
|
||||
cert, err := loadCertFromPEM(pemBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.AddCert(cert)
|
||||
}
|
||||
|
||||
// AddCertFromFile tries to adds a X509 certificate to the store given a filename
|
||||
func (s X509FileStore) AddCertFromFile(originFilname string) error {
|
||||
cert, err := loadCertFromFile(originFilname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
filename := s.genDestinationCertFilename(cert, originFilname)
|
||||
|
||||
return s.addNamedCert(cert, filename)
|
||||
}
|
||||
|
||||
// AddCertFromURL tries to adds a X509 certificate to the store given a HTTPS URL
|
||||
func (s X509FileStore) AddCertFromURL(urlStr string) error {
|
||||
url, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if we are adding via HTTPS
|
||||
if url.Scheme != "https" {
|
||||
return errors.New("only HTTPS URLs allowed.")
|
||||
}
|
||||
|
||||
// Download the certificate and write to directory
|
||||
resp, err := http.Get(url.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy the content to certBytes
|
||||
defer resp.Body.Close()
|
||||
certBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Try to extract the first valid PEM certificate from the bytes
|
||||
cert, err := loadCertFromPEM(certBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate a unique destination URL based on the path
|
||||
filename := s.genDestinationCertFilename(cert, url.Path)
|
||||
|
||||
return s.addNamedCert(cert, filename)
|
||||
}
|
||||
|
||||
// GetCertificates returns an array with all of the current X509 Certificates.
|
||||
func (s X509FileStore) GetCertificates() []*x509.Certificate {
|
||||
certs := make([]*x509.Certificate, len(s.fingerprintMap))
|
||||
i := 0
|
||||
for _, v := range s.fingerprintMap {
|
||||
certs[i] = v
|
||||
i++
|
||||
}
|
||||
return certs
|
||||
}
|
||||
|
||||
// GetCertificatePool returns an x509 CertPool loaded with all the certificates
|
||||
// in the store.
|
||||
func (s X509FileStore) GetCertificatePool() *x509.CertPool {
|
||||
pool := x509.NewCertPool()
|
||||
|
||||
for _, v := range s.fingerprintMap {
|
||||
pool.AddCert(v)
|
||||
}
|
||||
return pool
|
||||
}
|
||||
|
||||
// genDestinationCertFilename generates a unique destination certificate filename
|
||||
// given a sourceFilename to help keep indication to where the original file came from
|
||||
func (s X509FileStore) genDestinationCertFilename(cert *x509.Certificate, sourceFilename string) string {
|
||||
// Take the file name, extension and base name from filename
|
||||
_, fName := path.Split(sourceFilename)
|
||||
extName := path.Ext(sourceFilename)
|
||||
bName := fName[:len(fName)-len(extName)]
|
||||
|
||||
filename := path.Join(s.baseDir, bName+certExtension)
|
||||
|
||||
// If a file with the same name already exists in the destination directory
|
||||
// add hash to filename
|
||||
if _, err := os.Stat(filename); err == nil {
|
||||
fingerprint := fingerprintCert(cert)
|
||||
// Add the certificate fingerprint to the file basename_FINGERPRINT.crt
|
||||
filename = path.Join(s.baseDir, bName+"_"+string(fingerprint)+certExtension)
|
||||
}
|
||||
return filename
|
||||
}
|
||||
|
||||
// GetCertificateBySKID returns the certificate that matches a certain SKID or error
|
||||
func (s X509FileStore) GetCertificateBySKID(hexSKID string) (*x509.Certificate, error) {
|
||||
// If it does not look like a hex encoded sha256 hash, error
|
||||
if len(hexSKID) != 64 {
|
||||
return nil, errors.New("invalid Subject Key Identifier")
|
||||
}
|
||||
|
||||
// Check to see if this subject key identifier exists
|
||||
if cert, ok := s.fingerprintMap[ID(hexSKID)]; ok {
|
||||
return cert, nil
|
||||
|
||||
}
|
||||
return nil, errors.New("certificate not found in Key Store")
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
package trustmanager
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// X509MemStore implements X509Store as an in-memory object with no persistence
|
||||
type X509MemStore struct {
|
||||
validate Validator
|
||||
fingerprintMap map[ID]*x509.Certificate
|
||||
nameMap map[string][]ID
|
||||
}
|
||||
|
||||
// NewX509MemStore returns a new X509MemStore.
|
||||
func NewX509MemStore() *X509MemStore {
|
||||
validate := ValidatorFunc(func(cert *x509.Certificate) bool { return true })
|
||||
|
||||
return &X509MemStore{
|
||||
validate: validate,
|
||||
fingerprintMap: make(map[ID]*x509.Certificate),
|
||||
nameMap: make(map[string][]ID),
|
||||
}
|
||||
}
|
||||
|
||||
// AddCert adds a certificate to the store
|
||||
func (s X509MemStore) AddCert(cert *x509.Certificate) error {
|
||||
if cert == nil {
|
||||
return errors.New("adding nil Certificate to X509Store")
|
||||
}
|
||||
|
||||
if !s.validate.Validate(cert) {
|
||||
return errors.New("certificate failed validation")
|
||||
}
|
||||
|
||||
fingerprint := fingerprintCert(cert)
|
||||
|
||||
s.fingerprintMap[fingerprint] = cert
|
||||
name := string(cert.RawSubject)
|
||||
s.nameMap[name] = append(s.nameMap[name], fingerprint)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveCert removes a certificate from a X509MemStore.
|
||||
func (s X509MemStore) RemoveCert(cert *x509.Certificate) error {
|
||||
if cert == nil {
|
||||
return errors.New("removing nil Certificate to X509Store")
|
||||
}
|
||||
|
||||
fingerprint := fingerprintCert(cert)
|
||||
delete(s.fingerprintMap, fingerprint)
|
||||
name := string(cert.RawSubject)
|
||||
|
||||
// Filter the fingerprint out of this name entry
|
||||
fpList := s.nameMap[name]
|
||||
newfpList := fpList[:0]
|
||||
for _, x := range fpList {
|
||||
if x != fingerprint {
|
||||
newfpList = append(newfpList, x)
|
||||
}
|
||||
}
|
||||
|
||||
s.nameMap[name] = newfpList
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddCertFromPEM adds a certificate to the store from a PEM blob
|
||||
func (s X509MemStore) AddCertFromPEM(pemCerts []byte) error {
|
||||
ok := false
|
||||
for len(pemCerts) > 0 {
|
||||
var block *pem.Block
|
||||
block, pemCerts = pem.Decode(pemCerts)
|
||||
if block == nil {
|
||||
return errors.New("no PEM data found")
|
||||
}
|
||||
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return errors.New("error while parsing PEM certificate")
|
||||
}
|
||||
|
||||
s.AddCert(cert)
|
||||
ok = true
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return errors.New("no certificates found in PEM data")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddCertFromFile tries to adds a X509 certificate to the store given a filename
|
||||
func (s X509MemStore) AddCertFromFile(originFilname string) error {
|
||||
cert, err := loadCertFromFile(originFilname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.AddCert(cert)
|
||||
}
|
||||
|
||||
// AddCertFromURL tries to adds a X509 certificate to the store given a HTTPS URL
|
||||
func (s X509MemStore) AddCertFromURL(urlStr string) error {
|
||||
url, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if we are adding via HTTPS
|
||||
if url.Scheme != "https" {
|
||||
return errors.New("only HTTPS URLs allowed.")
|
||||
}
|
||||
|
||||
// Download the certificate and write to directory
|
||||
resp, err := http.Get(url.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Copy the content to certBytes
|
||||
defer resp.Body.Close()
|
||||
certBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Try to extract the first valid PEM certificate from the bytes
|
||||
cert, err := loadCertFromPEM(certBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.AddCert(cert)
|
||||
}
|
||||
|
||||
// GetCertificates returns an array with all of the current X509 Certificates.
|
||||
func (s X509MemStore) GetCertificates() []*x509.Certificate {
|
||||
certs := make([]*x509.Certificate, len(s.fingerprintMap))
|
||||
i := 0
|
||||
for _, v := range s.fingerprintMap {
|
||||
certs[i] = v
|
||||
i++
|
||||
}
|
||||
return certs
|
||||
}
|
||||
|
||||
// GetCertificatePool returns an x509 CertPool loaded with all the certificates
|
||||
// in the store.
|
||||
func (s X509MemStore) GetCertificatePool() *x509.CertPool {
|
||||
pool := x509.NewCertPool()
|
||||
|
||||
for _, v := range s.fingerprintMap {
|
||||
pool.AddCert(v)
|
||||
}
|
||||
return pool
|
||||
}
|
||||
|
||||
// GetCertificateBySKID returns the certificate that matches a certain SKID or error
|
||||
func (s X509MemStore) GetCertificateBySKID(hexSKID string) (*x509.Certificate, error) {
|
||||
// If it does not look like a hex encoded sha256 hash, error
|
||||
if len(hexSKID) != 64 {
|
||||
return nil, errors.New("invalid Subject Key Identifier")
|
||||
}
|
||||
|
||||
// Check to see if this subject key identifier exists
|
||||
if cert, ok := s.fingerprintMap[ID(hexSKID)]; ok {
|
||||
return cert, nil
|
||||
|
||||
}
|
||||
return nil, errors.New("certificate not found in Key Store")
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package trustmanager
|
||||
|
||||
import "crypto/x509"
|
||||
|
||||
const certExtension string = ".crt"
|
||||
|
||||
// X509Store is the interface for all X509Stores
|
||||
type X509Store interface {
|
||||
AddCert(cert *x509.Certificate) error
|
||||
AddCertFromPEM(pemCerts []byte) error
|
||||
AddCertFromFile(filename string) error
|
||||
AddCertFromURL(urlStr string) error
|
||||
RemoveCert(cert *x509.Certificate) error
|
||||
GetCertificateBySKID(hexSKID string) (*x509.Certificate, error)
|
||||
GetCertificates() []*x509.Certificate
|
||||
GetCertificatePool() *x509.CertPool
|
||||
}
|
||||
|
||||
type ID string
|
||||
|
||||
// Validator is a convenience type to create validating function
|
||||
type Validator interface {
|
||||
Validate(cert *x509.Certificate) bool
|
||||
}
|
||||
|
||||
// ValidatorFunc is a convenience type to create functions that implement
|
||||
// the Validator interface
|
||||
type ValidatorFunc func(cert *x509.Certificate) bool
|
||||
|
||||
// Validate implements the Validator interface to allow for any func() bool method
|
||||
// to be passed as a Validator
|
||||
func (vf ValidatorFunc) Validate(cert *x509.Certificate) bool {
|
||||
return vf(cert)
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package trustmanager
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// saveCertificate is an utility function that saves a certificate as a PEM
|
||||
// encoded block to a file.
|
||||
func saveCertificate(cert *x509.Certificate, filename string) error {
|
||||
block := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
|
||||
pemdata := string(pem.EncodeToMemory(&block))
|
||||
|
||||
err := ioutil.WriteFile(filename, []byte(pemdata), 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func fingerprintCert(cert *x509.Certificate) ID {
|
||||
fingerprintBytes := sha256.Sum256(cert.Raw)
|
||||
return ID(hex.EncodeToString(fingerprintBytes[:]))
|
||||
}
|
||||
|
||||
// loadCertsFromDir receives a store and a directory and calls loadCertFromFile
|
||||
// for each certificate found
|
||||
func loadCertsFromDir(s *X509FileStore, directory string) {
|
||||
certFiles, _ := filepath.Glob(path.Join(directory, fmt.Sprintf("*%s", certExtension)))
|
||||
for _, f := range certFiles {
|
||||
cert, err := loadCertFromFile(f)
|
||||
// Ignores files that do not contain valid certificates
|
||||
if err == nil {
|
||||
s.addNamedCert(cert, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loadCertFromFile tries to adds a X509 certificate to the store given a filename
|
||||
func loadCertFromFile(filename string) (*x509.Certificate, error) {
|
||||
// TODO(diogo): handle multiple certificates in one file. Demultiplex into
|
||||
// multiple files or load only first
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var block *pem.Block
|
||||
block, b = pem.Decode(b)
|
||||
for ; block != nil; block, b = pem.Decode(b) {
|
||||
if block.Type == "CERTIFICATE" {
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err == nil {
|
||||
return cert, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("could not load certificate from file")
|
||||
}
|
||||
|
||||
// loadCertFromPEM returns the first certificate found in a bunch of bytes or error
|
||||
// if nothing is found
|
||||
func loadCertFromPEM(pemBytes []byte) (*x509.Certificate, error) {
|
||||
for len(pemBytes) > 0 {
|
||||
var block *pem.Block
|
||||
block, pemBytes = pem.Decode(pemBytes)
|
||||
if block == nil {
|
||||
return nil, errors.New("no certificates found in PEM data")
|
||||
}
|
||||
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("no certificates found in PEM data")
|
||||
}
|
Loading…
Reference in New Issue