Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
paulgriffiths committed May 28, 2020
0 parents commit e5339f7
Show file tree
Hide file tree
Showing 62 changed files with 10,921 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Temporary files
*.out
*.swp
*~

# Log files
*.log

# Executable files
cmd/estserver/estserver
cmd/estclient/estclient
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Changelog

## [1.0.0] - 2020-05-28

### Added
- Initial release
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2020 GMO GlobalSign, Inc.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
116 changes: 116 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# est

[![GoDoc](https://godoc.org/github.com/globalsign/est?status.svg)](https://godoc.org/github.com/globalsign/est)

An implementation of the Enrollment over Secure Transport (EST) certificate
enrollment protocol as defined by [RFC7030](https://tools.ietf.org/html/rfc7030).

The implementation provides:

* An EST client library;
* An EST client command line utility using the client library; and
* An EST server which can be used for testing and development purposes.

The implementation is intended to be mostly feature-complete, including
support for:

* The optional `/csrattrs` and `/serverkeygen` operations, with support for
server-generated private keys returned with or without additional
encryption
* The optional additional path segment
* Optional HTTP-based client authentication on top of certificate-based
TLS authentication

In addition, a non-standard operation is implemented enabling EST-like
enrollment using the privacy preserving protocol for distributing credentials
for keys on a Trusted Platform Module (TPM) 2.0 device, as described in Part 1,
section 24 of the Trusted Platform Module 2.0 Library specification.

## Installation

go get -u github.com/globalsign/est
go install github.com/globalsign/est/cmd/estserver
go install github.com/globalsign/est/cmd/estclient

## Quickstart

### Starting the server

When started with no configuration file, the EST server listens on
localhost:8443 and generates a random, transient Certificate Authority (CA)
which can be used for testing:

user@host:$ estserver &
[1] 62405

Refer to the documentation for more details on using a configuration file.

### Getting the CA certificates

Because we're using a random, transient CA, we must retrieve the CA certificates
in insecure mode to establish an explicit trust anchor for subsequent EST
operations. Since we only need the root CA certificate to establish a trust
anchor, we use the `-rootout` flag:

user@host:$ estclient cacerts -server localhost:8443 -insecure -rootout -out anchor.pem

We will also obtain and store the full CA certificates chain, since we'll use
it shortly to demonstrate reenrollment. Since we now have an explicit trust
anchor, we can use it instead of the `-insecure` option. Since we're storing
the full chain, we don't use the `-rootout` option here:

user@host:$ estclient cacerts -server localhost:8443 -explicit anchor.pem -out cacerts.pem

### Enrolling with an existing private key

First we generate a new private key, here using openssl:

user@host:$ openssl genrsa 4096 > key.pem
Generating RSA private key, 4096 bit long modulus
.................+++
.............+++
e is 65537 (0x10001)

Then we generate a PKCS#10 certificate signing request, and enroll using the
explicit trust anchor we previously obtained:

user@host:$ estclient csr -key key.pem -cn 'John Doe' -emails '[email protected]' -out csr.pem
user@host:$ estclient enroll -server localhost:8443 -explicit anchor.pem -csr csr.pem -out cert.pem

Using a configuration file, we can enroll with a private key resident on a
hardware module, such as a hardware security module (HSM) or a Trusted Platform
Module 2.0 (TPM) device. Refer to the documentation for more details.

### Enrolling with a server-generated private key

If we're unable or unwilling to create our own private key, the EST server can
generate one for us, and return it along with our certificate:

user@host:$ estclient serverkeygen -server localhost:8443 -explicit anchor.pem -cn 'Jane Doe' -out cert.pem -keyout key.pem

Note that we can omit the `-csr` option when enrolling and the EST client can
dynamically generate a CSR for us using fields passed at the command line and
the private key we specified, or an automatically-generated ephemeral private
key if we are requesting server-side private key generation.

### Reenrolling

Whichever way we generated our private key, we can now use it to reenroll.

To reenroll a previously obtained certificate, we must use it to authenticate
ourselves during the TLS handshake with the EST server. Since our random,
transient CA uses an intermediate CA certificate, we must provide a chain of
certificates to the EST client, or the TLS handshake may fail.

Although providing the root CA certificate is optional for a TLS handshake,
the simplest option is to provide the certificate we received along with the
full chain of CA certificates which we previously obtained. To do this, we
can just append those CA certificates to the certificate we received, and
use that chain to reenroll:

user@host:$ cat cert.pem cacerts.pem >> certs.pem
user@host:$ estclient reenroll -server localhost:8443 -explicit anchor.pem -key key.pem -certs certs.pem -out newcert.pem

Note that when we omit the `-csr` option when reenrolling, the EST client
automatically generates a CSR for us by copying the subject field and subject
alternative name extension from the certificate we're renewing.
82 changes: 82 additions & 0 deletions ca.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
Copyright (c) 2020 GMO GlobalSign, Inc.
Licensed under the MIT License (the "License"); you may not use this file except
in compliance with the License. You may obtain a copy of the License at
https://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package est

import (
"context"
"crypto/x509"
"net/http"
)

// CA is a Certificate Authority backing an EST server. The server can be
// connected to any backing CA by providing an implementation of this interface.
//
// All operations receive:
// - a context, from which the EST server logger can be retrieved by calling
// LoggerFromContext
// - the optional URI additional path segment (RFC7030 3.2.2)
// - the HTTP request object from the server, from which the HTTP headers
// passed by the client (including the Host header, to support virtual
// servers) can be obtained
//
// Any error object returned from these functions which implements Error will be
// used by the EST server to determine the HTTP response code, human-readable
// error description, and Retry-After header value, if applicable. Any other
// error will be treated as an internal server error.
type CA interface {
// CACerts requests a copy of the current CA certificates. See RFC7030 4.1.
CACerts(ctx context.Context, aps string, r *http.Request) ([]*x509.Certificate, error)

// CSRAttrs requests a list of CA-desired CSR attributes. The returned list
// may be empty. See RFC7030 4.5.
CSRAttrs(ctx context.Context, aps string, r *http.Request) (CSRAttrs, error)

// Enroll requests a new certificate. See RFC7030 4.2.
Enroll(ctx context.Context, csr *x509.CertificateRequest, aps string, r *http.Request) (*x509.Certificate, error)

// Reenroll requests renewal/rekey of an existing certificate. See RFC7030
// 4.2.
Reenroll(ctx context.Context, cert *x509.Certificate, csr *x509.CertificateRequest, aps string, r *http.Request) (*x509.Certificate, error)

// ServerKeyGen requests a new certificate and a private key. The key must
// be returned as a DER-encoded PKCS8 PrivateKeyInfo structure if additional
// encryption is not being employed, or returned inside a CMS SignedData
// structure which itself is inside a CMS EnvelopedData structure. See
// RFC7030 4.4.
ServerKeyGen(ctx context.Context, csr *x509.CertificateRequest, aps string, r *http.Request) (*x509.Certificate, []byte, error)

// TPMEnroll requests a new certificate using the TPM 2.0 privacy-preserving
// protocol. An EK certificate chain with a length of at least one must be
// provided, along with the EK and AK public areas. The return values are an
// encrypted credential blob, an encrypted seed, and the certificate itself
// inside a CMS EnvelopedData encrypted with the credential as a pre-shared
// key.
TPMEnroll(ctx context.Context, csr *x509.CertificateRequest, ekcerts []*x509.Certificate, ekPub, akPub []byte, aps string, r *http.Request) ([]byte, []byte, []byte, error)
}

// Error represents an error which can be translated into an HTTP
// status code and message, and optionally specify a Retry-After period.
type Error interface {
// StatusCode returns the HTTP status code.
StatusCode() int

// Error returns a human-readable description of the error.
Error() string

// RetryAfter returns the value in seconds after which the client should
// retry the request.
RetryAfter() int
}
Loading

0 comments on commit e5339f7

Please sign in to comment.