func parseAndVerifyCertChain(x5c []string, roots *x509.CertPool) (leafKey libtrust.PublicKey, err error) { if len(x5c) == 0 { return nil, errors.New("empty x509 certificate chain") } // Ensure the first element is encoded correctly. leafCertDer, err := base64.StdEncoding.DecodeString(x5c[0]) if err != nil { return nil, fmt.Errorf("unable to decode leaf certificate: %s", err) } // And that it is a valid x509 certificate. leafCert, err := x509.ParseCertificate(leafCertDer) if err != nil { return nil, fmt.Errorf("unable to parse leaf certificate: %s", err) } // The rest of the certificate chain are intermediate certificates. intermediates := x509.NewCertPool() for i := 1; i < len(x5c); i++ { intermediateCertDer, err := base64.StdEncoding.DecodeString(x5c[i]) if err != nil { return nil, fmt.Errorf("unable to decode intermediate certificate: %s", err) } intermediateCert, err := x509.ParseCertificate(intermediateCertDer) if err != nil { return nil, fmt.Errorf("unable to parse intermediate certificate: %s", err) } intermediates.AddCert(intermediateCert) } verifyOpts := x509.VerifyOptions{ Intermediates: intermediates, Roots: roots, KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, } // TODO: this call returns certificate chains which we ignore for now, but // we should check them for revocations if we have the ability later. if _, err = leafCert.Verify(verifyOpts); err != nil { return nil, fmt.Errorf("unable to verify certificate chain: %s", err) } // Get the public key from the leaf certificate. leafCryptoKey, ok := leafCert.PublicKey.(crypto.PublicKey) if !ok { return nil, errors.New("unable to get leaf cert public key value") } leafKey, err = libtrust.FromCryptoPublicKey(leafCryptoKey) if err != nil { return nil, fmt.Errorf("unable to make libtrust public key from leaf certificate: %s", err) } return }
// newAccessController creates an accessController using the given options. func newAccessController(options map[string]interface{}) (auth.AccessController, error) { config, err := checkOptions(options) if err != nil { return nil, err } fp, err := os.Open(config.rootCertBundle) if err != nil { return nil, fmt.Errorf("unable to open token auth root certificate bundle file %q: %s", config.rootCertBundle, err) } defer fp.Close() rawCertBundle, err := ioutil.ReadAll(fp) if err != nil { return nil, fmt.Errorf("unable to read token auth root certificate bundle file %q: %s", config.rootCertBundle, err) } var rootCerts []*x509.Certificate pemBlock, rawCertBundle := pem.Decode(rawCertBundle) for pemBlock != nil { if pemBlock.Type == "CERTIFICATE" { cert, err := x509.ParseCertificate(pemBlock.Bytes) if err != nil { return nil, fmt.Errorf("unable to parse token auth root certificate: %s", err) } rootCerts = append(rootCerts, cert) } pemBlock, rawCertBundle = pem.Decode(rawCertBundle) } if len(rootCerts) == 0 { return nil, errors.New("token auth requires at least one token signing root certificate") } rootPool := x509.NewCertPool() trustedKeys := make(map[string]libtrust.PublicKey, len(rootCerts)) for _, rootCert := range rootCerts { rootPool.AddCert(rootCert) pubKey, err := libtrust.FromCryptoPublicKey(crypto.PublicKey(rootCert.PublicKey)) if err != nil { return nil, fmt.Errorf("unable to get public key from token auth root certificate: %s", err) } trustedKeys[pubKey.KeyID()] = pubKey } return &accessController{ realm: config.realm, issuer: config.issuer, service: config.service, rootCerts: rootPool, trustedKeys: trustedKeys, }, nil }
func loadCertAndKey(certFile, keyFile string) (pk libtrust.PublicKey, prk libtrust.PrivateKey, err error) { cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { return } x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) if err != nil { return } pk, err = libtrust.FromCryptoPublicKey(x509Cert.PublicKey) if err != nil { return } prk, err = libtrust.FromCryptoPrivateKey(cert.PrivateKey) return }
// NewIdentityAuthTLSConfig creates a tls.Config for the client to use for // libtrust identity authentication func NewIdentityAuthTLSConfig(trustKey libtrust.PrivateKey, knownHostsPath, proto, addr string) (*tls.Config, error) { tlsConfig := createTLSConfig() // Load known hosts knownHosts, err := libtrust.LoadKeySetFile(knownHostsPath) if err != nil { return nil, fmt.Errorf("Could not load trusted hosts file: %s", err) } // Generate CA pool from known hosts allowedHosts, err := libtrust.FilterByHosts(knownHosts, addr, false) if err != nil { return nil, fmt.Errorf("Error filtering hosts: %s", err) } certPool, err := libtrust.GenerateCACertPool(trustKey, allowedHosts) if err != nil { return nil, fmt.Errorf("Could not create CA pool: %s", err) } tlsConfig.ServerName = "docker" tlsConfig.RootCAs = certPool // Generate client cert from trust key x509Cert, err := libtrust.GenerateSelfSignedClientCert(trustKey) if err != nil { return nil, fmt.Errorf("Certificate generation error: %s", err) } tlsConfig.Certificates = []tls.Certificate{{ Certificate: [][]byte{x509Cert.Raw}, PrivateKey: trustKey.CryptoPrivateKey(), Leaf: x509Cert, }} // Connect to server to see if it is a known host tlsConfig.InsecureSkipVerify = true testConn, err := tls.Dial(proto, addr, tlsConfig) if err != nil { return nil, fmt.Errorf("TLS Handshake error: %s", err) } opts := x509.VerifyOptions{ Roots: tlsConfig.RootCAs, CurrentTime: time.Now(), DNSName: tlsConfig.ServerName, Intermediates: x509.NewCertPool(), } certs := testConn.ConnectionState().PeerCertificates for i, cert := range certs { if i == 0 { continue } opts.Intermediates.AddCert(cert) } _, err = certs[0].Verify(opts) if err != nil { if _, ok := err.(x509.UnknownAuthorityError); ok { pubKey, err := libtrust.FromCryptoPublicKey(certs[0].PublicKey) if err != nil { return nil, fmt.Errorf("Error extracting public key from certificate: %s", err) } // If server is not a known host, prompt user to ask whether it should // be trusted and add to the known hosts file if promptUnknownKey(pubKey, addr) { pubKey.AddExtendedField("hosts", []string{addr}) err = libtrust.AddKeySetFile(knownHostsPath, pubKey) if err != nil { return nil, fmt.Errorf("Error saving updated host keys file: %s", err) } ca, err := libtrust.GenerateCACert(trustKey, pubKey) if err != nil { return nil, fmt.Errorf("Error generating CA: %s", err) } tlsConfig.RootCAs.AddCert(ca) } else { return nil, fmt.Errorf("Cancelling request due to invalid certificate") } } else { return nil, fmt.Errorf("TLS verification error: %s", err) } } testConn.Close() tlsConfig.InsecureSkipVerify = false return tlsConfig, nil }