estclient

package module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 29, 2022 License: MIT Imports: 16 Imported by: 0

README

Go EST Client

GoDoc Build Status

A minimal EST client SDK, written in Go (see RFC 7030).

Support is provided for calling the EST endpoints:

  • /.well-known/est/cacerts
  • /.well-known/est/simpleenroll
  • /.well-known/est/simplereenroll

Enrollment functions support HTTP basic authentication and/or mutual TLS with a previously issued certificate. Check what your server is expecting.

Examples

See examples/examples.go for an example that uses the test server at http://www.testrfc7030.com. o To execute the example script, run go run examples/examples.go. Read on for a description of the code.

Get the EST certificates
client := estclient.NewEstClient("testrfc7030.com:8443")

cacerts, err := client.CaCerts()
if err != nil {
    panic(err)
}

fmt.Printf("EST Root Cert: %+v\n", cacerts.EstTA.Subject)
fmt.Printf("Old EST Root Cert: %+v\n", cacerts.OldWithOld)
fmt.Printf("Old Cert Signed By New Key: %+v\n", cacerts.OldWithNew)
fmt.Printf("New Cert Signed By Old Key: %+v\n", cacerts.NewWithOld)
fmt.Printf("Other chain certs: %+v\n", cacerts.EstChainCerts)

The EstTA cert is the root of trust for all certificates issued by the EST CA. The EST CA may be a subordinate of this trust anchor, in which case EstChainCerts should contain the necessary intermediate certificates to validate certificates issued by that EST CA.

If the root of trust has been renewed, there may be cross-signed certificates returned in OldWithOld, OldWithNew and NewWithOld.

Request a certificate
// Create key and certificate request
key, err := rsa.GenerateKey(rand.Reader, 2048)
panicOnError(err)

template := x509.CertificateRequest{Subject: pkix.Name{CommonName: "Test"}}

reqBytes, err := x509.CreateCertificateRequest(rand.Reader, &template, key)
panicOnError(err)

req, err := x509.ParseCertificateRequest(reqBytes)
panicOnError(err)

// Enroll with EST CA
authData := estclient.AuthData{ID: &id, Secret: &secret}
cert, err := client.SimpleEnroll(authData, req)
panicOnError(err)
fmt.Printf("Initial cert (DER): %x\n", cert.Raw)
Renew a certificate

This client assumes mutual TLS will be required, using the previously issued key and certificate. Additional HTTP authentication (with an id and secret) is optional. Pass nil if these values are not required by the server.

// Re-enroll with EST CA
authData = estclient.AuthData{ID: &id, Secret: &secret, Key: key, ClientCert: cert}
cert2, err := client.SimpleReenroll(authData, req)
panicOnError(err)
fmt.Printf("Renewed cert (DER): %x\n", cert2.Raw)

Contributing

Contributions are very welcome. Please ensure you add tests for new functionality and consider opening an issue to discuss larger changes before beginning.

When developing, you can disable integration tests (which hit testrfc7030.com:8443) by running go test -short ./....

TODOs

  • Improve coverage of non-happy paths
  • Support more initial enrollment (and re-enrollment) authentication options.
  • Tackle any "N" items from the analysis of the RFC below
  • Convert any "TBC" items into "Y" or "N"

Analysis of RFC requirements

Category Item DONE Notes
MUST NULL and anon cipher suites MUST NOT be used because they do not provide confidentiality or support mutual certificate-based or certificate-less authentication, respectively. N Not currently enforced
MUST If the CSR KeyUsage extension indicates that the private key can be used to generate digital signatures, then the client MUST generate the CSR signature using the private key. N This is not enforced currently.
MUST The client MUST wait at least the specified "retry-after" time before repeating the same request. N This is not enforced.
MUST HTTP Basic and Digest authentication MUST only be performed over TLS 1.1 [RFC4346] or later versions. TBC Not sure if we do/can enforce this
MUST TLS 1.1 [RFC4346] (or a later version) MUST be used for all EST communications. TBC Need to check if this is enforced.
MUST Certificate validation MUST be performed as per [RFC5280]. TBC Not sure if compliant - rely on the built-in validation within Go.
MUST The EST client MUST store the extracted EST CA certificate as an Explicit TA database entry for subsequent EST server authentication. TBC I have raised a question with IETF about this. Don't believe it should be a MUST.
MUST If the client disables the Implicit TA database, and if the EST server certificate was verified using an Implicit TA database entry, then the client MUST include the "Trusted CA Indication" extension in future TLS sessions [RFC6066]. TBC In part, depending upon response from IETF.
MUST TLS cipher suites that include "EXPORT" and "DES" in their names MUST NOT be used. TBC Not sure if this is automatically the case with Go TLS library.
MUST The EST client MUST be able to handle these certificates in the response. Y Note: this refers to the OldWithOld, OldWithNew, NewWithOld certs
MUST Implementations conforming to this standard MUST provide the ability to designate Explicit TAs. Y
MUST Implementations conforming to this standard MUST provide the ability to disable use of any Implicit TA database. Y Implicit TA database is disabled when explicit TA database provided.
MUST The EST client MUST be capable of generating and parsing Simple PKI messages (see Section 4.2). Y
MUST The client MUST also be able to request CA certificates from the EST server and parse the returned "bag" of certificates (see Section 4.1). Y
MUST If the client is not capable of retrying the request or it is not capable of Basic or Digest authentication, then the client MUST terminate the connection. Y
MUST The client MUST NOT respond to the server's HTTP authentication request unless the client has authorized the EST server (as per Section 3.6). Y
MUST The EST server MUST be authenticated during the TLS handshake unless the client is requesting Bootstrap Distribution of CA certificates (Section 4.1.1) or Full CMC (Section 4.3). Y 4.1.1 is not supported.
MUST HTTPS MUST be used. Y
MUST Certificate validation MUST be performed as per [RFC5280]. Y
MUST The EST server certificate MUST conform to the [RFC5280] certificate profile. Y
MUST The client MUST maintain a distinction between the use of Explicit and Implicit TA databases during authentication in order to support proper authorization. Y Although there doesn't appear to be much meaningful difference between the two authorisation approaches.
MUST The EST client MUST perform authorization checks as specified in Section 3.6. Y Partial support, no checking of id-kp-cmcRA. That part of RFC is ambiguous.
MUST If the certificate to be renewed or rekeyed is appropriate for the negotiated cipher suite, then the client MUST use it for the TLS handshake, otherwise the client SHOULD use an alternate certificate that is suitable for the cipher suite and contains the same subject identity information. Y Yes, assuming that the certificates issued by the CA are suitable for use in mutual TLS.
MUST The EST client certificate MUST conform to the [RFC5280] certificate profile. Y
MUST When performing renegotiation, TLS "secure_renegotiation" [RFC5746] MUST be used. Y Supported in the sense that Go defaults to disabling renegotiation.
MUST The client MUST check EST server authorization before accepting any server responses or responding to HTTP authentication requests. Y
MUST When the EST client Explicit TA database is used to validate the EST server certificate, the client MUST check either the configured URI or the most recent HTTP redirection URI against the server's identity according to the rules specified in [RFC6125], Section 6.4, or the EST server certificate MUST contain the id-kp-cmcRA [RFC6402] extended key usage extension. Y We check against the configured URI.
MUST When the EST client Implicit TA database is used to validate the EST server certificate, the client MUST check the configured URI and each HTTP redirection URI according to the rules specified in [RFC6125], Section 6.4. Y
MUST HTTP authentication requests MUST NOT be responded to if the server has not been authenticated as specified in Section 3.3.1 or if the optional certificate-less authentication is used as specified in Section 3.3.3. Y
MUST EST clients and servers MUST support the /cacerts function. Y
MUST The client MUST authenticate the EST server, as specified in Section 3.3.1 if certificate-based authentication is used or Section 3.3.3 if the optional certificate-less authentication is used, and check the server's authorization as given in Section 3.6, or follow the procedure outlined in Section 4.1.1. Y
MUST Any other response code indicates an error and the client MUST abort the protocol. Y
MUST The client MUST authenticate the EST server as specified in Section 3.3.1 if certificate-based authentication is used or Section 3.3.3 if the optional certificate-less authentication is used. Y
MUST The client MUST verify the authorization of the EST server as specified in Section 3.6. Y
MUST When HTTPS POSTing to /simpleenroll, the client MUST include a Simple PKI Request as specified in CMC [RFC5272], Section 3.1 (i.e., a PKCS #10 Certification Request [RFC2986]). Y
MUST A client MUST authenticate an EST server, as specified in Section 3.3.1 if certificate-based authentication is used or Section 3.3.3 if the optional certificate-less authentication is used, and check the server's authorization as given in Section 3.6. Y
MUST The client MUST ignore any OID or attribute it does not recognize. Y
MUST If a client does not support TLS client authentication, then it MUST support HTTP-based client authentication (Section 3.2.3) or certificate-less TLS authentication (Section 3.3.3) n/a We support client auth
MUST When using certificate-less mutual authentication in TLS for enrollment, the cipher suite MUST be based on a protocol that is resistant to dictionary attack and MUST be based on a zero knowledge protocol. n/a This is not a supported authentication mode.
MUST If the EST client continues with an unauthenticated connection, the client MUST extract the HTTP content data from the response (Sections 4.1.3 or 4.3.2) and engage a human user to authorize the CA certificate using out-of-band data such as a CA certificate "fingerprint" (e.g., a SHA-256 or SHA-512 [SHS] hash on the whole CA certificate). n/a Not a supported approach
MUST EST clients MUST NOT engage in any other protocol exchange until after the /cacerts response has been accepted and a new TLS session has been established (using TLS certificate-based Authentication). n/a 4.1.1 not supported
MUST After out-of-band validation occurs, all the other certificates MUST be validated using normal [RFC5280] certificate path validation (using the most recent CA certificate as the TA) before they can be used to build certificate paths during certificate validation. n/a
MUST If the key can be used to generate digital signatures but the requested CSR KeyUsage extension prohibits generation of digital signatures, then the CSR signature MAY still be generated using the private key, but the key MUST NOT be used for any other signature operations (this is consistent with the recommendations concerning submission of proof-of-possession to an RA or CA as described in [SP-800-57-Part-1]). n/a This is surely outside the scope of what the EST client can enforce.
MUST Cipher suites that have a NULL confidentiality algorithm MUST NOT be used as they will disclose the contents of an unprotected private key. n/a No support for server key gen
MUST If the client desires to receive the private key with encryption that exists outside of and in addition to that of the TLS transport used by EST or if server policy requires that the key be delivered in such a form, the client MUST include an attribute in the CSR indicating the encryption key to be used. n/a
MUST The client MUST also include an SMIMECapabilities attribute ([RFC2633], Section 2.5) in the CSR to indicate the key encipherment algorithms the client is willing to use. n/a
MUST To specify a symmetric encryption key to be used to encrypt the server-generated private key, the client MUST include a DecryptKeyIdentifier attribute (as defined in Section 2.2.5 of [RFC4108]) specifying the identifier of the secret key to be used by the server to encrypt the private key. n/a
MUST To specify an asymmetric encryption key to be used to encrypt the server-generated private key, the client MUST include an AsymmetricDecryptKeyIdentifier attribute. n/a

Documentation

Overview

Package estclient is minimal EST client SDK (see RFC 7030).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AuthData

type AuthData struct {

	// ID is a pre-shared ID used as part of HTTP basic authentication.
	ID *string

	// Secret is a pre-shared secret used as part of HTTP basic authentication.
	Secret *string

	// Key is an existing private key owned by the client. If Key is supplied, ClientCert must also be present. The
	// pair are used to perform client TLS authentication.
	Key crypto.PrivateKey

	// ClientCert is a certificate owned by the client. If ClientCert is supplied, Key must also be present. The
	// pair are used to perform client TLS authentication.
	ClientCert *x509.Certificate
	// contains filtered or unexported fields
}

AuthData provides the authentication data offered by the client to the server. Non-nil values will be used during authentication.

type CaCertsInfo

type CaCertsInfo struct {

	// EstTA is the trust anchor of the EST system. If the EST CA is a subordinate of
	// this trust anchor, then EstChainCerts should contain the necessary certificates to
	// build a chain from issued certificates through to the EstTA.
	EstTA *x509.Certificate

	// EstChainCerts contains the certificates necessary to construct a chain from
	// the certificates issued by the EST CA through to the EstTA certificiate. This
	// field will be nil or empty if the EstTA is the same certificate as the EST CA.
	EstChainCerts []*x509.Certificate

	// OldWithOld may be present if the EstTA has renewed its certificate. It is a copy
	// of the old EstTA certificate.
	OldWithOld *x509.Certificate

	// OldWithNew may be present if the EstTA has renewed its certificate. It is a certificate
	// containing the public key of the old certificate, signed with the new EstTA key.
	OldWithNew *x509.Certificate

	// NewWithOld may be present if the EstTA has renewed its certificate. It is a certificate
	// containing the new public key of the EstTA, signed with the old key.
	NewWithOld *x509.Certificate
}

CaCertsInfo contains the data returned by the EST server when calling /cacerts.

type ClientOptions

type ClientOptions struct {
	// InsecureSkipVerify, when true, causes the apiclient to accept any TLS server certificate
	// presented by the EST server. As the name suggests, this is insecure and for testing
	// purposes only
	InsecureSkipVerify bool

	// TLSTrustAnchor, if non-nil, designates an explicit trust anchor to use for the
	// TLS session to the EST server.
	TLSTrustAnchor *x509.Certificate

	// 3.2.2.  HTTP URIs for Control
	// https://www.example.com/.well-known/est/cacerts
	// https://www.example.com/.well-known/est/arbitraryLabel1/cacerts
	// https://www.example.com/.well-known/est/arbitraryLabel2/cacerts
	Label string

	/* Set of HTTP headers for each request. Useful to override the "Host" header for example */
	Headers map[string]string

	// Override TLS server name indication and Host header
	Sni string

	// Enable rest http client debug
	Debug bool
}

ClientOptions contains configuration settings for building the EST apiclient.

type EstClient

type EstClient interface {
	// CaCerts retrieves the EST CA certificate (which will sign
	// the apiclient certificates)
	CaCerts() (*CaCertsInfo, error)

	// SimpleEnroll requests a certificate from the EST server.
	SimpleEnroll(authData AuthData, req *x509.CertificateRequest) (*x509.Certificate, error)

	// SimpleReenroll renews a client certificate.
	SimpleReenroll(authData AuthData, req *x509.CertificateRequest) (*x509.Certificate, error)
}

EstClient enables clients to request certificates from an EST server.

func NewEstClient

func NewEstClient(host string) EstClient

NewEstClient creates a apiclient that communicates with the given host. The host string is a domain name with optional ":port" suffix.

func NewEstClientWithOptions

func NewEstClientWithOptions(host string, options ClientOptions) EstClient

NewEstClientWithOptions accepts additional options to configure the EST apiclient.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL