Merge pull request #65 from letsencrypt/vendorize
Vendorize all dependencies with godep.
This commit is contained in:
commit
e077548886
|
|
@ -11,6 +11,9 @@ _test
|
||||||
*.[568vq]
|
*.[568vq]
|
||||||
[568vq].out
|
[568vq].out
|
||||||
|
|
||||||
|
# Vim swap files
|
||||||
|
*.sw?
|
||||||
|
|
||||||
*.cgo1.go
|
*.cgo1.go
|
||||||
*.cgo2.c
|
*.cgo2.c
|
||||||
_cgo_defun.c
|
_cgo_defun.c
|
||||||
|
|
|
||||||
18
.travis.yml
18
.travis.yml
|
|
@ -13,21 +13,5 @@ before_install:
|
||||||
- go get github.com/mattn/goveralls
|
- go get github.com/mattn/goveralls
|
||||||
- go get github.com/modocache/gover
|
- go get github.com/modocache/gover
|
||||||
|
|
||||||
install:
|
|
||||||
- go get -t -v -tags "pkcs11" ./...
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- go vet -x ./...
|
- bash test.sh
|
||||||
- $HOME/gopath/bin/golint ./...
|
|
||||||
- go test -covermode=count -coverprofile=analysis.coverprofile ./analysis/
|
|
||||||
- go test -covermode=count -coverprofile=ca.coverprofile ./ca/
|
|
||||||
- go test -covermode=count -coverprofile=core.coverprofile ./core/
|
|
||||||
- go test -covermode=count -coverprofile=log.coverprofile ./log/
|
|
||||||
- go test -covermode=count -coverprofile=ra.coverprofile ./ra/
|
|
||||||
- go test -covermode=count -coverprofile=rpc.coverprofile ./rpc/
|
|
||||||
- go test -covermode=count -coverprofile=sa.coverprofile ./sa/
|
|
||||||
- go test -covermode=count -coverprofile=test.coverprofile ./test/
|
|
||||||
- go test -covermode=count -coverprofile=va.coverprofile ./va/
|
|
||||||
- go test -covermode=count -coverprofile=wfe.coverprofile ./wfe/
|
|
||||||
- $HOME/gopath/bin/gover
|
|
||||||
- $HOME/gopath/bin/goveralls -coverprofile=gover.coverprofile -service=travis-ci
|
|
||||||
|
|
|
||||||
10
Dockerfile
10
Dockerfile
|
|
@ -8,16 +8,6 @@ EXPOSE 4000
|
||||||
# Assume the configuration is in /etc/boulder
|
# Assume the configuration is in /etc/boulder
|
||||||
ENV BOULDER_CONFIG=/boulder/config.json
|
ENV BOULDER_CONFIG=/boulder/config.json
|
||||||
|
|
||||||
# Load the dependencies
|
|
||||||
RUN go-wrapper download github.com/bifurcation/gose && \
|
|
||||||
go-wrapper download github.com/codegangsta/cli && \
|
|
||||||
go-wrapper download github.com/streadway/amqp && \
|
|
||||||
go-wrapper download github.com/mattn/go-sqlite3 && \
|
|
||||||
go-wrapper download github.com/go-sql-driver/mysql && \
|
|
||||||
go-wrapper download github.com/cloudflare/cfssl/auth && \
|
|
||||||
go-wrapper download github.com/cloudflare/cfssl/config && \
|
|
||||||
go-wrapper download github.com/cloudflare/cfssl/signer
|
|
||||||
|
|
||||||
# Copy in the Boulder sources
|
# Copy in the Boulder sources
|
||||||
RUN mkdir -p /go/src/github.com/letsencrypt/boulder
|
RUN mkdir -p /go/src/github.com/letsencrypt/boulder
|
||||||
COPY . /go/src/github.com/letsencrypt/boulder
|
COPY . /go/src/github.com/letsencrypt/boulder
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/letsencrypt/boulder",
|
||||||
|
"GoVersion": "go1.4.1",
|
||||||
|
"Packages": [
|
||||||
|
"./..."
|
||||||
|
],
|
||||||
|
"Deps": [
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/cloudflare/cfssl/api",
|
||||||
|
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/cloudflare/cfssl/auth",
|
||||||
|
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/cloudflare/cfssl/config",
|
||||||
|
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/cloudflare/cfssl/csr",
|
||||||
|
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/cloudflare/cfssl/errors",
|
||||||
|
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/cloudflare/cfssl/helpers",
|
||||||
|
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/cloudflare/cfssl/log",
|
||||||
|
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/cloudflare/cfssl/signer",
|
||||||
|
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/codegangsta/cli",
|
||||||
|
"Comment": "1.2.0-64-ge1712f3",
|
||||||
|
"Rev": "e1712f381785e32046927f64a7c86fe569203196"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/go-sql-driver/mysql",
|
||||||
|
"Comment": "v1.2-88-ga197e5d",
|
||||||
|
"Rev": "a197e5d40516f2e9f74dcee085a5f2d4604e94df"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/mattn/go-sqlite3",
|
||||||
|
"Rev": "308067797b0fcce4ca06362580dc6db77c1bfeda"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/streadway/amqp",
|
||||||
|
"Rev": "150b7f24d6ad507e6026c13d85ce1f1391ac7400"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
This directory tree is generated automatically by godep.
|
||||||
|
|
||||||
|
Please do not edit.
|
||||||
|
|
||||||
|
See https://github.com/tools/godep for more information.
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
/pkg
|
||||||
|
/bin
|
||||||
|
|
@ -0,0 +1,204 @@
|
||||||
|
// Package api implements an HTTP-based API and server for CF-SSL.
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler is an interface providing a generic mechanism for handling HTTP requests.
|
||||||
|
type Handler interface {
|
||||||
|
Handle(w http.ResponseWriter, r *http.Request) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPHandler is a wrapper that encapsulates Handler interface as http.Handler.
|
||||||
|
// HttpHandler also enforces that the Handler only responds to requests with registered HTTP method.
|
||||||
|
type HTTPHandler struct {
|
||||||
|
Handler // CFSSL handler
|
||||||
|
Method string // The assoicated HTTP method
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandlerFunc is similar to the http.HandlerFunc type; it serves as
|
||||||
|
// an adapter allowing the use of ordinary functions as Handlers. If
|
||||||
|
// f is a function with the appropriate signature, HandlerFunc(f) is a
|
||||||
|
// Handler object that calls f.
|
||||||
|
type HandlerFunc func(http.ResponseWriter, *http.Request) error
|
||||||
|
|
||||||
|
// Handle calls f(w, r)
|
||||||
|
func (f HandlerFunc) Handle(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
return f(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleError is the centralised error handling and reporting.
|
||||||
|
func handleError(w http.ResponseWriter, err error) (code int) {
|
||||||
|
if err == nil {
|
||||||
|
return http.StatusOK
|
||||||
|
}
|
||||||
|
msg := err.Error()
|
||||||
|
httpCode := http.StatusInternalServerError
|
||||||
|
|
||||||
|
// If it is recognized as HttpError emitted from cf-ssl,
|
||||||
|
// we rewrite the status code accordingly. If it is a
|
||||||
|
// cf-ssl error, set the http status to StatusBadRequest
|
||||||
|
switch err := err.(type) {
|
||||||
|
case *errors.HTTPError:
|
||||||
|
httpCode = err.StatusCode
|
||||||
|
code = err.StatusCode
|
||||||
|
case *errors.Error:
|
||||||
|
httpCode = http.StatusBadRequest
|
||||||
|
code = err.ErrorCode
|
||||||
|
msg = err.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
response := NewErrorResponse(msg, code)
|
||||||
|
jsonMessage, err := json.Marshal(response)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to marshal JSON: %v", err)
|
||||||
|
} else {
|
||||||
|
msg = string(jsonMessage)
|
||||||
|
}
|
||||||
|
http.Error(w, msg, httpCode)
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServeHTTP encapsulates the call to underlying Handler to handle the request
|
||||||
|
// and return the response with proper HTTP status code
|
||||||
|
func (h HTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var err error
|
||||||
|
// Throw 405 when requested with an unsupported verb.
|
||||||
|
if r.Method != h.Method {
|
||||||
|
err = errors.NewMethodNotAllowed(r.Method)
|
||||||
|
} else {
|
||||||
|
err = h.Handle(w, r)
|
||||||
|
}
|
||||||
|
status := handleError(w, err)
|
||||||
|
log.Infof("%s - \"%s %s\" %d", r.RemoteAddr, r.Method, r.URL, status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// readRequestBlob takes a JSON-blob-encoded response body in the form
|
||||||
|
// map[string]string and returns it, the list of keywords presented,
|
||||||
|
// and any error that occurred.
|
||||||
|
func readRequestBlob(r *http.Request) (map[string]string, error) {
|
||||||
|
var blob map[string]string
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r.Body.Close()
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &blob)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return blob, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessRequestOneOf reads a JSON blob for the request and makes
|
||||||
|
// sure it contains one of a set of keywords. For example, a request
|
||||||
|
// might have the ('foo' && 'bar') keys, OR it might have the 'baz'
|
||||||
|
// key. In either case, we want to accept the request; however, if
|
||||||
|
// none of these sets shows up, the request is a bad request, and it
|
||||||
|
// should be returned.
|
||||||
|
func ProcessRequestOneOf(r *http.Request, keywordSets [][]string) (map[string]string, []string, error) {
|
||||||
|
blob, err := readRequestBlob(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var matched []string
|
||||||
|
for _, set := range keywordSets {
|
||||||
|
if matchKeywords(blob, set) {
|
||||||
|
if matched != nil {
|
||||||
|
return nil, nil, errors.NewBadRequestString("mismatched parameters")
|
||||||
|
}
|
||||||
|
matched = set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matched == nil {
|
||||||
|
return nil, nil, errors.NewBadRequestString("no valid parameter sets found")
|
||||||
|
}
|
||||||
|
return blob, matched, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessRequestFirstMatchOf reads a JSON blob for the request and returns
|
||||||
|
// the first match of a set of keywords. For example, a request
|
||||||
|
// might have one of the following combinations: (foo=1, bar=2), (foo=1), and (bar=2)
|
||||||
|
// By giving a specific ordering of those combinations, we could decide how to accept
|
||||||
|
// the request.
|
||||||
|
func ProcessRequestFirstMatchOf(r *http.Request, keywordSets [][]string) (map[string]string, []string, error) {
|
||||||
|
blob, err := readRequestBlob(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, set := range keywordSets {
|
||||||
|
if matchKeywords(blob, set) {
|
||||||
|
return blob, set, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, nil, errors.NewBadRequestString("no valid parameter sets found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchKeywords(blob map[string]string, keywords []string) bool {
|
||||||
|
for _, keyword := range keywords {
|
||||||
|
if _, ok := blob[keyword]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResponseMessage implements the standard for response errors and
|
||||||
|
// messages. A message has a code and a string message.
|
||||||
|
type ResponseMessage struct {
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response implements the CloudFlare standard for API
|
||||||
|
// responses. CFSSL does not currently use the messages field, but it
|
||||||
|
// is provided for compatability.
|
||||||
|
type Response struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Result interface{} `json:"result"`
|
||||||
|
Errors []ResponseMessage `json:"errors"`
|
||||||
|
Messages []ResponseMessage `json:"messages"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSuccessResponse is a shortcut for creating new successul API
|
||||||
|
// responses. CFSSL does not use the messages field, but it is
|
||||||
|
// provided to conform to the CloudFlare standard.
|
||||||
|
func NewSuccessResponse(result interface{}) Response {
|
||||||
|
return Response{
|
||||||
|
Success: true,
|
||||||
|
Result: result,
|
||||||
|
Errors: []ResponseMessage{},
|
||||||
|
Messages: []ResponseMessage{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewErrorResponse is a shortcut for creating an error response for a
|
||||||
|
// single error.
|
||||||
|
func NewErrorResponse(message string, code int) Response {
|
||||||
|
return Response{
|
||||||
|
Success: false,
|
||||||
|
Result: nil,
|
||||||
|
Errors: []ResponseMessage{{code, message}},
|
||||||
|
Messages: []ResponseMessage{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendResponse builds a response from the result, sets the JSON
|
||||||
|
// header, and writes to the http.ResponseWriter.
|
||||||
|
func SendResponse(w http.ResponseWriter, result interface{}) error {
|
||||||
|
response := NewSuccessResponse(result)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
err := enc.Encode(response)
|
||||||
|
return err
|
||||||
|
}
|
||||||
220
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/api_test.go
generated
vendored
Normal file
220
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/api_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ty = "Thank you!"
|
||||||
|
deny = "That's not true!"
|
||||||
|
)
|
||||||
|
|
||||||
|
func simpleHandle(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
_, _, err := ProcessRequestOneOf(r, [][]string{
|
||||||
|
[]string{"compliment"},
|
||||||
|
[]string{"critique"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return SendResponse(w, ty)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleverHandle(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
_, matched, err := ProcessRequestFirstMatchOf(r, [][]string{
|
||||||
|
[]string{"compliment"},
|
||||||
|
[]string{"critique"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if matched[0] == "critique" {
|
||||||
|
return SendResponse(w, deny)
|
||||||
|
}
|
||||||
|
|
||||||
|
return SendResponse(w, ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
func post(t *testing.T, obj map[string]interface{}, ts *httptest.Server) (resp *http.Response, body []byte) {
|
||||||
|
blob, err := json.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = http.Post(ts.URL, "application/json", bytes.NewReader(blob))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
body, err = ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func get(t *testing.T, ts *httptest.Server) (resp *http.Response, body []byte) {
|
||||||
|
resp, err := http.Get(ts.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err = ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRigidHandle(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(HTTPHandler{Handler: HandlerFunc(simpleHandle), Method: "POST"})
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
// Response to compliment
|
||||||
|
obj := map[string]interface{}{}
|
||||||
|
obj["compliment"] = "it's good"
|
||||||
|
resp, body := post(t, obj, ts)
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Errorf("Test expected 200, have %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
message := new(Response)
|
||||||
|
err := json.Unmarshal(body, message)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to read response body: %v", err)
|
||||||
|
t.Fatal("returned:", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if message.Result != ty {
|
||||||
|
t.Fatal("Wrong response")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response to critique
|
||||||
|
obj = map[string]interface{}{}
|
||||||
|
obj["critique"] = "it's bad"
|
||||||
|
resp, body = post(t, obj, ts)
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Errorf("Test expected 200, have %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
message = new(Response)
|
||||||
|
err = json.Unmarshal(body, message)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to read response body: %v", err)
|
||||||
|
t.Fatal("returned:", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if message.Result != ty {
|
||||||
|
t.Fatal("Wrong response")
|
||||||
|
}
|
||||||
|
|
||||||
|
// reject mixed review
|
||||||
|
obj = map[string]interface{}{}
|
||||||
|
obj["critique"] = "it's OK"
|
||||||
|
obj["compliment"] = "it's not bad"
|
||||||
|
resp, body = post(t, obj, ts)
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusBadRequest {
|
||||||
|
t.Errorf("Test expected 400, have %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reject empty review
|
||||||
|
obj = map[string]interface{}{}
|
||||||
|
resp, body = post(t, obj, ts)
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusBadRequest {
|
||||||
|
t.Errorf("Test expected 400, have %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reject GET
|
||||||
|
resp, body = get(t, ts)
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||||
|
t.Errorf("Test expected 405, have %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCleverHandle(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(HTTPHandler{Handler: HandlerFunc(cleverHandle), Method: "POST"})
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
// Response ty to compliment
|
||||||
|
obj := map[string]interface{}{}
|
||||||
|
obj["compliment"] = "it's good"
|
||||||
|
resp, body := post(t, obj, ts)
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Errorf("Test expected 200, have %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
message := new(Response)
|
||||||
|
err := json.Unmarshal(body, message)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to read response body: %v", err)
|
||||||
|
t.Fatal("returned:", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if message.Result != ty {
|
||||||
|
t.Fatal("Wrong response")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response deny to critique
|
||||||
|
obj = map[string]interface{}{}
|
||||||
|
obj["critique"] = "it's bad"
|
||||||
|
resp, body = post(t, obj, ts)
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Errorf("Test expected 200, have %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
message = new(Response)
|
||||||
|
err = json.Unmarshal(body, message)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to read response body: %v", err)
|
||||||
|
t.Fatal("returned:", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if message.Result != deny {
|
||||||
|
t.Fatal("Wrong response")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Be polite to mixed review
|
||||||
|
obj = map[string]interface{}{}
|
||||||
|
obj["critique"] = "it's OK"
|
||||||
|
obj["compliment"] = "it's not bad"
|
||||||
|
resp, body = post(t, obj, ts)
|
||||||
|
|
||||||
|
message = new(Response)
|
||||||
|
err = json.Unmarshal(body, message)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to read response body: %v", err)
|
||||||
|
t.Fatal("returned:", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if message.Result != ty {
|
||||||
|
t.Fatal("Wrong response")
|
||||||
|
}
|
||||||
|
|
||||||
|
// reject empty review
|
||||||
|
obj = map[string]interface{}{}
|
||||||
|
resp, body = post(t, obj, ts)
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusBadRequest {
|
||||||
|
t.Errorf("Test expected 400, have %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reject GET
|
||||||
|
resp, body = get(t, ts)
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||||
|
t.Errorf("Test expected 405, have %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
90
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/bundle/bundle.go
generated
vendored
Normal file
90
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/bundle/bundle.go
generated
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
package bundle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cfssl/bundler"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler accepts requests for either remote or uploaded
|
||||||
|
// certificates to be bundled, and returns a certificate bundle (or
|
||||||
|
// error).
|
||||||
|
type Handler struct {
|
||||||
|
bundler *bundler.Bundler
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHandler creates a new bundler that uses the root bundle and
|
||||||
|
// intermediate bundle in the trust chain.
|
||||||
|
func NewHandler(caBundleFile, intBundleFile string) (http.Handler, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
b := new(Handler)
|
||||||
|
if b.bundler, err = bundler.NewBundler(caBundleFile, intBundleFile); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("bundler API ready")
|
||||||
|
return api.HTTPHandler{Handler: b, Method: "POST"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle implements an http.Handler interface for the bundle handler.
|
||||||
|
func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
blob, matched, err := api.ProcessRequestFirstMatchOf(r,
|
||||||
|
[][]string{
|
||||||
|
[]string{"certificate"},
|
||||||
|
[]string{"domain"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("invalid request: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
flavor := blob["flavor"]
|
||||||
|
bf := bundler.Ubiquitous
|
||||||
|
if flavor != "" {
|
||||||
|
bf = bundler.BundleFlavor(flavor)
|
||||||
|
}
|
||||||
|
log.Infof("request for flavor %v", bf)
|
||||||
|
|
||||||
|
var result *bundler.Bundle
|
||||||
|
switch matched[0] {
|
||||||
|
case "domain":
|
||||||
|
bundle, err := h.bundler.BundleFromRemote(blob["domain"], blob["ip"], bf)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("couldn't bundle from remote: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
result = bundle
|
||||||
|
case "certificate":
|
||||||
|
bundle, err := h.bundler.BundleFromPEM([]byte(blob["certificate"]), []byte(blob["private_key"]), bf)
|
||||||
|
if err != nil {
|
||||||
|
log.Warning("bad PEM certifcate or private key")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
serverName := blob["domain"]
|
||||||
|
ip := blob["ip"]
|
||||||
|
|
||||||
|
if serverName != "" {
|
||||||
|
err := bundle.Cert.VerifyHostname(serverName)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip != "" {
|
||||||
|
err := bundle.Cert.VerifyHostname(ip)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = bundle
|
||||||
|
}
|
||||||
|
log.Info("wrote response")
|
||||||
|
return api.SendResponse(w, result)
|
||||||
|
}
|
||||||
214
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/bundle/bundle_test.go
generated
vendored
Normal file
214
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/bundle/bundle_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
package bundle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testCaBundleFile = "../testdata/ca-bundle.pem"
|
||||||
|
testIntBundleFile = "../testdata/int-bundle.pem"
|
||||||
|
testLeafCertFile = "../testdata/leaf.pem"
|
||||||
|
testLeafKeyFile = "../testdata/leaf.key"
|
||||||
|
testLeafWrongKeyFile = "../testdata/leaf.badkey"
|
||||||
|
testBrokenCertFile = "../testdata/broken.pem"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newTestHandler(t *testing.T) (h http.Handler) {
|
||||||
|
h, err := NewHandler(testCaBundleFile, testIntBundleFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBundleServer(t *testing.T) *httptest.Server {
|
||||||
|
ts := httptest.NewServer(newTestHandler(t))
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBundleFile(t *testing.T, domain, ip, certFile, keyFile, flavor string) (resp *http.Response, body []byte) {
|
||||||
|
ts := newBundleServer(t)
|
||||||
|
defer ts.Close()
|
||||||
|
var certPEM, keyPEM []byte
|
||||||
|
if certFile != "" {
|
||||||
|
var err error
|
||||||
|
certPEM, err = ioutil.ReadFile(certFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if keyFile != "" {
|
||||||
|
var err error
|
||||||
|
keyPEM, err = ioutil.ReadFile(keyFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := map[string]string{"flavor": flavor}
|
||||||
|
if len(domain) > 0 {
|
||||||
|
obj["domain"] = domain
|
||||||
|
}
|
||||||
|
if len(ip) > 0 {
|
||||||
|
obj["ip"] = ip
|
||||||
|
}
|
||||||
|
if len(certPEM) > 0 {
|
||||||
|
obj["certificate"] = string(certPEM)
|
||||||
|
}
|
||||||
|
if len(keyPEM) > 0 {
|
||||||
|
obj["private_key"] = string(keyPEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
blob, err := json.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = http.Post(ts.URL, "application/json", bytes.NewReader(blob))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
body, err = ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewHandler(t *testing.T) {
|
||||||
|
newTestHandler(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
type bundleTest struct {
|
||||||
|
Domain string
|
||||||
|
IP string
|
||||||
|
CertFile string
|
||||||
|
KeyFile string
|
||||||
|
Flavor string
|
||||||
|
ExpectedHTTPStatus int
|
||||||
|
ExpectedSuccess bool
|
||||||
|
ExpectedErrorCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
var bundleTests = []bundleTest{
|
||||||
|
// Test bundling with certificate
|
||||||
|
{
|
||||||
|
CertFile: testLeafCertFile,
|
||||||
|
ExpectedHTTPStatus: http.StatusOK,
|
||||||
|
ExpectedSuccess: true,
|
||||||
|
ExpectedErrorCode: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertFile: testLeafCertFile,
|
||||||
|
Flavor: "ubiquitous",
|
||||||
|
ExpectedHTTPStatus: http.StatusOK,
|
||||||
|
ExpectedSuccess: true,
|
||||||
|
ExpectedErrorCode: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertFile: testLeafCertFile,
|
||||||
|
Flavor: "optimal",
|
||||||
|
ExpectedHTTPStatus: http.StatusOK,
|
||||||
|
ExpectedSuccess: true,
|
||||||
|
ExpectedErrorCode: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertFile: testLeafCertFile,
|
||||||
|
KeyFile: testLeafKeyFile,
|
||||||
|
ExpectedHTTPStatus: http.StatusOK,
|
||||||
|
ExpectedSuccess: true,
|
||||||
|
ExpectedErrorCode: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertFile: testLeafCertFile,
|
||||||
|
Domain: "cfssl-leaf.com",
|
||||||
|
ExpectedHTTPStatus: http.StatusOK,
|
||||||
|
ExpectedSuccess: true,
|
||||||
|
ExpectedErrorCode: 0,
|
||||||
|
},
|
||||||
|
// Test bundling with remote domain
|
||||||
|
{
|
||||||
|
Domain: "google.com",
|
||||||
|
ExpectedHTTPStatus: http.StatusBadRequest,
|
||||||
|
ExpectedSuccess: false,
|
||||||
|
ExpectedErrorCode: 1220,
|
||||||
|
},
|
||||||
|
// Error testing.
|
||||||
|
{
|
||||||
|
CertFile: testLeafCertFile,
|
||||||
|
KeyFile: testLeafWrongKeyFile,
|
||||||
|
ExpectedHTTPStatus: http.StatusBadRequest,
|
||||||
|
ExpectedSuccess: false,
|
||||||
|
ExpectedErrorCode: 2300,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// no input parameter is specified
|
||||||
|
ExpectedHTTPStatus: http.StatusBadRequest,
|
||||||
|
ExpectedSuccess: false,
|
||||||
|
ExpectedErrorCode: http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertFile: testBrokenCertFile,
|
||||||
|
ExpectedHTTPStatus: http.StatusBadRequest,
|
||||||
|
ExpectedSuccess: false,
|
||||||
|
ExpectedErrorCode: 1003,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertFile: testLeafKeyFile,
|
||||||
|
KeyFile: testLeafKeyFile,
|
||||||
|
ExpectedHTTPStatus: http.StatusBadRequest,
|
||||||
|
ExpectedSuccess: false,
|
||||||
|
ExpectedErrorCode: 1003,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertFile: testLeafCertFile,
|
||||||
|
KeyFile: testLeafCertFile,
|
||||||
|
ExpectedHTTPStatus: http.StatusBadRequest,
|
||||||
|
ExpectedSuccess: false,
|
||||||
|
ExpectedErrorCode: 2003,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CertFile: testLeafCertFile,
|
||||||
|
Domain: "cloudflare-leaf.com",
|
||||||
|
ExpectedHTTPStatus: http.StatusBadRequest,
|
||||||
|
ExpectedSuccess: false,
|
||||||
|
ExpectedErrorCode: 1200,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBundle(t *testing.T) {
|
||||||
|
for i, test := range bundleTests {
|
||||||
|
resp, body := testBundleFile(t, test.Domain, test.IP, test.CertFile, test.KeyFile, test.Flavor)
|
||||||
|
if resp.StatusCode != test.ExpectedHTTPStatus {
|
||||||
|
t.Errorf("Test %d: expected: %d, have %d", i, test.ExpectedHTTPStatus, resp.StatusCode)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
message := new(api.Response)
|
||||||
|
err := json.Unmarshal(body, message)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to read response body: %v", err)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.ExpectedSuccess != message.Success {
|
||||||
|
t.Errorf("Test %d: expected: %v, have %v", i, test.ExpectedSuccess, message.Success)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
if test.ExpectedSuccess == true {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.ExpectedErrorCode != message.Errors[0].Code {
|
||||||
|
t.Errorf("Test %d: expected: %v, have %v", i, test.ExpectedErrorCode, message.Errors[0].Code)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/client/api.go
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/client/api.go
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
// SignResult is the result of signing a CSR.
|
||||||
|
type SignResult struct {
|
||||||
|
Certificate []byte `json:"certificate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoReq is the request struct for an info API request.
|
||||||
|
type InfoReq struct {
|
||||||
|
Label string `json:"label"`
|
||||||
|
Profile string `json:"profile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoResp is the response for an Info API request.
|
||||||
|
type InfoResp struct {
|
||||||
|
Certificate string `json:"certificate"`
|
||||||
|
}
|
||||||
170
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/client/client.go
generated
vendored
Normal file
170
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/client/client.go
generated
vendored
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
stderr "errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/auth"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type // A Server points to a remote CFSSL instance.
|
||||||
|
Server struct {
|
||||||
|
Address string
|
||||||
|
Port int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServer sets up a new server target. The address should be the
|
||||||
|
// DNS name (or "name:port") of the remote CFSSL instance. If no port
|
||||||
|
// is specified, the CFSSL default port (8888) is used.
|
||||||
|
func NewServer(addr string) *Server {
|
||||||
|
host, port, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
host, port, err = net.SplitHostPort(addr + ":8888")
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var portno int
|
||||||
|
if port == "" {
|
||||||
|
portno = 8888
|
||||||
|
} else {
|
||||||
|
portno, err = strconv.Atoi(port)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Server{host, portno}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *Server) getURL(endpoint string) string {
|
||||||
|
return fmt.Sprintf("http://%s:%d/api/v1/cfssl/%s", srv.Address, srv.Port, endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
// post connects to the remote server and returns a Response struct
|
||||||
|
func (srv *Server) post(url string, jsonData []byte) (*api.Response, error) {
|
||||||
|
buf := bytes.NewBuffer(jsonData)
|
||||||
|
resp, err := http.Post(url, "application/json", buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, err)
|
||||||
|
}
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(errors.APIClientError, errors.IOError, err)
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
var response api.Response
|
||||||
|
err = json.Unmarshal(body, &response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(errors.APIClientError, errors.JSONError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !response.Success || response.Result == nil {
|
||||||
|
if len(response.Errors) > 0 {
|
||||||
|
return nil, errors.Wrap(errors.APIClientError, errors.ServerRequestFailed, stderr.New(response.Errors[0].Message))
|
||||||
|
}
|
||||||
|
return nil, errors.New(errors.APIClientError, errors.ServerRequestFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &response, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthSign fills out an authenticated signing request to the server,
|
||||||
|
// receiving a certificate or error in response.
|
||||||
|
// It takes the serialized JSON request to send, remote address and
|
||||||
|
// authentication provider.
|
||||||
|
func (srv *Server) AuthSign(req, id []byte, provider auth.Provider) ([]byte, error) {
|
||||||
|
return srv.AuthReq(req, id, provider, "sign")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthInfo fills out an authenticated info request to the server,
|
||||||
|
// receiving a certificate or error in response.
|
||||||
|
// It takes the serialized JSON request to send, remote address and
|
||||||
|
// authentication provider.
|
||||||
|
func (srv *Server) AuthInfo(req, id []byte, provider auth.Provider) ([]byte, error) {
|
||||||
|
return srv.AuthReq(req, id, provider, "info")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthReq is the common logic for AuthSign and AuthInfo -- perform the given
|
||||||
|
// request, and return the resultant certificate.
|
||||||
|
// The target is either 'sign' or 'info'.
|
||||||
|
func (srv *Server) AuthReq(req, ID []byte, provider auth.Provider, target string) ([]byte, error) {
|
||||||
|
url := srv.getURL("auth" + target)
|
||||||
|
|
||||||
|
token, err := provider.Token(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(errors.APIClientError, errors.AuthenticationFailure, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
aReq := &auth.AuthenticatedRequest{
|
||||||
|
Timestamp: time.Now().Unix(),
|
||||||
|
RemoteAddress: ID,
|
||||||
|
Token: token,
|
||||||
|
Request: req,
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonData, err := json.Marshal(aReq)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(errors.APIClientError, errors.JSONError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := srv.post(url, jsonData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result, ok := response.Result.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New(errors.APIClientError, errors.JSONError)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, ok := result["certificate"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New(errors.APIClientError, errors.JSONError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(cert), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign sends a signature request to the remote CFSSL server,
|
||||||
|
// receiving a signed certificate or an error in response.
|
||||||
|
// It takes the serialized JSON request to send.
|
||||||
|
func (srv *Server) Sign(jsonData []byte) ([]byte, error) {
|
||||||
|
return srv.Req(jsonData, "sign")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info sends an info request to the remote CFSSL server, receiving a
|
||||||
|
// certificate or an error in response.
|
||||||
|
// It takes the serialized JSON request to send.
|
||||||
|
func (srv *Server) Info(jsonData []byte) ([]byte, error) {
|
||||||
|
return srv.Req(jsonData, "info")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Req performs the common logic for Sign and Info, performing the actual
|
||||||
|
// request and returning the resultant certificate.
|
||||||
|
func (srv *Server) Req(jsonData []byte, target string) ([]byte, error) {
|
||||||
|
url := srv.getURL(target)
|
||||||
|
|
||||||
|
response, err := srv.post(url, jsonData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := response.Result.(map[string]interface{})
|
||||||
|
cert := result["certificate"].(string)
|
||||||
|
if cert != "" {
|
||||||
|
return []byte(cert), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, stderr.New("response doesn't contain certificate."))
|
||||||
|
}
|
||||||
59
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/client/client_test.go
generated
vendored
Normal file
59
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/client/client_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
testProvider auth.Provider
|
||||||
|
testKey = "0123456789ABCDEF0123456789ABCDEF"
|
||||||
|
testAD = []byte{1, 2, 3, 4} // IP address 1.2.3.4
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewServer(t *testing.T) {
|
||||||
|
s := NewServer("1.1.1.1:::123456789")
|
||||||
|
|
||||||
|
if s != nil {
|
||||||
|
t.Fatalf("fatal error, server created with too many colons %v", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
s2 := NewServer("1.1.1.1:[]")
|
||||||
|
if s != nil {
|
||||||
|
t.Fatalf("%v", s2)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_, port, _ := net.SplitHostPort("")
|
||||||
|
if port != "" {
|
||||||
|
t.Fatalf("%v", port)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidPort(t *testing.T) {
|
||||||
|
s := NewServer("1.1.1.1:99999999999999999999999999999")
|
||||||
|
if s != nil {
|
||||||
|
t.Fatalf("%v", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthSign(t *testing.T) {
|
||||||
|
s := NewServer("1.1")
|
||||||
|
testProvider, _ = auth.New(testKey, nil)
|
||||||
|
testRequest := []byte(`testing 1 2 3`)
|
||||||
|
as, _ := s.AuthSign(testRequest, testAD, testProvider)
|
||||||
|
if as != nil {
|
||||||
|
t.Fatal("fatal error with auth sign function")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSign(t *testing.T) {
|
||||||
|
s := NewServer("1.1")
|
||||||
|
sign, _ := s.Sign([]byte{5, 5, 5, 5})
|
||||||
|
if sign != nil {
|
||||||
|
t.Fatalf("%v", sign)
|
||||||
|
}
|
||||||
|
}
|
||||||
300
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/generator/generator.go
generated
vendored
Normal file
300
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/generator/generator.go
generated
vendored
Normal file
|
|
@ -0,0 +1,300 @@
|
||||||
|
package generator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/csr"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/universal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sum contains digests for a certificate or certificate request.
|
||||||
|
type Sum struct {
|
||||||
|
MD5 string `json:"md5"`
|
||||||
|
SHA1 string `json:"sha-1"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validator is a type of function that contains the logic for validating
|
||||||
|
// a certificate request.
|
||||||
|
type Validator func(*csr.CertificateRequest) error
|
||||||
|
|
||||||
|
// A CertRequest stores a PEM-encoded private key and corresponding
|
||||||
|
// CSR; this is returned from the CSR generation endpoint.
|
||||||
|
type CertRequest struct {
|
||||||
|
Key string `json:"private_key"`
|
||||||
|
CSR string `json:"certificate_request"`
|
||||||
|
Sums map[string]Sum `json:"sums"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Handler accepts JSON-encoded certificate requests and
|
||||||
|
// returns a new private key and certificate request.
|
||||||
|
type Handler struct {
|
||||||
|
generator *csr.Generator
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHandler builds a new Handler from the
|
||||||
|
// validation function provided.
|
||||||
|
func NewHandler(validator Validator) (http.Handler, error) {
|
||||||
|
log.Info("setting up key / CSR generator")
|
||||||
|
return &api.HTTPHandler{
|
||||||
|
Handler: &Handler{
|
||||||
|
generator: &csr.Generator{Validator: validator},
|
||||||
|
},
|
||||||
|
Method: "POST",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func computeSum(in []byte) (sum Sum, err error) {
|
||||||
|
var data []byte
|
||||||
|
p, _ := pem.Decode(in)
|
||||||
|
if p == nil {
|
||||||
|
err = errors.NewBadRequestString("not a CSR or certificate")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch p.Type {
|
||||||
|
case "CERTIFICATE REQUEST":
|
||||||
|
var req *x509.CertificateRequest
|
||||||
|
req, err = x509.ParseCertificateRequest(p.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data = req.Raw
|
||||||
|
case "CERTIFICATE":
|
||||||
|
var cert *x509.Certificate
|
||||||
|
cert, err = x509.ParseCertificate(p.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data = cert.Raw
|
||||||
|
default:
|
||||||
|
err = errors.NewBadRequestString("not a CSR or certificate")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
md5Sum := md5.Sum(data)
|
||||||
|
sha1Sum := sha1.Sum(data)
|
||||||
|
sum.MD5 = fmt.Sprintf("%X", md5Sum[:])
|
||||||
|
sum.SHA1 = fmt.Sprintf("%X", sha1Sum[:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle responds to requests for the CA to generate a new private
|
||||||
|
// key and certificate request on behalf of the client. The format for
|
||||||
|
// these requests is documented in the API documentation.
|
||||||
|
func (g *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
log.Info("request for CSR")
|
||||||
|
req := new(csr.CertificateRequest)
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to read request body: %v", err)
|
||||||
|
return errors.NewBadRequest(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, req)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to unmarshal request: %v", err)
|
||||||
|
return errors.NewBadRequest(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.CA != nil {
|
||||||
|
log.Warningf("request received with CA section")
|
||||||
|
return errors.NewBadRequestString("ca section only permitted in initca")
|
||||||
|
}
|
||||||
|
|
||||||
|
csr, key, err := g.generator.ProcessRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to process CSR: %v", err)
|
||||||
|
// The validator returns a *cfssl/errors.HttpError
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sum, err := computeSum(csr)
|
||||||
|
if err != nil {
|
||||||
|
return errors.NewBadRequest(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both key and csr are returned PEM-encoded.
|
||||||
|
response := api.NewSuccessResponse(&CertRequest{
|
||||||
|
Key: string(key),
|
||||||
|
CSR: string(csr),
|
||||||
|
Sums: map[string]Sum{"certificate_request": sum},
|
||||||
|
})
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
err = enc.Encode(response)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// A CertGeneratorHandler accepts JSON-encoded certificate requests
|
||||||
|
// and returns a new private key and signed certificate; it handles
|
||||||
|
// sending the CSR to the server.
|
||||||
|
type CertGeneratorHandler struct {
|
||||||
|
generator *csr.Generator
|
||||||
|
signer signer.Signer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCertGeneratorHandler builds a new handler for generating
|
||||||
|
// certificates directly from certificate requests; the validator covers
|
||||||
|
// the certificate request and the CA's key and certificate are used to
|
||||||
|
// sign the generated request. If remote is not an empty string, the
|
||||||
|
// handler will send signature requests to the CFSSL instance contained
|
||||||
|
// in remote.
|
||||||
|
func NewCertGeneratorHandler(validator Validator, caFile, caKeyFile string, policy *config.Signing) (http.Handler, error) {
|
||||||
|
var err error
|
||||||
|
log.Info("setting up new generator / signer")
|
||||||
|
cg := new(CertGeneratorHandler)
|
||||||
|
|
||||||
|
if policy == nil {
|
||||||
|
policy = &config.Signing{
|
||||||
|
Default: config.DefaultConfig(),
|
||||||
|
Profiles: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
root := universal.Root{
|
||||||
|
Config: map[string]string{
|
||||||
|
"ca-file": caFile,
|
||||||
|
"ca-key-file": caKeyFile,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if cg.signer, err = universal.NewSigner(root, policy); err != nil {
|
||||||
|
log.Errorf("setting up signer failed: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cg.generator = &csr.Generator{Validator: validator}
|
||||||
|
|
||||||
|
return api.HTTPHandler{Handler: cg, Method: "POST"}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCertGeneratorHandlerFromSigner returns a handler directly from
|
||||||
|
// the signer and validation function.
|
||||||
|
func NewCertGeneratorHandlerFromSigner(validator Validator, signer signer.Signer) http.Handler {
|
||||||
|
return api.HTTPHandler{
|
||||||
|
Handler: &CertGeneratorHandler{
|
||||||
|
generator: &csr.Generator{Validator: validator},
|
||||||
|
signer: signer,
|
||||||
|
},
|
||||||
|
Method: "POST",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type genSignRequest struct {
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
Request *csr.CertificateRequest `json:"request"`
|
||||||
|
Profile string `json:"profile"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle responds to requests for the CA to generate a new private
|
||||||
|
// key and certificate on behalf of the client. The format for these
|
||||||
|
// requests is documented in the API documentation.
|
||||||
|
func (cg *CertGeneratorHandler) Handle(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
log.Info("request for CSR")
|
||||||
|
|
||||||
|
req := new(genSignRequest)
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to read request body: %v", err)
|
||||||
|
return errors.NewBadRequest(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, req)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to unmarshal request: %v", err)
|
||||||
|
return errors.NewBadRequest(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Request == nil {
|
||||||
|
log.Warning("empty request received")
|
||||||
|
return errors.NewBadRequestString("missing request section")
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Request.CA != nil {
|
||||||
|
log.Warningf("request received with CA section")
|
||||||
|
return errors.NewBadRequestString("ca section only permitted in initca")
|
||||||
|
}
|
||||||
|
|
||||||
|
csr, key, err := cg.generator.ProcessRequest(req.Request)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to process CSR: %v", err)
|
||||||
|
// The validator returns a *cfssl/errors.HttpError
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var certPEM []byte
|
||||||
|
|
||||||
|
var profile *config.SigningProfile
|
||||||
|
policy := cg.signer.Policy()
|
||||||
|
if policy != nil && policy.Profiles != nil && req.Profile != "" {
|
||||||
|
profile = policy.Profiles[req.Profile]
|
||||||
|
}
|
||||||
|
|
||||||
|
if profile == nil && policy != nil {
|
||||||
|
profile = policy.Default
|
||||||
|
}
|
||||||
|
|
||||||
|
// This API does not override the subject because it was already added to the CSR
|
||||||
|
signReq := signer.SignRequest{
|
||||||
|
Hosts: signer.SplitHosts(req.Hostname),
|
||||||
|
Request: string(csr),
|
||||||
|
Profile: req.Profile,
|
||||||
|
Label: req.Label,
|
||||||
|
}
|
||||||
|
|
||||||
|
certBytes, err := cg.signer.Sign(signReq)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to sign request: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
reqSum, err := computeSum(csr)
|
||||||
|
if err != nil {
|
||||||
|
return errors.NewBadRequest(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
certSum, err := computeSum(certBytes)
|
||||||
|
if err != nil {
|
||||||
|
return errors.NewBadRequest(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := map[string]interface{}{
|
||||||
|
"private_key": string(key),
|
||||||
|
"certificate_request": string(csr),
|
||||||
|
"certificate": string(certPEM),
|
||||||
|
"sums": map[string]Sum{
|
||||||
|
"certificate_request": reqSum,
|
||||||
|
"certificate": certSum,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return api.SendResponse(w, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CSRValidate contains the default validation logic for certificate requests to
|
||||||
|
// the API server. This follows the Baseline Requirements for the Issuance and
|
||||||
|
// Management of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser
|
||||||
|
// Forum (https://cabforum.org). Specifically, section 10.2.3 ("Information
|
||||||
|
// Requirements"), states:
|
||||||
|
//
|
||||||
|
// "Applicant information MUST include, but not be limited to, at least one
|
||||||
|
// Fully-Qualified Domain Name or IP address to be included in the Certificate’s
|
||||||
|
// SubjectAltName extension."
|
||||||
|
func CSRValidate(req *csr.CertificateRequest) error {
|
||||||
|
if len(req.Hosts) == 0 {
|
||||||
|
log.Warning("request for CSR is missing the host parameter")
|
||||||
|
return errors.NewBadRequestMissingParameter("hosts")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
70
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/generator/generator_test.go
generated
vendored
Normal file
70
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/generator/generator_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
package generator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/csr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func csrData(t *testing.T) *bytes.Reader {
|
||||||
|
req := &csr.CertificateRequest{
|
||||||
|
Names: []csr.Name{
|
||||||
|
{
|
||||||
|
C: "US",
|
||||||
|
ST: "California",
|
||||||
|
L: "San Francisco",
|
||||||
|
O: "CloudFlare",
|
||||||
|
OU: "Systems Engineering",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CN: "cloudflare.com",
|
||||||
|
Hosts: []string{"cloudflare.com"},
|
||||||
|
KeyRequest: &csr.KeyRequest{
|
||||||
|
Algo: "ecdsa",
|
||||||
|
Size: 256,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
csrBytes, err := json.Marshal(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return bytes.NewReader(csrBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGeneratorRESTfulVerbs(t *testing.T) {
|
||||||
|
handler, _ := NewHandler(CSRValidate)
|
||||||
|
ts := httptest.NewServer(handler)
|
||||||
|
data := csrData(t)
|
||||||
|
// POST should work.
|
||||||
|
req, _ := http.NewRequest("POST", ts.URL, data)
|
||||||
|
resp, _ := http.DefaultClient.Do(req)
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Fatal(resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test GET, PUT, DELETE and whatever, expect 400 errors.
|
||||||
|
req, _ = http.NewRequest("GET", ts.URL, data)
|
||||||
|
resp, _ = http.DefaultClient.Do(req)
|
||||||
|
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||||
|
t.Fatal(resp.Status)
|
||||||
|
}
|
||||||
|
req, _ = http.NewRequest("PUT", ts.URL, data)
|
||||||
|
resp, _ = http.DefaultClient.Do(req)
|
||||||
|
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||||
|
t.Fatal(resp.Status)
|
||||||
|
}
|
||||||
|
req, _ = http.NewRequest("DELETE", ts.URL, data)
|
||||||
|
resp, _ = http.DefaultClient.Do(req)
|
||||||
|
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||||
|
t.Fatal(resp.Status)
|
||||||
|
}
|
||||||
|
req, _ = http.NewRequest("WHATEVER", ts.URL, data)
|
||||||
|
resp, _ = http.DefaultClient.Do(req)
|
||||||
|
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||||
|
t.Fatal(resp.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
62
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/info/info.go
generated
vendored
Normal file
62
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/info/info.go
generated
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
package info
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cfssl/bundler"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api/client"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler is a type that contains the root certificates for the CA,
|
||||||
|
// and serves information on them for clients that need the certificates.
|
||||||
|
type Handler struct {
|
||||||
|
sign signer.Signer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHandler creates a new handler to serve information on the CA's
|
||||||
|
// certificates, taking a signer to use.
|
||||||
|
func NewHandler(s signer.Signer) (http.Handler, error) {
|
||||||
|
return &api.HTTPHandler{
|
||||||
|
Handler: &Handler{
|
||||||
|
sign: s,
|
||||||
|
},
|
||||||
|
Method: "POST",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle listens for incoming requests for CA information, and returns
|
||||||
|
// a list containing information on each root certificate.
|
||||||
|
func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
|
||||||
|
req := new(client.InfoReq)
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to read request body: %v", err)
|
||||||
|
return errors.NewBadRequest(err)
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(body, req)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to unmarshal request: %v", err)
|
||||||
|
return errors.NewBadRequest(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := h.sign.Certificate(req.Label, req.Profile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp := client.InfoResp{
|
||||||
|
Certificate: bundler.PemBlockToString(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}),
|
||||||
|
}
|
||||||
|
|
||||||
|
response := api.NewSuccessResponse(resp)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
return enc.Encode(response)
|
||||||
|
}
|
||||||
127
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/info/info_test.go
generated
vendored
Normal file
127
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/info/info_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
package info
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testCaFile = "../testdata/ca.pem"
|
||||||
|
testCaKeyFile = "../testdata/ca_key.pem"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newTestHandler(t *testing.T) (h http.Handler) {
|
||||||
|
signer, err := local.NewSignerFromFile(testCaFile, testCaKeyFile, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
h, err = NewHandler(signer)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewHandler(t *testing.T) {
|
||||||
|
newTestHandler(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInfoServer(t *testing.T) *httptest.Server {
|
||||||
|
ts := httptest.NewServer(newTestHandler(t))
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
func testInfoFile(t *testing.T, req map[string]interface{}) (resp *http.Response, body []byte) {
|
||||||
|
ts := newInfoServer(t)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
blob, err := json.Marshal(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = http.Post(ts.URL, "application/json", bytes.NewReader(blob))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
body, err = ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type infoTest struct {
|
||||||
|
RequestObject map[string]interface{}
|
||||||
|
ExpectedHTTPStatus int
|
||||||
|
ExpectedSuccess bool
|
||||||
|
ExpectedErrorCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
var infoTests = []infoTest{
|
||||||
|
{
|
||||||
|
map[string]interface{}{
|
||||||
|
"label": "",
|
||||||
|
"profile": "",
|
||||||
|
},
|
||||||
|
http.StatusOK,
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
map[string]interface{}{
|
||||||
|
"label": "badlabel",
|
||||||
|
"profile": "",
|
||||||
|
},
|
||||||
|
http.StatusBadRequest,
|
||||||
|
false,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
map[string]interface{}{
|
||||||
|
"label": 123,
|
||||||
|
},
|
||||||
|
http.StatusBadRequest,
|
||||||
|
false,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInfo(t *testing.T) {
|
||||||
|
for i, test := range infoTests {
|
||||||
|
resp, body := testInfoFile(t, test.RequestObject)
|
||||||
|
if resp.StatusCode != test.ExpectedHTTPStatus {
|
||||||
|
t.Fatalf("Test %d: expected: %d, have %d", i, test.ExpectedHTTPStatus, resp.StatusCode)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
message := new(api.Response)
|
||||||
|
err := json.Unmarshal(body, message)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to read response body: %v", err)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.ExpectedSuccess != message.Success {
|
||||||
|
t.Fatalf("Test %d: expected: %v, have %v", i, test.ExpectedSuccess, message.Success)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
if test.ExpectedSuccess == true {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.ExpectedErrorCode != message.Errors[0].Code {
|
||||||
|
t.Fatalf("Test %d: expected: %v, have %v", i, test.ExpectedErrorCode, message.Errors[0].Code)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
58
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/initca/initca.go
generated
vendored
Normal file
58
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/initca/initca.go
generated
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
package initca
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cfssl/initca"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/csr"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A NewCA contains a private key and certificate suitable for serving
|
||||||
|
// as the root key for a new certificate authority.
|
||||||
|
type NewCA struct {
|
||||||
|
Key string `json:"private_key"`
|
||||||
|
Cert string `json:"certificate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialCAHandler is an HTTP handler that accepts a JSON blob in the
|
||||||
|
// same format as the CSR endpoint; this blob should contain the
|
||||||
|
// identity information for the CA's root key. This endpoint is not
|
||||||
|
// suitable for creating intermediate certificates.
|
||||||
|
func initialCAHandler(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
log.Info("setting up initial CA handler")
|
||||||
|
req := new(csr.CertificateRequest)
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to read request body: %v", err)
|
||||||
|
return errors.NewBadRequest(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, req)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to unmarshal request: %v", err)
|
||||||
|
return errors.NewBadRequest(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
key, cert, err := initca.New(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to initialise new CA: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
response := api.NewSuccessResponse(&NewCA{string(key), string(cert)})
|
||||||
|
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
err = enc.Encode(response)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHandler returns a new http.Handler that handles request to
|
||||||
|
// initialize a CA.
|
||||||
|
func NewHandler() http.Handler {
|
||||||
|
return api.HTTPHandler{Handler: api.HandlerFunc(initialCAHandler), Method: "POST"}
|
||||||
|
}
|
||||||
69
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/initca/initca_test.go
generated
vendored
Normal file
69
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/initca/initca_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
package initca
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/csr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func csrData(t *testing.T) *bytes.Reader {
|
||||||
|
req := &csr.CertificateRequest{
|
||||||
|
Names: []csr.Name{
|
||||||
|
{
|
||||||
|
C: "US",
|
||||||
|
ST: "California",
|
||||||
|
L: "San Francisco",
|
||||||
|
O: "CloudFlare",
|
||||||
|
OU: "Systems Engineering",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CN: "cloudflare.com",
|
||||||
|
Hosts: []string{"cloudflare.com"},
|
||||||
|
KeyRequest: &csr.KeyRequest{
|
||||||
|
Algo: "ecdsa",
|
||||||
|
Size: 256,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
csrBytes, err := json.Marshal(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return bytes.NewReader(csrBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitCARESTfulVerbs(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(NewHandler())
|
||||||
|
data := csrData(t)
|
||||||
|
// POST should work.
|
||||||
|
req, _ := http.NewRequest("POST", ts.URL, data)
|
||||||
|
resp, _ := http.DefaultClient.Do(req)
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Fatal(resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test GET, PUT, DELETE and whatever, expect 400 errors.
|
||||||
|
req, _ = http.NewRequest("GET", ts.URL, data)
|
||||||
|
resp, _ = http.DefaultClient.Do(req)
|
||||||
|
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||||
|
t.Fatal(resp.Status)
|
||||||
|
}
|
||||||
|
req, _ = http.NewRequest("PUT", ts.URL, data)
|
||||||
|
resp, _ = http.DefaultClient.Do(req)
|
||||||
|
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||||
|
t.Fatal(resp.Status)
|
||||||
|
}
|
||||||
|
req, _ = http.NewRequest("DELETE", ts.URL, data)
|
||||||
|
resp, _ = http.DefaultClient.Do(req)
|
||||||
|
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||||
|
t.Fatal(resp.Status)
|
||||||
|
}
|
||||||
|
req, _ = http.NewRequest("WHATEVER", ts.URL, data)
|
||||||
|
resp, _ = http.DefaultClient.Do(req)
|
||||||
|
if resp.StatusCode != http.StatusMethodNotAllowed {
|
||||||
|
t.Fatal(resp.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
314
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/sign/sign.go
generated
vendored
Normal file
314
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/sign/sign.go
generated
vendored
Normal file
|
|
@ -0,0 +1,314 @@
|
||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/auth"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/universal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Handler accepts requests with a hostname and certficate
|
||||||
|
// parameter (which should be PEM-encoded) and returns a new signed
|
||||||
|
// certificate. It includes upstream servers indexed by their
|
||||||
|
// profile name.
|
||||||
|
type Handler struct {
|
||||||
|
signer signer.Signer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHandler generates a new Handler using the certificate
|
||||||
|
// authority private key and certficate to sign certificates. If remote
|
||||||
|
// is not an empty string, the handler will send signature requests to
|
||||||
|
// the CFSSL instance contained in remote by default.
|
||||||
|
func NewHandler(caFile, caKeyFile string, policy *config.Signing) (http.Handler, error) {
|
||||||
|
root := universal.Root{
|
||||||
|
Config: map[string]string{
|
||||||
|
"cert-file": caFile,
|
||||||
|
"key-file": caKeyFile,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
s, err := universal.NewSigner(root, policy)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("setting up signer failed: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewHandlerFromSigner(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHandlerFromSigner generates a new Handler directly from
|
||||||
|
// an existing signer.
|
||||||
|
func NewHandlerFromSigner(signer signer.Signer) (h *api.HTTPHandler, err error) {
|
||||||
|
policy := signer.Policy()
|
||||||
|
if policy == nil {
|
||||||
|
err = errors.New(errors.PolicyError, errors.InvalidPolicy)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign will only respond for profiles that have no auth provider.
|
||||||
|
// So if all of the profiles require authentication, we return an error.
|
||||||
|
haveUnauth := (policy.Default.Provider == nil)
|
||||||
|
for _, profile := range policy.Profiles {
|
||||||
|
if haveUnauth {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
haveUnauth = (profile.Provider == nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !haveUnauth {
|
||||||
|
err = errors.New(errors.PolicyError, errors.InvalidPolicy)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return &api.HTTPHandler{
|
||||||
|
Handler: &Handler{
|
||||||
|
signer: signer,
|
||||||
|
},
|
||||||
|
Method: "POST",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This type is meant to be unmarshalled from JSON so that there can be a
|
||||||
|
// hostname field in the API
|
||||||
|
// TODO: Change the API such that the normal struct can be used.
|
||||||
|
type jsonSignRequest struct {
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
Hosts []string `json:"hosts"`
|
||||||
|
Request string `json:"certificate_request"`
|
||||||
|
Subject *signer.Subject `json:"subject,omitempty"`
|
||||||
|
Profile string `json:"profile"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func jsonReqToTrue(js jsonSignRequest) signer.SignRequest {
|
||||||
|
sub := new(signer.Subject)
|
||||||
|
if js.Subject == nil {
|
||||||
|
sub = nil
|
||||||
|
} else {
|
||||||
|
// make a copy
|
||||||
|
*sub = *js.Subject
|
||||||
|
}
|
||||||
|
|
||||||
|
if js.Hostname != "" {
|
||||||
|
return signer.SignRequest{
|
||||||
|
Hosts: signer.SplitHosts(js.Hostname),
|
||||||
|
Subject: sub,
|
||||||
|
Request: js.Request,
|
||||||
|
Profile: js.Profile,
|
||||||
|
Label: js.Label,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return signer.SignRequest{
|
||||||
|
Hosts: js.Hosts,
|
||||||
|
Subject: sub,
|
||||||
|
Request: js.Request,
|
||||||
|
Profile: js.Profile,
|
||||||
|
Label: js.Label,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle responds to requests for the CA to sign the certificate request
|
||||||
|
// present in the "certificate_request" parameter for the host named
|
||||||
|
// in the "hostname" parameter. The certificate should be PEM-encoded. If
|
||||||
|
// provided, subject information from the "subject" parameter will be used
|
||||||
|
// in place of the subject information from the CSR.
|
||||||
|
func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
log.Info("signature request received")
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.Body.Close()
|
||||||
|
|
||||||
|
var req jsonSignRequest
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &req)
|
||||||
|
if err != nil {
|
||||||
|
return errors.NewBadRequestString("Unable to parse sign request")
|
||||||
|
}
|
||||||
|
|
||||||
|
signReq := jsonReqToTrue(req)
|
||||||
|
if len(signReq.Hosts) == 0 {
|
||||||
|
return errors.NewBadRequestString("missing parameter 'hostname' or 'hosts'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Request == "" {
|
||||||
|
return errors.NewBadRequestString("missing parameter 'certificate_request'")
|
||||||
|
}
|
||||||
|
|
||||||
|
var cert []byte
|
||||||
|
var profile *config.SigningProfile
|
||||||
|
|
||||||
|
policy := h.signer.Policy()
|
||||||
|
if policy != nil && policy.Profiles != nil && req.Profile != "" {
|
||||||
|
profile = policy.Profiles[req.Profile]
|
||||||
|
}
|
||||||
|
|
||||||
|
if profile == nil && policy != nil {
|
||||||
|
profile = policy.Default
|
||||||
|
}
|
||||||
|
|
||||||
|
if profile.Provider != nil {
|
||||||
|
log.Error("profile requires authentication")
|
||||||
|
return errors.NewBadRequestString("authentication required")
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err = h.signer.Sign(signReq)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to sign request: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := map[string]string{"certificate": string(cert)}
|
||||||
|
log.Info("wrote response")
|
||||||
|
return api.SendResponse(w, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An AuthHandler verifies and signs incoming signature requests.
|
||||||
|
type AuthHandler struct {
|
||||||
|
signer signer.Signer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuthHandler generates a new AuthHandler using the certificate
|
||||||
|
// authority private key and certficate to sign certificates. If remote
|
||||||
|
// is not an empty string, the handler will send signature requests to
|
||||||
|
// the CFSSL instance contained in remote by default.
|
||||||
|
func NewAuthHandler(caFile, caKeyFile string, policy *config.Signing) (http.Handler, error) {
|
||||||
|
root := universal.Root{
|
||||||
|
Config: map[string]string{
|
||||||
|
"cert-file": caFile,
|
||||||
|
"key-file": caKeyFile,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
s, err := universal.NewSigner(root, policy)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("setting up signer failed: %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewAuthHandlerFromSigner(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAuthHandlerFromSigner creates a new AuthHandler from the signer
|
||||||
|
// that is passed in.
|
||||||
|
func NewAuthHandlerFromSigner(signer signer.Signer) (http.Handler, error) {
|
||||||
|
policy := signer.Policy()
|
||||||
|
if policy == nil {
|
||||||
|
return nil, errors.New(errors.PolicyError, errors.InvalidPolicy)
|
||||||
|
}
|
||||||
|
|
||||||
|
if policy.Default == nil && policy.Profiles == nil {
|
||||||
|
return nil, errors.New(errors.PolicyError, errors.InvalidPolicy)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthSign will not respond for profiles that have no auth provider.
|
||||||
|
// So if there are no profiles with auth providers in this policy,
|
||||||
|
// we return an error.
|
||||||
|
haveAuth := (policy.Default.Provider != nil)
|
||||||
|
for _, profile := range policy.Profiles {
|
||||||
|
if haveAuth {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
haveAuth = (profile.Provider != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !haveAuth {
|
||||||
|
return nil, errors.New(errors.PolicyError, errors.InvalidPolicy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &api.HTTPHandler{
|
||||||
|
Handler: &AuthHandler{
|
||||||
|
signer: signer,
|
||||||
|
},
|
||||||
|
Method: "POST",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle receives the incoming request, validates it, and processes it.
|
||||||
|
func (h *AuthHandler) Handle(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
log.Info("signature request received")
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to read response body: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.Body.Close()
|
||||||
|
|
||||||
|
var aReq auth.AuthenticatedRequest
|
||||||
|
err = json.Unmarshal(body, &aReq)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to unmarshal authenticated request: %v", err)
|
||||||
|
return errors.NewBadRequest(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var req jsonSignRequest
|
||||||
|
err = json.Unmarshal(aReq.Request, &req)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to unmarshal request from authenticated request: %v", err)
|
||||||
|
return errors.NewBadRequestString("Unable to parse authenticated sign request")
|
||||||
|
}
|
||||||
|
|
||||||
|
signReq := jsonReqToTrue(req)
|
||||||
|
if len(signReq.Hosts) == 0 {
|
||||||
|
return errors.NewBadRequestString("missing parameter 'hostname' or 'hosts'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Request == "" {
|
||||||
|
return errors.NewBadRequestString("missing parameter 'certificate_request'")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity checks to ensure that we have a valid policy. This
|
||||||
|
// should have been checked in NewAuthHandler.
|
||||||
|
policy := h.signer.Policy()
|
||||||
|
if policy == nil {
|
||||||
|
log.Critical("signer was initialised without a signing policy")
|
||||||
|
return errors.NewBadRequestString("invalid policy")
|
||||||
|
}
|
||||||
|
profile := policy.Default
|
||||||
|
|
||||||
|
if policy.Profiles != nil {
|
||||||
|
profile = policy.Profiles[req.Profile]
|
||||||
|
}
|
||||||
|
|
||||||
|
if profile == nil {
|
||||||
|
log.Critical("signer was initialised without any valid profiles")
|
||||||
|
return errors.NewBadRequestString("invalid profile")
|
||||||
|
}
|
||||||
|
|
||||||
|
if profile.Provider == nil {
|
||||||
|
log.Error("profile has no authentication provider")
|
||||||
|
return errors.NewBadRequestString("no authentication provider")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !profile.Provider.Verify(&aReq) {
|
||||||
|
log.Warning("received authenticated request with invalid token")
|
||||||
|
return errors.NewBadRequestString("invalid token")
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Hostname == "" && len(req.Hosts) == 0 {
|
||||||
|
return errors.NewBadRequestString("missing hostnames")
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Request == "" {
|
||||||
|
return errors.NewBadRequestString("missing certificate_request parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := h.signer.Sign(jsonReqToTrue(req))
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("signature failed: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := map[string]string{"certificate": string(cert)}
|
||||||
|
log.Info("wrote response")
|
||||||
|
return api.SendResponse(w, result)
|
||||||
|
}
|
||||||
400
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/sign/sign_test.go
generated
vendored
Normal file
400
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/sign/sign_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,400 @@
|
||||||
|
package sign
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/auth"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testCaFile = "../testdata/ca.pem"
|
||||||
|
testCaKeyFile = "../testdata/ca_key.pem"
|
||||||
|
testCSRFile = "../testdata/csr.pem"
|
||||||
|
testBrokenCertFile = "../testdata/broken.pem"
|
||||||
|
testBrokenCSRFile = "../testdata/broken_csr.pem"
|
||||||
|
)
|
||||||
|
|
||||||
|
var validLocalConfig = `
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
"expiry": "1m"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
var validAuthLocalConfig = `
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
"expiry": "1m",
|
||||||
|
"auth_key": "sample"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth_keys": {
|
||||||
|
"sample": {
|
||||||
|
"type":"standard",
|
||||||
|
"key":"0123456789ABCDEF0123456789ABCDEF"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
func newTestHandler(t *testing.T) (h http.Handler) {
|
||||||
|
h, err := NewHandler(testCaFile, testCaKeyFile, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewHandler(t *testing.T) {
|
||||||
|
newTestHandler(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewHandlerWithProfile(t *testing.T) {
|
||||||
|
conf, err := config.LoadConfig([]byte(validLocalConfig))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = NewHandler(testCaFile, testCaKeyFile, conf.Signing)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewHandlerWithAuthProfile(t *testing.T) {
|
||||||
|
conf, err := config.LoadConfig([]byte(validAuthLocalConfig))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = NewHandler(testCaFile, testCaKeyFile, conf.Signing)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("All profiles have auth keys. Should have failed to create non-auth sign handler.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewHandlerError(t *testing.T) {
|
||||||
|
// using testBrokenCSRFile as badly formed key
|
||||||
|
_, err := NewHandler(testCaFile, testBrokenCSRFile, nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expect error when create a signer with broken file.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSignServer(t *testing.T) *httptest.Server {
|
||||||
|
ts := httptest.NewServer(newTestHandler(t))
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSignFileOldInterface(t *testing.T, hostname, csrFile string) (resp *http.Response, body []byte) {
|
||||||
|
ts := newSignServer(t)
|
||||||
|
defer ts.Close()
|
||||||
|
var csrPEM []byte
|
||||||
|
if csrFile != "" {
|
||||||
|
var err error
|
||||||
|
csrPEM, err = ioutil.ReadFile(csrFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj := map[string]string{}
|
||||||
|
if len(hostname) > 0 {
|
||||||
|
obj["hostname"] = hostname
|
||||||
|
}
|
||||||
|
if len(csrPEM) > 0 {
|
||||||
|
obj["certificate_request"] = string(csrPEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
blob, err := json.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = http.Post(ts.URL, "application/json", bytes.NewReader(blob))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
body, err = ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSignFile(t *testing.T, hostname, csrFile string) (resp *http.Response, body []byte) {
|
||||||
|
ts := newSignServer(t)
|
||||||
|
defer ts.Close()
|
||||||
|
var csrPEM []byte
|
||||||
|
if csrFile != "" {
|
||||||
|
var err error
|
||||||
|
csrPEM, err = ioutil.ReadFile(csrFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj := map[string]interface{}{}
|
||||||
|
if len(hostname) > 0 {
|
||||||
|
obj["hosts"] = []string{hostname}
|
||||||
|
}
|
||||||
|
if len(csrPEM) > 0 {
|
||||||
|
obj["certificate_request"] = string(csrPEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
blob, err := json.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = http.Post(ts.URL, "application/json", bytes.NewReader(blob))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
body, err = ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
testHostName = "localhost"
|
||||||
|
testDomainName = "cloudflare.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
type signTest struct {
|
||||||
|
Hostname string
|
||||||
|
CSRFile string
|
||||||
|
ExpectedHTTPStatus int
|
||||||
|
ExpectedSuccess bool
|
||||||
|
ExpectedErrorCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
var signTests = []signTest{
|
||||||
|
{
|
||||||
|
testHostName,
|
||||||
|
testCSRFile,
|
||||||
|
http.StatusOK,
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testDomainName,
|
||||||
|
testCSRFile,
|
||||||
|
http.StatusOK,
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"",
|
||||||
|
testCSRFile,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
false,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testDomainName,
|
||||||
|
"",
|
||||||
|
http.StatusBadRequest,
|
||||||
|
false,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testDomainName,
|
||||||
|
testBrokenCSRFile,
|
||||||
|
http.StatusBadRequest,
|
||||||
|
false,
|
||||||
|
1002,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSign(t *testing.T) {
|
||||||
|
for i, test := range signTests {
|
||||||
|
resp, body := testSignFile(t, test.Hostname, test.CSRFile)
|
||||||
|
if resp.StatusCode != test.ExpectedHTTPStatus {
|
||||||
|
t.Logf("Test %d: expected: %d, have %d", i, test.ExpectedHTTPStatus, resp.StatusCode)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
message := new(api.Response)
|
||||||
|
err := json.Unmarshal(body, message)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("failed to read response body: %v", err)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.ExpectedSuccess != message.Success {
|
||||||
|
t.Logf("Test %d: expected: %v, have %v", i, test.ExpectedSuccess, message.Success)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
if test.ExpectedSuccess == true {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.ExpectedErrorCode != message.Errors[0].Code {
|
||||||
|
t.Fatalf("Test %d: expected: %v, have %v", i, test.ExpectedErrorCode, message.Errors[0].Code)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test for backward compatibility
|
||||||
|
// TODO remove after API transition is complete.
|
||||||
|
for i, test := range signTests {
|
||||||
|
resp, body := testSignFileOldInterface(t, test.Hostname, test.CSRFile)
|
||||||
|
if resp.StatusCode != test.ExpectedHTTPStatus {
|
||||||
|
t.Logf("Test %d: expected: %d, have %d", i, test.ExpectedHTTPStatus, resp.StatusCode)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
message := new(api.Response)
|
||||||
|
err := json.Unmarshal(body, message)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("failed to read response body: %v", err)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.ExpectedSuccess != message.Success {
|
||||||
|
t.Logf("Test %d: expected: %v, have %v", i, test.ExpectedSuccess, message.Success)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
if test.ExpectedSuccess == true {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.ExpectedErrorCode != message.Errors[0].Code {
|
||||||
|
t.Fatalf("Test %d: expected: %v, have %v", i, test.ExpectedErrorCode, message.Errors[0].Code)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestAuthHandler(t *testing.T) http.Handler {
|
||||||
|
conf, err := config.LoadConfig([]byte(validAuthLocalConfig))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
h, err := NewAuthHandler(testCaFile, testCaKeyFile, conf.Signing)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewAuthHandler(t *testing.T) {
|
||||||
|
newTestAuthHandler(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewAuthHandlerWithNoAuthConfig(t *testing.T) {
|
||||||
|
conf, err := config.LoadConfig([]byte(validLocalConfig))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = NewAuthHandler(testCaFile, testCaKeyFile, conf.Signing)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Config doesn't have auth keys. Should have failed.")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAuthSignFile(t *testing.T, hostname, csrFile string, profile *config.SigningProfile) (resp *http.Response, body []byte) {
|
||||||
|
ts := newAuthSignServer(t)
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
var csrPEM []byte
|
||||||
|
if csrFile != "" {
|
||||||
|
var err error
|
||||||
|
csrPEM, err = ioutil.ReadFile(csrFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj := map[string]string{}
|
||||||
|
if len(hostname) > 0 {
|
||||||
|
obj["hostname"] = hostname
|
||||||
|
}
|
||||||
|
if len(csrPEM) > 0 {
|
||||||
|
obj["certificate_request"] = string(csrPEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
reqBlob, err := json.Marshal(obj)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var aReq auth.AuthenticatedRequest
|
||||||
|
aReq.Request = reqBlob
|
||||||
|
aReq.Token, err = profile.Provider.Token(aReq.Request)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
blob, err := json.Marshal(aReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = http.Post(ts.URL, "application/json", bytes.NewReader(blob))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
body, err = ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAuthSignServer(t *testing.T) *httptest.Server {
|
||||||
|
ts := httptest.NewServer(newTestAuthHandler(t))
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthSign(t *testing.T) {
|
||||||
|
conf, err := config.LoadConfig([]byte(validAuthLocalConfig))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for i, test := range signTests {
|
||||||
|
resp, body := testAuthSignFile(t, test.Hostname, test.CSRFile, conf.Signing.Default)
|
||||||
|
if resp.StatusCode != test.ExpectedHTTPStatus {
|
||||||
|
t.Logf("Test %d: expected: %d, have %d", i, test.ExpectedHTTPStatus, resp.StatusCode)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
message := new(api.Response)
|
||||||
|
err := json.Unmarshal(body, message)
|
||||||
|
if err != nil {
|
||||||
|
t.Logf("failed to read response body: %v", err)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.ExpectedSuccess != message.Success {
|
||||||
|
t.Fatalf("Test %d: expected: %v, have %v", i, test.ExpectedSuccess, message.Success)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
if test.ExpectedSuccess == true {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.ExpectedErrorCode != message.Errors[0].Code {
|
||||||
|
t.Fatalf("Test %d: expected: %v, have %v", i, test.ExpectedErrorCode, message.Errors[0].Code)
|
||||||
|
t.Fatal(resp.Status, test.ExpectedHTTPStatus, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
13
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/broken.pem
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/broken.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICATCCAWoCCQDidF+uNJR6czANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
|
||||||
|
cyBQdHkgTHRkMB4XDTEyMDUwMTIyNTUxN1oXDTEzMDUwMTIyNTUxN1owRTELMAkG
|
||||||
|
A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
|
||||||
|
nodhz31kLEJoeLSkRmrv8l7exkGtO0REtIbirj9BBy64ZXVBE7khKGO2cnM8U7yj
|
||||||
|
w7Ntfh+IvCjZVA3d2XqHS3Pjrt4HmU/cGCONE8+NEXoqdzLUDPOix1qDDRBvXs81
|
||||||
|
IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtpjl
|
||||||
|
KAV2qh6CYHZbdqixhDerjvJcD4Nsd7kExEZfHuECAwEAATANBgkqhkiG9w0BAQUF
|
||||||
|
AAOBgQCyOqs7+qpMrYCgL6OamDeCVojLoEp036PsnaYWf2NPmsVXdpYW40Foyyjp
|
||||||
|
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
|
||||||
|
iv5otkxO5rxtGPv7o2J1eMBpCuSkydvoz3Ey/QwGqbBwEXQ4xYCgra336gqW2KQt
|
||||||
|
+LnDCkE8f5oBhCIisExc2i8PDvsRsY70g/2gs983ImJjVR8sDw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
30
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/broken_csr.pem
generated
vendored
Normal file
30
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/broken_csr.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIFGzCCAwUCAQAwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
|
||||||
|
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
|
||||||
|
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMR0wGwYDVQQDExRjbG91ZGZsYXJl
|
||||||
|
LWludGVyLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOUKdX6+
|
||||||
|
PSxU/LxKocsCUj7HCc+FaDOPZV68Po3PVm7UF5DmbnLgJYJ/4aZEZM/v5r8LnXQX
|
||||||
|
DqumYicHQ2DHHBDasLTx8m0KeKOUYf9WMQ8gdjmVFoCiZwzxGDHok66/0Glkkqmv
|
||||||
|
2nJQxXncl5ZFta4sfmcQx3KT02l61LaBbG3j8PbRCWEr+0eRE6twuYRR13AgZ3AT
|
||||||
|
wnMjzxzvsW67qmAy0cq+XgYYfTK9vhPs+8J0fxXa0Iftu3yuhd30xLIVXLu45GR+
|
||||||
|
i6KnsSxVERSaVxjkS+lHXjUpdtmqI5CK6wn67vqYRRA2TzAJHX8Jb+KL2/UEo5WN
|
||||||
|
fAJ8S0heODQA8nHVU1JIfpegOlQRMv55DgnQUv1c1uwO5hqvv7MPQ3X/m9Kjccs1
|
||||||
|
FBH1/SVuzKyxYEQ34LErX3HI+6avbVnRtTR/UHkfnZVIXSrcjUm73BGj33hrtiKl
|
||||||
|
0ZyZnaUKGZPuvebOUFNiXemhTbqrfi/zAb1Tsm/h+xkn5EZ5sMj5NHdAbpih3TqX
|
||||||
|
2gRhnFZcFjtJM6zzC5O7eG5Kdqf8iladXTXtWxzrUPkb5CupzFl1dyS3dqdkoIXv
|
||||||
|
kmlScnu+6jBOaYeVvwogxr2Y69y4Zfg/qbPyBOLZquX9ovbuSP1DQmC//LV5t7YH
|
||||||
|
HY/1MXr5U0MMvcn+9JWUV6ou3at4AgEqfK0vAgMBAAGgSzBJBgkqhkiG9w0BCQ4x
|
||||||
|
PDA6MDgGA1UdEQQxMC+CFGNsb3VkZmxhcmUtaW50ZXIuY29tghd3d3djbG91ZGZs
|
||||||
|
YXJlLWludGVyLmNvbTALBgkqhkiG9w0BAQ0DggIBAHtSt/v+IHQmSK5UiQWwjRWA
|
||||||
|
ZezIWVlJuselW8DEPNHzDtnraVhjPSFP995Cqh9fc89kx2Bt9hDhjNteTB+pJW6B
|
||||||
|
aCRRZygJ6/m3Ii1XqTFgfEJBWwuIX1Req0PCW/ayegdLzzYbSZ31wRICCveBQyGw
|
||||||
|
vRtzIBUeMvz9MgLJ8zx7eN7fDhrvy+Y1SkC4g0sAQTYYfM9P/He4k5hx79hmd2YC
|
||||||
|
mUDAlNZV0g0dY0qR4cITmhniIFW5iZBplY7DmqooUXrj5yEga2QMj/RA16lPzHbz
|
||||||
|
7ceUlcH2L6/V6zMR/rfCiGRoWInxWSuuJhLIVLmoEo0590w6KVEZifHxsRpl4l09
|
||||||
|
imvzwTSQGIrY8jF9AxOD0rRA9wXCT9h8XtBWyJZ1/DmzJG8+7oZ/HdE9XhzwNujD
|
||||||
|
Q6lBOj+dznju7k/snYCZVq501JLPeql8vQrq0O/xSqSK4yN1IG4NisZeDK2BZEOy
|
||||||
|
QhnKXodIKf+zXnFw86lZ/ZwHQFr6jOSxmbrZ2OiY34m7Yd9oeIaMPviysRih2x4Q
|
||||||
|
O6DFz72f97+xFZuXIbmn8DPQV8U9bk/gbrfUCPnx/icS8UoPsBKc9Gio0FZO4+8A
|
||||||
|
4/ac3oeN0zy/WjsBP+J50CRUXMrRI9KO+/bI4pcT14B31YbuSo6ygIkIkj7YDh36
|
||||||
|
+4ZG6HnUPQI8HteF9hzp=BROKEN==
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
17
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/ca-bundle.pem
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/ca-bundle.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICyDCCAjGgAwIBAgIJAPCgd7rafQZGMA0GCSqGSIb3DQEBBQUAMH0xCzAJBgNV
|
||||||
|
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNp
|
||||||
|
c2NvMRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEW
|
||||||
|
MBQGA1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDA0MTExNjQyMjBaFw0yNDA0MDgx
|
||||||
|
NjQyMjBaMH0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYD
|
||||||
|
VQQHDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQL
|
||||||
|
DAtERVZfVEVTVElORzEWMBQGA1UEAwwNQ0ZTU0xfVEVTVF9DQTCBnzANBgkqhkiG
|
||||||
|
9w0BAQEFAAOBjQAwgYkCgYEAm6f+jkP2t5q/vM0YAUZZkhq/EAYD+L1CMS59jJOL
|
||||||
|
omfDnKUWOGKi/k7URBg1HNL3vm7/ESDazZWFy9l/nibWxNkSUPkQIrvrGsNivkRU
|
||||||
|
zXkwgNX8IN8LOYAQ3BWxAqitXTpLjf4FeCTB6G59v9eYlAX3kicXRdY+cqhEvLFb
|
||||||
|
u3MCAwEAAaNQME4wHQYDVR0OBBYEFLhe765nULfW8wflar5Vs2c6DZI+MB8GA1Ud
|
||||||
|
IwQYMBaAFLhe765nULfW8wflar5Vs2c6DZI+MAwGA1UdEwQFMAMBAf8wDQYJKoZI
|
||||||
|
hvcNAQEFBQADgYEABYqqOUq3ZrtMYaTAoeA7Cr/OBMjBV+/TiOe8fRNoPZ7+aKSg
|
||||||
|
E1baohCGqougm+/XOtBXeLv5tVQihz/2iKdwHmX4HjkxzevAXyazjxeW4IDA21Jl
|
||||||
|
fKd7xUJHM0Du/opoDkXWr/vRVztOB33ndlAK7ruSLfTR3E9HoUe3aRH7ceQ=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/ca.pem
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/ca.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEmzCCA4OgAwIBAgIMAMSvNBgypwaaSQ5iMA0GCSqGSIb3DQEBBQUAMIGMMQsw
|
||||||
|
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
|
||||||
|
YW5jaXNjbzETMBEGA1UEChMKQ0ZTU0wgVEVTVDEbMBkGA1UEAxMSQ0ZTU0wgVEVT
|
||||||
|
VCBSb290IENBMR4wHAYJKoZIhvcNAQkBFg90ZXN0QHRlc3QubG9jYWwwHhcNMTIx
|
||||||
|
MjEyMDIxMDMxWhcNMjIxMDIxMDIxMDMxWjCBjDELMAkGA1UEBhMCVVMxEzARBgNV
|
||||||
|
BAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAoT
|
||||||
|
CkNGU1NMIFRFU1QxGzAZBgNVBAMTEkNGU1NMIFRFU1QgUm9vdCBDQTEeMBwGCSqG
|
||||||
|
SIb3DQEJARYPdGVzdEB0ZXN0LmxvY2FsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||||
|
MIIBCgKCAQEAsRp1xSfIDoD/40Bo4Hls3sFn4dav5NgxbZGpVyGF7dJI9u0eEnL4
|
||||||
|
BUGssPaUFLWC83CZxujUEiEfE0oKX+uOhhGv3+j5xSTNM764m2eSiN53cdZtK05d
|
||||||
|
hwq9uS8LtjKOQeN1mQ5qmiqxBMdjkKgMsVw5lMCgoYKo57kaKFyXzdpNVDzqw+pt
|
||||||
|
HWmuNtDQjK3qT5Ma06mYPmIGYhIZYLY7oJGg9ZEaNR0GIw4zIT5JRsNiaSb5wTLw
|
||||||
|
aa0n/4vLJyVjLJcYmJBvZWj8g+taK+C4INu/jGux+bmsC9hq14tbOaTNAn/NE0qN
|
||||||
|
8oHwcRBEqfOdEYdZkxI5NWPiKNW/Q+AeXQIDAQABo4H6MIH3MB0GA1UdDgQWBBS3
|
||||||
|
0veEuqg51fusEM4p/YuWpBPsvTCBxAYDVR0jBIG8MIG5gBS30veEuqg51fusEM4p
|
||||||
|
/YuWpBPsvaGBkqSBjzCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3Ju
|
||||||
|
aWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAoTCkNGU1NMIFRFU1Qx
|
||||||
|
GzAZBgNVBAMTEkNGU1NMIFRFU1QgUm9vdCBDQTEeMBwGCSqGSIb3DQEJARYPdGVz
|
||||||
|
dEB0ZXN0LmxvY2FsggwAxK80GDKnBppJDmIwDwYDVR0TBAgwBgEB/wIBADANBgkq
|
||||||
|
hkiG9w0BAQUFAAOCAQEAJ7r1EZYDwed6rS0+YKHdkRGRQ5Rz6A9DIVBPXrSMAGj3
|
||||||
|
F5EF2m/GJbhpVbnNJTVlgP9DDyabOZNxzdrCr4cHMkYYnocDdgAodnkw6GZ/GJTc
|
||||||
|
depbVTR4TpihFNzeDEGJePrEwM1DouGswpu97jyuCYZ3z1a60+a+3C1GwWaJ7Aet
|
||||||
|
Uqm+yLTUrMISsfnDPqJdM1NeqW3jiZ4IgcqJkieCCSpag9Xuzrp9q6rjmePvlQkv
|
||||||
|
qz020JGg6VijJ+c6Tf5y0XqbAhkBTqYtVamu9gEth9utn12EhdNjTZMPKMjjgFUd
|
||||||
|
H0N6yOEuQMl4ky7RxZBM0iPyeob6i4z2LEQilgv9MQ==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/ca_key.pem
generated
vendored
Normal file
28
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/ca_key.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCxGnXFJ8gOgP/j
|
||||||
|
QGjgeWzewWfh1q/k2DFtkalXIYXt0kj27R4ScvgFQayw9pQUtYLzcJnG6NQSIR8T
|
||||||
|
Sgpf646GEa/f6PnFJM0zvribZ5KI3ndx1m0rTl2HCr25Lwu2Mo5B43WZDmqaKrEE
|
||||||
|
x2OQqAyxXDmUwKChgqjnuRooXJfN2k1UPOrD6m0daa420NCMrepPkxrTqZg+YgZi
|
||||||
|
EhlgtjugkaD1kRo1HQYjDjMhPklGw2JpJvnBMvBprSf/i8snJWMslxiYkG9laPyD
|
||||||
|
61or4Lgg27+Ma7H5uawL2GrXi1s5pM0Cf80TSo3ygfBxEESp850Rh1mTEjk1Y+Io
|
||||||
|
1b9D4B5dAgMBAAECggEAKHhjcSomDSptTwDo9mLI/h40HudwSlsc8GzYxZBjinUD
|
||||||
|
N2n39T9QbeMUE1xFenX/9qFEgq+xxnLLJx1EQacSapCgIAqdCO/f9HMgvGJumdg8
|
||||||
|
c0cMq1i9Bp7tu+OESZ5D48qWlOM2eQRIb08g8W11eRIaFmPuUPoKnuktkQuXpPJc
|
||||||
|
YbS/+JuA8SDwe6sV0cMCQuS+iHFfeGwWCKrDUkhLwcL3waW3od2XFyOeFFWFhl0h
|
||||||
|
HmM/mWKRuRdqR7hrmArTwFZVkB+o/1ywVYXIv+JQm0eNZ5PKLNJGL2f5oxbMR/JI
|
||||||
|
AoK0bAlJmYaFp96h1KpbPwLEL/0hHSWA7sAyJIgQAQKBgQDaEAZor/w4ZUTekT1+
|
||||||
|
cbId0yA+ikDXQOfXaNCSh9Pex+Psjd5zVVOqyVFJ29daRju3d7rmpN4Cm5V4h0l1
|
||||||
|
/2ad207rjCAnpCHtaddJWNyJzF2IL2IaoCZQRp0k7zOjBGQpoWDTwBaEin5CCv3P
|
||||||
|
kkdQkKz6FDP1xskHSLZr21/QCQKBgQDP6jXutEgGjf3yKpMFk/69EamJdon8clbt
|
||||||
|
hl7cOyWtobnZhdOWVZPe00Oo3Jag2aWgFFsm3EtwnUCnR4d4+fXRKS2LkhfIUZcz
|
||||||
|
cKy17Ileggdd8UGhL4RDrF/En9tJL86WcVkcoOrqLcGB2FLWrVhVpHFK74eLMCH/
|
||||||
|
uc/+ioPItQKBgHYoDsD08s7AGMQcoNx90MyWVLduhFnegoFW+wUa8jOZzieka6/E
|
||||||
|
wVQeR5yksZjpy3vLNYu6M83n7eLkM2rrm/fXGHlLcTTpm7SgEBZfPwivotKjEh5p
|
||||||
|
PrlqucWEk082lutz1RqHz+u7e1Rfzk2F7nx6GDBdeBYpw03eGXJx6QW5AoGBAIJq
|
||||||
|
4puyAEAET1fZNtHX7IGCk7sDXTi6LCbgE57HhzHr8V0t4fQ6CABMuvMwM1gATjEk
|
||||||
|
s6yjoLqqGUUUzDipanViBAy5fiuManC868lN7zkWDTLzQ3ytBqVAee4na/DziP27
|
||||||
|
ae9YTSLJwskE/alloLRP6zTbHUXE0n7LelmrX1DFAoGBAMFLl+Lu+WFgCHxBjn43
|
||||||
|
rHpJbQZQmsFhAMhkN4hsj6dJfAGn2gRLRiVRAika+8QF65xMZiVQWUVSUZADWERi
|
||||||
|
0SXGjzN1wYxO3Qzy3LYwws6fxFAq5lo79eb38yFT2lHdqK3x/QgiDSRVl+R6cExV
|
||||||
|
xQB518/lp2eIeMpglWByDwJX
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
13
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/cert.pem
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/cert.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIB7jCCAVmgAwIBAgIBADALBgkqhkiG9w0BAQUwJjEQMA4GA1UEChMHQWNtZSBD
|
||||||
|
bzESMBAGA1UEAxMJMTI3LjAuMC4xMB4XDTEyMDkwNzIyMDAwNFoXDTEzMDkwNzIy
|
||||||
|
MDUwNFowJjEQMA4GA1UEChMHQWNtZSBDbzESMBAGA1UEAxMJMTI3LjAuMC4xMIGd
|
||||||
|
MAsGCSqGSIb3DQEBAQOBjQAwgYkCgYEAm6f+jkP2t5q/vM0YAUZZkhq/EAYD+L1C
|
||||||
|
MS59jJOLomfDnKUWOGKi/k7URBg1HNL3vm7/ESDazZWFy9l/nibWxNkSUPkQIrvr
|
||||||
|
GsNivkRUzXkwgNX8IN8LOYAQ3BWxAqitXTpLjf4FeCTB6G59v9eYlAX3kicXRdY+
|
||||||
|
cqhEvLFbu3MCAwEAAaMyMDAwDgYDVR0PAQH/BAQDAgCgMA0GA1UdDgQGBAQBAgME
|
||||||
|
MA8GA1UdIwQIMAaABAECAwQwCwYJKoZIhvcNAQEFA4GBABndWRIcfi+QB9Sakr+m
|
||||||
|
dYnXTgYCnFio53L2Z+6EHTGG+rEhWtUEGhL4p4pzXX4siAnjWvwcgXTo92cafcfi
|
||||||
|
uB7wRfK+NL9CTJdpN6cdL+fiNHzH8hsl3bj1nL0CSmdn2hkUWVLbLhSgWlib/I8O
|
||||||
|
aq+K7aVrgHkPnWeRiG6tl+ZA
|
||||||
|
-----END CERTIFICATE-----
|
||||||
12
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/csr.pem
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/csr.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIB0jCCAVcCAQAwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
|
||||||
|
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
|
||||||
|
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMR0wGwYDVQQDExRjbG91ZGZsYXJl
|
||||||
|
LWludGVyLmNvbTB2MBAGByqGSM49AgEGBSuBBAAiA2IABCFZIzSRsH9xdF1iR+8k
|
||||||
|
ElbcbqAYnYuSTbEOxYcREHGRJd2/v9YhetEwWNmIuisCbgOpyBO9zyFxsnzYU4cO
|
||||||
|
A/AomW2nJEP7n4M9g8r8clhQz8y6+013jP9MEqf4pqMVnqBLMEkGCSqGSIb3DQEJ
|
||||||
|
DjE8MDowOAYDVR0RBDEwL4IUY2xvdWRmbGFyZS1pbnRlci5jb22CF3d3d2Nsb3Vk
|
||||||
|
ZmxhcmUtaW50ZXIuY29tMAoGCCqGSM49BAMDA2kAMGYCMQD6kSGGc3/DeFAWrPUX
|
||||||
|
qSlnTTm57DpzUoHQE306DfbFB6DFfoORNM5Z98chnZ+Ell4CMQCzYhOvIh3+GPGF
|
||||||
|
MuYYIAfQV2JG+n7pjfpJ+X1Ee2bOtA4ZO39P9/FTEtJUXt+Ivqw=
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
53
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/int-bundle.pem
generated
vendored
Normal file
53
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/int-bundle.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEizCCA/agAwIBAgIIeM7v534l+W0wCwYJKoZIhvcNAQELMH0xCzAJBgNVBAYT
|
||||||
|
AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv
|
||||||
|
MRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQG
|
||||||
|
A1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDA0MTEyMTIyMzdaFw0xOTA0MTEyMTI3
|
||||||
|
MzdaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UE
|
||||||
|
CxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET
|
||||||
|
MBEGA1UECBMKQ2FsaWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5j
|
||||||
|
b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDlCnV+vj0sVPy8SqHL
|
||||||
|
AlI+xwnPhWgzj2VevD6Nz1Zu1BeQ5m5y4CWCf+GmRGTP7+a/C510Fw6rpmInB0Ng
|
||||||
|
xxwQ2rC08fJtCnijlGH/VjEPIHY5lRaAomcM8Rgx6JOuv9BpZJKpr9pyUMV53JeW
|
||||||
|
RbWuLH5nEMdyk9NpetS2gWxt4/D20QlhK/tHkROrcLmEUddwIGdwE8JzI88c77Fu
|
||||||
|
u6pgMtHKvl4GGH0yvb4T7PvCdH8V2tCH7bt8roXd9MSyFVy7uORkfouip7EsVREU
|
||||||
|
mlcY5EvpR141KXbZqiOQiusJ+u76mEUQNk8wCR1/CW/ii9v1BKOVjXwCfEtIXjg0
|
||||||
|
APJx1VNSSH6XoDpUETL+eQ4J0FL9XNbsDuYar7+zD0N1/5vSo3HLNRQR9f0lbsys
|
||||||
|
sWBEN+CxK19xyPumr21Z0bU0f1B5H52VSF0q3I1Ju9wRo994a7YipdGcmZ2lChmT
|
||||||
|
7r3mzlBTYl3poU26q34v8wG9U7Jv4fsZJ+RGebDI+TR3QG6Yod06l9oEYZxWXBY7
|
||||||
|
STOs8wuTu3huSnan/IpWnV017Vsc61D5G+QrqcxZdXckt3anZKCF75JpUnJ7vuow
|
||||||
|
TmmHlb8KIMa9mOvcuGX4P6mz8gTi2arl/aL27kj9Q0Jgv/y1ebe2Bx2P9TF6+VND
|
||||||
|
DL3J/vSVlFeqLt2reAIBKnytLwIDAQABo4GDMIGAMA4GA1UdDwEB/wQEAwIApDAS
|
||||||
|
BgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTXXUgpaSwO9HOrQBxGqOOSFHsH
|
||||||
|
EDAfBgNVHSMEGDAWgBS4Xu+uZ1C31vMH5Wq+VbNnOg2SPjAaBgNVHREEEzARgg9j
|
||||||
|
ZnNzbC1pbnRlci5jb20wCwYJKoZIhvcNAQELA4GBABqJOYgV+qEgkG/BIgsGaJ/Z
|
||||||
|
Neey0x0MwxPvA87e24GiYxYXX8ypR2DfLtuSjYfT0PVOWI5+3o9b3wnHhOu0aVe8
|
||||||
|
YK/7XUWOakt8Jv/fE0fGs4Ps5IeMynWBgwrf/6IQWEfnf/1siCrTf0yUEn0PMGu6
|
||||||
|
q2sLytoPYeibTYLuP1ED
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEbjCCAligAwIBAgIIeHSbZwALpoAwCwYJKoZIhvcNAQELMIGMMQswCQYDVQQG
|
||||||
|
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
|
||||||
|
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
|
||||||
|
cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwNDExMjEy
|
||||||
|
MjM4WhcNMTkwNDExMjEyNzM4WjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs
|
||||||
|
b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT
|
||||||
|
DVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNs
|
||||||
|
b3VkZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGw
|
||||||
|
f3F0XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73P
|
||||||
|
IXGyfNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo4GDMIGA
|
||||||
|
MA4GA1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBRB
|
||||||
|
+YoiUjIm34/wBwHdJGE4Wufs/DAfBgNVHSMEGDAWgBTXXUgpaSwO9HOrQBxGqOOS
|
||||||
|
FHsHEDAaBgNVHREEEzARgg9jZnNzbC1pbnRlci5jb20wCwYJKoZIhvcNAQELA4IC
|
||||||
|
AQCaj2i8wr9r3FS8Tw5QHD+tPmryrHsiLlERVanTif9kt/fRc1/hm/pv2lTLK8kK
|
||||||
|
U5Eti1jCB2T/DQGj4Z/amRndasXpUb5wTtMb9V6jN4pRfgw+C5ska9o5zFrIGJF0
|
||||||
|
GbSe1VVUedJ1LH3US3a79eVGmyAwcfTRMNhn+e+uYky2VYCQIEGGQ8rZAM3TveoT
|
||||||
|
N8J7Lqwtuo3DWz0IYx60DUvabpqJ+9Dl6rhTvTfyYvQK4vl2xApGf4Uo87JbNQfq
|
||||||
|
q40UXfBtMaAvIPEKCyTdOVVDrfgW0DQTl7wS+Z3p6kNm0NMI53TFTbgIuU9QiPPB
|
||||||
|
I5NdqISEPFW/HS5q0+zR1KdG4EmEjmpCX78s+uviHpHQloWQT9ov4KbXbf8y5Xso
|
||||||
|
lv+2gcd5TVjYxPRbo3SMtGRQho5uq2BNy6Q0K0//3OE+X+v+ZDi8n4MU3uA7dGGA
|
||||||
|
7uAUZOYPzNKS7ryW3h4PZIfiI5Fv9tBNnu9O3I2UH6fHNFQQLzJPCXertPmrORjP
|
||||||
|
EyCNCOhfsNwLd5Qq53cDbG1mkZro/xKDvAOx2LQcGFtmx4v1NXI204V50aSzy8vY
|
||||||
|
vQnM0gEY/YxoCq3wSjc9yeUftyv2LIgJvuXjkeHkV7gQQ+jx/HY6J7fnJGSzKMKp
|
||||||
|
/GPaPCNKvCY/72ik2gbmdvLbaRGeVJ07JO46YWEUrGb/1A==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
39
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/leaf.badkey
generated
vendored
Normal file
39
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/leaf.badkey
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIG5AIBAAKCAYEA9xYBDoV2tPx8lqZ/bH/wLvoPsg1/CXeknvRcNuxw1gu6c3IJ
|
||||||
|
BrKZlkFtiU6Y8FADiUBOVab/Y0cQ/9EdeB2srPH4M5KNiPdWZPgxARWnRq5Ez8pv
|
||||||
|
VASP2E2Zya1UnH5iJBau8e6SwBl8UaXnGwcA+CUv+FXcZtdoFh0Lqt3AdItQOkHV
|
||||||
|
jSE6Cfiv5lsSW0ikMcoHFOHNps4/9A4A/griT5lRDqQIycN7WD2k4+aKVreCWxbS
|
||||||
|
teU35yIDJV6PGUtw8k41arJ+kwuwYM3+YklR0Dsj0RxXn07oLqnf6IeNUogGhNVO
|
||||||
|
7RvLdpfvrhlevHVXmmYj40fkGjU15KkZOKigMw/gDInI6Sc2jp8oPX9tjkaQYkF2
|
||||||
|
t7AWOq01lh5TleMIoBFUqVcy+X/qejla0JaKCEyt/fiPUo7/SgucyFl8GrKfSdEL
|
||||||
|
UOKx5Vr2ZZ48QSfIlXle+tGtFD0AYUsO0ud0wclW5C+g8E27raTuR4RaZOj8/pmB
|
||||||
|
7XNDszwxQ/97dBRpAgMBAAECggGAcWoWPhYg8N5cScJPBvyKwOVjQvVS9IOIerXr
|
||||||
|
hgJtoLJteQRFBGACg6ewobAEH3p6xQtRaZtn6qf6M5JHFpV4Z0ICDZodgVsWuu35
|
||||||
|
gGfyCk1/pGllRIl7hWvJRXtcNSEF507KKp65mZeZKtkeBZfnZ/+Zz0GKE2KYkl3u
|
||||||
|
txVme5he0P7bCRbRTzZpdzEicegcBgaXzYwAG6rcTCgJaJKSYrsbK787kXE7MrvI
|
||||||
|
7hsqMLe3DByjx35ZdKx2CTcoNBId9RODWnPpANVrlNv7kbaZRqd5OI8b7JfblFsq
|
||||||
|
F6vCzvDq+Quc8ID1zxRZv761pexejtDzghgQy7X2EVvMlHh4//wErgq6WfPjwyvU
|
||||||
|
/zZczO0L/c1XwwkfBU6Yf6UuYCKngwifgvb7aGU4/aGNcD5SHRITwCHK/E9JrkR8
|
||||||
|
pkqerMxsf9uP5FxGdwOm1k77Lkap7Kx2Utt5l7stOY0fFUFz1YQdAHJUzhmbP3Zy
|
||||||
|
C+TeX2/9+CudXM1parW7HQRlZeMJAoHBAP545khACfRvUWpxdQohp1Ol0FuDosYg
|
||||||
|
NC75q12T8ovllx8Qly3aafJdd0NTvFmrBkBPTL3pCUWCyGZh6/E00fUL4dtD3zwz
|
||||||
|
QUbm6hWGTgKHdeLLdae2wxcZ/NqmTvpY9o/p4jS9+StRKQtdsftLKCmRv7wfYkju
|
||||||
|
UT7O+gRyGat/Rqpr9cTSKBXHUT+WJlITDrwk5QdydF7eKzLT8DROgcRRE1+FMJkj
|
||||||
|
pO5ChuAxZr0Q0fISRm9Lu7aJ3H8QFfboGwKBwQD4kcCkZvRdz8BQsOsyHQ3SlGhx
|
||||||
|
5nwA7SPadXtfnpoW0ZlEdHwkPJzU1Z50z1ulEQymBTARPUQ4s28MQt8NXuRzHBrW
|
||||||
|
PMUGgsspzT6FjiskhUc8k9PAZbEJE/axLKK2qSKktGuZj+VFih/9XPPTX4xSzlOg
|
||||||
|
ntJEr2tc3TIv+JEOuJX6VT2URFLXgdOHXxAejS0DTGIg1aB4VGQpWzfbcJ6Cyf11
|
||||||
|
YyoyYWA25wdw7sB9kDHsd0Ej0mld5+l8JOd8hcsCgcA9jCpOcUa3GzF66EQhljAt
|
||||||
|
WB6D89urxeA5OGPNN1pjob0iY1XdXkVfvGF7JEaa/XV+mm96Q2HdsRsdQDPb3CWn
|
||||||
|
+h6/dLQKkG8KYhFd8WTu0aqelw026kpXTQ7OJ4lUna3M8wmmLgiVBIVD3X6NxAjL
|
||||||
|
vRe9vW19LD70TQVFi/9PbnI+B+yilR3i3pl1IrDUCw32TYojefhRdbTHD2G6lP5n
|
||||||
|
6CAia0ls0KU0h1yt3uT1d5r/zJHCm3OkW8W76b0WQd8CgcEAh0czk4WgiomtPXz7
|
||||||
|
k3tycV9pdEuewxZMQ/FaIpD7hV2uzy2h/kqqg756jVHoq24a9yOtpEQ2o7Erx32B
|
||||||
|
TRKOvALYrC3IgKGgFfDojODxo9+RBGvjezsc3TbrNEN5jnWAMCkswhcpDO5+OHJl
|
||||||
|
FG1UviAiLTEieFUL1i9fx/G8aEmW/fV0HQQOHdE/INZgvG/Sxo/Ee+AnhDVRiZxm
|
||||||
|
StwAuGdbtI4ygday+U5Eo3acdfmK4gmI/wjdZUj4riKbhQ5/AoHBAI0yzo+PIFi6
|
||||||
|
HjNYVoC7rZ39oQ0YCrEWrui+DRdEjnjec31Jw02AtKnv5swpDDHjgnIcd9ciQY48
|
||||||
|
rk7eC6IkVrL9hOxUzC9YQZX/2MBiOLjUkDkSLt+d5PL0OXiSg1O4fGJdGiVPF0Fc
|
||||||
|
sF9p1UNEfGvXjzUB3ay0kMyCLitNe1BCvJlYXdSV9YmAMNvguE7TNU3OPiVv65PK
|
||||||
|
6OndznX41Pw7OlnLaq1sFQcYBmf5E7QSKYP+4HeV89Sc824VlCNxwA==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/leaf.key
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/leaf.key
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpAIBAAKCAQEA0C6SSsXfuse2IV8+6hSYqSPQdoQwZ5BYQnSxuKylArCrMXx8
|
||||||
|
JGHrJP6Pj7GxRmH40v9u9VwZvcrQOm8yUTuzAEf2Kd3uvXmVKJb2vc0BopsflpSE
|
||||||
|
OLEuddTSHlHgdVHylqpbzB7ZrmyXXuWTtTFEaGmPVUmWcOBOy6pc/7hZv7HkTjaH
|
||||||
|
LQu/uohic/NjO0oJaaUwds6muwTCNSmMvtvoP51pyQJeuZjYIoWnnu+/DbtZYmH4
|
||||||
|
4VbHD0U+uSNKLZa4beWqDq5ZDwQvEVkuLqL331awzgIf0a4bhP+uc1kdWXZ8V+8a
|
||||||
|
Bbqtq6g6o9HdrzgNRR+9S3EvEelCrxuWw9FQ3QIDAQABAoIBAQDFQ5vzplQ9lIgM
|
||||||
|
T0g6XpHZk8oww0lqmOhI8HKG33Dsf6N4HNE1WGOMhnpaWrH0U1mH9eqaLE9n/Aob
|
||||||
|
lMpFFyCin42uVlGm0NJ5x7K+Xsex4POpp8kyPxIbLTJ88HCUOrZ39a1OWd1C3jsA
|
||||||
|
/OFdy/VaSsw6sKQRCTsg2amN1o2UibDJYVW47ycv9cwjk/GEzzOSq32a9o6g6Gwd
|
||||||
|
g3ycroIaxhDlGjS5l0IZ/ozhN+AS5dYcPgJRsYD/jTBqTSzIW2ePrcheznoRcgLK
|
||||||
|
bb+UVQC+PZX8kycCcerPbcGc2YcBpZgmIkCj85+ITFt/BhH7+TSH9G7F8LTKAaJg
|
||||||
|
qlYKF14BAoGBAPz8Jx0vAcv/4zIfCckuNy3kVu4PHBTMTBO5+tUg6CZgktRrroiV
|
||||||
|
+Zq1lCuj2/Px3Lx9oaUie52iV5xgmEEax77xa1rVezY1PhGSFmngHqfumUJf8EEB
|
||||||
|
snlAUpwBHvWU9B9OxKOHRrD9Y9ptXcBK30ZHLJT4t5JvbHVrKZF2J82hAoGBANKp
|
||||||
|
ue+dOafhgc1F/ThD2VLuIi6Garf1pqNG3OMugMfieHAmr1RRYWwFErLoijt9dpe9
|
||||||
|
gXVecUm1KO4/0ZkR+7YDzUSifXvcizaw+XqjrtFerrz+Yao4gZssFnw/sLc2pbWm
|
||||||
|
1DHWxRnmh6MyHEEiA0KxElgutswhP8GIKN7INOG9AoGAR1sD2Upp8lVBiuCQTQtZ
|
||||||
|
CvutvUXLwN4C00mQw06dzD1PDNU2jFXo6kcu/MQiBQOCJDQ3RLGeNk8U8QmZyDs6
|
||||||
|
fdPwWNWABEEuOZx/7+sEGo/E8KDIzj0hTuvioRf72H7kAHSiKBG+0asW4AQa/mLf
|
||||||
|
6R2oKHiipo4BBHluZxXxkiECgYEAuYXnzfH0+LhMi+77VjXKipJVYAvYqDGak2iw
|
||||||
|
1xH5MA9uabZn6iXRWkQNd6n7MvEHJBMsk6ScuIDmjwt9FwUTW/R1LeC8CfzsTToG
|
||||||
|
O88zAggUczTD5hjlazakhr/AbVmfDh7h+RJferPe+AYFhAbkQDOZKDfbnGIbt+Cl
|
||||||
|
va0rhTECgYAFb38TvJmEIzB1/nZ7sKbFmr2pYgzBqspQcprws6gZlWydd4OoTZiv
|
||||||
|
QzSBDi3tGt07yJuntVlbuI6qejhFMmonGZuntNTvTZMmx2+W/F8EGByfWpLtB9W5
|
||||||
|
S+tx5/0d4MhOYHlt0EcdC7j881swY9LCrc/EOqg1O4BlTJ5+UJer+Q==
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
21
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/leaf.pem
generated
vendored
Normal file
21
Godeps/_workspace/src/github.com/cloudflare/cfssl/api/testdata/leaf.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDfDCCAwKgAwIBAgIIUYJhG37C300wCgYIKoZIzj0EAwMwgYwxCzAJBgNVBAYT
|
||||||
|
AlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJlMRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2lu
|
||||||
|
ZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9y
|
||||||
|
bmlhMR0wGwYDVQQDExRjbG91ZGZsYXJlLWludGVyLmNvbTAeFw0xNDA0MTEyMTIy
|
||||||
|
MzhaFw0xOTA0MTEyMTI3MzhaMIGLMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xv
|
||||||
|
dWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMN
|
||||||
|
U2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEcMBoGA1UEAxMTY2xv
|
||||||
|
dWRmbGFyZS1sZWFmLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||||
|
ANAukkrF37rHtiFfPuoUmKkj0HaEMGeQWEJ0sbispQKwqzF8fCRh6yT+j4+xsUZh
|
||||||
|
+NL/bvVcGb3K0DpvMlE7swBH9ind7r15lSiW9r3NAaKbH5aUhDixLnXU0h5R4HVR
|
||||||
|
8paqW8we2a5sl17lk7UxRGhpj1VJlnDgTsuqXP+4Wb+x5E42hy0Lv7qIYnPzYztK
|
||||||
|
CWmlMHbOprsEwjUpjL7b6D+dackCXrmY2CKFp57vvw27WWJh+OFWxw9FPrkjSi2W
|
||||||
|
uG3lqg6uWQ8ELxFZLi6i999WsM4CH9GuG4T/rnNZHVl2fFfvGgW6rauoOqPR3a84
|
||||||
|
DUUfvUtxLxHpQq8blsPRUN0CAwEAAaOBgTB/MA4GA1UdDwEB/wQEAwIApDASBgNV
|
||||||
|
HRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBShnoK2Oquaq/XjlNBMxs5yPTSJvjAf
|
||||||
|
BgNVHSMEGDAWgBRB+YoiUjIm34/wBwHdJGE4Wufs/DAZBgNVHREEEjAQgg5jZnNz
|
||||||
|
bC1sZWFmLmNvbTAKBggqhkjOPQQDAwNoADBlAjAhMWEJzBwuN5bVACPCAoVPSWI2
|
||||||
|
+0DQi4Tu6sBNQl+dsyO+FPyA3+aYc0NgnBwcj+0CMQC7JOdfdWJPZj6rOAXvGV3I
|
||||||
|
jGJRHZmu5q5K+9teIK1b9mustpnDJgniKAHtBGecXy4=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// Package auth implements an interface for providing CFSSL
|
||||||
|
// authentication. This is meant to authenticate a client CFSSL to a
|
||||||
|
// remote CFSSL in order to prevent unauthorised use of the signature
|
||||||
|
// capabilities. This package provides both the interface and a
|
||||||
|
// standard HMAC-based implementation.
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
)
|
||||||
|
|
||||||
|
// An AuthenticatedRequest contains a request and authentication
|
||||||
|
// token. The Provider may determine whether to validate the timestamp
|
||||||
|
// and remote address.
|
||||||
|
type AuthenticatedRequest struct {
|
||||||
|
// An Authenticator decides whether to use this field.
|
||||||
|
Timestamp int64 `json:"timestamp,omitempty"`
|
||||||
|
RemoteAddress []byte `json:"remote_address,omitempty"`
|
||||||
|
Token []byte `json:"token"`
|
||||||
|
Request []byte `json:"request"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Provider can generate tokens from a request and verify a
|
||||||
|
// request. The handling of additional authentication data (such as
|
||||||
|
// the IP address) is handled by the concrete type, as is any
|
||||||
|
// serialisation and state-keeping.
|
||||||
|
type Provider interface {
|
||||||
|
Token(req []byte) (token []byte, err error)
|
||||||
|
Verify(aReq *AuthenticatedRequest) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard implements an HMAC-SHA-256 authentication provider. It may
|
||||||
|
// be supplied additional data at creation time that will be used as
|
||||||
|
// request || additional-data with the HMAC.
|
||||||
|
type Standard struct {
|
||||||
|
key []byte
|
||||||
|
ad []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// New generates a new standard authentication provider from the key
|
||||||
|
// and additional data. The additional data will be used when
|
||||||
|
// generating a new token.
|
||||||
|
func New(key string, ad []byte) (*Standard, error) {
|
||||||
|
keyBytes, err := hex.DecodeString(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Standard{keyBytes, ad}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token generates a new authentication token from the request.
|
||||||
|
func (p Standard) Token(req []byte) (token []byte, err error) {
|
||||||
|
h := hmac.New(sha256.New, p.key)
|
||||||
|
h.Write(req)
|
||||||
|
h.Write(p.ad)
|
||||||
|
return h.Sum(nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify determines whether an authenticated request is valid.
|
||||||
|
func (p Standard) Verify(ad *AuthenticatedRequest) bool {
|
||||||
|
if ad == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard token generation returns no error.
|
||||||
|
token, _ := p.Token(ad.Request)
|
||||||
|
if len(ad.Token) != len(token) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return hmac.Equal(token, ad.Token)
|
||||||
|
}
|
||||||
159
Godeps/_workspace/src/github.com/cloudflare/cfssl/auth/auth_test.go
generated
vendored
Normal file
159
Godeps/_workspace/src/github.com/cloudflare/cfssl/auth/auth_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
testProvider Provider
|
||||||
|
testProviderAD Provider
|
||||||
|
testKey = "0123456789ABCDEF0123456789ABCDEF"
|
||||||
|
testAD = []byte{1, 2, 3, 4} // IP address 1.2.3.4
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
_, err := New("ABC", nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected failure with improperly-hex-encoded key")
|
||||||
|
}
|
||||||
|
|
||||||
|
testProvider, err = New(testKey, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testProviderAD, err = New(testKey, testAD)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
testRequest1A = &AuthenticatedRequest{
|
||||||
|
Request: []byte(`testing 1 2 3`),
|
||||||
|
}
|
||||||
|
testRequest1B = &AuthenticatedRequest{
|
||||||
|
Request: []byte(`testing 1 2 3`),
|
||||||
|
}
|
||||||
|
testRequest2 = &AuthenticatedRequest{
|
||||||
|
Request: []byte(`testing 3 2 1`),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sanity check: can a newly-generated token be verified?
|
||||||
|
func TestVerifyTrue(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
testRequest1A.Token, err = testProvider.Token(testRequest1A.Request)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testRequest1B.Token, err = testProviderAD.Token(testRequest1B.Request)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testProvider.Verify(testRequest1A) {
|
||||||
|
t.Fatal("failed to verify request 1A")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testProviderAD.Verify(testRequest1B) {
|
||||||
|
t.Fatal("failed to verify request 1B")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check: ensure that additional data is actually used in
|
||||||
|
// verification.
|
||||||
|
func TestVerifyAD(t *testing.T) {
|
||||||
|
if testProvider.Verify(testRequest1B) {
|
||||||
|
t.Fatal("no-AD provider verifies request with AD")
|
||||||
|
}
|
||||||
|
|
||||||
|
if testProviderAD.Verify(testRequest1A) {
|
||||||
|
t.Fatal("AD provider verifies request without AD")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check: verification fails if tokens are not the same length.
|
||||||
|
func TestTokenLength(t *testing.T) {
|
||||||
|
token := testRequest1A.Token[:]
|
||||||
|
testRequest1A.Token = testRequest1A.Token[1:]
|
||||||
|
|
||||||
|
if testProvider.Verify(testRequest1A) {
|
||||||
|
t.Fatal("invalid token should not be verified")
|
||||||
|
}
|
||||||
|
|
||||||
|
testRequest1A.Token = token
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check: token fails validation if the request is changed.
|
||||||
|
func TestBadRequest(t *testing.T) {
|
||||||
|
testRequest2.Token = testRequest1A.Token
|
||||||
|
if testProvider.Verify(testRequest2) {
|
||||||
|
t.Fatal("bad request should fail verification")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check: a null request should fail to verify.
|
||||||
|
func TestNullRequest(t *testing.T) {
|
||||||
|
if testProvider.Verify(nil) {
|
||||||
|
t.Fatal("null request should fail verification")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check: verify a pre-generated authenticated request.
|
||||||
|
func TestPreGenerated(t *testing.T) {
|
||||||
|
in, err := ioutil.ReadFile("testdata/authrequest.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var req AuthenticatedRequest
|
||||||
|
err = json.Unmarshal(in, &req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testProvider.Verify(&req) {
|
||||||
|
t.Fatal("failed to verify pre-generated request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var bmRequest []byte
|
||||||
|
|
||||||
|
func TestLoadBenchmarkRequest(t *testing.T) {
|
||||||
|
in, err := ioutil.ReadFile("testdata/request.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bmRequest = in
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkToken(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, err := testProvider.Token(bmRequest)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkVerify(b *testing.B) {
|
||||||
|
token, _ := testProvider.Token(bmRequest)
|
||||||
|
req := &AuthenticatedRequest{
|
||||||
|
Token: token,
|
||||||
|
Request: bmRequest,
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if !testProvider.Verify(req) {
|
||||||
|
b.Fatal("failed to verify request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
Godeps/_workspace/src/github.com/cloudflare/cfssl/auth/testdata/authrequest.json
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/cloudflare/cfssl/auth/testdata/authrequest.json
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{"token": "tSU1WTE/322iXrOBfJSQ9/u1dleqpwUmCj1LXYHw07Y=", "request": "ewoJImhvc3RuYW1lIjogImt5bGVpc29tLm5ldCIsCgkicmVxdWVzdCI6ICItLS0tLUJFR0lOIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQoJICAgIE1JSUQwVENDQWpzQ0FRQXdZREVMTUFrR0ExVUVCaE1DVlZNeEVqQVFCZ05WQkFvVENXUnliM0J6YjI1a1pURVEKCSAgICBNQTRHQTFVRUN4TUhRMFl0UTJoaGRERVdNQlFHQTFVRUJ4TU5VMkZ1SUVaeVlXNWphWE5qYnpFVE1CRUdBMVVFCgkgICAgQ0JNS1EyRnNhV1p2Y201cFlUQ0NBYUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0dQQURDQ0FZb0NnZ0dCQU1jQwoJICAgIEdCbDVMVHJla0dGV2hvdGtkYlorUjFNbG9hcld4UXY5alA0QWVrdDhVT2ljeXBIdkZPNnhPdFN3SG8rcjMyaUUKCSAgICBxblM1eXYvMDFQMk1KdXlxbmRuY1RTTXNPbFQvN242N1RNMDB1MDFLLzljL3NvZ0tFS2pseXBsVFA3eUZkRy9jCgkgICAgT3UvOXFLYi9KYWxkMndFTEZZRTZ4cTJSREZ5eHlpWk9CM2c3WjdGeGE1ZDZhZGZHUndaek50VUw0LzhzK0x5aQoJICAgIHFkdzlJMWZrUWQ2MDRwb1pGTjB3clFzNGxmaFdUVWZnMHJIdWg1d2dHS1AzVnpacGJ0OEZiMXZOamZiSHRvaHgKCSAgICBHMlBDVTZKeStEYzFiU2ZVeldjUW5lbnA4NThXNEY4ejdwRjV5YmRuRlIzMTNIam9zcVhuRzI4eklUck9hZE1UCgkgICAgSGFKNnpPaGdFYWZVT1dYT3pqTm9mRkJGYTJJdUNBVCtJVFJZMXRDL2dxcHhHd0gveXVWTjE5Qkc4VXBuMCtIQQoJICAgIGllMm1LQ0hmU0JBS1QvWGU0dW1QZWF4U2JJcVdzVzhjaytkM2I0b3I5Ulp2NWNaUmNUM29pa0p0K1NRRzY5cFcKCSAgICA0T0FiYitBQnNzL05JdXJpNnowZTdERWVJTDV6bXlTSnFkdFlIZE5ZTjcrK3Y5eEJOc0w0SXNVNklFeTMrUUlECgkgICAgQVFBQm9DNHdMQVlKS29aSWh2Y05BUWtPTVI4d0hUQWJCZ05WSFJFRUZEQVNnaEJqWmk1a2NtOXdjMjl1WkdVdQoJICAgIGJtVjBNQXNHQ1NxR1NJYjNEUUVCREFPQ0FZRUFoTUFxQmlySStrMWFVM2xmQUdRaVNtOHl0T3paaWozODloSXIKCSAgICBuVXA4K1duVHVWVGI4WFozL1YrTDlFblRJbUY2dTF3ZWFqWGQzU3VlNDk1NzBMYlltSXV4QmtHcDUwL0JkVUR6CgkgICAgdUI2eHNoaEpXczEySnhVYjkxSW1tMGJUUncyek1xZXdnYTZmdHpaL0FLNG1zeFFBMlVJYmNXWmRzS2J1TTdzbwoJICAgIEpUZlZXOWlPd3FIdC82NFpqNHRCWmY5THpPRHI3a051S0tMbndqaXpIMTg3eGZJSWhkcmpGOFdTN0g5QVBCMU8KCSAgICBTdUVVRGZxaDBTV1IzbHRXdUF1VVdlbzZTS2NIVnVzeS9HNFlFK1BCeXcxZVY3RzRTYmVHNVowbytHT1VVSy9GCgkgICAgYjU1R21XMXhhNExBcnMxQSt6ZUZidkovQkFwc2JVMmI2V1ZtTmE3V3BIejdXWElGT0p1WUpnRWtWS1BKbkt1cwoJICAgIHFxczNGZ1VxejBadjdUSzhtTWlFVEpvWFpzNnpDdk15c1FldTNKL29qZ3RBanZNaHpRYzZQUy9udk90SmRJZysKCSAgICBIMHFYNDlmaHAxQnJZeXNsYWx6UUlGMCtIMHFTVWV5b1V5VjJ3YkxCQUxhcHhNZnZUVmxoTnduYWN0Y0tReHE0CgkgICAgK3dUKzJQVEowYk0vNUFWMFRPMVNQVDBBVmlKaAoJICAgIC0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLSIsCgkicHJvZmlsZSI6ICIiLAoJInJlbW90ZSI6ICIiLAoJImxhYmVsIjogInByaW1hcnkiCn0KCg=="}
|
||||||
30
Godeps/_workspace/src/github.com/cloudflare/cfssl/auth/testdata/request.json
generated
vendored
Normal file
30
Godeps/_workspace/src/github.com/cloudflare/cfssl/auth/testdata/request.json
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"hostname": "kyleisom.net",
|
||||||
|
"request": "-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIID0TCCAjsCAQAwYDELMAkGA1UEBhMCVVMxEjAQBgNVBAoTCWRyb3Bzb25kZTEQ
|
||||||
|
MA4GA1UECxMHQ0YtQ2hhdDEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UE
|
||||||
|
CBMKQ2FsaWZvcm5pYTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAMcC
|
||||||
|
GBl5LTrekGFWhotkdbZ+R1MloarWxQv9jP4Aekt8UOicypHvFO6xOtSwHo+r32iE
|
||||||
|
qnS5yv/01P2MJuyqndncTSMsOlT/7n67TM00u01K/9c/sogKEKjlyplTP7yFdG/c
|
||||||
|
Ou/9qKb/Jald2wELFYE6xq2RDFyxyiZOB3g7Z7Fxa5d6adfGRwZzNtUL4/8s+Lyi
|
||||||
|
qdw9I1fkQd604poZFN0wrQs4lfhWTUfg0rHuh5wgGKP3VzZpbt8Fb1vNjfbHtohx
|
||||||
|
G2PCU6Jy+Dc1bSfUzWcQnenp858W4F8z7pF5ybdnFR313HjosqXnG28zITrOadMT
|
||||||
|
HaJ6zOhgEafUOWXOzjNofFBFa2IuCAT+ITRY1tC/gqpxGwH/yuVN19BG8Upn0+HA
|
||||||
|
ie2mKCHfSBAKT/Xe4umPeaxSbIqWsW8ck+d3b4or9RZv5cZRcT3oikJt+SQG69pW
|
||||||
|
4OAbb+ABss/NIuri6z0e7DEeIL5zmySJqdtYHdNYN7++v9xBNsL4IsU6IEy3+QID
|
||||||
|
AQABoC4wLAYJKoZIhvcNAQkOMR8wHTAbBgNVHREEFDASghBjZi5kcm9wc29uZGUu
|
||||||
|
bmV0MAsGCSqGSIb3DQEBDAOCAYEAhMAqBirI+k1aU3lfAGQiSm8ytOzZij389hIr
|
||||||
|
nUp8+WnTuVTb8XZ3/V+L9EnTImF6u1weajXd3Sue49570LbYmIuxBkGp50/BdUDz
|
||||||
|
uB6xshhJWs12JxUb91Imm0bTRw2zMqewga6ftzZ/AK4msxQA2UIbcWZdsKbuM7so
|
||||||
|
JTfVW9iOwqHt/64Zj4tBZf9LzODr7kNuKKLnwjizH187xfIIhdrjF8WS7H9APB1O
|
||||||
|
SuEUDfqh0SWR3ltWuAuUWeo6SKcHVusy/G4YE+PByw1eV7G4SbeG5Z0o+GOUUK/F
|
||||||
|
b55GmW1xa4LArs1A+zeFbvJ/BApsbU2b6WVmNa7WpHz7WXIFOJuYJgEkVKPJnKus
|
||||||
|
qqs3FgUqz0Zv7TK8mMiETJoXZs6zCvMysQeu3J/ojgtAjvMhzQc6PS/nvOtJdIg+
|
||||||
|
H0qX49fhp1BrYyslalzQIF0+H0qSUeyoUyV2wbLBALapxMfvTVlhNwnactcKQxq4
|
||||||
|
+wT+2PTJ0bM/5AV0TO1SPT0AViJh
|
||||||
|
-----END CERTIFICATE REQUEST-----",
|
||||||
|
"profile": "",
|
||||||
|
"remote": "",
|
||||||
|
"label": "primary"
|
||||||
|
}
|
||||||
|
|
||||||
419
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/config.go
generated
vendored
Normal file
419
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/config.go
generated
vendored
Normal file
|
|
@ -0,0 +1,419 @@
|
||||||
|
// Package config contains the configuration logic for CF-SSL.
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/asn1"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/auth"
|
||||||
|
cferr "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A SigningProfile stores information that the CA needs to store
|
||||||
|
// signature policy.
|
||||||
|
type SigningProfile struct {
|
||||||
|
Usage []string `json:"usages"`
|
||||||
|
IssuerURL []string `json:"issuer_urls"`
|
||||||
|
OCSP string `json:"ocsp_url"`
|
||||||
|
CRL string `json:"crl_url"`
|
||||||
|
CA bool `json:"is_ca"`
|
||||||
|
PolicyStrings []string `json:"policies"`
|
||||||
|
OCSPNoCheck bool `json:"ocsp_no_check"`
|
||||||
|
ExpiryString string `json:"expiry"`
|
||||||
|
BackdateString string `json:"backdate"`
|
||||||
|
AuthKeyName string `json:"auth_key"`
|
||||||
|
RemoteName string `json:"remote"`
|
||||||
|
NotBefore time.Time `json:"not_before"`
|
||||||
|
NotAfter time.Time `json:"not_after"`
|
||||||
|
|
||||||
|
Policies []asn1.ObjectIdentifier
|
||||||
|
Expiry time.Duration
|
||||||
|
Backdate time.Duration
|
||||||
|
Provider auth.Provider
|
||||||
|
RemoteServer string
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseObjectIdentifier(oidString string) (oid asn1.ObjectIdentifier, err error) {
|
||||||
|
validOID, err := regexp.MatchString("\\d(\\.\\d+)*", oidString)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !validOID {
|
||||||
|
err = errors.New("Invalid OID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
segments := strings.Split(oidString, ".")
|
||||||
|
oid = make(asn1.ObjectIdentifier, len(segments))
|
||||||
|
for i, intString := range segments {
|
||||||
|
oid[i], err = strconv.Atoi(intString)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeFormat = "2006-01-02T15:04:05"
|
||||||
|
|
||||||
|
// populate is used to fill in the fields that are not in JSON
|
||||||
|
//
|
||||||
|
// First, the ExpiryString parameter is needed to parse
|
||||||
|
// expiration timestamps from JSON. The JSON decoder is not able to
|
||||||
|
// decode a string time duration to a time.Duration, so this is called
|
||||||
|
// when loading the configuration to properly parse and fill out the
|
||||||
|
// Expiry parameter.
|
||||||
|
// This function is also used to create references to the auth key
|
||||||
|
// and default remote for the profile.
|
||||||
|
// It returns true if ExpiryString is a valid representation of a
|
||||||
|
// time.Duration, and the AuthKeyString and RemoteName point to
|
||||||
|
// valid objects. It returns false otherwise.
|
||||||
|
func (p *SigningProfile) populate(cfg *Config) error {
|
||||||
|
if p == nil {
|
||||||
|
return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("can't parse nil profile"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if p.RemoteName == "" {
|
||||||
|
log.Debugf("parse expiry in profile")
|
||||||
|
if p.ExpiryString == "" {
|
||||||
|
return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("empty expiry string"))
|
||||||
|
}
|
||||||
|
|
||||||
|
dur, err := time.ParseDuration(p.ExpiryString)
|
||||||
|
if err != nil {
|
||||||
|
return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("expiry is valid")
|
||||||
|
p.Expiry = dur
|
||||||
|
|
||||||
|
if p.BackdateString != "" {
|
||||||
|
dur, err = time.ParseDuration(p.BackdateString)
|
||||||
|
if err != nil {
|
||||||
|
return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Backdate = dur
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.NotBefore.IsZero() && !p.NotAfter.IsZero() && p.NotAfter.Before(p.NotBefore) {
|
||||||
|
return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(p.PolicyStrings) > 0 {
|
||||||
|
p.Policies = make([]asn1.ObjectIdentifier, len(p.PolicyStrings))
|
||||||
|
for i, oidString := range p.PolicyStrings {
|
||||||
|
p.Policies[i], err = parseObjectIdentifier(oidString)
|
||||||
|
if err != nil {
|
||||||
|
return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Debug("match remote in profile to remotes section")
|
||||||
|
if remote := cfg.Remotes[p.RemoteName]; remote != "" {
|
||||||
|
if err := p.updateRemote(remote); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
|
||||||
|
errors.New("failed to find remote in remotes section"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.AuthKeyName != "" {
|
||||||
|
log.Debug("match auth key in profile to auth_keys section")
|
||||||
|
if key, ok := cfg.AuthKeys[p.AuthKeyName]; ok == true {
|
||||||
|
if key.Type == "standard" {
|
||||||
|
p.Provider, err = auth.New(key.Key, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("failed to create new standard auth provider: %v", err)
|
||||||
|
return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
|
||||||
|
errors.New("failed to create new standard auth provider"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Debugf("unknown authentication type %v", key.Type)
|
||||||
|
return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
|
||||||
|
errors.New("unknown authentication type"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
|
||||||
|
errors.New("failed to find auth_key in auth_keys section"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateRemote takes a signing profile and initializes the remote server object
|
||||||
|
// to the hostname:port combination sent by remote
|
||||||
|
func (p *SigningProfile) updateRemote(remote string) error {
|
||||||
|
if remote != "" {
|
||||||
|
p.RemoteServer = remote
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OverrideRemotes takes a signing configuration and updates the remote server object
|
||||||
|
// to the hostname:port combination sent by remote
|
||||||
|
func (p *Signing) OverrideRemotes(remote string) error {
|
||||||
|
if remote != "" {
|
||||||
|
var err error
|
||||||
|
for _, profile := range p.Profiles {
|
||||||
|
err = profile.updateRemote(remote)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = p.Default.updateRemote(remote)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NeedsRemoteSigner returns true if one of the profiles has a remote set
|
||||||
|
func (p *Signing) NeedsRemoteSigner() bool {
|
||||||
|
for _, profile := range p.Profiles {
|
||||||
|
if profile.RemoteName != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.Default.RemoteName != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// NeedsLocalSigner returns true if one of the profiles doe not have a remote set
|
||||||
|
func (p *Signing) NeedsLocalSigner() bool {
|
||||||
|
for _, profile := range p.Profiles {
|
||||||
|
if profile.RemoteName == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.Default.RemoteName == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Usages parses the list of key uses in the profile, translating them
|
||||||
|
// to a list of X.509 key usages and extended key usages. The unknown
|
||||||
|
// uses are collected into a slice that is also returned.
|
||||||
|
func (p *SigningProfile) Usages() (ku x509.KeyUsage, eku []x509.ExtKeyUsage, unk []string) {
|
||||||
|
for _, keyUse := range p.Usage {
|
||||||
|
if kuse, ok := KeyUsage[keyUse]; ok {
|
||||||
|
ku |= kuse
|
||||||
|
} else if ekuse, ok := ExtKeyUsage[keyUse]; ok {
|
||||||
|
eku = append(eku, ekuse)
|
||||||
|
} else {
|
||||||
|
unk = append(unk, keyUse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// A valid profile must be a valid local profile or a valid remote profile.
|
||||||
|
// A valid local profile has defined at least key usages to be used, and a
|
||||||
|
// valid local default profile has defined at least a default expiration.
|
||||||
|
// A valid remote profile (default or not) has remote signer initialized.
|
||||||
|
// In addition, a remote profile must has a valid auth provider if auth
|
||||||
|
// key defined.
|
||||||
|
func (p *SigningProfile) validProfile(isDefault bool) bool {
|
||||||
|
if p == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.RemoteName != "" {
|
||||||
|
log.Debugf("validate remote profile")
|
||||||
|
|
||||||
|
if p.RemoteServer == "" {
|
||||||
|
log.Debugf("invalid remote profile: no remote signer specified")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.AuthKeyName != "" && p.Provider == nil {
|
||||||
|
log.Debugf("invalid remote profile: auth key name is defined but no auth provider is set")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Debugf("validate local profile")
|
||||||
|
if !isDefault {
|
||||||
|
if len(p.Usage) == 0 {
|
||||||
|
log.Debugf("invalid local profile: no usages specified")
|
||||||
|
return false
|
||||||
|
} else if _, _, unk := p.Usages(); len(unk) == len(p.Usage) {
|
||||||
|
log.Debugf("invalid local profile: no valid usages")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if p.Expiry == 0 {
|
||||||
|
log.Debugf("invalid local profile: no expiry set")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("profile is valid")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signing codifies the signature configuration policy for a CA.
|
||||||
|
type Signing struct {
|
||||||
|
Profiles map[string]*SigningProfile `json:"profiles"`
|
||||||
|
Default *SigningProfile `json:"default"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config stores configuration information for the CA.
|
||||||
|
type Config struct {
|
||||||
|
Signing *Signing `json:"signing"`
|
||||||
|
AuthKeys map[string]AuthKey `json:"auth_keys,omitempty"`
|
||||||
|
Remotes map[string]string `json:"remotes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid ensures that Config is a valid configuration. It should be
|
||||||
|
// called immediately after parsing a configuration file.
|
||||||
|
func (c *Config) Valid() bool {
|
||||||
|
return c.Signing.Valid()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid checks the signature policies, ensuring they are valid
|
||||||
|
// policies. A policy is valid if it has defined at least key usages
|
||||||
|
// to be used, and a valid default profile has defined at least a
|
||||||
|
// default expiration.
|
||||||
|
func (p *Signing) Valid() bool {
|
||||||
|
if p == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("validating configuration")
|
||||||
|
if !p.Default.validProfile(true) {
|
||||||
|
log.Debugf("default profile is invalid")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sp := range p.Profiles {
|
||||||
|
if !sp.validProfile(false) {
|
||||||
|
log.Debugf("invalid profile")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyUsage contains a mapping of string names to key usages.
|
||||||
|
var KeyUsage = map[string]x509.KeyUsage{
|
||||||
|
"signing": x509.KeyUsageDigitalSignature,
|
||||||
|
"digital signature": x509.KeyUsageDigitalSignature,
|
||||||
|
"content committment": x509.KeyUsageContentCommitment,
|
||||||
|
"key encipherment": x509.KeyUsageKeyEncipherment,
|
||||||
|
"data encipherment": x509.KeyUsageDataEncipherment,
|
||||||
|
"cert sign": x509.KeyUsageCertSign,
|
||||||
|
"crl sign": x509.KeyUsageCRLSign,
|
||||||
|
"encipher only": x509.KeyUsageEncipherOnly,
|
||||||
|
"decipher only": x509.KeyUsageDecipherOnly,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtKeyUsage contains a mapping of string names to extended key
|
||||||
|
// usages.
|
||||||
|
var ExtKeyUsage = map[string]x509.ExtKeyUsage{
|
||||||
|
"any": x509.ExtKeyUsageAny,
|
||||||
|
"server auth": x509.ExtKeyUsageServerAuth,
|
||||||
|
"client auth": x509.ExtKeyUsageClientAuth,
|
||||||
|
"code signing": x509.ExtKeyUsageCodeSigning,
|
||||||
|
"email protection": x509.ExtKeyUsageEmailProtection,
|
||||||
|
"s/mime": x509.ExtKeyUsageEmailProtection,
|
||||||
|
"ipsec end system": x509.ExtKeyUsageIPSECEndSystem,
|
||||||
|
"ipsec tunnel": x509.ExtKeyUsageIPSECTunnel,
|
||||||
|
"ipsec user": x509.ExtKeyUsageIPSECUser,
|
||||||
|
"timestamping": x509.ExtKeyUsageTimeStamping,
|
||||||
|
"ocsp signing": x509.ExtKeyUsageOCSPSigning,
|
||||||
|
"microsoft sgc": x509.ExtKeyUsageMicrosoftServerGatedCrypto,
|
||||||
|
"netscape sgc": x509.ExtKeyUsageNetscapeServerGatedCrypto,
|
||||||
|
}
|
||||||
|
|
||||||
|
// An AuthKey contains an entry for a key used for authentication.
|
||||||
|
type AuthKey struct {
|
||||||
|
// Type contains information needed to select the appropriate
|
||||||
|
// constructor. For example, "standard" for HMAC-SHA-256,
|
||||||
|
// "standard-ip" for HMAC-SHA-256 incorporating the client's
|
||||||
|
// IP.
|
||||||
|
Type string `json:"type"`
|
||||||
|
// Key contains the key information, such as a hex-encoded
|
||||||
|
// HMAC key.
|
||||||
|
Key string `json:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultConfig returns a default configuration specifying basic key
|
||||||
|
// usage and a 1 year expiration time. The key usages chosen are
|
||||||
|
// signing, key encipherment, client auth and server auth.
|
||||||
|
func DefaultConfig() *SigningProfile {
|
||||||
|
d := helpers.OneYear
|
||||||
|
return &SigningProfile{
|
||||||
|
Usage: []string{"signing", "key encipherment", "server auth", "client auth"},
|
||||||
|
Expiry: d,
|
||||||
|
ExpiryString: "8760h",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadFile attempts to load the configuration file stored at the path
|
||||||
|
// and returns the configuration. On error, it returns nil.
|
||||||
|
func LoadFile(path string) (*Config, error) {
|
||||||
|
log.Debugf("loading configuration file from %s", path)
|
||||||
|
if path == "" {
|
||||||
|
return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid path"))
|
||||||
|
}
|
||||||
|
|
||||||
|
body, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("could not read configuration file"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return LoadConfig(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfig attempts to load the configuration from a byte slice.
|
||||||
|
// On error, it returns nil.
|
||||||
|
func LoadConfig(config []byte) (*Config, error) {
|
||||||
|
var cfg = &Config{}
|
||||||
|
err := json.Unmarshal(config, &cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("failed to unmarshal configuration"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Signing.Default == nil {
|
||||||
|
log.Debugf("no default given: using default config")
|
||||||
|
cfg.Signing.Default = DefaultConfig()
|
||||||
|
} else {
|
||||||
|
if err := cfg.Signing.Default.populate(cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := range cfg.Signing.Profiles {
|
||||||
|
if err := cfg.Signing.Profiles[k].populate(cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cfg.Valid() {
|
||||||
|
return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid configuration"))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("configuration ok")
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
337
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/config_test.go
generated
vendored
Normal file
337
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/config_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,337 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var expiry = 1 * time.Minute
|
||||||
|
|
||||||
|
var invalidProfileConfig = &Config{
|
||||||
|
Signing: &Signing{
|
||||||
|
Profiles: map[string]*SigningProfile{
|
||||||
|
"invalid": {
|
||||||
|
Usage: []string{"wiretapping"},
|
||||||
|
Expiry: expiry,
|
||||||
|
},
|
||||||
|
"empty": {},
|
||||||
|
},
|
||||||
|
Default: &SigningProfile{
|
||||||
|
Usage: []string{"digital signature"},
|
||||||
|
Expiry: expiry,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidDefaultConfig = &Config{
|
||||||
|
Signing: &Signing{
|
||||||
|
Profiles: map[string]*SigningProfile{
|
||||||
|
"key usage": {
|
||||||
|
Usage: []string{"digital signature"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Default: &SigningProfile{
|
||||||
|
Usage: []string{"s/mime"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var validConfig = &Config{
|
||||||
|
Signing: &Signing{
|
||||||
|
Profiles: map[string]*SigningProfile{
|
||||||
|
"valid": {
|
||||||
|
Usage: []string{"digital signature"},
|
||||||
|
Expiry: expiry,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Default: &SigningProfile{
|
||||||
|
Usage: []string{"digital signature"},
|
||||||
|
Expiry: expiry,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var validMixedConfig = `
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"CA": {
|
||||||
|
"auth_key": "sample",
|
||||||
|
"remote": "localhost"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"usages": ["s/mime"],
|
||||||
|
"expiry": "720h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
"expiry": "8000h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth_keys": {
|
||||||
|
"sample": {
|
||||||
|
"type":"standard",
|
||||||
|
"key":"0123456789ABCDEF0123456789ABCDEF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remotes": {
|
||||||
|
"localhost": "127.0.0.1:8888"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
var validMinimalRemoteConfig = `
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"default": {
|
||||||
|
"auth_key": "sample",
|
||||||
|
"remote": "localhost"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth_keys": {
|
||||||
|
"sample": {
|
||||||
|
"type":"standard",
|
||||||
|
"key":"0123456789ABCDEF0123456789ABCDEF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remotes": {
|
||||||
|
"localhost": "127.0.0.1:8888"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
var validMinimalLocalConfig = `
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
"expiry": "8000h"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
func TestInvalidProfile(t *testing.T) {
|
||||||
|
if invalidProfileConfig.Signing.Profiles["invalid"].validProfile(false) {
|
||||||
|
t.Fatal("invalid profile accepted as valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
if invalidProfileConfig.Signing.Profiles["empty"].validProfile(false) {
|
||||||
|
t.Fatal("invalid profile accepted as valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
if invalidProfileConfig.Valid() {
|
||||||
|
t.Fatal("invalid config accepted as valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !invalidProfileConfig.Signing.Profiles["invalid"].validProfile(true) {
|
||||||
|
t.Fatal("invalid profile should be a valid default profile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoteProfiles(t *testing.T) {
|
||||||
|
var validRemoteProfile = &SigningProfile{
|
||||||
|
RemoteName: "localhost",
|
||||||
|
RemoteServer: "localhost:8080",
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidRemoteProfile = &SigningProfile{
|
||||||
|
RemoteName: "localhost",
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidRemoteAuthProfile = &SigningProfile{
|
||||||
|
RemoteName: "localhost",
|
||||||
|
RemoteServer: "localhost:8080",
|
||||||
|
AuthKeyName: "blahblah",
|
||||||
|
}
|
||||||
|
|
||||||
|
if !validRemoteProfile.validProfile(true) ||
|
||||||
|
!validRemoteProfile.validProfile(false) {
|
||||||
|
t.Fatal("valid remote profile is rejected.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if invalidRemoteProfile.validProfile(true) ||
|
||||||
|
invalidRemoteProfile.validProfile(false) {
|
||||||
|
t.Fatal("invalid remote profile is accepted.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if invalidRemoteAuthProfile.validProfile(true) ||
|
||||||
|
invalidRemoteAuthProfile.validProfile(false) {
|
||||||
|
t.Fatal("invalid remote profile is accepted.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidDefault(t *testing.T) {
|
||||||
|
if invalidDefaultConfig.Signing.Default.validProfile(true) {
|
||||||
|
t.Fatal("invalid default accepted as valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
if invalidDefaultConfig.Valid() {
|
||||||
|
t.Fatal("invalid config accepted as valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !invalidDefaultConfig.Signing.Default.validProfile(false) {
|
||||||
|
t.Fatal("invalid default profile should be a valid profile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidConfig(t *testing.T) {
|
||||||
|
if !validConfig.Valid() {
|
||||||
|
t.Fatal("Valid config is not valid")
|
||||||
|
}
|
||||||
|
bytes, _ := json.Marshal(validConfig)
|
||||||
|
fmt.Printf("%v", string(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultConfig(t *testing.T) {
|
||||||
|
if !DefaultConfig().validProfile(false) {
|
||||||
|
t.Fatal("global default signing profile should be a valid profile.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !DefaultConfig().validProfile(true) {
|
||||||
|
t.Fatal("global default signing profile should be a valid default profile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParse(t *testing.T) {
|
||||||
|
var validProfiles = []*SigningProfile{
|
||||||
|
{
|
||||||
|
ExpiryString: "8760h",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ExpiryString: "168h",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ExpiryString: "300s",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidProfiles = []*SigningProfile{
|
||||||
|
nil,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
ExpiryString: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ExpiryString: "365d",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ExpiryString: "1y",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ExpiryString: "one year",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range validProfiles {
|
||||||
|
if p.populate(nil) != nil {
|
||||||
|
t.Fatalf("Failed to parse ExpiryString=%s", p.ExpiryString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range invalidProfiles {
|
||||||
|
if p.populate(nil) == nil {
|
||||||
|
if p != nil {
|
||||||
|
t.Fatalf("ExpiryString=%s should not be parseable", p.ExpiryString)
|
||||||
|
}
|
||||||
|
t.Fatalf("Nil profile should not be parseable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadFile(t *testing.T) {
|
||||||
|
validConfigFiles := []string{
|
||||||
|
"testdata/valid_config.json",
|
||||||
|
"testdata/valid_config_auth.json",
|
||||||
|
"testdata/valid_config_no_default.json",
|
||||||
|
"testdata/valid_config_auth_no_default.json",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, configFile := range validConfigFiles {
|
||||||
|
_, err := LoadFile(configFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Load valid config file failed.", configFile, "error is ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoadInvalidConfigFile(t *testing.T) {
|
||||||
|
invalidConfigFiles := []string{"", "testdata/no_such_file",
|
||||||
|
"testdata/invalid_default.json",
|
||||||
|
"testdata/invalid_profiles.json",
|
||||||
|
"testdata/invalid_usage.json",
|
||||||
|
"testdata/invalid_config.json",
|
||||||
|
"testdata/invalid_auth.json",
|
||||||
|
"testdata/invalid_auth_bad_key.json",
|
||||||
|
"testdata/invalid_no_auth_keys.json",
|
||||||
|
"testdata/invalid_remote.json",
|
||||||
|
"testdata/invalid_no_remotes.json",
|
||||||
|
}
|
||||||
|
for _, configFile := range invalidConfigFiles {
|
||||||
|
_, err := LoadFile(configFile)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Invalid config is loaded.", configFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNeedLocalSigner(t *testing.T) {
|
||||||
|
|
||||||
|
c, err := LoadConfig([]byte(validMixedConfig))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("load valid config failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This signing config needs both local signer and remote signer.
|
||||||
|
if c.Signing.NeedsLocalSigner() != true {
|
||||||
|
t.Fatal("incorrect NeedsLocalSigner().")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Signing.NeedsRemoteSigner() != true {
|
||||||
|
t.Fatal("incorrect NeedsRemoteSigner()")
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteConfig, err := LoadConfig([]byte(validMinimalRemoteConfig))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Load valid config failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if remoteConfig.Signing.NeedsLocalSigner() != false {
|
||||||
|
t.Fatal("incorrect NeedsLocalSigner().")
|
||||||
|
}
|
||||||
|
|
||||||
|
if remoteConfig.Signing.NeedsRemoteSigner() != true {
|
||||||
|
t.Fatal("incorrect NeedsRemoteSigner().")
|
||||||
|
}
|
||||||
|
|
||||||
|
localConfig, err := LoadConfig([]byte(validMinimalLocalConfig))
|
||||||
|
if localConfig.Signing.NeedsLocalSigner() != true {
|
||||||
|
t.Fatal("incorrect NeedsLocalSigner().")
|
||||||
|
}
|
||||||
|
|
||||||
|
if localConfig.Signing.NeedsRemoteSigner() != false {
|
||||||
|
t.Fatal("incorrect NeedsRemoteSigner().")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOverrideRemotes(t *testing.T) {
|
||||||
|
c, err := LoadConfig([]byte(validMixedConfig))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("load valid config failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
host := "localhost:8888"
|
||||||
|
c.Signing.OverrideRemotes(host)
|
||||||
|
|
||||||
|
if c.Signing.Default.RemoteServer != host {
|
||||||
|
t.Fatal("should override default profile's RemoteServer")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range c.Signing.Profiles {
|
||||||
|
if p.RemoteServer != host {
|
||||||
|
t.Fatal("failed to override profile's RemoteServer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_auth.json
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_auth.json
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"CA": {
|
||||||
|
"remote": "localhost",
|
||||||
|
"auth_key": "garbage"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"usages": ["s/mime"],
|
||||||
|
"expiry": "720h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
"expiry": "8000h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth_keys": {
|
||||||
|
"garbage": {
|
||||||
|
"type":"stadardo",
|
||||||
|
"key":"0123456789ABCDEF0123456789ABCDEF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remotes": {
|
||||||
|
"localhost": "127.0.0.1:8888"
|
||||||
|
}
|
||||||
|
}
|
||||||
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_auth_bad_key.json
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_auth_bad_key.json
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"CA": {
|
||||||
|
"remote": "localhost",
|
||||||
|
"auth_key": "garbage"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"usages": ["s/mime"],
|
||||||
|
"expiry": "720h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
"expiry": "8000h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth_keys": {
|
||||||
|
"garbage": {
|
||||||
|
"type":"standard",
|
||||||
|
"key":"BAD_KEY"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remotes": {
|
||||||
|
"localhost": "127.0.0.1:8888"
|
||||||
|
}
|
||||||
|
}
|
||||||
17
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_config.json
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_config.json
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"CA": {
|
||||||
|
"usages": ["cert sign"],
|
||||||
|
"expiry": "720h"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"usages": ["s/mime"],
|
||||||
|
"expiry": "720h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_default.json
generated
vendored
Normal file
18
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_default.json
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"CA": {
|
||||||
|
"usages": ["cert sign"],
|
||||||
|
"expiry": "720h"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"usages": ["s/mime"],
|
||||||
|
"expiry": "720h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
"expiry": "invalid_expiry"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_no_auth_keys.json
generated
vendored
Normal file
23
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_no_auth_keys.json
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"CA": {
|
||||||
|
"remote": "localhost",
|
||||||
|
"auth_key": "garbage"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"usages": ["s/mime"],
|
||||||
|
"expiry": "720h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
"expiry": "8000h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth_keys": {
|
||||||
|
},
|
||||||
|
"remotes": {
|
||||||
|
"localhost": "127.0.0.1:8888"
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_no_remotes.json
generated
vendored
Normal file
24
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_no_remotes.json
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"CA": {
|
||||||
|
"auth_key": "garbage",
|
||||||
|
"remote": "localhoster"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"usages": ["s/mime"],
|
||||||
|
"expiry": "720h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
"expiry": "8000h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth_keys": {
|
||||||
|
"garbage": {
|
||||||
|
"type":"standard",
|
||||||
|
"key":"0123456789ABCDEF0123456789ABCDEF"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_profile.json
generated
vendored
Normal file
18
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_profile.json
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"CA": {
|
||||||
|
"usages": ["cert sign"],
|
||||||
|
"expiry": "720h"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"usages": ["s/mime"],
|
||||||
|
"expiry": "invalid_expiry"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
"expiry": "8000h"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_remotes.json
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_remotes.json
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"CA": {
|
||||||
|
"auth_key": "garbage",
|
||||||
|
"remote": "localhoster"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"usages": ["s/mime"],
|
||||||
|
"expiry": "720h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
"expiry": "8000h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth_keys": {
|
||||||
|
"garbage": {
|
||||||
|
"type":"standard",
|
||||||
|
"key":"0123456789ABCDEF0123456789ABCDEF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remotes": {
|
||||||
|
"localhost": "127.0.0.1:8888"
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_usage.json
generated
vendored
Normal file
18
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/invalid_usage.json
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"CA": {
|
||||||
|
"usages": ["cert sign"],
|
||||||
|
"expiry": "720h"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"usages": ["BAD_USAGE"],
|
||||||
|
"expiry": "720h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
"expiry": "8000h"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/valid_config.json
generated
vendored
Normal file
24
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/valid_config.json
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"CA": {
|
||||||
|
"usages": ["cert sign"],
|
||||||
|
"expiry": "720h"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"usages": ["s/mime"],
|
||||||
|
"expiry": "720h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
"expiry": "8000h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth_key": {
|
||||||
|
"garbage": {
|
||||||
|
"type":"standard",
|
||||||
|
"key":"0123456789ABCDEF0123456789ABCDEF"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/valid_config_auth.json
generated
vendored
Normal file
29
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/valid_config_auth.json
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"CA": {
|
||||||
|
"usages": ["cert sign"],
|
||||||
|
"expiry": "720h",
|
||||||
|
"auth_key": "garbage",
|
||||||
|
"remote": "localhost"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"usages": ["s/mime"],
|
||||||
|
"expiry": "720h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"usages": ["digital signature", "email protection"],
|
||||||
|
"expiry": "8000h"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth_keys": {
|
||||||
|
"garbage": {
|
||||||
|
"type":"standard",
|
||||||
|
"key":"0123456789ABCDEF0123456789ABCDEF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remotes": {
|
||||||
|
"localhost": "127.0.0.1:8888"
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/valid_config_auth_no_default.json
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/valid_config_auth_no_default.json
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"CA": {
|
||||||
|
"auth_key": "garbage",
|
||||||
|
"remote": "localhost"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth_keys": {
|
||||||
|
"garbage": {
|
||||||
|
"type":"standard",
|
||||||
|
"key":"0123456789ABCDEF0123456789ABCDEF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remotes": {
|
||||||
|
"localhost": "127.0.0.1:8888"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/valid_config_no_default.json
generated
vendored
Normal file
14
Godeps/_workspace/src/github.com/cloudflare/cfssl/config/testdata/valid_config_no_default.json
generated
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"profiles": {
|
||||||
|
"CA": {
|
||||||
|
"usages": ["cert sign"],
|
||||||
|
"expiry": "720h"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"usages": ["s/mime"],
|
||||||
|
"expiry": "720h"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,236 @@
|
||||||
|
// Package csr implements certificate requests for CF-SSL.
|
||||||
|
package csr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
cferr "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
curveP256 = 256
|
||||||
|
curveP384 = 384
|
||||||
|
curveP521 = 521
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Name contains the SubjectInfo fields.
|
||||||
|
type Name struct {
|
||||||
|
C string // Country
|
||||||
|
ST string // State
|
||||||
|
L string // Locality
|
||||||
|
O string // OrganisationName
|
||||||
|
OU string // OrganisationalUnitName
|
||||||
|
}
|
||||||
|
|
||||||
|
// A KeyRequest contains the algorithm and key size for a new private
|
||||||
|
// key.
|
||||||
|
type KeyRequest struct {
|
||||||
|
Algo string `json:"algo"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// The DefaultKeyRequest is used when no key request data is provided
|
||||||
|
// in the request. This should be a safe default.
|
||||||
|
var DefaultKeyRequest = KeyRequest{
|
||||||
|
Algo: "ecdsa",
|
||||||
|
Size: curveP256,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate generates a key as specified in the request. Currently,
|
||||||
|
// only ECDSA and RSA are supported.
|
||||||
|
func (kr *KeyRequest) Generate() (interface{}, error) {
|
||||||
|
log.Debugf("generate key from request: algo=%s, size=%d", kr.Algo, kr.Size)
|
||||||
|
switch kr.Algo {
|
||||||
|
case "rsa":
|
||||||
|
if kr.Size < 2048 {
|
||||||
|
return nil, errors.New("RSA key is too weak")
|
||||||
|
}
|
||||||
|
return rsa.GenerateKey(rand.Reader, kr.Size)
|
||||||
|
case "ecdsa":
|
||||||
|
var curve elliptic.Curve
|
||||||
|
switch kr.Size {
|
||||||
|
case curveP256:
|
||||||
|
curve = elliptic.P256()
|
||||||
|
case curveP384:
|
||||||
|
curve = elliptic.P384()
|
||||||
|
case curveP521:
|
||||||
|
curve = elliptic.P521()
|
||||||
|
default:
|
||||||
|
return nil, errors.New("invalid curve")
|
||||||
|
}
|
||||||
|
return ecdsa.GenerateKey(curve, rand.Reader)
|
||||||
|
default:
|
||||||
|
return nil, errors.New("invalid algorithm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SigAlgo returns an appropriate X.509 signature algorithm given the
|
||||||
|
// key request's type and size.
|
||||||
|
func (kr *KeyRequest) SigAlgo() x509.SignatureAlgorithm {
|
||||||
|
switch kr.Algo {
|
||||||
|
case "rsa":
|
||||||
|
switch {
|
||||||
|
case kr.Size >= 4096:
|
||||||
|
return x509.SHA512WithRSA
|
||||||
|
case kr.Size >= 3072:
|
||||||
|
return x509.SHA384WithRSA
|
||||||
|
case kr.Size >= 2048:
|
||||||
|
return x509.SHA256WithRSA
|
||||||
|
default:
|
||||||
|
return x509.SHA1WithRSA
|
||||||
|
}
|
||||||
|
case "ecdsa":
|
||||||
|
switch {
|
||||||
|
case kr.Size == curveP521:
|
||||||
|
return x509.ECDSAWithSHA512
|
||||||
|
case kr.Size == curveP384:
|
||||||
|
return x509.ECDSAWithSHA384
|
||||||
|
case kr.Size == curveP256:
|
||||||
|
return x509.ECDSAWithSHA256
|
||||||
|
default:
|
||||||
|
return x509.ECDSAWithSHA1
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return x509.UnknownSignatureAlgorithm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CAConfig is a section used in the requests initialising a new CA.
|
||||||
|
type CAConfig struct {
|
||||||
|
PathLength int `json:"pathlen"`
|
||||||
|
Expiry string `json:"expiry"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// A CertificateRequest encapsulates the API interface to the
|
||||||
|
// certificate request functionality.
|
||||||
|
type CertificateRequest struct {
|
||||||
|
CN string
|
||||||
|
Names []Name `json:"names"`
|
||||||
|
Hosts []string `json:"hosts"`
|
||||||
|
KeyRequest *KeyRequest `json:"key,omitempty"`
|
||||||
|
CA *CAConfig `json:"ca,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendIf appends to a if s is not an empty string.
|
||||||
|
func appendIf(s string, a *[]string) {
|
||||||
|
if s != "" {
|
||||||
|
*a = append(*a, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the PKIX name for the request.
|
||||||
|
func (cr *CertificateRequest) Name() pkix.Name {
|
||||||
|
var name pkix.Name
|
||||||
|
name.CommonName = cr.CN
|
||||||
|
|
||||||
|
for _, n := range cr.Names {
|
||||||
|
appendIf(n.C, &name.Country)
|
||||||
|
appendIf(n.ST, &name.Province)
|
||||||
|
appendIf(n.L, &name.Locality)
|
||||||
|
appendIf(n.O, &name.Organization)
|
||||||
|
appendIf(n.OU, &name.OrganizationalUnit)
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseRequest takes a certificate request and generates a key and
|
||||||
|
// CSR from it. It does no validation -- caveat emptor. It will,
|
||||||
|
// however, fail if the key request is not valid (i.e., an unsupported
|
||||||
|
// curve or RSA key size). The lack of validation was specifically
|
||||||
|
// chosen to allow the end user to define a policy and validate the
|
||||||
|
// request appropriately before calling this function.
|
||||||
|
func ParseRequest(req *CertificateRequest) (csr, key []byte, err error) {
|
||||||
|
log.Info("received CSR")
|
||||||
|
if req.KeyRequest == nil {
|
||||||
|
req.KeyRequest = &KeyRequest{
|
||||||
|
Algo: DefaultKeyRequest.Algo,
|
||||||
|
Size: DefaultKeyRequest.Size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("generating key: %s-%d", req.KeyRequest.Algo, req.KeyRequest.Size)
|
||||||
|
priv, err := req.KeyRequest.Generate()
|
||||||
|
if err != nil {
|
||||||
|
err = cferr.Wrap(cferr.PrivateKeyError, cferr.GenerationFailed, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch priv := priv.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
key = x509.MarshalPKCS1PrivateKey(priv)
|
||||||
|
block := pem.Block{
|
||||||
|
Type: "RSA PRIVATE KEY",
|
||||||
|
Bytes: key,
|
||||||
|
}
|
||||||
|
key = pem.EncodeToMemory(&block)
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
key, err = x509.MarshalECPrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
err = cferr.Wrap(cferr.PrivateKeyError, cferr.Unknown, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
block := pem.Block{
|
||||||
|
Type: "EC PRIVATE KEY",
|
||||||
|
Bytes: key,
|
||||||
|
}
|
||||||
|
key = pem.EncodeToMemory(&block)
|
||||||
|
default:
|
||||||
|
panic("Generate should have failed to produce a valid key.")
|
||||||
|
}
|
||||||
|
|
||||||
|
var tpl = x509.CertificateRequest{
|
||||||
|
Subject: req.Name(),
|
||||||
|
SignatureAlgorithm: req.KeyRequest.SigAlgo(),
|
||||||
|
DNSNames: req.Hosts,
|
||||||
|
}
|
||||||
|
csr, err = x509.CreateCertificateRequest(rand.Reader, &tpl, priv)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to generate a CSR: %v", err)
|
||||||
|
// The use of CertificateError was a matter of some
|
||||||
|
// debate; it is the one edge case in which a new
|
||||||
|
// error category specifically for CSRs might be
|
||||||
|
// useful, but it was deemed that one edge case did
|
||||||
|
// not a new category justify.
|
||||||
|
err = cferr.Wrap(cferr.CertificateError, cferr.BadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
block := pem.Block{
|
||||||
|
Type: "CERTIFICATE REQUEST",
|
||||||
|
Bytes: csr,
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("encoded CSR")
|
||||||
|
csr = pem.EncodeToMemory(&block)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Generator is responsible for validating certificate requests.
|
||||||
|
type Generator struct {
|
||||||
|
Validator func(*CertificateRequest) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessRequest validates and processes the incoming request. It is
|
||||||
|
// a wrapper around a validator and the ParseRequest function.
|
||||||
|
func (g *Generator) ProcessRequest(req *CertificateRequest) (csr, key []byte, err error) {
|
||||||
|
|
||||||
|
log.Info("generate received request")
|
||||||
|
err = g.Validator(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("invalid request: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
csr, key, err = ParseRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
373
Godeps/_workspace/src/github.com/cloudflare/cfssl/csr/csr_test.go
generated
vendored
Normal file
373
Godeps/_workspace/src/github.com/cloudflare/cfssl/csr/csr_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,373 @@
|
||||||
|
package csr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestKeyRequest ensures that key generation returns the same type of
|
||||||
|
// key specified in the KeyRequest.
|
||||||
|
func TestKeyRequest(t *testing.T) {
|
||||||
|
var kr = &KeyRequest{"ecdsa", 256}
|
||||||
|
|
||||||
|
priv, err := kr.Generate()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch priv.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
if kr.Algo != "rsa" {
|
||||||
|
t.Fatal("RSA key generated, but expected", kr.Algo)
|
||||||
|
}
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
if kr.Algo != "ecdsa" {
|
||||||
|
t.Fatal("ECDSA key generated, but expected", kr.Algo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPKIXName validates building a pkix.Name structure from a
|
||||||
|
// CertificateRequest.
|
||||||
|
func TestPKIXName(t *testing.T) {
|
||||||
|
var kr = KeyRequest{"ecdsa", 256}
|
||||||
|
var cr = &CertificateRequest{
|
||||||
|
CN: "Test Common Name",
|
||||||
|
Names: []Name{
|
||||||
|
{
|
||||||
|
C: "US",
|
||||||
|
ST: "California",
|
||||||
|
L: "San Francisco",
|
||||||
|
O: "CloudFlare, Inc.",
|
||||||
|
OU: "Systems Engineering",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
C: "GB",
|
||||||
|
ST: "London",
|
||||||
|
L: "London",
|
||||||
|
O: "CloudFlare, Inc",
|
||||||
|
OU: "Systems Engineering",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
|
||||||
|
KeyRequest: &kr,
|
||||||
|
}
|
||||||
|
|
||||||
|
name := cr.Name()
|
||||||
|
if len(name.Country) != 2 {
|
||||||
|
t.Fatal("Expected two countries in SubjInfo.")
|
||||||
|
} else if len(name.Province) != 2 {
|
||||||
|
t.Fatal("Expected two states in SubjInfo.")
|
||||||
|
} else if len(name.Locality) != 2 {
|
||||||
|
t.Fatal("Expected two localities in SubjInfo.")
|
||||||
|
} else if len(name.Country) != 2 {
|
||||||
|
t.Fatal("Expected two countries in SubjInfo.")
|
||||||
|
} else if len(name.Organization) != 2 {
|
||||||
|
t.Fatal("Expected two organization in SubjInfo.")
|
||||||
|
} else if len(name.OrganizationalUnit) != 2 {
|
||||||
|
t.Fatal("Expected two organizational units in SubjInfo.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestParseRequest ensures that a valid certificate request does not
|
||||||
|
// error.
|
||||||
|
func TestParseRequest(t *testing.T) {
|
||||||
|
var kr = KeyRequest{"ecdsa", 256}
|
||||||
|
var cr = &CertificateRequest{
|
||||||
|
CN: "Test Common Name",
|
||||||
|
Names: []Name{
|
||||||
|
{
|
||||||
|
C: "US",
|
||||||
|
ST: "California",
|
||||||
|
L: "San Francisco",
|
||||||
|
O: "CloudFlare, Inc.",
|
||||||
|
OU: "Systems Engineering",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
C: "GB",
|
||||||
|
ST: "London",
|
||||||
|
L: "London",
|
||||||
|
O: "CloudFlare, Inc",
|
||||||
|
OU: "Systems Engineering",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
|
||||||
|
KeyRequest: &kr,
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := ParseRequest(cr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func whichCurve(sz int) elliptic.Curve {
|
||||||
|
switch sz {
|
||||||
|
case 256:
|
||||||
|
return elliptic.P256()
|
||||||
|
case 384:
|
||||||
|
return elliptic.P384()
|
||||||
|
case 521:
|
||||||
|
return elliptic.P521()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestECGeneration ensures that the proper curve is used depending on
|
||||||
|
// the bit size specified in a key request and that an appropriate
|
||||||
|
// signature algorithm is returned.
|
||||||
|
func TestECGeneration(t *testing.T) {
|
||||||
|
var eckey *ecdsa.PrivateKey
|
||||||
|
|
||||||
|
for _, sz := range []int{256, 384, 521} {
|
||||||
|
kr := &KeyRequest{Algo: "ecdsa", Size: sz}
|
||||||
|
priv, err := kr.Generate()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
eckey = priv.(*ecdsa.PrivateKey)
|
||||||
|
if eckey.Curve != whichCurve(sz) {
|
||||||
|
t.Fatal("Generated key has wrong curve.")
|
||||||
|
}
|
||||||
|
if sa := kr.SigAlgo(); sa == x509.UnknownSignatureAlgorithm {
|
||||||
|
t.Fatal("Invalid signature algorithm!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRSAKeyGeneration(t *testing.T) {
|
||||||
|
var rsakey *rsa.PrivateKey
|
||||||
|
|
||||||
|
for _, sz := range []int{2048, 3072, 4096} {
|
||||||
|
kr := &KeyRequest{Algo: "rsa", Size: sz}
|
||||||
|
priv, err := kr.Generate()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
rsakey = priv.(*rsa.PrivateKey)
|
||||||
|
if rsakey.PublicKey.N.BitLen() != kr.Size {
|
||||||
|
t.Fatal("Generated key has wrong size.")
|
||||||
|
}
|
||||||
|
if sa := kr.SigAlgo(); sa == x509.UnknownSignatureAlgorithm {
|
||||||
|
t.Fatal("Invalid signature algorithm!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBadKeyRequest ensures that generating a key from a KeyRequest
|
||||||
|
// fails with an invalid algorithm, or an invalid RSA or ECDSA key
|
||||||
|
// size. An invalid ECDSA key size is any size other than 256, 384, or
|
||||||
|
// 521; an invalid RSA key size is any size less than 2048 bits.
|
||||||
|
func TestBadKeyRequest(t *testing.T) {
|
||||||
|
kr := &KeyRequest{Algo: "yolocrypto", Size: 1024}
|
||||||
|
if _, err := kr.Generate(); err == nil {
|
||||||
|
t.Fatal("Key generation should fail with invalid algorithm")
|
||||||
|
} else if sa := kr.SigAlgo(); sa != x509.UnknownSignatureAlgorithm {
|
||||||
|
t.Fatal("The wrong signature algorithm was returned from SigAlgo!")
|
||||||
|
}
|
||||||
|
|
||||||
|
kr.Algo = "ecdsa"
|
||||||
|
if _, err := kr.Generate(); err == nil {
|
||||||
|
t.Fatal("Key generation should fail with invalid key size")
|
||||||
|
} else if sa := kr.SigAlgo(); sa != x509.ECDSAWithSHA1 {
|
||||||
|
t.Fatal("The wrong signature algorithm was returned from SigAlgo!")
|
||||||
|
}
|
||||||
|
|
||||||
|
kr.Algo = "rsa"
|
||||||
|
if _, err := kr.Generate(); err == nil {
|
||||||
|
t.Fatal("Key generation should fail with invalid key size")
|
||||||
|
} else if sa := kr.SigAlgo(); sa != x509.SHA1WithRSA {
|
||||||
|
t.Fatal("The wrong signature algorithm was returned from SigAlgo!")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDefaultKeyRequest makes sure that certificate requests without
|
||||||
|
// explicit key requests fall back to the default key request.
|
||||||
|
func TestDefaultKeyRequest(t *testing.T) {
|
||||||
|
var req = &CertificateRequest{
|
||||||
|
Names: []Name{
|
||||||
|
{
|
||||||
|
C: "US",
|
||||||
|
ST: "California",
|
||||||
|
L: "San Francisco",
|
||||||
|
O: "CloudFlare",
|
||||||
|
OU: "Systems Engineering",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CN: "cloudflare.com",
|
||||||
|
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
|
||||||
|
}
|
||||||
|
_, priv, err := ParseRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the default key type changes, this will need to be changed.
|
||||||
|
block, _ := pem.Decode(priv)
|
||||||
|
if block == nil {
|
||||||
|
t.Fatal("Bad private key was generated!")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch block.Type {
|
||||||
|
case "RSA PRIVATE KEY":
|
||||||
|
if DefaultKeyRequest.Algo != "rsa" {
|
||||||
|
t.Fatal("Invalid default key request.")
|
||||||
|
}
|
||||||
|
case "EC PRIVATE KEY":
|
||||||
|
if DefaultKeyRequest.Algo != "ecdsa" {
|
||||||
|
t.Fatal("Invalid default key request.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRSACertRequest validates parsing a certificate request with an
|
||||||
|
// RSA key.
|
||||||
|
func TestRSACertRequest(t *testing.T) {
|
||||||
|
var req = &CertificateRequest{
|
||||||
|
Names: []Name{
|
||||||
|
{
|
||||||
|
C: "US",
|
||||||
|
ST: "California",
|
||||||
|
L: "San Francisco",
|
||||||
|
O: "CloudFlare",
|
||||||
|
OU: "Systems Engineering",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CN: "cloudflare.com",
|
||||||
|
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
|
||||||
|
KeyRequest: &KeyRequest{
|
||||||
|
Algo: "rsa",
|
||||||
|
Size: 2048,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, _, err := ParseRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBadCertRequest checks for failure conditions of ParseRequest.
|
||||||
|
func TestBadCertRequest(t *testing.T) {
|
||||||
|
var req = &CertificateRequest{
|
||||||
|
Names: []Name{
|
||||||
|
{
|
||||||
|
C: "US",
|
||||||
|
ST: "California",
|
||||||
|
L: "San Francisco",
|
||||||
|
O: "CloudFlare",
|
||||||
|
OU: "Systems Engineering",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CN: "cloudflare.com",
|
||||||
|
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
|
||||||
|
KeyRequest: &KeyRequest{
|
||||||
|
Algo: "yolo-crypto",
|
||||||
|
Size: 2048,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, _, err := ParseRequest(req)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("ParseRequest should fail with a bad key algorithm.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testValidator is a stripped-down validator that checks to make sure
|
||||||
|
// the request has a common name. It should mimic some of the
|
||||||
|
// functionality expected in an actual validator.
|
||||||
|
func testValidator(req *CertificateRequest) error {
|
||||||
|
if req.CN == "" {
|
||||||
|
return errors.NewBadRequestMissingParameter("CN")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestGenerator ensures that a valid request is processed properly
|
||||||
|
// and returns a certificate request and key.
|
||||||
|
func TestGenerator(t *testing.T) {
|
||||||
|
g := &Generator{testValidator}
|
||||||
|
var req = &CertificateRequest{
|
||||||
|
Names: []Name{
|
||||||
|
{
|
||||||
|
C: "US",
|
||||||
|
ST: "California",
|
||||||
|
L: "San Francisco",
|
||||||
|
O: "CloudFlare",
|
||||||
|
OU: "Systems Engineering",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CN: "cloudflare.com",
|
||||||
|
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
|
||||||
|
KeyRequest: &KeyRequest{
|
||||||
|
Algo: "rsa",
|
||||||
|
Size: 2048,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := g.ProcessRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestBadGenerator ensures that a request that fails the validator is
|
||||||
|
// not processed.
|
||||||
|
func TestBadGenerator(t *testing.T) {
|
||||||
|
g := &Generator{testValidator}
|
||||||
|
missingCN := &CertificateRequest{
|
||||||
|
Names: []Name{
|
||||||
|
{
|
||||||
|
C: "US",
|
||||||
|
ST: "California",
|
||||||
|
L: "San Francisco",
|
||||||
|
O: "CloudFlare",
|
||||||
|
OU: "Systems Engineering",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Missing CN
|
||||||
|
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
|
||||||
|
KeyRequest: &KeyRequest{
|
||||||
|
Algo: "rsa",
|
||||||
|
Size: 2048,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err := g.ProcessRequest(missingCN)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Request should have failed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWeakCSR(t *testing.T) {
|
||||||
|
weakKey := &CertificateRequest{
|
||||||
|
Names: []Name{
|
||||||
|
{
|
||||||
|
C: "US",
|
||||||
|
ST: "California",
|
||||||
|
L: "San Francisco",
|
||||||
|
O: "CloudFlare",
|
||||||
|
OU: "Systems Engineering",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CN: "cloudflare.com",
|
||||||
|
Hosts: []string{"cloudflare.com", "www.cloudflare.com"},
|
||||||
|
KeyRequest: &KeyRequest{
|
||||||
|
Algo: "rsa",
|
||||||
|
Size: 1024,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
g := &Generator{testValidator}
|
||||||
|
|
||||||
|
_, _, err := g.ProcessRequest(weakKey)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Request should have failed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
Package errors provides error types returned in CF SSL.
|
||||||
|
|
||||||
|
1. Type Error is intended for errors produced by CF SSL packages.
|
||||||
|
It formats to a json object that consists of an error message and a 4-digit code for error reasoning.
|
||||||
|
|
||||||
|
Example: {"code":1002, "message": "Failed to decode certificate"}
|
||||||
|
|
||||||
|
The index of codes are listed below:
|
||||||
|
1XXX: CertificateError
|
||||||
|
1000: Unknown
|
||||||
|
1001: ReadFailed
|
||||||
|
1002: DecodeFailed
|
||||||
|
1003: ParseFailed
|
||||||
|
1100: SelfSigned
|
||||||
|
12XX: VerifyFailed
|
||||||
|
121X: CertificateInvalid
|
||||||
|
1210: NotAuthorizedToSign
|
||||||
|
1211: Expired
|
||||||
|
1212: CANotAuthorizedForThisName
|
||||||
|
1213: TooManyIntermediates
|
||||||
|
1214: IncompatibleUsage
|
||||||
|
1220: UnknownAuthority
|
||||||
|
2XXX: PrivatekeyError
|
||||||
|
2000: Unknown
|
||||||
|
2001: ReadFailed
|
||||||
|
2002: DecodeFailed
|
||||||
|
2003: ParseFailed
|
||||||
|
2100: Encrypted
|
||||||
|
2200: NotRSA
|
||||||
|
2300: KeyMismatch
|
||||||
|
2400: GenerationFailed
|
||||||
|
2500: Unavailable
|
||||||
|
3XXX: IntermediatesError
|
||||||
|
4XXX: RootError
|
||||||
|
5XXX: PolicyError
|
||||||
|
5100: NoKeyUsages
|
||||||
|
5200: InvalidPolicy
|
||||||
|
5300: InvalidRequest
|
||||||
|
6XXX: DialError
|
||||||
|
|
||||||
|
2. Type HttpError is intended for CF SSL API to consume. It contains a HTTP status code that will be read and returned
|
||||||
|
by the API server.
|
||||||
|
*/
|
||||||
|
package errors
|
||||||
351
Godeps/_workspace/src/github.com/cloudflare/cfssl/errors/error.go
generated
vendored
Normal file
351
Godeps/_workspace/src/github.com/cloudflare/cfssl/errors/error.go
generated
vendored
Normal file
|
|
@ -0,0 +1,351 @@
|
||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error is the error type usually returned by functions in CF SSL package.
|
||||||
|
// It contains a 4-digit error code where the most significant digit
|
||||||
|
// describes the category where the error occurred and the rest 3 digits
|
||||||
|
// describe the specific error reason.
|
||||||
|
type Error struct {
|
||||||
|
ErrorCode int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Category is the most significant digit of the error code.
|
||||||
|
type Category int
|
||||||
|
|
||||||
|
// Reason is the last 3 digits of the error code.
|
||||||
|
type Reason int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Success indicates no error occurred.
|
||||||
|
Success Category = 1000 * iota // 0XXX
|
||||||
|
|
||||||
|
// CertificateError indicates a fault in a certificate.
|
||||||
|
CertificateError // 1XXX
|
||||||
|
|
||||||
|
// PrivateKeyError indicates a fault in a private key.
|
||||||
|
PrivateKeyError // 2XXX
|
||||||
|
|
||||||
|
// IntermediatesError indicates a fault in an intermediate.
|
||||||
|
IntermediatesError // 3XXX
|
||||||
|
|
||||||
|
// RootError indicates a fault in a root.
|
||||||
|
RootError // 4XXX
|
||||||
|
|
||||||
|
// PolicyError indicates an error arising from a malformed or
|
||||||
|
// non-existent policy, or a breach of policy.
|
||||||
|
PolicyError // 5XXX
|
||||||
|
|
||||||
|
// DialError indicates a network fault.
|
||||||
|
DialError // 6XXX
|
||||||
|
|
||||||
|
// APIClientError indicates a problem with the API client.
|
||||||
|
APIClientError // 7XXX
|
||||||
|
|
||||||
|
// OCSPError indicates a problem with OCSP signing
|
||||||
|
OCSPError // 8XXX
|
||||||
|
)
|
||||||
|
|
||||||
|
// None is a non-specified error.
|
||||||
|
const (
|
||||||
|
None Reason = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
// Warning code for a success
|
||||||
|
const (
|
||||||
|
BundleExpiringBit int = 1 << iota // 0x01
|
||||||
|
BundleNotUbiquitousBit // 0x02
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parsing errors
|
||||||
|
const (
|
||||||
|
Unknown Reason = iota // X000
|
||||||
|
ReadFailed // X001
|
||||||
|
DecodeFailed // X002
|
||||||
|
ParseFailed // X003
|
||||||
|
)
|
||||||
|
|
||||||
|
// The following represent certificate non-parsing errors, and must be
|
||||||
|
// specified along with CertificateError.
|
||||||
|
const (
|
||||||
|
// SelfSigned indicates that a certificate is self-signed and
|
||||||
|
// cannot be used in the manner being attempted.
|
||||||
|
SelfSigned Reason = 100 * (iota + 1) // Code 11XX
|
||||||
|
|
||||||
|
// VerifyFailed is an X.509 verification failure. The least two
|
||||||
|
// significant digits of 12XX is determined as the actual x509
|
||||||
|
// error is examined.
|
||||||
|
VerifyFailed // Code 12XX
|
||||||
|
|
||||||
|
// BadRequest indicates that the certificate request is invalid.
|
||||||
|
BadRequest // Code 13XX
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
certificateInvalid = 10 * (iota + 1) //121X
|
||||||
|
unknownAuthority //122x
|
||||||
|
)
|
||||||
|
|
||||||
|
// The following represent private-key non-parsing errors, and must be
|
||||||
|
// specified with PrivateKeyError.
|
||||||
|
const (
|
||||||
|
// Encrypted indicates that the private key is a PKCS #8 encrypted
|
||||||
|
// private key. At this time, CFSSL does not support decrypting
|
||||||
|
// these keys.
|
||||||
|
Encrypted Reason = 100 * (iota + 1) //21XX
|
||||||
|
|
||||||
|
// NotRSAOrECC indicates that they key is not an RSA or ECC
|
||||||
|
// private key; these are the only two private key types supported
|
||||||
|
// at this time by CFSSL.
|
||||||
|
NotRSAOrECC //22XX
|
||||||
|
|
||||||
|
// KeyMismatch indicates that the private key does not match
|
||||||
|
// the public key or certificate being presented with the key.
|
||||||
|
KeyMismatch //23XX
|
||||||
|
|
||||||
|
// GenerationFailed indicates that a private key could not
|
||||||
|
// be generated.
|
||||||
|
GenerationFailed //24XX
|
||||||
|
|
||||||
|
// Unavailable indicates that a private key mechanism (such as
|
||||||
|
// PKCS #11) was requested but support for that mechanism is
|
||||||
|
// not available.
|
||||||
|
Unavailable
|
||||||
|
)
|
||||||
|
|
||||||
|
// The following are policy-related non-parsing errors, and must be
|
||||||
|
// specified along with PolicyError.
|
||||||
|
const (
|
||||||
|
// NoKeyUsages indicates that the profile does not permit any
|
||||||
|
// key usages for the certificate.
|
||||||
|
NoKeyUsages Reason = 100 * (iota + 1) // 51XX
|
||||||
|
|
||||||
|
// InvalidPolicy indicates that policy being requested is not
|
||||||
|
// a valid policy or does not exist.
|
||||||
|
InvalidPolicy // 52XX
|
||||||
|
|
||||||
|
// InvalidRequest indicates a certificate request violated the
|
||||||
|
// constraints of the policy being applied to the request.
|
||||||
|
InvalidRequest // 53XX
|
||||||
|
)
|
||||||
|
|
||||||
|
// The following are API client related errors, and should be
|
||||||
|
// specified with APIClientError.
|
||||||
|
const (
|
||||||
|
// AuthenticationFailure occurs when the client is unable
|
||||||
|
// to obtain an authentication token for the request.
|
||||||
|
AuthenticationFailure Reason = 100 * (iota + 1)
|
||||||
|
|
||||||
|
// JSONError wraps an encoding/json error.
|
||||||
|
JSONError
|
||||||
|
|
||||||
|
// IOError wraps an io/ioutil error.
|
||||||
|
IOError
|
||||||
|
|
||||||
|
// ClientHTTPError wraps a net/http error.
|
||||||
|
ClientHTTPError
|
||||||
|
|
||||||
|
// ServerRequestFailed covers any other failures from the API
|
||||||
|
// client.
|
||||||
|
ServerRequestFailed
|
||||||
|
)
|
||||||
|
|
||||||
|
// The following are OCSP related errors, and should be
|
||||||
|
// specified with OCSPError
|
||||||
|
const (
|
||||||
|
// IssuerMismatch ocurs when the certificate in the OCSP signing
|
||||||
|
// request was not issued by the CA that this responder responds for.
|
||||||
|
IssuerMismatch Reason = 100 * (iota + 1) // 81XX
|
||||||
|
|
||||||
|
// InvalidStatus occurs when the OCSP signing requests includes an
|
||||||
|
// invalid value for the certificate status.
|
||||||
|
InvalidStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
// The error interface implementation, which formats to a JSON object string.
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
marshaled, err := json.Marshal(e)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return string(marshaled)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns an error that contains an error code and message derived from
|
||||||
|
// the given category, reason. Currently, to avoid confusion, it is not
|
||||||
|
// allowed to create an error of category Success
|
||||||
|
func New(category Category, reason Reason) *Error {
|
||||||
|
errorCode := int(category) + int(reason)
|
||||||
|
var msg string
|
||||||
|
switch category {
|
||||||
|
case OCSPError:
|
||||||
|
switch reason {
|
||||||
|
case ReadFailed:
|
||||||
|
msg = "No certificate provided"
|
||||||
|
case IssuerMismatch:
|
||||||
|
msg = "Certificate not issued by this issuer"
|
||||||
|
case InvalidStatus:
|
||||||
|
msg = "Invalid revocation status"
|
||||||
|
}
|
||||||
|
case CertificateError:
|
||||||
|
switch reason {
|
||||||
|
case Unknown:
|
||||||
|
msg = "Unknown certificate error"
|
||||||
|
case ReadFailed:
|
||||||
|
msg = "Failed to read certificate"
|
||||||
|
case DecodeFailed:
|
||||||
|
msg = "Failed to decode certificate"
|
||||||
|
case ParseFailed:
|
||||||
|
msg = "Failed to parse certificate"
|
||||||
|
case SelfSigned:
|
||||||
|
msg = "Certificate is self signed"
|
||||||
|
case VerifyFailed:
|
||||||
|
msg = "Unable to verify certificate"
|
||||||
|
case BadRequest:
|
||||||
|
msg = "Invalid certificate request"
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category CertificateError.",
|
||||||
|
reason))
|
||||||
|
|
||||||
|
}
|
||||||
|
case PrivateKeyError:
|
||||||
|
switch reason {
|
||||||
|
case Unknown:
|
||||||
|
msg = "Unknown private key error"
|
||||||
|
case ReadFailed:
|
||||||
|
msg = "Failed to read private key"
|
||||||
|
case DecodeFailed:
|
||||||
|
msg = "Failed to decode private key"
|
||||||
|
case ParseFailed:
|
||||||
|
msg = "Failed to parse private key"
|
||||||
|
case Encrypted:
|
||||||
|
msg = "Private key is encrypted."
|
||||||
|
case NotRSAOrECC:
|
||||||
|
msg = "Private key algorithm is not RSA or ECC"
|
||||||
|
case KeyMismatch:
|
||||||
|
msg = "Private key does not match public key"
|
||||||
|
case GenerationFailed:
|
||||||
|
msg = "Failed to new private key"
|
||||||
|
case Unavailable:
|
||||||
|
msg = "Private key is unavailable"
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category PrivateKeyError.",
|
||||||
|
reason))
|
||||||
|
}
|
||||||
|
case IntermediatesError:
|
||||||
|
switch reason {
|
||||||
|
case Unknown:
|
||||||
|
msg = "Unknown intermediate certificate error"
|
||||||
|
case ReadFailed:
|
||||||
|
msg = "Failed to read intermediate certificate"
|
||||||
|
case DecodeFailed:
|
||||||
|
msg = "Failed to decode intermediate certificate"
|
||||||
|
case ParseFailed:
|
||||||
|
msg = "Failed to parse intermediate certificate"
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category IntermediatesError.",
|
||||||
|
reason))
|
||||||
|
}
|
||||||
|
case RootError:
|
||||||
|
switch reason {
|
||||||
|
case Unknown:
|
||||||
|
msg = "Unknown root certificate error"
|
||||||
|
case ReadFailed:
|
||||||
|
msg = "Failed to read root certificate"
|
||||||
|
case DecodeFailed:
|
||||||
|
msg = "Failed to decode root certificate"
|
||||||
|
case ParseFailed:
|
||||||
|
msg = "Failed to parse root certificate"
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category RootError.",
|
||||||
|
reason))
|
||||||
|
}
|
||||||
|
case PolicyError:
|
||||||
|
switch reason {
|
||||||
|
case Unknown:
|
||||||
|
msg = "Unknown policy error"
|
||||||
|
case NoKeyUsages:
|
||||||
|
msg = "Invalid policy: no key usage available"
|
||||||
|
case InvalidPolicy:
|
||||||
|
msg = "Invalid or unknown policy"
|
||||||
|
case InvalidRequest:
|
||||||
|
msg = "Policy violation request"
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category PolicyError.",
|
||||||
|
reason))
|
||||||
|
}
|
||||||
|
case DialError:
|
||||||
|
switch reason {
|
||||||
|
case Unknown:
|
||||||
|
msg = "Failed to dial remote server"
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category DialError.",
|
||||||
|
reason))
|
||||||
|
}
|
||||||
|
case APIClientError:
|
||||||
|
switch reason {
|
||||||
|
case AuthenticationFailure:
|
||||||
|
msg = "API client authentication failure"
|
||||||
|
case JSONError:
|
||||||
|
msg = "API client JSON config error"
|
||||||
|
case ClientHTTPError:
|
||||||
|
msg = "API client HTTP error"
|
||||||
|
case IOError:
|
||||||
|
msg = "API client IO error"
|
||||||
|
case ServerRequestFailed:
|
||||||
|
msg = "API client error: Server request failed"
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category APIClientError.",
|
||||||
|
reason))
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unsupported CF-SSL error type: %d.",
|
||||||
|
category))
|
||||||
|
}
|
||||||
|
return &Error{ErrorCode: errorCode, Message: msg}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap returns an error that contains the given error and an error code derived from
|
||||||
|
// the given category, reason and the error. Currently, to avoid confusion, it is not
|
||||||
|
// allowed to create an error of category Success
|
||||||
|
func Wrap(category Category, reason Reason, err error) *Error {
|
||||||
|
errorCode := int(category) + int(reason)
|
||||||
|
if err == nil {
|
||||||
|
panic("Wrap needs a supplied error to initialize.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// do not double wrap a error
|
||||||
|
switch err.(type) {
|
||||||
|
case *Error:
|
||||||
|
panic("Unable to wrap a wrapped error.")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch category {
|
||||||
|
case CertificateError:
|
||||||
|
// given VerifyFailed , report the status with more detailed status code
|
||||||
|
// for some certificate errors we care.
|
||||||
|
if reason == VerifyFailed {
|
||||||
|
switch errorType := err.(type) {
|
||||||
|
case x509.CertificateInvalidError:
|
||||||
|
errorCode += certificateInvalid + int(errorType.Reason)
|
||||||
|
case x509.UnknownAuthorityError:
|
||||||
|
errorCode += unknownAuthority
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PrivateKeyError, IntermediatesError, RootError, PolicyError, DialError, APIClientError:
|
||||||
|
// no-op, just use the error
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unsupported CF-SSL error type: %d.",
|
||||||
|
category))
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Error{ErrorCode: errorCode, Message: err.Error()}
|
||||||
|
|
||||||
|
}
|
||||||
69
Godeps/_workspace/src/github.com/cloudflare/cfssl/errors/error_test.go
generated
vendored
Normal file
69
Godeps/_workspace/src/github.com/cloudflare/cfssl/errors/error_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
err := New(CertificateError, Unknown)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Error creation failed.")
|
||||||
|
}
|
||||||
|
if err.ErrorCode != int(CertificateError)+int(Unknown) {
|
||||||
|
t.Fatal("Error code construction failed.")
|
||||||
|
}
|
||||||
|
if err.Message != "Unknown certificate error" {
|
||||||
|
t.Fatal("Error message construction failed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWrap(t *testing.T) {
|
||||||
|
msg := "Arbitrary error message"
|
||||||
|
err := Wrap(CertificateError, Unknown, errors.New(msg))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Error creation failed.")
|
||||||
|
}
|
||||||
|
if err.ErrorCode != int(CertificateError)+int(Unknown) {
|
||||||
|
t.Fatal("Error code construction failed.")
|
||||||
|
}
|
||||||
|
if err.Message != msg {
|
||||||
|
t.Fatal("Error message construction failed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Wrap(CertificateError, VerifyFailed, x509.CertificateInvalidError{Reason: x509.Expired})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Error creation failed.")
|
||||||
|
}
|
||||||
|
if err.ErrorCode != int(CertificateError)+int(VerifyFailed)+certificateInvalid+int(x509.Expired) {
|
||||||
|
t.Fatal("Error code construction failed.")
|
||||||
|
}
|
||||||
|
if err.Message != "x509: certificate has expired or is not yet valid" {
|
||||||
|
t.Fatal("Error message construction failed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshal(t *testing.T) {
|
||||||
|
msg := "Arbitrary error message"
|
||||||
|
err := Wrap(CertificateError, Unknown, errors.New(msg))
|
||||||
|
bytes, _ := json.Marshal(err)
|
||||||
|
var received Error
|
||||||
|
json.Unmarshal(bytes, &received)
|
||||||
|
if received.ErrorCode != int(CertificateError)+int(Unknown) {
|
||||||
|
t.Fatal("Error code construction failed.")
|
||||||
|
}
|
||||||
|
if received.Message != msg {
|
||||||
|
t.Fatal("Error message construction failed.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorString(t *testing.T) {
|
||||||
|
msg := "Arbitrary error message"
|
||||||
|
err := Wrap(CertificateError, Unknown, errors.New(msg))
|
||||||
|
str := err.Error()
|
||||||
|
if str != `{"code":1000,"message":"`+msg+`"}` {
|
||||||
|
t.Fatal("Incorrect Error():", str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPError is an augmented error with a HTTP status code.
|
||||||
|
type HTTPError struct {
|
||||||
|
StatusCode int
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implements the error interface.
|
||||||
|
func (e *HTTPError) Error() string {
|
||||||
|
return e.error.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMethodNotAllowed returns an appropriate error in the case that
|
||||||
|
// an HTTP client uses an invalid method (i.e. a GET in place of a POST)
|
||||||
|
// on an API endpoint.
|
||||||
|
func NewMethodNotAllowed(method string) *HTTPError {
|
||||||
|
return &HTTPError{http.StatusMethodNotAllowed, errors.New(`Method is not allowed:"` + method + `"`)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBadRequest creates a HttpError with the given error and error code 400.
|
||||||
|
func NewBadRequest(err error) *HTTPError {
|
||||||
|
return &HTTPError{http.StatusBadRequest, err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBadRequestString returns a HttpError with the supplied message
|
||||||
|
// and error code 400.
|
||||||
|
func NewBadRequestString(s string) *HTTPError {
|
||||||
|
return NewBadRequest(errors.New(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBadRequestMissingParameter returns a 400 HttpError as a required
|
||||||
|
// parameter is missing in the HTTP request.
|
||||||
|
func NewBadRequestMissingParameter(s string) *HTTPError {
|
||||||
|
return NewBadRequestString(`Missing parameter "` + s + `"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBadRequestUnwantedParameter returns a 400 HttpError as a unnecessary
|
||||||
|
// parameter is present in the HTTP request.
|
||||||
|
func NewBadRequestUnwantedParameter(s string) *HTTPError {
|
||||||
|
return NewBadRequestString(`Unwanted parameter "` + s + `"`)
|
||||||
|
}
|
||||||
227
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/helpers.go
generated
vendored
Normal file
227
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/helpers.go
generated
vendored
Normal file
|
|
@ -0,0 +1,227 @@
|
||||||
|
// Package helpers implements utility functionality common to many
|
||||||
|
// CF-SSL packages.
|
||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
cferr "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OneYear is a time.Duration representing a year's worth of seconds.
|
||||||
|
const OneYear = 8760 * time.Hour
|
||||||
|
|
||||||
|
// OneDay is a time.Duration representing a day's worth of seconds.
|
||||||
|
const OneDay = 24 * time.Hour
|
||||||
|
|
||||||
|
// KeyLength returns the bit size of ECDSA or RSA PublicKey
|
||||||
|
func KeyLength(key interface{}) int {
|
||||||
|
if key == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if ecdsaKey, ok := key.(*ecdsa.PublicKey); ok {
|
||||||
|
return ecdsaKey.Curve.Params().BitSize
|
||||||
|
} else if rsaKey, ok := key.(*rsa.PublicKey); ok {
|
||||||
|
return rsaKey.N.BitLen()
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpiryTime returns the time when the certificate chain is expired.
|
||||||
|
func ExpiryTime(chain []*x509.Certificate) *time.Time {
|
||||||
|
if len(chain) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
notAfter := chain[0].NotAfter
|
||||||
|
for _, cert := range chain {
|
||||||
|
if cert.NotAfter.Before(notAfter) {
|
||||||
|
notAfter = cert.NotAfter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ¬After
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignatureString returns the TLS signature string corresponding to
|
||||||
|
// an X509 signature algorithm.
|
||||||
|
func SignatureString(alg x509.SignatureAlgorithm) string {
|
||||||
|
switch alg {
|
||||||
|
case x509.MD2WithRSA:
|
||||||
|
return "MD2WithRSA"
|
||||||
|
case x509.MD5WithRSA:
|
||||||
|
return "MD5WithRSA"
|
||||||
|
case x509.SHA1WithRSA:
|
||||||
|
return "SHA1WithRSA"
|
||||||
|
case x509.SHA256WithRSA:
|
||||||
|
return "SHA256WithRSA"
|
||||||
|
case x509.SHA384WithRSA:
|
||||||
|
return "SHA384WithRSA"
|
||||||
|
case x509.SHA512WithRSA:
|
||||||
|
return "SHA512WithRSA"
|
||||||
|
case x509.DSAWithSHA1:
|
||||||
|
return "DSAWithSHA1"
|
||||||
|
case x509.DSAWithSHA256:
|
||||||
|
return "DSAWithSHA256"
|
||||||
|
case x509.ECDSAWithSHA1:
|
||||||
|
return "ECDSAWithSHA1"
|
||||||
|
case x509.ECDSAWithSHA256:
|
||||||
|
return "ECDSAWithSHA256"
|
||||||
|
case x509.ECDSAWithSHA384:
|
||||||
|
return "ECDSAWithSHA384"
|
||||||
|
case x509.ECDSAWithSHA512:
|
||||||
|
return "ECDSAWithSHA512"
|
||||||
|
default:
|
||||||
|
return "Unknown Signature"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashAlgoString returns the hash algorithm name contains in the signature
|
||||||
|
// method.
|
||||||
|
func HashAlgoString(alg x509.SignatureAlgorithm) string {
|
||||||
|
switch alg {
|
||||||
|
case x509.MD2WithRSA:
|
||||||
|
return "MD2"
|
||||||
|
case x509.MD5WithRSA:
|
||||||
|
return "MD5"
|
||||||
|
case x509.SHA1WithRSA:
|
||||||
|
return "SHA1"
|
||||||
|
case x509.SHA256WithRSA:
|
||||||
|
return "SHA256"
|
||||||
|
case x509.SHA384WithRSA:
|
||||||
|
return "SHA384"
|
||||||
|
case x509.SHA512WithRSA:
|
||||||
|
return "SHA512"
|
||||||
|
case x509.DSAWithSHA1:
|
||||||
|
return "SHA1"
|
||||||
|
case x509.DSAWithSHA256:
|
||||||
|
return "SHA256"
|
||||||
|
case x509.ECDSAWithSHA1:
|
||||||
|
return "SHA1"
|
||||||
|
case x509.ECDSAWithSHA256:
|
||||||
|
return "SHA256"
|
||||||
|
case x509.ECDSAWithSHA384:
|
||||||
|
return "SHA384"
|
||||||
|
case x509.ECDSAWithSHA512:
|
||||||
|
return "SHA512"
|
||||||
|
default:
|
||||||
|
return "Unknown Hash Algorithm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseCertificatesPEM parses a sequence of PEM-encoded certificate and returns them.
|
||||||
|
func ParseCertificatesPEM(certsPEM []byte) ([]*x509.Certificate, error) {
|
||||||
|
var certs []*x509.Certificate
|
||||||
|
var err error
|
||||||
|
certsPEM = bytes.TrimSpace(certsPEM)
|
||||||
|
for len(certsPEM) > 0 {
|
||||||
|
var cert *x509.Certificate
|
||||||
|
cert, certsPEM, err = ParseOneCertificateFromPEM(certsPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cferr.New(cferr.CertificateError, cferr.ParseFailed)
|
||||||
|
} else if cert == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
certs = append(certs, cert)
|
||||||
|
}
|
||||||
|
if len(certsPEM) > 0 {
|
||||||
|
return nil, cferr.New(cferr.CertificateError, cferr.DecodeFailed)
|
||||||
|
}
|
||||||
|
return certs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseSelfSignedCertificatePEM parses a PEM-encoded certificate and check if it is self-signed.
|
||||||
|
func ParseSelfSignedCertificatePEM(certPEM []byte) (*x509.Certificate, error) {
|
||||||
|
cert, err := ParseCertificatePEM(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := cert.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature); err != nil {
|
||||||
|
return nil, cferr.Wrap(cferr.CertificateError, cferr.VerifyFailed, err)
|
||||||
|
}
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseCertificatePEM parses and returns a PEM-encoded certificate.
|
||||||
|
func ParseCertificatePEM(certPEM []byte) (*x509.Certificate, error) {
|
||||||
|
certPEM = bytes.TrimSpace(certPEM)
|
||||||
|
cert, rest, err := ParseOneCertificateFromPEM(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
// Log the actual parsing error but throw a default parse error message.
|
||||||
|
log.Debugf("Certificate parsing error: %v", err)
|
||||||
|
return nil, cferr.New(cferr.CertificateError, cferr.ParseFailed)
|
||||||
|
} else if cert == nil {
|
||||||
|
return nil, cferr.New(cferr.CertificateError, cferr.DecodeFailed)
|
||||||
|
} else if len(rest) > 0 {
|
||||||
|
return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, errors.New("The PEM file should contain only one certificate."))
|
||||||
|
}
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseOneCertificateFromPEM attempts to parse one certificate from the top of the certsPEM,
|
||||||
|
// which may contain multiple certs.
|
||||||
|
func ParseOneCertificateFromPEM(certsPEM []byte) (cert *x509.Certificate, rest []byte, err error) {
|
||||||
|
block, rest := pem.Decode(certsPEM)
|
||||||
|
if block == nil {
|
||||||
|
return nil, rest, nil
|
||||||
|
}
|
||||||
|
cert, err = x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, rest, err
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePrivateKeyPEM parses and returns a PEM-encoded private
|
||||||
|
// key. The private key may be either an unencrypted PKCS#8, PKCS#1,
|
||||||
|
// or elliptic private key.
|
||||||
|
func ParsePrivateKeyPEM(keyPEM []byte) (key crypto.Signer, err error) {
|
||||||
|
keyDER, _ := pem.Decode(keyPEM)
|
||||||
|
if keyDER == nil {
|
||||||
|
return nil, cferr.New(cferr.PrivateKeyError, cferr.DecodeFailed)
|
||||||
|
}
|
||||||
|
if procType, ok := keyDER.Headers["Proc-Type"]; ok {
|
||||||
|
if strings.Contains(procType, "ENCRYPTED") {
|
||||||
|
return nil, cferr.New(cferr.PrivateKeyError, cferr.Encrypted)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ParsePrivateKeyDER(keyDER.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePrivateKeyDER parses a PKCS #1, PKCS #8, or elliptic curve
|
||||||
|
// DER-encoded private key. The key must not be in PEM format.
|
||||||
|
func ParsePrivateKeyDER(keyDER []byte) (key crypto.Signer, err error) {
|
||||||
|
generalKey, err := x509.ParsePKCS8PrivateKey(keyDER)
|
||||||
|
if err != nil {
|
||||||
|
generalKey, err = x509.ParsePKCS1PrivateKey(keyDER)
|
||||||
|
if err != nil {
|
||||||
|
generalKey, err = x509.ParseECPrivateKey(keyDER)
|
||||||
|
if err != nil {
|
||||||
|
// We don't include the actual error into
|
||||||
|
// the final error. The reason might be
|
||||||
|
// we don't want to leak any info about
|
||||||
|
// the private key.
|
||||||
|
return nil, cferr.New(cferr.PrivateKeyError,
|
||||||
|
cferr.ParseFailed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch generalKey.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
return generalKey.(*rsa.PrivateKey), nil
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
return generalKey.(*ecdsa.PrivateKey), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, cferr.New(cferr.PrivateKeyError, cferr.ParseFailed)
|
||||||
|
}
|
||||||
273
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/helpers_test.go
generated
vendored
Normal file
273
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/helpers_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,273 @@
|
||||||
|
package helpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testCertFile = "testdata/cert.pem"
|
||||||
|
testBundleFile = "testdata/bundle.pem"
|
||||||
|
testExtraWSCertFile = "testdata/cert_with_whitespace.pem"
|
||||||
|
testExtraWSBundleFile = "testdata/bundle_with_whitespace.pem"
|
||||||
|
testMessedUpBundleFile = "testdata/messed_up_bundle.pem"
|
||||||
|
testMessedUpCertFile = "testdata/messedupcert.pem"
|
||||||
|
testEmptyCertFile = "testdata/emptycert.pem"
|
||||||
|
testPrivateKey = "testdata/priv_key.pem"
|
||||||
|
testMessedUpPrivateKey = "testdata/messed_up_priv_key.pem"
|
||||||
|
testEncryptedPrivateKey = "testdata/enc_priv_key.pem"
|
||||||
|
testEmptyPem = "testdata/empty.pem"
|
||||||
|
testNoHeaderCert = "testdata/noheadercert.pem"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestKeyLength(t *testing.T) {
|
||||||
|
expNil := 0
|
||||||
|
recNil := KeyLength(nil)
|
||||||
|
if expNil != recNil {
|
||||||
|
t.Fatal("KeyLength on nil did not return 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
expNonsense := 0
|
||||||
|
inNonsense := "string?"
|
||||||
|
outNonsense := KeyLength(inNonsense)
|
||||||
|
if expNonsense != outNonsense {
|
||||||
|
t.Fatal("KeyLength malfunctioning on nonsense input")
|
||||||
|
}
|
||||||
|
|
||||||
|
//test the ecdsa branch
|
||||||
|
ecdsaPriv, _ := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
||||||
|
ecdsaIn, _ := ecdsaPriv.Public().(*ecdsa.PublicKey)
|
||||||
|
expEcdsa := ecdsaIn.Curve.Params().BitSize
|
||||||
|
outEcdsa := KeyLength(ecdsaIn)
|
||||||
|
if expEcdsa != outEcdsa {
|
||||||
|
t.Fatal("KeyLength malfunctioning on ecdsa input")
|
||||||
|
}
|
||||||
|
|
||||||
|
//test the rsa branch
|
||||||
|
rsaPriv, _ := rsa.GenerateKey(rand.Reader, 256)
|
||||||
|
rsaIn, _ := rsaPriv.Public().(*rsa.PublicKey)
|
||||||
|
expRsa := rsaIn.N.BitLen()
|
||||||
|
outRsa := KeyLength(rsaIn)
|
||||||
|
|
||||||
|
if expRsa != outRsa {
|
||||||
|
t.Fatal("KeyLength malfunctioning on rsa input")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExpiryTime(t *testing.T) {
|
||||||
|
// nil case
|
||||||
|
var expNil *time.Time
|
||||||
|
inNil := []*x509.Certificate{}
|
||||||
|
outNil := ExpiryTime(inNil)
|
||||||
|
if expNil != outNil {
|
||||||
|
t.Fatal("Expiry time is malfunctioning on empty input")
|
||||||
|
}
|
||||||
|
|
||||||
|
//read a pem file and use that expiry date
|
||||||
|
bytes, _ := ioutil.ReadFile(testBundleFile)
|
||||||
|
certs, err := ParseCertificatesPEM(bytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
expected := time.Date(2014, time.April, 15, 0, 0, 0, 0, time.UTC)
|
||||||
|
out := ExpiryTime(certs)
|
||||||
|
if out == nil {
|
||||||
|
t.Fatal("Expiry time returning null")
|
||||||
|
}
|
||||||
|
if *out != expected {
|
||||||
|
t.Fatalf("Expected %v, got %v", expected, *out)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHashAlgoString(t *testing.T) {
|
||||||
|
if HashAlgoString(x509.MD2WithRSA) != "MD2" {
|
||||||
|
t.Fatal("standin")
|
||||||
|
}
|
||||||
|
if HashAlgoString(x509.MD5WithRSA) != "MD5" {
|
||||||
|
t.Fatal("standin")
|
||||||
|
}
|
||||||
|
if HashAlgoString(x509.SHA1WithRSA) != "SHA1" {
|
||||||
|
t.Fatal("standin")
|
||||||
|
}
|
||||||
|
if HashAlgoString(x509.SHA256WithRSA) != "SHA256" {
|
||||||
|
t.Fatal("standin")
|
||||||
|
}
|
||||||
|
if HashAlgoString(x509.SHA384WithRSA) != "SHA384" {
|
||||||
|
t.Fatal("standin")
|
||||||
|
}
|
||||||
|
if HashAlgoString(x509.SHA512WithRSA) != "SHA512" {
|
||||||
|
t.Fatal("standin")
|
||||||
|
}
|
||||||
|
if HashAlgoString(x509.DSAWithSHA1) != "SHA1" {
|
||||||
|
t.Fatal("standin")
|
||||||
|
}
|
||||||
|
if HashAlgoString(x509.DSAWithSHA256) != "SHA256" {
|
||||||
|
t.Fatal("standin")
|
||||||
|
}
|
||||||
|
if HashAlgoString(x509.ECDSAWithSHA1) != "SHA1" {
|
||||||
|
t.Fatal("standin")
|
||||||
|
}
|
||||||
|
if HashAlgoString(x509.ECDSAWithSHA256) != "SHA256" {
|
||||||
|
t.Fatal("standin")
|
||||||
|
}
|
||||||
|
if HashAlgoString(x509.ECDSAWithSHA384) != "SHA384" {
|
||||||
|
t.Fatal("standin")
|
||||||
|
}
|
||||||
|
if HashAlgoString(x509.ECDSAWithSHA512) != "SHA512" {
|
||||||
|
t.Fatal("standin")
|
||||||
|
}
|
||||||
|
if HashAlgoString(math.MaxInt32) != "Unknown Hash Algorithm" {
|
||||||
|
t.Fatal("standin")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignatureString(t *testing.T) {
|
||||||
|
if SignatureString(x509.MD2WithRSA) != "MD2WithRSA" {
|
||||||
|
t.Fatal("Signature String functioning improperly")
|
||||||
|
}
|
||||||
|
if SignatureString(x509.MD5WithRSA) != "MD5WithRSA" {
|
||||||
|
t.Fatal("Signature String functioning improperly")
|
||||||
|
}
|
||||||
|
if SignatureString(x509.SHA1WithRSA) != "SHA1WithRSA" {
|
||||||
|
t.Fatal("Signature String functioning improperly")
|
||||||
|
}
|
||||||
|
if SignatureString(x509.SHA256WithRSA) != "SHA256WithRSA" {
|
||||||
|
t.Fatal("Signature String functioning improperly")
|
||||||
|
}
|
||||||
|
if SignatureString(x509.SHA384WithRSA) != "SHA384WithRSA" {
|
||||||
|
t.Fatal("Signature String functioning improperly")
|
||||||
|
}
|
||||||
|
if SignatureString(x509.SHA512WithRSA) != "SHA512WithRSA" {
|
||||||
|
t.Fatal("Signature String functioning improperly")
|
||||||
|
}
|
||||||
|
if SignatureString(x509.DSAWithSHA1) != "DSAWithSHA1" {
|
||||||
|
t.Fatal("Signature String functioning improperly")
|
||||||
|
}
|
||||||
|
if SignatureString(x509.DSAWithSHA256) != "DSAWithSHA256" {
|
||||||
|
t.Fatal("Signature String functioning improperly")
|
||||||
|
}
|
||||||
|
if SignatureString(x509.ECDSAWithSHA1) != "ECDSAWithSHA1" {
|
||||||
|
t.Fatal("Signature String functioning improperly")
|
||||||
|
}
|
||||||
|
if SignatureString(x509.ECDSAWithSHA256) != "ECDSAWithSHA256" {
|
||||||
|
t.Fatal("Signature String functioning improperly")
|
||||||
|
}
|
||||||
|
if SignatureString(x509.ECDSAWithSHA384) != "ECDSAWithSHA384" {
|
||||||
|
t.Fatal("Signature String functioning improperly")
|
||||||
|
}
|
||||||
|
if SignatureString(x509.ECDSAWithSHA512) != "ECDSAWithSHA512" {
|
||||||
|
t.Fatal("Signature String functioning improperly")
|
||||||
|
}
|
||||||
|
if SignatureString(math.MaxInt32) != "Unknown Signature" {
|
||||||
|
t.Fatal("Signature String functioning improperly")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseCertificatePEM(t *testing.T) {
|
||||||
|
for _, testFile := range []string{testCertFile, testExtraWSCertFile} {
|
||||||
|
certPEM, err := ioutil.ReadFile(testFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ParseCertificatePEM(certPEM); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, testFile := range []string{testBundleFile, testMessedUpCertFile, testEmptyCertFile} {
|
||||||
|
certPEM, err := ioutil.ReadFile(testFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ParseCertificatePEM(certPEM); err == nil {
|
||||||
|
t.Fatal("Incorrect cert failed to raise error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseCertificatesPEM(t *testing.T) {
|
||||||
|
// expected cases
|
||||||
|
for _, testFile := range []string{testBundleFile, testExtraWSBundleFile} {
|
||||||
|
bundlePEM, err := ioutil.ReadFile(testFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ParseCertificatesPEM(bundlePEM); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// test failure cases
|
||||||
|
// few lines deleted, then headers removed
|
||||||
|
for _, testFile := range []string{testMessedUpBundleFile, testNoHeaderCert} {
|
||||||
|
bundlePEM, err := ioutil.ReadFile(testFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ParseCertificatesPEM(bundlePEM); err == nil {
|
||||||
|
t.Fatal("Incorrectly-formatted file failed to produce an error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelfSignedCertificatePEM(t *testing.T) {
|
||||||
|
testPEM, _ := ioutil.ReadFile(testCertFile)
|
||||||
|
_, err := ParseSelfSignedCertificatePEM(testPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// a few lines deleted from the pem file
|
||||||
|
wrongPEM, _ := ioutil.ReadFile(testMessedUpCertFile)
|
||||||
|
_, err2 := ParseSelfSignedCertificatePEM(wrongPEM)
|
||||||
|
if err2 == nil {
|
||||||
|
t.Fatal("Improper pem file failed to raise an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// alter the signature of a valid certificate
|
||||||
|
blk, _ := pem.Decode(testPEM)
|
||||||
|
blk.Bytes[len(blk.Bytes)-10]++ // some hacking to get to the sig
|
||||||
|
alteredBytes := pem.EncodeToMemory(blk)
|
||||||
|
_, err = ParseSelfSignedCertificatePEM(alteredBytes)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Incorrect cert failed to produce an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParsePrivateKeyPEM(t *testing.T) {
|
||||||
|
|
||||||
|
// expected case
|
||||||
|
testPEM, _ := ioutil.ReadFile(testPrivateKey)
|
||||||
|
_, err := ParsePrivateKeyPEM(testPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// error cases
|
||||||
|
errCases := []string{
|
||||||
|
testMessedUpPrivateKey, // a few lines deleted
|
||||||
|
testEmptyPem, //empty file
|
||||||
|
testEncryptedPrivateKey, // encrypted key
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, fname := range errCases {
|
||||||
|
testPEM, _ = ioutil.ReadFile(fname)
|
||||||
|
_, err = ParsePrivateKeyPEM(testPEM)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Incorrect private key failed to produce an error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
53
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/bundle.pem
generated
vendored
Normal file
53
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/bundle.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEczCCAl2gAwIBAgIIDARj8BWNsscwCwYJKoZIhvcNAQELMIGMMQswCQYDVQQG
|
||||||
|
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
|
||||||
|
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
|
||||||
|
cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwMzAyMDAw
|
||||||
|
MDAwWhcNMTkwNDAxMDAwMDAwWjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs
|
||||||
|
b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT
|
||||||
|
DVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNs
|
||||||
|
b3VkZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGw
|
||||||
|
f3F0XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73P
|
||||||
|
IXGyfNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo4GIMIGF
|
||||||
|
MA4GA1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBRB
|
||||||
|
+YoiUjIm34/wBwHdJGE4Wufs/DAfBgNVHSMEGDAWgBTXXUgpaSwO9HOrQBxGqOOS
|
||||||
|
FHsHEDAfBgNVHREEGDAWghRjbG91ZGZsYXJlLWludGVyLmNvbTALBgkqhkiG9w0B
|
||||||
|
AQsDggIBACRqAC5EJEe+8ihv1WzCUMEMb7KtS0BqoNbdXE32ia66PgJSQmHcmeJd
|
||||||
|
FI1UjL0DlljTM2tc+8KxR/1/qnKiI+W/D4wFTWOY/JWFOd15q7lXuKGl+8PMkAHF
|
||||||
|
A145JCr6oZoO9G9wUwVUrbmXAbyPCOfzsEQ2+mD9F1ZpoEjzVhtGf0R+vnYrRw8j
|
||||||
|
4WCv5AIcYRAf7HZxbhMILF1bccNlqyUtdH+/MTHXpjkjJjA5KbsHBrAEfjAXkD7c
|
||||||
|
WWOay6m7mVWb3PPFmGorP6t29baEETK9ZTZSrfD9rnExjjUCftWJEn0M4Pp98DvT
|
||||||
|
br6+bg8jwtq73qdyOfNsC/Sod18UuHH7MTQA22yqAF5jIlcYtAHGlNnl+sDPZACs
|
||||||
|
369/Z9rOL9vPFL+Z3F/uJtqZzvN1QiCkj8jWzR0u9fh3eQwZADM2RwgwS4Gs2Ygh
|
||||||
|
PsypDo33sFOwfX93KqKBsTHssn8SSDDaSnZ8bu1ATEdshbVieecuQx40UadPuJpw
|
||||||
|
EPVqTR5AhviXQ9bKrTnU5T7EgkW9vNydkpLQQlMg3QE8hsndv4loGZbZGfNtqQHS
|
||||||
|
/mg1t07S+7OEa4YaMW+wVOBOqTdW7OXlZFLfCcF5SYLM0SnlTMklRMxiqI4JqZXH
|
||||||
|
0thnUGD0JjfLX4rTaZUzT3lrXXWzpS2jzutXQkjGv4nhGGprIDuT
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEkDCCA/ugAwIBAgIIWnP9jF/2nogwCwYJKoZIhvcNAQELMH0xCzAJBgNVBAYT
|
||||||
|
AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv
|
||||||
|
MRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQG
|
||||||
|
A1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDAzMDEwMDAwMDBaFw0xNDA0MTUwMDAw
|
||||||
|
MDBaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UE
|
||||||
|
CxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET
|
||||||
|
MBEGA1UECBMKQ2FsaWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5j
|
||||||
|
b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDlCnV+vj0sVPy8SqHL
|
||||||
|
AlI+xwnPhWgzj2VevD6Nz1Zu1BeQ5m5y4CWCf+GmRGTP7+a/C510Fw6rpmInB0Ng
|
||||||
|
xxwQ2rC08fJtCnijlGH/VjEPIHY5lRaAomcM8Rgx6JOuv9BpZJKpr9pyUMV53JeW
|
||||||
|
RbWuLH5nEMdyk9NpetS2gWxt4/D20QlhK/tHkROrcLmEUddwIGdwE8JzI88c77Fu
|
||||||
|
u6pgMtHKvl4GGH0yvb4T7PvCdH8V2tCH7bt8roXd9MSyFVy7uORkfouip7EsVREU
|
||||||
|
mlcY5EvpR141KXbZqiOQiusJ+u76mEUQNk8wCR1/CW/ii9v1BKOVjXwCfEtIXjg0
|
||||||
|
APJx1VNSSH6XoDpUETL+eQ4J0FL9XNbsDuYar7+zD0N1/5vSo3HLNRQR9f0lbsys
|
||||||
|
sWBEN+CxK19xyPumr21Z0bU0f1B5H52VSF0q3I1Ju9wRo994a7YipdGcmZ2lChmT
|
||||||
|
7r3mzlBTYl3poU26q34v8wG9U7Jv4fsZJ+RGebDI+TR3QG6Yod06l9oEYZxWXBY7
|
||||||
|
STOs8wuTu3huSnan/IpWnV017Vsc61D5G+QrqcxZdXckt3anZKCF75JpUnJ7vuow
|
||||||
|
TmmHlb8KIMa9mOvcuGX4P6mz8gTi2arl/aL27kj9Q0Jgv/y1ebe2Bx2P9TF6+VND
|
||||||
|
DL3J/vSVlFeqLt2reAIBKnytLwIDAQABo4GIMIGFMA4GA1UdDwEB/wQEAwIApDAS
|
||||||
|
BgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTXXUgpaSwO9HOrQBxGqOOSFHsH
|
||||||
|
EDAfBgNVHSMEGDAWgBS4Xu+uZ1C31vMH5Wq+VbNnOg2SPjAfBgNVHREEGDAWghRj
|
||||||
|
bG91ZGZsYXJlLWludGVyLmNvbTALBgkqhkiG9w0BAQsDgYEAfPLKCAHnPzgMYLX/
|
||||||
|
fWznVvOEFAAYZByPFx4QdMBbDZUtxHyvJIBs6PdxrdSuDwSiMqE7qQIi+jzzwGl9
|
||||||
|
fC7vf45B2zCX0OW51QL2oWNBdKlGgB+b2pwyME82lX/Pr7V1GY10u+ep1xdZDnch
|
||||||
|
DaMsXjQQTJu0iuG3qKEuCmUwOmc=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
56
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/bundle_with_whitespace.pem
generated
vendored
Normal file
56
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/bundle_with_whitespace.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEczCCAl2gAwIBAgIIDARj8BWNsscwCwYJKoZIhvcNAQELMIGMMQswCQYDVQQG
|
||||||
|
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
|
||||||
|
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
|
||||||
|
cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwMzAyMDAw
|
||||||
|
MDAwWhcNMTkwNDAxMDAwMDAwWjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs
|
||||||
|
b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT
|
||||||
|
DVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNs
|
||||||
|
b3VkZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGw
|
||||||
|
f3F0XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73P
|
||||||
|
IXGyfNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo4GIMIGF
|
||||||
|
MA4GA1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBRB
|
||||||
|
+YoiUjIm34/wBwHdJGE4Wufs/DAfBgNVHSMEGDAWgBTXXUgpaSwO9HOrQBxGqOOS
|
||||||
|
FHsHEDAfBgNVHREEGDAWghRjbG91ZGZsYXJlLWludGVyLmNvbTALBgkqhkiG9w0B
|
||||||
|
AQsDggIBACRqAC5EJEe+8ihv1WzCUMEMb7KtS0BqoNbdXE32ia66PgJSQmHcmeJd
|
||||||
|
FI1UjL0DlljTM2tc+8KxR/1/qnKiI+W/D4wFTWOY/JWFOd15q7lXuKGl+8PMkAHF
|
||||||
|
A145JCr6oZoO9G9wUwVUrbmXAbyPCOfzsEQ2+mD9F1ZpoEjzVhtGf0R+vnYrRw8j
|
||||||
|
4WCv5AIcYRAf7HZxbhMILF1bccNlqyUtdH+/MTHXpjkjJjA5KbsHBrAEfjAXkD7c
|
||||||
|
WWOay6m7mVWb3PPFmGorP6t29baEETK9ZTZSrfD9rnExjjUCftWJEn0M4Pp98DvT
|
||||||
|
br6+bg8jwtq73qdyOfNsC/Sod18UuHH7MTQA22yqAF5jIlcYtAHGlNnl+sDPZACs
|
||||||
|
369/Z9rOL9vPFL+Z3F/uJtqZzvN1QiCkj8jWzR0u9fh3eQwZADM2RwgwS4Gs2Ygh
|
||||||
|
PsypDo33sFOwfX93KqKBsTHssn8SSDDaSnZ8bu1ATEdshbVieecuQx40UadPuJpw
|
||||||
|
EPVqTR5AhviXQ9bKrTnU5T7EgkW9vNydkpLQQlMg3QE8hsndv4loGZbZGfNtqQHS
|
||||||
|
/mg1t07S+7OEa4YaMW+wVOBOqTdW7OXlZFLfCcF5SYLM0SnlTMklRMxiqI4JqZXH
|
||||||
|
0thnUGD0JjfLX4rTaZUzT3lrXXWzpS2jzutXQkjGv4nhGGprIDuT
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEkDCCA/ugAwIBAgIIWnP9jF/2nogwCwYJKoZIhvcNAQELMH0xCzAJBgNVBAYT
|
||||||
|
AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv
|
||||||
|
MRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQG
|
||||||
|
A1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDAzMDEwMDAwMDBaFw0xNDA0MTUwMDAw
|
||||||
|
MDBaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UE
|
||||||
|
CxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET
|
||||||
|
MBEGA1UECBMKQ2FsaWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5j
|
||||||
|
b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDlCnV+vj0sVPy8SqHL
|
||||||
|
AlI+xwnPhWgzj2VevD6Nz1Zu1BeQ5m5y4CWCf+GmRGTP7+a/C510Fw6rpmInB0Ng
|
||||||
|
xxwQ2rC08fJtCnijlGH/VjEPIHY5lRaAomcM8Rgx6JOuv9BpZJKpr9pyUMV53JeW
|
||||||
|
RbWuLH5nEMdyk9NpetS2gWxt4/D20QlhK/tHkROrcLmEUddwIGdwE8JzI88c77Fu
|
||||||
|
u6pgMtHKvl4GGH0yvb4T7PvCdH8V2tCH7bt8roXd9MSyFVy7uORkfouip7EsVREU
|
||||||
|
mlcY5EvpR141KXbZqiOQiusJ+u76mEUQNk8wCR1/CW/ii9v1BKOVjXwCfEtIXjg0
|
||||||
|
APJx1VNSSH6XoDpUETL+eQ4J0FL9XNbsDuYar7+zD0N1/5vSo3HLNRQR9f0lbsys
|
||||||
|
sWBEN+CxK19xyPumr21Z0bU0f1B5H52VSF0q3I1Ju9wRo994a7YipdGcmZ2lChmT
|
||||||
|
7r3mzlBTYl3poU26q34v8wG9U7Jv4fsZJ+RGebDI+TR3QG6Yod06l9oEYZxWXBY7
|
||||||
|
STOs8wuTu3huSnan/IpWnV017Vsc61D5G+QrqcxZdXckt3anZKCF75JpUnJ7vuow
|
||||||
|
TmmHlb8KIMa9mOvcuGX4P6mz8gTi2arl/aL27kj9Q0Jgv/y1ebe2Bx2P9TF6+VND
|
||||||
|
DL3J/vSVlFeqLt2reAIBKnytLwIDAQABo4GIMIGFMA4GA1UdDwEB/wQEAwIApDAS
|
||||||
|
BgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTXXUgpaSwO9HOrQBxGqOOSFHsH
|
||||||
|
EDAfBgNVHSMEGDAWgBS4Xu+uZ1C31vMH5Wq+VbNnOg2SPjAfBgNVHREEGDAWghRj
|
||||||
|
bG91ZGZsYXJlLWludGVyLmNvbTALBgkqhkiG9w0BAQsDgYEAfPLKCAHnPzgMYLX/
|
||||||
|
fWznVvOEFAAYZByPFx4QdMBbDZUtxHyvJIBs6PdxrdSuDwSiMqE7qQIi+jzzwGl9
|
||||||
|
fC7vf45B2zCX0OW51QL2oWNBdKlGgB+b2pwyME82lX/Pr7V1GY10u+ep1xdZDnch
|
||||||
|
DaMsXjQQTJu0iuG3qKEuCmUwOmc=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
13
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/cert.pem
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/cert.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIB7jCCAVmgAwIBAgIBADALBgkqhkiG9w0BAQUwJjEQMA4GA1UEChMHQWNtZSBD
|
||||||
|
bzESMBAGA1UEAxMJMTI3LjAuMC4xMB4XDTEyMDkwNzIyMDAwNFoXDTEzMDkwNzIy
|
||||||
|
MDUwNFowJjEQMA4GA1UEChMHQWNtZSBDbzESMBAGA1UEAxMJMTI3LjAuMC4xMIGd
|
||||||
|
MAsGCSqGSIb3DQEBAQOBjQAwgYkCgYEAm6f+jkP2t5q/vM0YAUZZkhq/EAYD+L1C
|
||||||
|
MS59jJOLomfDnKUWOGKi/k7URBg1HNL3vm7/ESDazZWFy9l/nibWxNkSUPkQIrvr
|
||||||
|
GsNivkRUzXkwgNX8IN8LOYAQ3BWxAqitXTpLjf4FeCTB6G59v9eYlAX3kicXRdY+
|
||||||
|
cqhEvLFbu3MCAwEAAaMyMDAwDgYDVR0PAQH/BAQDAgCgMA0GA1UdDgQGBAQBAgME
|
||||||
|
MA8GA1UdIwQIMAaABAECAwQwCwYJKoZIhvcNAQEFA4GBABndWRIcfi+QB9Sakr+m
|
||||||
|
dYnXTgYCnFio53L2Z+6EHTGG+rEhWtUEGhL4p4pzXX4siAnjWvwcgXTo92cafcfi
|
||||||
|
uB7wRfK+NL9CTJdpN6cdL+fiNHzH8hsl3bj1nL0CSmdn2hkUWVLbLhSgWlib/I8O
|
||||||
|
aq+K7aVrgHkPnWeRiG6tl+ZA
|
||||||
|
-----END CERTIFICATE-----
|
||||||
15
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/cert_with_whitespace.pem
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/cert_with_whitespace.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIB7jCCAVmgAwIBAgIBADALBgkqhkiG9w0BAQUwJjEQMA4GA1UEChMHQWNtZSBD
|
||||||
|
bzESMBAGA1UEAxMJMTI3LjAuMC4xMB4XDTEyMDkwNzIyMDAwNFoXDTEzMDkwNzIy
|
||||||
|
MDUwNFowJjEQMA4GA1UEChMHQWNtZSBDbzESMBAGA1UEAxMJMTI3LjAuMC4xMIGd
|
||||||
|
MAsGCSqGSIb3DQEBAQOBjQAwgYkCgYEAm6f+jkP2t5q/vM0YAUZZkhq/EAYD+L1C
|
||||||
|
MS59jJOLomfDnKUWOGKi/k7URBg1HNL3vm7/ESDazZWFy9l/nibWxNkSUPkQIrvr
|
||||||
|
GsNivkRUzXkwgNX8IN8LOYAQ3BWxAqitXTpLjf4FeCTB6G59v9eYlAX3kicXRdY+
|
||||||
|
cqhEvLFbu3MCAwEAAaMyMDAwDgYDVR0PAQH/BAQDAgCgMA0GA1UdDgQGBAQBAgME
|
||||||
|
MA8GA1UdIwQIMAaABAECAwQwCwYJKoZIhvcNAQEFA4GBABndWRIcfi+QB9Sakr+m
|
||||||
|
dYnXTgYCnFio53L2Z+6EHTGG+rEhWtUEGhL4p4pzXX4siAnjWvwcgXTo92cafcfi
|
||||||
|
uB7wRfK+NL9CTJdpN6cdL+fiNHzH8hsl3bj1nL0CSmdn2hkUWVLbLhSgWlib/I8O
|
||||||
|
aq+K7aVrgHkPnWeRiG6tl+ZA
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
1
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/empty.pem
generated
vendored
Normal file
1
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/empty.pem
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
|
||||||
2
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/emptycert.pem
generated
vendored
Normal file
2
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/emptycert.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
-----END CERTIFICATE-----LSKFSKLF
|
||||||
30
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/enc_priv_key.pem
generated
vendored
Normal file
30
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/enc_priv_key.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: AES-128-CBC,90B8A5792FA2FE75B2053582F3DF394F
|
||||||
|
|
||||||
|
yVY2xuth5fdJBg9gg+6eP3qTsr0CJ2mGEDW6rvYmYuATSRF1hVERrsznxJYjYLaw
|
||||||
|
JHec8FVr78y4aXxI/aFzlxLkS8f12WjTtIhzHwhzgSJDwVOXSRphnLAeHWnhEKLe
|
||||||
|
7kO+vzoTPIc3ECwdvtr6//z2tP1/sac+yIhL6C+x2rS5hFHhmDUXtILPxxfHJCiM
|
||||||
|
qtKiiOZz3W6008CeJMC9ZPlKHDvpq7aIL4rfVP/GkZ+/teQkgWNpMxac7+gWLKuK
|
||||||
|
109v6pu+8KT49D6SMsaZPvAb5PXcIB79ZCPI1JX0V26CKcswba4RHG/h1xifwyAF
|
||||||
|
OIvmK29mmFqbx5GPlUefRUuPwRJKCXFiK6LTdhCwLYodtXde4ibvOFYy4onGoVax
|
||||||
|
I5WVaOhQMqp+mxA6z7odrIvuFcQGixIA+peaaSbpNZSZGuxRvVefcdxPbJ+26Ijs
|
||||||
|
wq8uyalbwhKtjPTPNkMaaYzJdWS7wd2DS4RM9JT8Y1h6NTftCY3c+/txOlt5pQzW
|
||||||
|
T8n+NTd4o+PFOHzMnmEnrtf9Y/SSzXDB2OPCD95YdIXItQDdKcjK0NmnY8GNfkWL
|
||||||
|
G30NJNy3/DR7Sa5u4xuqNgcgTFhgZaOQ1IVB3p5VjknqAX3gWFu2DrqzbH45071A
|
||||||
|
He7VbdbzBpMHI2EdiCVOuK9fD/5sv25u9vVC2NHtG/YcoEQv+RB52TNHn9kdiMj1
|
||||||
|
gLaywPqGjFmaPxI0xX07BrL+D9RruUT1GAEyw4JAHuJZIyq3+V98wmV/pEqwc7hp
|
||||||
|
8WuSi6YddetfF4NPA5cGWt8qZ1it+wD/1ydQEAQsxdANqi0XVudYpYox02EoRS02
|
||||||
|
up0sd9zqz83pN9RyOOKtGcHdt85gb9DYRVeff1UszMaoVULxqxYetwtzpiHn6grL
|
||||||
|
DmnSk+DYgvXKOVt8tmSJysDTumhK1VN3xb34TYYJxeBOQJLzWFjGSELEpphZAQSj
|
||||||
|
rS4OM1FwoP48wvASGiWD4VUJ6v+6F+NDvJr01S+zWGLg1EeUZJmXGHW5GrGd4Kgx
|
||||||
|
3rdeOsrED9oXKp2cpgx9avXJ9upixja9MbAPp7RkSyeHMPvsuaI44xvOP3f0crmG
|
||||||
|
d/5CdBKVT7nFaeTGSx/78kHb3VJyopAMm9k0V3CheKwBXXSbXmV1+0muBxMHsEI3
|
||||||
|
aEKaI0y5cDfTewzo/U0l0kGtxF6kUPN1pdjFpAvssRlkGttFOC2nWxHwaNHpn7Kq
|
||||||
|
gFAlN6P4cyB6kb+LvckIYTZ/tV39dx7PfL0KG5TWjJ4a9GSoL1IrAhQq+Qv6oUEt
|
||||||
|
1vlejZoKyZ/35fni0fmeYNho+pCPimm6l+sHTuXkrWgGLr0S9O00HFLz11D7R4o9
|
||||||
|
7mF4JkMNztT+ENOdT4xQBi3OGjRGMwtE6PsQPfDeu13Vq6eDtdEGUdhW1kAsGnBi
|
||||||
|
eJRuysnGpnoWofJ7yS0+DhnS4GAVi907TMrQWwmez9V4CXl4NBc8X9T69TFL2LsW
|
||||||
|
2KU9NUXdiCRZqZHD41gd3+RuRA/oXh50V9oaow+uepwYKTFyzde5IH1/DgBd7tOd
|
||||||
|
Fa2fM5/zSA0uFPRb3yCVhRg5d6J9t5yaPAz7Jp0D1mDDGsMBD1O/FYJvWoANEwUX
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
48
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/messed_up_bundle.pem
generated
vendored
Normal file
48
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/messed_up_bundle.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEczCCAl2gAwIBAgIIDARj8BWNsscwCwYJKoZIhvcNAQELMIGMMQswCQYDVQQG
|
||||||
|
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
|
||||||
|
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
|
||||||
|
cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwMzAyMDAw
|
||||||
|
MDAwWhcNMTkwNDAxMDAwMDAwWjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs
|
||||||
|
b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT
|
||||||
|
FHsHEDAfBgNVHREEGDAWghRjbG91ZGZsYXJlLWludGVyLmNvbTALBgkqhkiG9w0B
|
||||||
|
AQsDggIBACRqAC5EJEe+8ihv1WzCUMEMb7KtS0BqoNbdXE32ia66PgJSQmHcmeJd
|
||||||
|
FI1UjL0DlljTM2tc+8KxR/1/qnKiI+W/D4wFTWOY/JWFOd15q7lXuKGl+8PMkAHF
|
||||||
|
A145JCr6oZoO9G9wUwVUrbmXAbyPCOfzsEQ2+mD9F1ZpoEjzVhtGf0R+vnYrRw8j
|
||||||
|
4WCv5AIcYRAf7HZxbhMILF1bccNlqyUtdH+/MTHXpjkjJjA5KbsHBrAEfjAXkD7c
|
||||||
|
WWOay6m7mVWb3PPFmGorP6t29baEETK9ZTZSrfD9rnExjjUCftWJEn0M4Pp98DvT
|
||||||
|
br6+bg8jwtq73qdyOfNsC/Sod18UuHH7MTQA22yqAF5jIlcYtAHGlNnl+sDPZACs
|
||||||
|
369/Z9rOL9vPFL+Z3F/uJtqZzvN1QiCkj8jWzR0u9fh3eQwZADM2RwgwS4Gs2Ygh
|
||||||
|
PsypDo33sFOwfX93KqKBsTHssn8SSDDaSnZ8bu1ATEdshbVieecuQx40UadPuJpw
|
||||||
|
EPVqTR5AhviXQ9bKrTnU5T7EgkW9vNydkpLQQlMg3QE8hsndv4loGZbZGfNtqQHS
|
||||||
|
/mg1t07S+7OEa4YaMW+wVOBOqTdW7OXlZFLfCcF5SYLM0SnlTMklRMxiqI4JqZXH
|
||||||
|
0thnUGD0JjfLX4rTaZUzT3lrXXWzpS2jzutXQkjGv4nhGGprIDuT
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEkDCCA/ugAwIBAgIIWnP9jF/2nogwCwYJKoZIhvcNAQELMH0xCzAJBgNVBAYT
|
||||||
|
AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv
|
||||||
|
MRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQG
|
||||||
|
A1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDAzMDEwMDAwMDBaFw0xNDA0MTUwMDAw
|
||||||
|
MDBaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UE
|
||||||
|
CxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET
|
||||||
|
MBEGA1UECBMKQ2FsaWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5j
|
||||||
|
b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDlCnV+vj0sVPy8SqHL
|
||||||
|
AlI+xwnPhWgzj2VevD6Nz1Zu1BeQ5m5y4CWCf+GmRGTP7+a/C510Fw6rpmInB0Ng
|
||||||
|
xxwQ2rC08fJtCnijlGH/VjEPIHY5lRaAomcM8Rgx6JOuv9BpZJKpr9pyUMV53JeW
|
||||||
|
RbWuLH5nEMdyk9NpetS2gWxt4/D20QlhK/tHkROrcLmEUddwIGdwE8JzI88c77Fu
|
||||||
|
u6pgMtHKvl4GGH0yvb4T7PvCdH8V2tCH7bt8roXd9MSyFVy7uORkfouip7EsVREU
|
||||||
|
mlcY5EvpR141KXbZqiOQiusJ+u76mEUQNk8wCR1/CW/ii9v1BKOVjXwCfEtIXjg0
|
||||||
|
APJx1VNSSH6XoDpUETL+eQ4J0FL9XNbsDuYar7+zD0N1/5vSo3HLNRQR9f0lbsys
|
||||||
|
sWBEN+CxK19xyPumr21Z0bU0f1B5H52VSF0q3I1Ju9wRo994a7YipdGcmZ2lChmT
|
||||||
|
7r3mzlBTYl3poU26q34v8wG9U7Jv4fsZJ+RGebDI+TR3QG6Yod06l9oEYZxWXBY7
|
||||||
|
STOs8wuTu3huSnan/IpWnV017Vsc61D5G+QrqcxZdXckt3anZKCF75JpUnJ7vuow
|
||||||
|
TmmHlb8KIMa9mOvcuGX4P6mz8gTi2arl/aL27kj9Q0Jgv/y1ebe2Bx2P9TF6+VND
|
||||||
|
DL3J/vSVlFeqLt2reAIBKnytLwIDAQABo4GIMIGFMA4GA1UdDwEB/wQEAwIApDAS
|
||||||
|
BgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTXXUgpaSwO9HOrQBxGqOOSFHsH
|
||||||
|
EDAfBgNVHSMEGDAWgBS4Xu+uZ1C31vMH5Wq+VbNnOg2SPjAfBgNVHREEGDAWghRj
|
||||||
|
bG91ZGZsYXJlLWludGVyLmNvbTALBgkqhkiG9w0BAQsDgYEAfPLKCAHnPzgMYLX/
|
||||||
|
fWznVvOEFAAYZByPFx4QdMBbDZUtxHyvJIBs6PdxrdSuDwSiMqE7qQIi+jzzwGl9
|
||||||
|
fC7vf45B2zCX0OW51QL2oWNBdKlGgB+b2pwyME82lX/Pr7V1GY10u+ep1xdZDnch
|
||||||
|
DaMsXjQQTJu0iuG3qKEuCmUwOmc=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
SDFSDKjkfdlsdfj
|
||||||
20
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/messed_up_priv_key.pem
generated
vendored
Normal file
20
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/messed_up_priv_key.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEAvGKyz9ZzIXI/BFrtqbVQMmKQkPZGndyfV3AzeSb2ulbS/s5k
|
||||||
|
yNJMH/jKZiSCvZiJNnW+JNlJrgLxORMmPStPz/N/0L0vCTotQKZUiaBttFgHgobQ
|
||||||
|
LFsbMnumt9It5W/uOwgWI9binuzvqyPXywLlYwOq3jkOmA22ymhflzRrl6a3jzcY
|
||||||
|
hT9evxHl0gV4bN7KZ5p4wK/UUuG1uMEQLw87lUwRRHeW3ZG52VL38+redka+f5pa
|
||||||
|
SGKyG5j0oe1NPLqAjckNgqvDdPMY2gicmCq0VSLzTNpHRsURTUSJvC/iv34vVfba
|
||||||
|
gIYgTvm8BvGbJSlZqP4kEVlOfd3vmB0ttUeoDwIDAQABAoIBAHZdpXCFlA1d1U6N
|
||||||
|
O2s4a01dNOyAcVpa9xtfelgTLU9jomtLj3PG/uHP1oxbQHKUVxKK5JAOnwbg/mQY
|
||||||
|
LhydDCbjHlovpFAt56UJXXCkBoocDYvr3P0huXL80oIJY6EXtR4ONKsMJ5Qn12c2
|
||||||
|
vC3ogey2rzO1sf/EDigbcIR3AWtk1Tx8ZDUooktOFypIsDQgjjxXiURGssAlMPSh
|
||||||
|
6GVgO4JRRG6oRxEna7yDe7izmh/hC5sxSYLsEikCgYEAsBHhb/Qef5obRCSrfFuQ
|
||||||
|
41P7MCtGrXVxKD3iCDGQCzVbEbYGpmZnGsXSaHljp2FtnamaGGEudYziozGKPHjs
|
||||||
|
pbTbsLIDbmNwxz1WcaZ1iyIjtOxcAEqDod8hY4hL6SaxypwTHn4Ydbw2NGzp11Eg
|
||||||
|
Di4SVL82utjycATdKFvBzdsCgYB/3M+GMrt0Sh87rKcQLdL709Kzjcfhvm4HjIbJ
|
||||||
|
GSXGPCZaYMKaXRTdNAjtRKxMawc9qcf0xSBEHL0GkB158TzusDQtjP1anTcYOnl6
|
||||||
|
GsO4bRivp314iNlP4r3S3bIXqBxCGH3HbrvpdPFAN//qjYmAki2lFQZywfvbQOE8
|
||||||
|
oFQHwQKBgHqJkTck2DGlXQIwA7jirLggISXjSPlsG4w4LuhY9ivyNKLUi4x5k1cE
|
||||||
|
bX7SrRtJErQ1WaDN4TFG25xnysi5h+aPinuySatd0XmA5+dE1YjTqqShMO+lUpzi
|
||||||
|
PrOQl6Eva/uw5BDAcUH4AaXTNRvvtXQptUil9qXyOh6fszikA9Mm
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
11
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/messedupcert.pem
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/messedupcert.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIB7jCCAVmgAwIBAgIBADALBgkqhkiG9w0BAQUwJjEQMA4GA1UEChMHQWNtZSBD
|
||||||
|
bzESMBAGA1UEAxMJMTI3LjAuMC4xMB4XDTEyMDkwNzIyMDAwNFoXDTEzMDkwNzIy
|
||||||
|
MDUwNFowJjEQMA4GA1UEChMHQWNtZSBDbzESMBAGA1UEAxMJMTI3LjAuMC4xMIGd
|
||||||
|
MAsGCSqGSIb3DQEBAQOBjQAwgYkCgYEAm6f+jkP2t5q/vM0YAUZZkhq/EAYD+L1C
|
||||||
|
cqhEvLFbu3MCAwEAAaMyMDAwDgYDVR0PAQH/BAQDAgCgMA0GA1UdDgQGBAQBAgME
|
||||||
|
MA8GA1UdIwQIMAaABAECAwQwCwYJKoZIhvcNAQEFA4GBABndWRIcfi+QB9Sakr+m
|
||||||
|
dYnXTgYCnFio53L2Z+6EHTGG+rEhWtUEGhL4p4pzXX4siAnjWvwcgXTo92cafcfi
|
||||||
|
uB7wRfK+NL9CTJdpN6cdL+fiNHzH8hsl3bj1nL0CSmdn2hkUWVLbLhSgWlib/I8O
|
||||||
|
aq+K7aVrgHkPnWeRiG6tl+ZA
|
||||||
|
-----END CERTIFICATE-----
|
||||||
9
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/noheadercert.pem
generated
vendored
Normal file
9
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/noheadercert.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
MIIB7jCCAVmgAwIBAgIBADALBgkqhkiG9w0BAQUwJjEQMA4GA1UEChMHQWNtZSBD
|
||||||
|
bzESMBAGA1UEAxMJMTI3LjAuMC4xMB4XDTEyMDkwNzIyMDAwNFoXDTEzMDkwNzIy
|
||||||
|
MDUwNFowJjEQMA4GA1UEChMHQWNtZSBDbzESMBAGA1UEAxMJMTI3LjAuMC4xMIGd
|
||||||
|
MAsGCSqGSIb3DQEBAQOBjQAwgYkCgYEAm6f+jkP2t5q/vM0YAUZZkhq/EAYD+L1C
|
||||||
|
cqhEvLFbu3MCAwEAAaMyMDAwDgYDVR0PAQH/BAQDAgCgMA0GA1UdDgQGBAQBAgME
|
||||||
|
MA8GA1UdIwQIMAaABAECAwQwCwYJKoZIhvcNAQEFA4GBABndWRIcfi+QB9Sakr+m
|
||||||
|
dYnXTgYCnFio53L2Z+6EHTGG+rEhWtUEGhL4p4pzXX4siAnjWvwcgXTo92cafcfi
|
||||||
|
uB7wRfK+NL9CTJdpN6cdL+fiNHzH8hsl3bj1nL0CSmdn2hkUWVLbLhSgWlib/I8O
|
||||||
|
aq+K7aVrgHkPnWeRiG6tl+ZA
|
||||||
28
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/priv_key.pem
generated
vendored
Normal file
28
Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers/testdata/priv_key.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEAvGKyz9ZzIXI/BFrtqbVQMmKQkPZGndyfV3AzeSb2ulbS/s5k
|
||||||
|
yNJMH/jKZiSCvZiJNnW+JNlJrgLxORMmPStPz/N/0L0vCTotQKZUiaBttFgHgobQ
|
||||||
|
LFsbMnumt9It5W/uOwgWI9binuzvqyPXywLlYwOq3jkOmA22ymhflzRrl6a3jzcY
|
||||||
|
hT9evxHl0gV4bN7KZ5p4wK/UUuG1uMEQLw87lUwRRHeW3ZG52VL38+redka+f5pa
|
||||||
|
SGKyG5j0oe1NPLqAjckNgqvDdPMY2gicmCq0VSLzTNpHRsURTUSJvC/iv34vVfba
|
||||||
|
gIYgTvm8BvGbJSlZqP4kEVlOfd3vmB0ttUeoDwIDAQABAoIBAHZdpXCFlA1d1U6N
|
||||||
|
O2s4a01dNOyAcVpa9xtfelgTLU9jomtLj3PG/uHP1oxbQHKUVxKK5JAOnwbg/mQY
|
||||||
|
LhydDCbjHlovpFAt56UJXXCkBoocDYvr3P0huXL80oIJY6EXtR4ONKsMJ5Qn12c2
|
||||||
|
vC3ogey2rzO1sf/EDigbcIR3AWtk1Tx8ZDUooktOFypIsDQgjjxXiURGssAlMPSh
|
||||||
|
1BFz4StRUK4bESaja0GiHwbuxHa+XYEBlK5OqMo/fpWqpgHhV/42+7wdcBMJsMr8
|
||||||
|
rFBe6m+r6TTbLSGJNowyd05XrjoAI35qduckpJ3Voun90i4ynTudjdJ/vHpIqB74
|
||||||
|
qQLFW2ECgYEA+GSRVqobaKKakNUFGmK0I5T5Tikz5f137YXXER6aLtDQNiSrlXNi
|
||||||
|
0aphkC/EfRO3oNvamq5+55bmmgDVoNNPDfpajKz+LZyG8GC2EXlTKO0hZS64KRRl
|
||||||
|
C+bd+ZsYiUDImNVRbIHN82f+BQgsgXlTaWpBOrEqmoePO/J44O4eX3cCgYEAwieq
|
||||||
|
amY4UaY+MhWPJFRK1y9M3hM8+N9N/35CFewQUdFJosC6vVQ4t8jNkSOxVQdgbNwE
|
||||||
|
i/bTBgIwg82JJYbBUPuCVeTT3i6zxymf/FLumrI73URD81IN6FiH1skg0hSFrvs0
|
||||||
|
6GVgO4JRRG6oRxEna7yDe7izmh/hC5sxSYLsEikCgYEAsBHhb/Qef5obRCSrfFuQ
|
||||||
|
41P7MCtGrXVxKD3iCDGQCzVbEbYGpmZnGsXSaHljp2FtnamaGGEudYziozGKPHjs
|
||||||
|
pbTbsLIDbmNwxz1WcaZ1iyIjtOxcAEqDod8hY4hL6SaxypwTHn4Ydbw2NGzp11Eg
|
||||||
|
Di4SVL82utjycATdKFvBzdsCgYB/3M+GMrt0Sh87rKcQLdL709Kzjcfhvm4HjIbJ
|
||||||
|
GSXGPCZaYMKaXRTdNAjtRKxMawc9qcf0xSBEHL0GkB158TzusDQtjP1anTcYOnl6
|
||||||
|
GsO4bRivp314iNlP4r3S3bIXqBxCGH3HbrvpdPFAN//qjYmAki2lFQZywfvbQOE8
|
||||||
|
oFQHwQKBgHqJkTck2DGlXQIwA7jirLggISXjSPlsG4w4LuhY9ivyNKLUi4x5k1cE
|
||||||
|
bX7SrRtJErQ1WaDN4TFG25xnysi5h+aPinuySatd0XmA5+dE1YjTqqShMO+lUpzi
|
||||||
|
PrOQl6Eva/uw5BDAcUH4AaXTNRvvtXQptUil9qXyOh6fszikA9Mm
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
// Package log implements a wrapper around the Go standard library's
|
||||||
|
// logging package. Clients should set the current log level; only
|
||||||
|
// messages below that level will actually be logged. For example, if
|
||||||
|
// Level is set to LevelWarning, only log messages at the Warning,
|
||||||
|
// Error, and Critical levels will be logged.
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
golog "log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The following constants represent logging levels in increasing levels of seriousness.
|
||||||
|
const (
|
||||||
|
LevelDebug = iota
|
||||||
|
LevelInfo
|
||||||
|
LevelWarning
|
||||||
|
LevelError
|
||||||
|
LevelCritical
|
||||||
|
)
|
||||||
|
|
||||||
|
var levelPrefix = [...]string{
|
||||||
|
LevelDebug: "[DEBUG] ",
|
||||||
|
LevelInfo: "[INFO] ",
|
||||||
|
LevelWarning: "[WARNING] ",
|
||||||
|
LevelError: "[ERROR] ",
|
||||||
|
LevelCritical: "[CRITICAL] ",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Level stores the current logging level.
|
||||||
|
var Level = LevelDebug
|
||||||
|
|
||||||
|
func outputf(l int, format string, v []interface{}) {
|
||||||
|
if l >= Level {
|
||||||
|
golog.Printf(fmt.Sprint(levelPrefix[l], format), v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func output(l int, v []interface{}) {
|
||||||
|
if l >= Level {
|
||||||
|
golog.Print(levelPrefix[l], fmt.Sprint(v...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Criticalf logs a formatted message at the "critical" level. The
|
||||||
|
// arguments are handled in the same manner as fmt.Printf.
|
||||||
|
func Criticalf(format string, v ...interface{}) {
|
||||||
|
outputf(LevelCritical, format, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Critical logs its arguments at the "critical" level.
|
||||||
|
func Critical(v ...interface{}) {
|
||||||
|
output(LevelCritical, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf logs a formatted message at the "error" level. The arguments
|
||||||
|
// are handled in the same manner as fmt.Printf.
|
||||||
|
func Errorf(format string, v ...interface{}) {
|
||||||
|
outputf(LevelError, format, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error logs its arguments at the "error" level.
|
||||||
|
func Error(v ...interface{}) {
|
||||||
|
output(LevelError, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warningf logs a formatted message at the "warning" level. The
|
||||||
|
// arguments are handled in the same manner as fmt.Printf.
|
||||||
|
func Warningf(format string, v ...interface{}) {
|
||||||
|
outputf(LevelWarning, format, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning logs its arguments at the "warning" level.
|
||||||
|
func Warning(v ...interface{}) {
|
||||||
|
output(LevelWarning, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof logs a formatted message at the "info" level. The arguments
|
||||||
|
// are handled in the same manner as fmt.Printf.
|
||||||
|
func Infof(format string, v ...interface{}) {
|
||||||
|
outputf(LevelInfo, format, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info logs its arguments at the "info" level.
|
||||||
|
func Info(v ...interface{}) {
|
||||||
|
output(LevelInfo, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugf logs a formatted message at the "debug" level. The arguments
|
||||||
|
// are handled in the same manner as fmt.Printf.
|
||||||
|
func Debugf(format string, v ...interface{}) {
|
||||||
|
outputf(LevelDebug, format, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug logs its arguments at the "debug" level.
|
||||||
|
func Debug(v ...interface{}) {
|
||||||
|
output(LevelDebug, v)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
package log
|
||||||
165
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/local.go
generated
vendored
Normal file
165
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/local.go
generated
vendored
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
// Package local implements certificate signature functionality for CF-SSL.
|
||||||
|
package local
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
|
||||||
|
cferr "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Signer contains a signer that uses the standard library to
|
||||||
|
// support both ECDSA and RSA CA keys.
|
||||||
|
type Signer struct {
|
||||||
|
ca *x509.Certificate
|
||||||
|
priv crypto.Signer
|
||||||
|
policy *config.Signing
|
||||||
|
sigAlgo x509.SignatureAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSigner creates a new Signer directly from a
|
||||||
|
// private key and certificate, with optional policy.
|
||||||
|
func NewSigner(priv crypto.Signer, cert *x509.Certificate, sigAlgo x509.SignatureAlgorithm, policy *config.Signing) (*Signer, error) {
|
||||||
|
if policy == nil {
|
||||||
|
policy = &config.Signing{
|
||||||
|
Profiles: map[string]*config.SigningProfile{},
|
||||||
|
Default: config.DefaultConfig()}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !policy.Valid() {
|
||||||
|
return nil, cferr.New(cferr.PolicyError, cferr.InvalidPolicy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Signer{
|
||||||
|
ca: cert,
|
||||||
|
priv: priv,
|
||||||
|
sigAlgo: sigAlgo,
|
||||||
|
policy: policy,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSignerFromFile generates a new local signer from a caFile
|
||||||
|
// and a caKey file, both PEM encoded.
|
||||||
|
func NewSignerFromFile(caFile, caKeyFile string, policy *config.Signing) (*Signer, error) {
|
||||||
|
log.Debug("Loading CA: ", caFile)
|
||||||
|
ca, err := ioutil.ReadFile(caFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debug("Loading CA key: ", caKeyFile)
|
||||||
|
cakey, err := ioutil.ReadFile(caKeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cferr.Wrap(cferr.CertificateError, cferr.ReadFailed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedCa, err := helpers.ParseCertificatePEM(ca)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, err := helpers.ParsePrivateKeyPEM(cakey)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Malformed private key %v", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewSigner(priv, parsedCa, signer.DefaultSigAlgo(priv), policy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Signer) sign(template *x509.Certificate, profile *config.SigningProfile) (cert []byte, err error) {
|
||||||
|
err = signer.FillTemplate(template, s.policy.Default, profile)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
serialNumber := template.SerialNumber
|
||||||
|
var initRoot bool
|
||||||
|
if s.ca == nil {
|
||||||
|
if !template.IsCA {
|
||||||
|
err = cferr.New(cferr.PolicyError, cferr.InvalidRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
template.DNSNames = nil
|
||||||
|
s.ca = template
|
||||||
|
initRoot = true
|
||||||
|
template.MaxPathLen = signer.MaxPathLen
|
||||||
|
} else if template.IsCA {
|
||||||
|
template.MaxPathLen = 1
|
||||||
|
template.DNSNames = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
derBytes, err := x509.CreateCertificate(rand.Reader, template, s.ca, template.PublicKey, s.priv)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if initRoot {
|
||||||
|
s.ca, err = x509.ParseCertificate(derBytes)
|
||||||
|
if err != nil {
|
||||||
|
err = cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||||
|
log.Infof("signed certificate with serial number %s", serialNumber)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign signs a new certificate based on the PEM-encoded client
|
||||||
|
// certificate or certificate request with the signing profile,
|
||||||
|
// specified by profileName.
|
||||||
|
func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
|
||||||
|
profile := s.policy.Profiles[req.Profile]
|
||||||
|
if profile == nil {
|
||||||
|
profile = s.policy.Default
|
||||||
|
}
|
||||||
|
|
||||||
|
block, _ := pem.Decode([]byte(req.Request))
|
||||||
|
if block == nil {
|
||||||
|
return nil, cferr.New(cferr.CertificateError, cferr.DecodeFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if block.Type != "CERTIFICATE REQUEST" {
|
||||||
|
return nil, cferr.Wrap(cferr.CertificateError,
|
||||||
|
cferr.BadRequest, errors.New("not a certificate or csr"))
|
||||||
|
}
|
||||||
|
|
||||||
|
template, err := signer.ParseCertificateRequest(s, block.Bytes, req.Subject, req.Hosts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.sign(template, profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SigAlgo returns the RSA signer's signature algorithm.
|
||||||
|
func (s *Signer) SigAlgo() x509.SignatureAlgorithm {
|
||||||
|
return s.sigAlgo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Certificate returns the signer's certificate.
|
||||||
|
func (s *Signer) Certificate(label, profile string) (*x509.Certificate, error) {
|
||||||
|
if label != "" {
|
||||||
|
return nil, cferr.NewBadRequestString("label specified for local operation")
|
||||||
|
}
|
||||||
|
cert := *s.ca
|
||||||
|
return &cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPolicy sets the signer's signature policy.
|
||||||
|
func (s *Signer) SetPolicy(policy *config.Signing) {
|
||||||
|
s.policy = policy
|
||||||
|
}
|
||||||
|
|
||||||
|
// Policy returns the signer's policy.
|
||||||
|
func (s *Signer) Policy() *config.Signing {
|
||||||
|
return s.policy
|
||||||
|
}
|
||||||
555
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/local_test.go
generated
vendored
Normal file
555
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/local_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,555 @@
|
||||||
|
package local
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"io/ioutil"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/csr"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testCaFile = "testdata/ca.pem"
|
||||||
|
testCaKeyFile = "testdata/ca_key.pem"
|
||||||
|
testECDSACaFile = "testdata/ecdsa256_ca.pem"
|
||||||
|
testECDSACaKeyFile = "testdata/ecdsa256_ca_key.pem"
|
||||||
|
)
|
||||||
|
|
||||||
|
var expiry = 1 * time.Minute
|
||||||
|
|
||||||
|
// Start a signer with the testing RSA CA cert and key.
|
||||||
|
func newTestSigner(t *testing.T) (s *Signer) {
|
||||||
|
s, err := NewSignerFromFile(testCaFile, testCaKeyFile, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewSignerFromFilePolicy(t *testing.T) {
|
||||||
|
var CAConfig = &config.Config{
|
||||||
|
Signing: &config.Signing{
|
||||||
|
Profiles: map[string]*config.SigningProfile{
|
||||||
|
"signature": &config.SigningProfile{
|
||||||
|
Usage: []string{"digital signature"},
|
||||||
|
Expiry: expiry,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Default: &config.SigningProfile{
|
||||||
|
Usage: []string{"cert sign", "crl sign"},
|
||||||
|
ExpiryString: "43800h",
|
||||||
|
Expiry: expiry,
|
||||||
|
CA: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := NewSignerFromFile(testCaFile, testCaKeyFile, CAConfig.Signing)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewSignerFromFileInvalidPolicy(t *testing.T) {
|
||||||
|
var invalidConfig = &config.Config{
|
||||||
|
Signing: &config.Signing{
|
||||||
|
Profiles: map[string]*config.SigningProfile{
|
||||||
|
"invalid": &config.SigningProfile{
|
||||||
|
Usage: []string{"wiretapping"},
|
||||||
|
Expiry: expiry,
|
||||||
|
},
|
||||||
|
"empty": &config.SigningProfile{},
|
||||||
|
},
|
||||||
|
Default: &config.SigningProfile{
|
||||||
|
Usage: []string{"digital signature"},
|
||||||
|
Expiry: expiry,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := NewSignerFromFile(testCaFile, testCaKeyFile, invalidConfig.Signing)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(err.Error(), `"code":5200`) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewSignerFromFileNoUsageInPolicy(t *testing.T) {
|
||||||
|
var invalidConfig = &config.Config{
|
||||||
|
Signing: &config.Signing{
|
||||||
|
Profiles: map[string]*config.SigningProfile{
|
||||||
|
"invalid": &config.SigningProfile{
|
||||||
|
Usage: []string{},
|
||||||
|
Expiry: expiry,
|
||||||
|
},
|
||||||
|
"empty": &config.SigningProfile{},
|
||||||
|
},
|
||||||
|
Default: &config.SigningProfile{
|
||||||
|
Usage: []string{"digital signature"},
|
||||||
|
Expiry: expiry,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := NewSignerFromFile(testCaFile, testCaKeyFile, invalidConfig.Signing)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expect InvalidPolicy error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(err.Error(), `"code":5200`) {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewSignerFromFileEdgeCases(t *testing.T) {
|
||||||
|
|
||||||
|
res, err := NewSignerFromFile("nil", "nil", nil)
|
||||||
|
if res != nil && err == nil {
|
||||||
|
t.Fatal("Incorrect inputs failed to produce correct results")
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err = NewSignerFromFile(testCaFile, "nil", nil)
|
||||||
|
if res != nil && err == nil {
|
||||||
|
t.Fatal("Incorrect inputs failed to produce correct results")
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err = NewSignerFromFile("../../helpers/testdata/messedupcert.pem", "local.go", nil)
|
||||||
|
if res != nil && err == nil {
|
||||||
|
t.Fatal("Incorrect inputs failed to produce correct results")
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err = NewSignerFromFile("../../helpers/testdata/cert.pem", "../../helpers/testdata/messed_up_priv_key.pem", nil)
|
||||||
|
if res != nil && err == nil {
|
||||||
|
t.Fatal("Incorrect inputs failed to produce correct results")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test the private method
|
||||||
|
func testSign(t *testing.T) {
|
||||||
|
signer, err := NewSignerFromFile("testdata/ca.pem", "testdata/ca_key.pem", nil)
|
||||||
|
if signer == nil || err != nil {
|
||||||
|
t.Fatal("Failed to produce signer")
|
||||||
|
}
|
||||||
|
|
||||||
|
pem, _ := ioutil.ReadFile("../../helpers/testdata/cert.pem")
|
||||||
|
cert, _ := helpers.ParseCertificatePEM(pem)
|
||||||
|
|
||||||
|
badcert := *cert
|
||||||
|
badcert.PublicKey = nil
|
||||||
|
profl := config.SigningProfile{Usage: []string{"Certificates", "Rule"}}
|
||||||
|
_, err = signer.sign(&badcert, &profl)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Improper input failed to raise an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// nil profile
|
||||||
|
_, err = signer.sign(cert, &profl)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Nil profile failed to raise an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// empty profile
|
||||||
|
_, err = signer.sign(cert, &config.SigningProfile{})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Empty profile failed to raise an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// empty expiry
|
||||||
|
prof := signer.policy.Default
|
||||||
|
prof.Expiry = 0
|
||||||
|
_, err = signer.sign(cert, prof)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("nil expiry raised an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// non empty urls
|
||||||
|
prof = signer.policy.Default
|
||||||
|
prof.CRL = "stuff"
|
||||||
|
prof.OCSP = "stuff"
|
||||||
|
prof.IssuerURL = []string{"stuff"}
|
||||||
|
_, err = signer.sign(cert, prof)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("non nil urls raised an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// nil ca
|
||||||
|
nilca := *signer
|
||||||
|
prof = signer.policy.Default
|
||||||
|
prof.CA = false
|
||||||
|
nilca.ca = nil
|
||||||
|
_, err = nilca.sign(cert, prof)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("nil ca with isca false raised an error")
|
||||||
|
}
|
||||||
|
prof.CA = true
|
||||||
|
_, err = nilca.sign(cert, prof)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("nil ca with CA true raised an error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSign(t *testing.T) {
|
||||||
|
testSign(t)
|
||||||
|
s, err := NewSignerFromFile("testdata/ca.pem", "testdata/ca_key.pem", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to produce signer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test the empty request
|
||||||
|
_, err = s.Sign(signer.SignRequest{})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Empty request failed to produce an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
// not a csr
|
||||||
|
certPem, err := ioutil.ReadFile("../../helpers/testdata/cert.pem")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// csr with ip as hostname
|
||||||
|
pem, err := ioutil.ReadFile("testdata/ip.csr")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// improper request
|
||||||
|
validReq := signer.SignRequest{Hosts: signer.SplitHosts(testHostName), Request: string(certPem)}
|
||||||
|
_, err = s.Sign(validReq)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("A bad case failed to raise an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
validReq = signer.SignRequest{Hosts: signer.SplitHosts("128.84.126.213"), Request: string(pem)}
|
||||||
|
_, err = s.Sign(validReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("A bad case failed to raise an error")
|
||||||
|
}
|
||||||
|
|
||||||
|
pem, err = ioutil.ReadFile("testdata/ex.csr")
|
||||||
|
validReq = signer.SignRequest{
|
||||||
|
Request: string(pem),
|
||||||
|
Hosts: []string{"example.com"},
|
||||||
|
}
|
||||||
|
s.Sign(validReq)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Failed to sign")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCertificate(t *testing.T) {
|
||||||
|
s, err := NewSignerFromFile("testdata/ca.pem", "testdata/ca_key.pem", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := s.Certificate("", "")
|
||||||
|
if !reflect.DeepEqual(*c, *s.ca) || err != nil {
|
||||||
|
t.Fatal("Certificate() producing incorrect results")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPolicy(t *testing.T) {
|
||||||
|
s, err := NewSignerFromFile("testdata/ca.pem", "testdata/ca_key.pem", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sgn := config.Signing{}
|
||||||
|
|
||||||
|
s.SetPolicy(&sgn)
|
||||||
|
if s.Policy() != &sgn {
|
||||||
|
t.Fatal("Policy is malfunctioning")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCustomSigner(t *testing.T, testCaFile, testCaKeyFile string) (s *Signer) {
|
||||||
|
s, err := NewSignerFromFile(testCaFile, testCaKeyFile, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewSignerFromFile(t *testing.T) {
|
||||||
|
newTestSigner(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
testHostName = "localhost"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testSignFile(t *testing.T, certFile string) ([]byte, error) {
|
||||||
|
s := newTestSigner(t)
|
||||||
|
|
||||||
|
pem, err := ioutil.ReadFile(certFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Sign(signer.SignRequest{Hosts: signer.SplitHosts(testHostName), Request: string(pem)})
|
||||||
|
}
|
||||||
|
|
||||||
|
type csrTest struct {
|
||||||
|
file string
|
||||||
|
keyAlgo string
|
||||||
|
keyLen int
|
||||||
|
// Error checking function
|
||||||
|
errorCallback func(*testing.T, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helper function that returns a errorCallback function which expects an error.
|
||||||
|
func ExpectError() func(*testing.T, error) {
|
||||||
|
return func(t *testing.T, err error) {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Expected error. Got nothing.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var csrTests = []csrTest{
|
||||||
|
{
|
||||||
|
file: "testdata/rsa2048.csr",
|
||||||
|
keyAlgo: "rsa",
|
||||||
|
keyLen: 2048,
|
||||||
|
errorCallback: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "testdata/rsa3072.csr",
|
||||||
|
keyAlgo: "rsa",
|
||||||
|
keyLen: 3072,
|
||||||
|
errorCallback: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "testdata/rsa4096.csr",
|
||||||
|
keyAlgo: "rsa",
|
||||||
|
keyLen: 4096,
|
||||||
|
errorCallback: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "testdata/ecdsa256.csr",
|
||||||
|
keyAlgo: "ecdsa",
|
||||||
|
keyLen: 256,
|
||||||
|
errorCallback: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "testdata/ecdsa384.csr",
|
||||||
|
keyAlgo: "ecdsa",
|
||||||
|
keyLen: 384,
|
||||||
|
errorCallback: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "testdata/ecdsa521.csr",
|
||||||
|
keyAlgo: "ecdsa",
|
||||||
|
keyLen: 521,
|
||||||
|
errorCallback: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignCSRs(t *testing.T) {
|
||||||
|
s := newTestSigner(t)
|
||||||
|
hostname := "cloudflare.com"
|
||||||
|
for _, test := range csrTests {
|
||||||
|
csr, err := ioutil.ReadFile(test.file)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("CSR loading error:", err)
|
||||||
|
}
|
||||||
|
// It is possible to use different SHA2 algorithm with RSA CA key.
|
||||||
|
rsaSigAlgos := []x509.SignatureAlgorithm{x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA}
|
||||||
|
for _, sigAlgo := range rsaSigAlgos {
|
||||||
|
s.sigAlgo = sigAlgo
|
||||||
|
certBytes, err := s.Sign(signer.SignRequest{Hosts: signer.SplitHosts(hostname), Request: string(csr)})
|
||||||
|
if test.errorCallback != nil {
|
||||||
|
test.errorCallback(t, err)
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no error. Got %s. Param %s %d", err.Error(), test.keyAlgo, test.keyLen)
|
||||||
|
}
|
||||||
|
cert, _ := helpers.ParseCertificatePEM(certBytes)
|
||||||
|
if cert.SignatureAlgorithm != s.SigAlgo() {
|
||||||
|
t.Fatal("Cert Signature Algorithm does not match the issuer.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestECDSASigner(t *testing.T) {
|
||||||
|
s := newCustomSigner(t, testECDSACaFile, testECDSACaKeyFile)
|
||||||
|
hostname := "cloudflare.com"
|
||||||
|
for _, test := range csrTests {
|
||||||
|
csr, err := ioutil.ReadFile(test.file)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("CSR loading error:", err)
|
||||||
|
}
|
||||||
|
// Try all ECDSA SignatureAlgorithm
|
||||||
|
SigAlgos := []x509.SignatureAlgorithm{x509.ECDSAWithSHA1, x509.ECDSAWithSHA256, x509.ECDSAWithSHA384, x509.ECDSAWithSHA512}
|
||||||
|
for _, sigAlgo := range SigAlgos {
|
||||||
|
s.sigAlgo = sigAlgo
|
||||||
|
certBytes, err := s.Sign(signer.SignRequest{Hosts: signer.SplitHosts(hostname), Request: string(csr)})
|
||||||
|
if test.errorCallback != nil {
|
||||||
|
test.errorCallback(t, err)
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no error. Got %s. Param %s %d", err.Error(), test.keyAlgo, test.keyLen)
|
||||||
|
}
|
||||||
|
cert, _ := helpers.ParseCertificatePEM(certBytes)
|
||||||
|
if cert.SignatureAlgorithm != s.SigAlgo() {
|
||||||
|
t.Fatal("Cert Signature Algorithm does not match the issuer.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ecdsaInterCSR = "testdata/ecdsa256-inter.csr"
|
||||||
|
ecdsaInterKey = "testdata/ecdsa256-inter.key"
|
||||||
|
rsaInterCSR = "testdata/rsa2048-inter.csr"
|
||||||
|
rsaInterKey = "testdata/rsa2048-inter.key"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCAIssuing(t *testing.T) {
|
||||||
|
var caCerts = []string{testCaFile, testECDSACaFile}
|
||||||
|
var caKeys = []string{testCaKeyFile, testECDSACaKeyFile}
|
||||||
|
var interCSRs = []string{ecdsaInterCSR, rsaInterCSR}
|
||||||
|
var interKeys = []string{ecdsaInterKey, rsaInterKey}
|
||||||
|
var CAPolicy = &config.Signing{
|
||||||
|
Default: &config.SigningProfile{
|
||||||
|
Usage: []string{"cert sign", "crl sign"},
|
||||||
|
ExpiryString: "1h",
|
||||||
|
Expiry: 1 * time.Hour,
|
||||||
|
CA: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var hostname = "cloudflare-inter.com"
|
||||||
|
// Each RSA or ECDSA root CA issues two intermediate CAs (one ECDSA and one RSA).
|
||||||
|
// For each intermediate CA, use it to issue additional RSA and ECDSA intermediate CSRs.
|
||||||
|
for i, caFile := range caCerts {
|
||||||
|
caKeyFile := caKeys[i]
|
||||||
|
s := newCustomSigner(t, caFile, caKeyFile)
|
||||||
|
s.policy = CAPolicy
|
||||||
|
for j, csr := range interCSRs {
|
||||||
|
csrBytes, _ := ioutil.ReadFile(csr)
|
||||||
|
certBytes, err := s.Sign(signer.SignRequest{Hosts: signer.SplitHosts(hostname), Request: string(csrBytes)})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
interCert, err := helpers.ParseCertificatePEM(certBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
keyBytes, _ := ioutil.ReadFile(interKeys[j])
|
||||||
|
interKey, _ := helpers.ParsePrivateKeyPEM(keyBytes)
|
||||||
|
interSigner := &Signer{interCert, interKey, CAPolicy, signer.DefaultSigAlgo(interKey)}
|
||||||
|
for _, anotherCSR := range interCSRs {
|
||||||
|
anotherCSRBytes, _ := ioutil.ReadFile(anotherCSR)
|
||||||
|
bytes, err := interSigner.Sign(
|
||||||
|
signer.SignRequest{
|
||||||
|
Hosts: signer.SplitHosts(hostname),
|
||||||
|
Request: string(anotherCSRBytes),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
cert, err := helpers.ParseCertificatePEM(bytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if cert.SignatureAlgorithm != interSigner.SigAlgo() {
|
||||||
|
t.Fatal("Cert Signature Algorithm does not match the issuer.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const testCSR = "testdata/ecdsa256.csr"
|
||||||
|
|
||||||
|
func TestOverrideSubject(t *testing.T) {
|
||||||
|
csrPEM, err := ioutil.ReadFile(testCSR)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &signer.Subject{
|
||||||
|
Names: []csr.Name{
|
||||||
|
{O: "example.net"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
s := newCustomSigner(t, testECDSACaFile, testECDSACaKeyFile)
|
||||||
|
|
||||||
|
request := signer.SignRequest{
|
||||||
|
Hosts: []string{"127.0.0.1", "localhost"},
|
||||||
|
Request: string(csrPEM),
|
||||||
|
Subject: req,
|
||||||
|
}
|
||||||
|
certPEM, err := s.Sign(request)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := helpers.ParseCertificatePEM(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cert.Subject.Organization[0] != "example.net" {
|
||||||
|
t.Fatalf("Failed to override subject: want example.net but have %s", cert.Subject.Organization[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Overrode subject info")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOverwriteHosts(t *testing.T) {
|
||||||
|
csrPEM, err := ioutil.ReadFile(testCSR)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := newCustomSigner(t, testECDSACaFile, testECDSACaKeyFile)
|
||||||
|
|
||||||
|
request := signer.SignRequest{
|
||||||
|
Hosts: []string{"127.0.0.1", "localhost"},
|
||||||
|
Request: string(csrPEM),
|
||||||
|
Subject: nil,
|
||||||
|
}
|
||||||
|
certPEM, err := s.Sign(request)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := helpers.ParseCertificatePEM(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the hosts, and add the ips
|
||||||
|
hosts, ips := cert.DNSNames, cert.IPAddresses
|
||||||
|
for _, ip := range ips {
|
||||||
|
hosts = append(hosts, ip.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare the sorted host lists
|
||||||
|
sort.Strings(hosts)
|
||||||
|
sort.Strings(request.Hosts)
|
||||||
|
if !reflect.DeepEqual(hosts, request.Hosts) {
|
||||||
|
t.Fatalf("Hosts not the same. cert hosts: %v, expected: %v", hosts, request.Hosts)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Overwrote hosts")
|
||||||
|
|
||||||
|
}
|
||||||
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ca.pem
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ca.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEmzCCA4OgAwIBAgIMAMSvNBgypwaaSQ5iMA0GCSqGSIb3DQEBBQUAMIGMMQsw
|
||||||
|
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
|
||||||
|
YW5jaXNjbzETMBEGA1UEChMKQ0ZTU0wgVEVTVDEbMBkGA1UEAxMSQ0ZTU0wgVEVT
|
||||||
|
VCBSb290IENBMR4wHAYJKoZIhvcNAQkBFg90ZXN0QHRlc3QubG9jYWwwHhcNMTIx
|
||||||
|
MjEyMDIxMDMxWhcNMjIxMDIxMDIxMDMxWjCBjDELMAkGA1UEBhMCVVMxEzARBgNV
|
||||||
|
BAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAoT
|
||||||
|
CkNGU1NMIFRFU1QxGzAZBgNVBAMTEkNGU1NMIFRFU1QgUm9vdCBDQTEeMBwGCSqG
|
||||||
|
SIb3DQEJARYPdGVzdEB0ZXN0LmxvY2FsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||||
|
MIIBCgKCAQEAsRp1xSfIDoD/40Bo4Hls3sFn4dav5NgxbZGpVyGF7dJI9u0eEnL4
|
||||||
|
BUGssPaUFLWC83CZxujUEiEfE0oKX+uOhhGv3+j5xSTNM764m2eSiN53cdZtK05d
|
||||||
|
hwq9uS8LtjKOQeN1mQ5qmiqxBMdjkKgMsVw5lMCgoYKo57kaKFyXzdpNVDzqw+pt
|
||||||
|
HWmuNtDQjK3qT5Ma06mYPmIGYhIZYLY7oJGg9ZEaNR0GIw4zIT5JRsNiaSb5wTLw
|
||||||
|
aa0n/4vLJyVjLJcYmJBvZWj8g+taK+C4INu/jGux+bmsC9hq14tbOaTNAn/NE0qN
|
||||||
|
8oHwcRBEqfOdEYdZkxI5NWPiKNW/Q+AeXQIDAQABo4H6MIH3MB0GA1UdDgQWBBS3
|
||||||
|
0veEuqg51fusEM4p/YuWpBPsvTCBxAYDVR0jBIG8MIG5gBS30veEuqg51fusEM4p
|
||||||
|
/YuWpBPsvaGBkqSBjzCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3Ju
|
||||||
|
aWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAoTCkNGU1NMIFRFU1Qx
|
||||||
|
GzAZBgNVBAMTEkNGU1NMIFRFU1QgUm9vdCBDQTEeMBwGCSqGSIb3DQEJARYPdGVz
|
||||||
|
dEB0ZXN0LmxvY2FsggwAxK80GDKnBppJDmIwDwYDVR0TBAgwBgEB/wIBADANBgkq
|
||||||
|
hkiG9w0BAQUFAAOCAQEAJ7r1EZYDwed6rS0+YKHdkRGRQ5Rz6A9DIVBPXrSMAGj3
|
||||||
|
F5EF2m/GJbhpVbnNJTVlgP9DDyabOZNxzdrCr4cHMkYYnocDdgAodnkw6GZ/GJTc
|
||||||
|
depbVTR4TpihFNzeDEGJePrEwM1DouGswpu97jyuCYZ3z1a60+a+3C1GwWaJ7Aet
|
||||||
|
Uqm+yLTUrMISsfnDPqJdM1NeqW3jiZ4IgcqJkieCCSpag9Xuzrp9q6rjmePvlQkv
|
||||||
|
qz020JGg6VijJ+c6Tf5y0XqbAhkBTqYtVamu9gEth9utn12EhdNjTZMPKMjjgFUd
|
||||||
|
H0N6yOEuQMl4ky7RxZBM0iPyeob6i4z2LEQilgv9MQ==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ca_key.pem
generated
vendored
Normal file
28
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ca_key.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCxGnXFJ8gOgP/j
|
||||||
|
QGjgeWzewWfh1q/k2DFtkalXIYXt0kj27R4ScvgFQayw9pQUtYLzcJnG6NQSIR8T
|
||||||
|
Sgpf646GEa/f6PnFJM0zvribZ5KI3ndx1m0rTl2HCr25Lwu2Mo5B43WZDmqaKrEE
|
||||||
|
x2OQqAyxXDmUwKChgqjnuRooXJfN2k1UPOrD6m0daa420NCMrepPkxrTqZg+YgZi
|
||||||
|
EhlgtjugkaD1kRo1HQYjDjMhPklGw2JpJvnBMvBprSf/i8snJWMslxiYkG9laPyD
|
||||||
|
61or4Lgg27+Ma7H5uawL2GrXi1s5pM0Cf80TSo3ygfBxEESp850Rh1mTEjk1Y+Io
|
||||||
|
1b9D4B5dAgMBAAECggEAKHhjcSomDSptTwDo9mLI/h40HudwSlsc8GzYxZBjinUD
|
||||||
|
N2n39T9QbeMUE1xFenX/9qFEgq+xxnLLJx1EQacSapCgIAqdCO/f9HMgvGJumdg8
|
||||||
|
c0cMq1i9Bp7tu+OESZ5D48qWlOM2eQRIb08g8W11eRIaFmPuUPoKnuktkQuXpPJc
|
||||||
|
YbS/+JuA8SDwe6sV0cMCQuS+iHFfeGwWCKrDUkhLwcL3waW3od2XFyOeFFWFhl0h
|
||||||
|
HmM/mWKRuRdqR7hrmArTwFZVkB+o/1ywVYXIv+JQm0eNZ5PKLNJGL2f5oxbMR/JI
|
||||||
|
AoK0bAlJmYaFp96h1KpbPwLEL/0hHSWA7sAyJIgQAQKBgQDaEAZor/w4ZUTekT1+
|
||||||
|
cbId0yA+ikDXQOfXaNCSh9Pex+Psjd5zVVOqyVFJ29daRju3d7rmpN4Cm5V4h0l1
|
||||||
|
/2ad207rjCAnpCHtaddJWNyJzF2IL2IaoCZQRp0k7zOjBGQpoWDTwBaEin5CCv3P
|
||||||
|
kkdQkKz6FDP1xskHSLZr21/QCQKBgQDP6jXutEgGjf3yKpMFk/69EamJdon8clbt
|
||||||
|
hl7cOyWtobnZhdOWVZPe00Oo3Jag2aWgFFsm3EtwnUCnR4d4+fXRKS2LkhfIUZcz
|
||||||
|
cKy17Ileggdd8UGhL4RDrF/En9tJL86WcVkcoOrqLcGB2FLWrVhVpHFK74eLMCH/
|
||||||
|
uc/+ioPItQKBgHYoDsD08s7AGMQcoNx90MyWVLduhFnegoFW+wUa8jOZzieka6/E
|
||||||
|
wVQeR5yksZjpy3vLNYu6M83n7eLkM2rrm/fXGHlLcTTpm7SgEBZfPwivotKjEh5p
|
||||||
|
PrlqucWEk082lutz1RqHz+u7e1Rfzk2F7nx6GDBdeBYpw03eGXJx6QW5AoGBAIJq
|
||||||
|
4puyAEAET1fZNtHX7IGCk7sDXTi6LCbgE57HhzHr8V0t4fQ6CABMuvMwM1gATjEk
|
||||||
|
s6yjoLqqGUUUzDipanViBAy5fiuManC868lN7zkWDTLzQ3ytBqVAee4na/DziP27
|
||||||
|
ae9YTSLJwskE/alloLRP6zTbHUXE0n7LelmrX1DFAoGBAMFLl+Lu+WFgCHxBjn43
|
||||||
|
rHpJbQZQmsFhAMhkN4hsj6dJfAGn2gRLRiVRAika+8QF65xMZiVQWUVSUZADWERi
|
||||||
|
0SXGjzN1wYxO3Qzy3LYwws6fxFAq5lo79eb38yFT2lHdqK3x/QgiDSRVl+R6cExV
|
||||||
|
xQB518/lp2eIeMpglWByDwJX
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
10
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ecdsa256-inter.csr
generated
vendored
Normal file
10
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ecdsa256-inter.csr
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIBezCCASECAQAwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
|
||||||
|
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
|
||||||
|
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMR0wGwYDVQQDExRjbG91ZGZsYXJl
|
||||||
|
LWludGVyLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgOKlWwIAIeURde
|
||||||
|
yvDMhgfn6xPp1gn8oUeLmsniBm7I+j84IsVzUso8/MpjMZ9nB8lQUanhv3Kmqcyj
|
||||||
|
HNj+iFegMjAwBgkqhkiG9w0BCQ4xIzAhMB8GA1UdEQQYMBaCFGNsb3VkZmxhcmUt
|
||||||
|
aW50ZXIuY29tMAoGCCqGSM49BAMCA0gAMEUCIEJcy2mn2YyK8lVE+HHmr2OsmdbH
|
||||||
|
4CLDVXFBwxke8ObqAiEAx/il1cDKvQ/I36b4XjBnOX2jcQ5oaCNPFFBE74WQ/ps=
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
5
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ecdsa256-inter.key
generated
vendored
Normal file
5
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ecdsa256-inter.key
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEILbwI4u4bw+HtafMqFnrL7LOrqNEZH5rW5ygSrigfrVLoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEuA4qVbAgAh5RF17K8MyGB+frE+nWCfyhR4uayeIGbsj6PzgixXNS
|
||||||
|
yjz8ymMxn2cHyVBRqeG/cqapzKMc2P6IVw==
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
11
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ecdsa256.csr
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ecdsa256.csr
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIBgTCCASgCAQAwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
|
||||||
|
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
|
||||||
|
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRcwFQYDVQQDEw5jbG91ZGZsYXJl
|
||||||
|
LmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBn9Ldie6BOcMHezn2dPuYqW
|
||||||
|
z/NoLYMLGNBqhOxUyEidYClI0JW2pWyUgT3A2UazFp1WgE94y7Z+2YlfRz+vcrKg
|
||||||
|
PzA9BgkqhkiG9w0BCQ4xMDAuMCwGA1UdEQQlMCOCDmNsb3VkZmxhcmUuY29tghF3
|
||||||
|
d3djbG91ZGZsYXJlLmNvbTAKBggqhkjOPQQDAgNHADBEAiBM+QRxe8u6rkdr10Jy
|
||||||
|
cxbR6NxrGrNeg5QqiOqF96JEmgIgDbtjd5e3y3I8W/+ih2us3WtMxgnTXfqPd48i
|
||||||
|
VLcv28Q=
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
20
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ecdsa256_ca.pem
generated
vendored
Normal file
20
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ecdsa256_ca.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDUzCCAj2gAwIBAgIIbjeSyheUvjYwCwYJKoZIhvcNAQELMIGMMQswCQYDVQQG
|
||||||
|
EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj
|
||||||
|
bzETMBEGA1UEChMKQ0ZTU0wgVEVTVDEbMBkGA1UEAxMSQ0ZTU0wgVEVTVCBSb290
|
||||||
|
IENBMR4wHAYJKoZIhvcNAQkBFg90ZXN0QHRlc3QubG9jYWwwHhcNMTQwNTI0MDQ1
|
||||||
|
MTQwWhcNMTUwNTI0MDQ1NjQwWjCBizELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs
|
||||||
|
b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT
|
||||||
|
DVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHDAaBgNVBAMTE2Ns
|
||||||
|
b3VkZmxhcmUtbGVhZi5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASMRv3x
|
||||||
|
vcv4I5QF7we+23hES2waKDffBRhQMVVAOSIJcpb4JnzcVJiPJjNlMPbczi5vbzkQ
|
||||||
|
K2kkjOP+okqQia3go4GGMIGDMA4GA1UdDwEB/wQEAwIABDAdBgNVHSUEFjAUBggr
|
||||||
|
BgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQU
|
||||||
|
4t+cr91ma5IxOPeiezgN8W9FBNowHwYDVR0jBBgwFoAUt9L3hLqoOdX7rBDOKf2L
|
||||||
|
lqQT7L0wCwYJKoZIhvcNAQELA4IBAQAWloyDhrcYFSaZjzb8+UKxnukPUzd7BGaX
|
||||||
|
BvLktbN7hrX+z+ntA5UgXWo7uNgf2L3VwS0mVnRowwmrGV8Pbw9FX5WSisBQ+JJJ
|
||||||
|
JC4ABYT2N7N+B488zKZuMZY8NmSR/ples0Suz3oArUn4ZBGxANyOR6haBbYfupDF
|
||||||
|
LaCtAdQwZzNPfHAo2NsENSOlzGVhV0r1ZqalzkBf70K0KuAoLRbNG3Og17UeMb8K
|
||||||
|
5sXa7WvubgZ7/D3lr//F56yJYyfTq8SWcIi4e9AUWY5qK+Sr+7W9/gSY3baaHxY9
|
||||||
|
T9SO4O1ENFJ8ecWRPdsiBNCpl53qMuYW2lh72N35Iyug6qKFDYg5
|
||||||
|
-----END CERTIFICATE-----
|
||||||
5
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ecdsa256_ca_key.pem
generated
vendored
Normal file
5
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ecdsa256_ca_key.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
-----BEGIN EC PRIVATE KEY-----
|
||||||
|
MHcCAQEEIC2qaVydr67HuwWMrPQ3ljCVSsnbV7HbN78KqEX6a0GuoAoGCCqGSM49
|
||||||
|
AwEHoUQDQgAEjEb98b3L+COUBe8Hvtt4REtsGig33wUYUDFVQDkiCXKW+CZ83FSY
|
||||||
|
jyYzZTD23M4ub285ECtpJIzj/qJKkImt4A==
|
||||||
|
-----END EC PRIVATE KEY-----
|
||||||
12
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ecdsa384.csr
generated
vendored
Normal file
12
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ecdsa384.csr
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIBvzCCAUUCAQAwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
|
||||||
|
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
|
||||||
|
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRcwFQYDVQQDEw5jbG91ZGZsYXJl
|
||||||
|
LmNvbTB2MBAGByqGSM49AgEGBSuBBAAiA2IABBk/Q+zMsZOJGkufRzGCWtSUtRjq
|
||||||
|
0QqChDGWbHLaa0h6ODVeEoKYOMvFJTg4V186tuuBe97KEey0OPDegzCBp5kBIiwg
|
||||||
|
HB/0xWoKdnfdRk6VyjmubPx399cGoZn8aCqgC6A/MD0GCSqGSIb3DQEJDjEwMC4w
|
||||||
|
LAYDVR0RBCUwI4IOY2xvdWRmbGFyZS5jb22CEXd3d2Nsb3VkZmxhcmUuY29tMAoG
|
||||||
|
CCqGSM49BAMDA2gAMGUCMQC57VfwMXDyL5kM7vmO2ynbpgSAuFZT6Yd3C3NnV2jz
|
||||||
|
Biozw3eqIDXqCb2LI09stZMCMGIwCuVARr2IRctxf7AmX7/O2SIaIhCpMFKRedQ7
|
||||||
|
RiWGZIucp5r6AfT9381PB29bHA==
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
13
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ecdsa521.csr
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ecdsa521.csr
generated
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIICCjCCAWsCAQAwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
|
||||||
|
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
|
||||||
|
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRcwFQYDVQQDEw5jbG91ZGZsYXJl
|
||||||
|
LmNvbTCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAEAHt/s9KTZETzu94JIAjZ3BaS
|
||||||
|
toSG65hGIc1e0Gt7PhdQxPp5FP2D8rQ1wc+pcZhD2O8525kPxopaqTd+fWKBuD3O
|
||||||
|
AULzoH2OX+atIuumTQzLNbTsIbP0tY3dh7d8LItuERkZn1NfsNl3z6bnNAaR137m
|
||||||
|
f4aWv49ImbA/Tkv8VmoKX279oD8wPQYJKoZIhvcNAQkOMTAwLjAsBgNVHREEJTAj
|
||||||
|
gg5jbG91ZGZsYXJlLmNvbYIRd3d3Y2xvdWRmbGFyZS5jb20wCgYIKoZIzj0EAwQD
|
||||||
|
gYwAMIGIAkIA8OX9LxWOVnyfB25DFBz6JkjhyDpBM/PXlgLnWb/n2mEuMMB44DOG
|
||||||
|
pljDV768PSW11AC3DtULoIyR92z0TyLEKYoCQgHdGd6PwUtDW5mrAMJQDgebjsxu
|
||||||
|
MwfcdthzKlFlSmRpHMBnRMOJjlg5f9CTBg9d6wEdv7ZIrQSO6eqQHDQRM0VMnw==
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
11
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ex.csr
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ex.csr
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIBnzCCAQgCAQAwXzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMQ8wDQYDVQQH
|
||||||
|
DAZJdGhhY2ExHDAaBgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQxFDASBgNVBAMM
|
||||||
|
C2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBPmzv1c1e
|
||||||
|
QAa1yTtJ45oPOCARrhqDYV66urzNX1zHDZzi4lruIfI3q+1McACs4FIGJAkBUC2O
|
||||||
|
ZCamsR6ym5PaL9+dGfgVvf6w/GoBb65bxuw/IgHnzhfEHsk9nV8WthTEHmT9m9lh
|
||||||
|
kPMZBVDIVFW6iOCCpAwR6I9XXB30oKTINwIDAQABoAAwDQYJKoZIhvcNAQELBQAD
|
||||||
|
gYEAndd8OjJ+Jr74jqwuV9cUDqlItsLc84TYn+lly0EPezGQIIYz2KUoDyHQ+PQ9
|
||||||
|
7JI3G3FWR8Wpow7HooLJRxHNWOw7u8ekLCP0LjkoHse+Dou5C0jzo99jfrjXNWGt
|
||||||
|
DZO0Wrpu2eDclqwMJO/DtiovzcmOsGC52NHUW6+Moo9N2lM=
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
11
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ip.csr
generated
vendored
Normal file
11
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/ip.csr
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIBlTCB/wIBADBWMQswCQYDVQQGEwJVUzELMAkGA1UECAwCTlkxDzANBgNVBAcM
|
||||||
|
Bkl0aGFjYTEQMA4GA1UECgwHQ29ybmVsbDEXMBUGA1UEAwwOMTI4Ljg0LjEyNi4y
|
||||||
|
MTMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAME+bO/VzV5ABrXJO0njmg84
|
||||||
|
IBGuGoNhXrq6vM1fXMcNnOLiWu4h8jer7UxwAKzgUgYkCQFQLY5kJqaxHrKbk9ov
|
||||||
|
350Z+BW9/rD8agFvrlvG7D8iAefOF8QeyT2dXxa2FMQeZP2b2WGQ8xkFUMhUVbqI
|
||||||
|
4IKkDBHoj1dcHfSgpMg3AgMBAAGgADANBgkqhkiG9w0BAQsFAAOBgQBS7FBieNEN
|
||||||
|
PfXQRhPeiZ86QatshBBrj+TmhdC4GjtJ9lQA2NSRg2HnSHDErxdezZ7tw1ordd5D
|
||||||
|
hZpJ8XkPggsb7mghwPD7Zzgp0M/ldqbZ9fFEtNcpiEL05vKtap5uSGzNn32NDbQa
|
||||||
|
g+4QnDavffTQuzfuOoGJ9bG3jQtxo9HZCA==
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
15
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/key.pem
generated
vendored
Normal file
15
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/key.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIICXAIBAAKBgQCbp/6OQ/a3mr+8zRgBRlmSGr8QBgP4vUIxLn2Mk4uiZ8OcpRY4
|
||||||
|
YqL+TtREGDUc0ve+bv8RINrNlYXL2X+eJtbE2RJQ+RAiu+saw2K+RFTNeTCA1fwg
|
||||||
|
3ws5gBDcFbECqK1dOkuN/gV4JMHobn2/15iUBfeSJxdF1j5yqES8sVu7cwIDAQAB
|
||||||
|
AoGBALZOnnBV3aLRlnw04kar9MCQnvLPeNteHyanQtjg/oxqZ8sR9+J2dFzSSv6u
|
||||||
|
M5bc6Nmb+xY+msZqt9g3l6bN6n+qCvNnLauIY/YPjd577uMTpx/QTOQSK8oc5Dhi
|
||||||
|
WgdU8GCtUmY+LE8qYx2NFitKCN4hubdrI76c+rnezIPVncZRAkEA9T5+vlfwk/Zl
|
||||||
|
DOte+JtbXx3RtXKFJPMirOFqNVp1qnIlUm8XtBW6760ugiNYbVbGHgbd8JsZnkPH
|
||||||
|
NC17TNLVJwJBAKJ7pDlJ2mvVr0cLrFhjAibz45dOipt8B4+dKtDIEuqbtKzJCGuP
|
||||||
|
SCk4X2SgYz0gC5kH62S7rn6Bsa9lM98dztUCQASdLWNFYkhWXWZV006YFar/c5+X
|
||||||
|
TPv5+xAHmajxT79qMFuRrX983Sx/NJ3MLnC4LjgIZwqM0HmSyt+nb2dtnAcCQCKi
|
||||||
|
nIUhuw+Vg0FvuZM1t7W581/DfERckfgJFqFepLmh60eRqtvStR0kSSFYFw9mj1JV
|
||||||
|
n9XfM/j/iHLM7du3rOkCQAw9R64yjcIBwcoSQxW/dr0Q9j+SnYgt+EhyXYXT30DS
|
||||||
|
DdOJ06GXtb/P0peFBp26BnQU4CSS75yseZ1TdB4ZqaA=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
19
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/rsa2048-inter.csr
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/rsa2048-inter.csr
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIDCjCCAfQCAQAwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
|
||||||
|
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
|
||||||
|
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRcwFQYDVQQDEw5jbG91ZGZsYXJl
|
||||||
|
LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOLFLykOd2j31AQn
|
||||||
|
kaToYtstGvw5wLb4YnlzipQ6aULlD0H0GHM9IwhdSmcTWUWPb/U83g/ma1uD3Pp2
|
||||||
|
IdWd6xfjyOJF5XhgkyfRY65wS6vPZRm2MNSFXem+0AKHdhxIhb/QPMASqC/yaiPi
|
||||||
|
nvtOpBiCNl1Q2N4y9pkV0oD/T4rrn3RXP6iL1k4CNRS54JPCd+aI5Om+axVPU8Id
|
||||||
|
ZeUXQwXISaFrcC/bFXAHGX5hBMVu34lhCxvR4smweZkVmW++bIv26az8TSb5nVn4
|
||||||
|
TstLJIaOoOqot0sis04+0oX/GXfTPfkWyzfTVFN7cb9H+gz0FZJKtXQZv6qdntji
|
||||||
|
9FdR+pkCAwEAAaBAMD4GCSqGSIb3DQEJDjExMC8wLQYDVR0RBCYwJIIOY2xvdWRm
|
||||||
|
bGFyZS5jb22CEnd3dy5jbG91ZGZsYXJlLmNvbTALBgkqhkiG9w0BAQsDggEBABfM
|
||||||
|
9XTMqMqmfAAymWC4/W+vbh301KBoydcTnDQ/7B+ftHRE0O3FUsdL3wobj3qBieJo
|
||||||
|
MiQwiL7+GksszHvN9+YOUi70wpFuKghLhadb7p5GzL0+JgK2eQnLYb37/lQSiWwn
|
||||||
|
hht1YMOzErR/KHlxNUafk71bDEeytUcOvvtujf86nZiEnBpvp47zDjMkDersczM0
|
||||||
|
wj7S50IY8/vRsc2Q8vy+Q7D2FPEwjs4wCGVSqzwX2NPn3fZb/2pWRCie9kxHUfUP
|
||||||
|
L5xO4WoFGuirT6E2GnUWDdH661Pj5yEKvmr+qPl+eVoLjrtx0g5rAmA7rGlGrkqp
|
||||||
|
r4idH/BbJUaDlRHM/Hk=
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/rsa2048-inter.key
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/rsa2048-inter.key
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpQIBAAKCAQEA4sUvKQ53aPfUBCeRpOhi2y0a/DnAtvhieXOKlDppQuUPQfQY
|
||||||
|
cz0jCF1KZxNZRY9v9TzeD+ZrW4Pc+nYh1Z3rF+PI4kXleGCTJ9FjrnBLq89lGbYw
|
||||||
|
1IVd6b7QAod2HEiFv9A8wBKoL/JqI+Ke+06kGII2XVDY3jL2mRXSgP9PiuufdFc/
|
||||||
|
qIvWTgI1FLngk8J35ojk6b5rFU9Twh1l5RdDBchJoWtwL9sVcAcZfmEExW7fiWEL
|
||||||
|
G9HiybB5mRWZb75si/bprPxNJvmdWfhOy0skho6g6qi3SyKzTj7Shf8Zd9M9+RbL
|
||||||
|
N9NUU3txv0f6DPQVkkq1dBm/qp2e2OL0V1H6mQIDAQABAoIBAQCzT3HcCAlZoeUu
|
||||||
|
p88dU3efkUnuOQhuZXcQS9E/JfTHpXHsF8Qhky0ZVxMW8BC91Q6VHt0EO5GWWm0o
|
||||||
|
SrK0Q9t6F25npRcumUaizIoCi9756tMpgouX8CDzTCMUbOJyuNGxe0oeImKFDyzo
|
||||||
|
VTCazHMqwgOUw/HHuQqOv9ekkrzlva8U+Z5MGZB4B2acHIAJHO9uYGzdeAjF3grm
|
||||||
|
dQ3QFGXJM0JzPmXfnUiDeOWIoVbo4YROFhf7qNlcnyLdkrYe0/XsSYQM9dRGKRPK
|
||||||
|
nkOkMv0sC8rOqNuJUn3tf1OOjzVQxlzB8Key6MOQ1c+kqsdCnL88/93CvI5NHazx
|
||||||
|
hwUmesmBAoGBAPpkDtgeWjxeIjOfuxXDYb04XbVmKquKNOIEk5OADmaacSGzdemh
|
||||||
|
XLRaNVMEYMcgMJViDDKW8g4k+zuZgzooMxNynlLNU5wfazwX2LLjReJFvZb/SxMM
|
||||||
|
N9+vQo8fcGz+p5g1tbeE6w86mpsTiAGx9Wa4J4GnY8jF6XUjZHO0X91pAoGBAOfZ
|
||||||
|
qrDkPMDSiVk62FP6LlPrj09bt1NTkBfv5dWhN/XeHjuus7unDhNiRmphhgF0VZse
|
||||||
|
XPtT/PUO0YgYlyaYJDDDE0IxgHuoK9wvEb2sqEtkZSw7IUhehheZ/+YfXzSA5fwa
|
||||||
|
vhXt0ghB0d9oVJuRoxb17MncjpjDAKy0QR5drR2xAoGBAMlNwkVseZ2JDLQ2WgHQ
|
||||||
|
N/cZpvUc83dAQO3pQgBW9rz0s7mlf0naqh5xW+enYGsW7RhcYHQXuPk4MCelbsRF
|
||||||
|
53JeNv1ZCDw/YkZI4bZIVDnrWdZY3zGsJAuY6skIPKnUPkd3/uVRXm267ut4U2MR
|
||||||
|
gLsZmOF7AxU6UEwVrT/8pwnpAoGAKxbVFlMUx3FZfW/mTJUujwI0fDc7dw0MtqYr
|
||||||
|
POzdjaBeVhE97h46C3g0Rgkh8ptAXbfi6ALP/GtonbaUQOP9teJLbf3tNw4mOKG2
|
||||||
|
1l2EWZ6q/vFuWhjXKwO//3DNLODX3WbK9SBh7I7vBmpJbzA980J5Y3rONa3oLjDB
|
||||||
|
+XbHecECgYEArOEv2D3fE3Hd6rEbxXinqekxMa+V1OCDO1IPz4wwr9RDMVUMxwqF
|
||||||
|
f0es1PQ2eMJGrAMbySxPfSZG05ou/tA+zR0qPwc/+dX0BbaXCiNT3gbhvL1L2fBc
|
||||||
|
7wr+MIUe2fi54JUWrUNMDHngRhXRKt2rZZRTfqVaFmZX02Y3fMZ2dWg=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
||||||
19
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/rsa2048.csr
generated
vendored
Normal file
19
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/rsa2048.csr
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIDCTCCAfMCAQAwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
|
||||||
|
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
|
||||||
|
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRcwFQYDVQQDEw5jbG91ZGZsYXJl
|
||||||
|
LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALTWdoYxX4KN51fP
|
||||||
|
WxQAyGH++VsPbfpAoXIbCPXSmU04BvIxyjzpHQ0ChMKkT/2VNcUeFJwk2fCf+ZwU
|
||||||
|
f0raTQTplofwkckE0gEYA3WcEfJp+hbvbTb/2recsf+JE6JACYJe2Uu5wsjtrE5j
|
||||||
|
A+7aT2BEU9RWzBdSy/5281ZfW3PArqcWaf8+RUyA3WRxVWmjmhFsVB+mdNLhCpW0
|
||||||
|
C0QNMYR1ppEZiKVnEdao8gcI5sOvSd+35t8g82aPXcNSPU6jKcx1YNUPX5wgPEmu
|
||||||
|
+anfc9RliQbYqqJYVODgBmV8IR5grw93yTsODoWKtFQ4PKVlnt9CD8AS/iSMQYm3
|
||||||
|
OUogqgMCAwEAAaA/MD0GCSqGSIb3DQEJDjEwMC4wLAYDVR0RBCUwI4IOY2xvdWRm
|
||||||
|
bGFyZS5jb22CEXd3d2Nsb3VkZmxhcmUuY29tMAsGCSqGSIb3DQEBCwOCAQEAl809
|
||||||
|
gk9uZkRK+MJVYDSLjgGR2xqk5qOwnhovnispA7N3Z1GshodJRQa6ngNCKuXIm2/6
|
||||||
|
AxB9kDGK14n186Qq4odXqHSHs8FG9i0zUcBXeLv1rPAKtwKTas/SLmsOpPgWPZFa
|
||||||
|
iYiHHeu4HjOQoF987d7uGRYwc3xfstKwJsEXc12eCw2NH8TM1tJgSc/o6CzIpA91
|
||||||
|
QnZKhx6uGM4xI2gnOaJA1YikNhyFGBuOGMZgd0k2+/IcR2pg0z4pc5oQw1bXLANx
|
||||||
|
anqlA/MDrCM9v9019bRJ73zK8LQ3k/FW61PA9nL7RZ8ku65R+uYcVEdLa8pUeqnH
|
||||||
|
cJZNboDRsItpccZuRQ==
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
24
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/rsa3072.csr
generated
vendored
Normal file
24
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/rsa3072.csr
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIECTCCAnMCAQAwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
|
||||||
|
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
|
||||||
|
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRcwFQYDVQQDEw5jbG91ZGZsYXJl
|
||||||
|
LmNvbTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAL0zzgBv+VTwZOPy
|
||||||
|
LtuLFweQrj5Lfrje2hnNB7Y3TD4+yCM/cA4yTILixCe/B+N7LQysJgVDbW8u6BZQ
|
||||||
|
8ZqeDKOP6KCt37WhmcbT45tLpHmH+Z/uAnCz0hVc/7AyJ3CJXo6PaDCcJjgLuUun
|
||||||
|
W47iy4h79AxyuzELmUeZZGYcO8nqClqcnAzQ6sClGZvJwSbYg2QAFGoA2lHqZ9uN
|
||||||
|
ygAxNLd+rX9cP+yFwAeKzuKtOnVPiJD5lT3wufSkAbd6M7lOoqmTYnbv0A1WfA/e
|
||||||
|
upXno9lbgB6iwF5U0V7OtxdA1bTbvgJgNLlxFF1do0sB28CWmqCFNwLfzcPzt5A4
|
||||||
|
gLnOyLhNZOmUMXn35KOtp1Zv/yethlgZHxUYGcl6OYwMEFye3Du6dgnTwONzaLhA
|
||||||
|
7hMI8R60p2YrTLkgSKdFohAY/mKuxHyXxugOHHthlRCOn9m49edcdZ1HrkJXm9jd
|
||||||
|
P9katjCXgTwSdTQlvaMJkfH7wF3ZMjAxPcDf4RKFEpF2wABeNQIDAQABoD8wPQYJ
|
||||||
|
KoZIhvcNAQkOMTAwLjAsBgNVHREEJTAjgg5jbG91ZGZsYXJlLmNvbYIRd3d3Y2xv
|
||||||
|
dWRmbGFyZS5jb20wCwYJKoZIhvcNAQEMA4IBgQBF/RCHNAAOAaRI4VyO0tRPA5Dw
|
||||||
|
0/1/pgmBm/VejHIwDJnMFCl9njh0RSo1RgsVLhw6ovYbk3ORb4OD4UczPTq3GrFp
|
||||||
|
KP9uPR+2pR4FWJpCVfCl76YabQv6fUDdiT7ojzyRhsAmkd5rOdiMvWV3Rp+YmBuU
|
||||||
|
KH/dwkukfn+OeJIbERS5unzOBtQL+g5dU4CHWAqJQIqHr373w38OlYN+JY9QLrYy
|
||||||
|
sWU9Ye6RjdySXPJ5UzyfOEfc9Ji89RJsVeceB1+As5u5vBvtzGgIMSFUzN947RZo
|
||||||
|
DZ48JiB71VpmKXbn9LIRn25dlbVMzxRdSeZ194L3JFVAf9OxJTsc1QNFhOacoFgy
|
||||||
|
hqvtN2iKntEyPo2nacYhpz/FAdJ2JThNH+4WtpPWAqx8Lw/e1OttiDt+6M0FEuVz
|
||||||
|
svkSHnK206yo+a9Md37nUDDYxtlJEB+9F2qUZNQ7Hv+dxjmJOIgHOXxy1pLEdpVU
|
||||||
|
rGdGLVXeJNPCh9x+GK21QjdxZABmYAaF8k36Pv4=
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
29
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/rsa4096.csr
generated
vendored
Normal file
29
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local/testdata/rsa4096.csr
generated
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIFCTCCAvMCAQAwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
|
||||||
|
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
|
||||||
|
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRcwFQYDVQQDEw5jbG91ZGZsYXJl
|
||||||
|
LmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANkKL22jMn3eFCpj
|
||||||
|
T6lbeq4nC3aEqwTGrLARidAmO29WIhzs6LxRpM6xSMoPI6DvJVUGpMFEKF4xNTc5
|
||||||
|
X9/gSFrw2eI5Q3U3aGcaToSCxH4hXejwIzX8Ftlb/LfpXhbSsFr5MS3kiTY4zZxM
|
||||||
|
n3dSy2gZljD/g0tlQf5BdHdR4WKRhWnqRiGng+BmW4rjbcO7SoN33jSXsMcguCg5
|
||||||
|
8dmYuf5G5KVXsqwEoCQBeKGnca9orcm4i90VnGt4qZUpfAn1cADzYGpRzX79USJ6
|
||||||
|
tol4ovgGPN08LJFqcVl+dK8VzJ03JWBhI1jePbWS4Bz5oNtkhQQXilU+G6FQxc6a
|
||||||
|
UPf6KcFyOB+qMJmEwJZD9yaNK1YbsKfSztQEsb1JEezQnVHxp91Ch3AcWoikuOiY
|
||||||
|
yCg0V5lcK15SLv1+5sj9YzF7ngMmThcIJ6B5gS3swpD5AX6FJaI1BrGwT/RXKKQP
|
||||||
|
tRX1BySLx8RcINjFb5wv3q9QIE8vrW1BOk9f4dfmxiFYnc+6bCCbIrg7APQVtKTa
|
||||||
|
ixNJFSqZz7fm9loeNPHHXfUT5RoW5yzVa8igc+yv4qeYsWHcZ4c/Y91OJp19HMjM
|
||||||
|
bYm2alt8XagBgJjO0FW8wvsKwhhlhWK0WO6sQ7Fkl7fH1GtxEpc248hAW24SZMmS
|
||||||
|
led3LblCT8IC3a9BLhqJ2q8cfPp9AgMBAAGgPzA9BgkqhkiG9w0BCQ4xMDAuMCwG
|
||||||
|
A1UdEQQlMCOCDmNsb3VkZmxhcmUuY29tghF3d3djbG91ZGZsYXJlLmNvbTALBgkq
|
||||||
|
hkiG9w0BAQ0DggIBAAgz3NuN43+F+8+WhQ9hb7DOp6Amut7XubOkEBtBVgP3R8U1
|
||||||
|
uSsgocR1rvnZ1/bhkeGyTly0eQPhcSEdMo/GgIrcn+co0KLcDyV6Rf3Cgksx9dUZ
|
||||||
|
TzHSkxmFkxlxYfIGes6abH+2OPiacwK2gLvvmXFYIxEhv+LKzzteQi0xlinewv7R
|
||||||
|
FnSykZ4QialsFyCgOjOxa11aEdRv6T8qKwhjUOk0VedtzOkt/k95aydTNLjXl2OV
|
||||||
|
jloeTsbB00yWIqdyhG12+TgcJOa0pNP1zTjgFPodMuRUuiAcbT7Mt7sLCefKNzvZ
|
||||||
|
Ln6b4y7e6N3YLOHALTIP+LI4y8ar47WlXCNw/zeOM2sW8udjYrukN6WOV3X68oMf
|
||||||
|
Zsv6jqyGSaCDwdImR4VECUVvkabg9Sq4pz+ijTT+9cNA66omYL+/QAh0GahlROgW
|
||||||
|
kDGI8zeEUoAC8RkAbFGMJA8jEbAfbT000ZwnLX2SZ8YRQX4Jd1FTmAH99FkvvT8N
|
||||||
|
ovaGRSQQI5rWQGQYqF67So7PywEaEXeUHTBrv41Msva6CdaWHn7bh/fj4B21ETS7
|
||||||
|
VJvrk5DLJTyruqon7EVJU1pn38ppaXF4Z6a9n3C8TqudT/gdJUYn/SBo5jx20uGJ
|
||||||
|
d9k6vDqixntvk/TRZ848k1AXiv5uUJTdnoPPhzSGjxEaeKuB0R1ZHomVdjU4
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
4
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/pkcs11/doc.go
generated
vendored
Normal file
4
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/pkcs11/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// Package pkcs11 implements support for PKCS #11 signers. If the
|
||||||
|
// package has not been built with the `pkcs11` tag, the `New`
|
||||||
|
// function will be a stub.
|
||||||
|
package pkcs11
|
||||||
53
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/pkcs11/pkcs11.go
generated
vendored
Normal file
53
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/pkcs11/pkcs11.go
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
// +build pkcs11
|
||||||
|
|
||||||
|
package pkcs11
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cfssl/crypto/pkcs11key"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config contains configuration information required to use a PKCS
|
||||||
|
// #11 key.
|
||||||
|
type Config struct {
|
||||||
|
Module string
|
||||||
|
Token string
|
||||||
|
PIN string
|
||||||
|
Label string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled is set to true if PKCS #11 support is present.
|
||||||
|
const Enabled = true
|
||||||
|
|
||||||
|
// New returns a new PKCS #11 signer.
|
||||||
|
func New(caCertFile string, policy *config.Signing, cfg *Config) (signer.Signer, error) {
|
||||||
|
if cfg == nil {
|
||||||
|
return nil, errors.New(errors.PrivateKeyError, errors.ReadFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("Loading PKCS #11 module %s", cfg.Module)
|
||||||
|
certData, err := ioutil.ReadFile(caCertFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(errors.PrivateKeyError, errors.ReadFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := helpers.ParseCertificatePEM(certData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, err := pkcs11key.New(cfg.Module, cfg.Token, cfg.PIN, cfg.Label)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(errors.PrivateKeyError, errors.ReadFailed)
|
||||||
|
}
|
||||||
|
sigAlgo := signer.DefaultSigAlgo(priv)
|
||||||
|
|
||||||
|
return local.NewSigner(priv, cert, sigAlgo, policy)
|
||||||
|
}
|
||||||
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/pkcs11/pkcs11_stub.go
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/pkcs11/pkcs11_stub.go
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
// +build !pkcs11
|
||||||
|
|
||||||
|
package pkcs11
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config contains configuration information required to use a PKCS
|
||||||
|
// #11 key.
|
||||||
|
type Config struct {
|
||||||
|
Module string
|
||||||
|
Token string
|
||||||
|
PIN string
|
||||||
|
Label string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New always returns an error. If PKCS #11 support is needed, the
|
||||||
|
// program should be built with the `pkcs11` build tag.
|
||||||
|
func New(caCertFile string, policy *config.Signing, cfg *Config) (signer.Signer, error) {
|
||||||
|
return nil, errors.New(errors.PrivateKeyError, errors.Unknown)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled is set to true if PKCS #11 support is present.
|
||||||
|
const Enabled = false
|
||||||
114
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/remote/remote.go
generated
vendored
Normal file
114
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/remote/remote.go
generated
vendored
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
package remote
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api/client"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
|
||||||
|
cferr "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Signer represents a CFSSL instance running as signing server.
|
||||||
|
// fulfills the signer.Signer interface
|
||||||
|
type Signer struct {
|
||||||
|
policy *config.Signing
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSigner creates a new remote Signer directly from a
|
||||||
|
// signing policy.
|
||||||
|
func NewSigner(policy *config.Signing) (*Signer, error) {
|
||||||
|
if policy != nil {
|
||||||
|
if !policy.Valid() {
|
||||||
|
return nil, cferr.New(cferr.PolicyError,
|
||||||
|
cferr.InvalidPolicy)
|
||||||
|
}
|
||||||
|
return &Signer{policy: policy}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, cferr.New(cferr.PolicyError,
|
||||||
|
cferr.InvalidPolicy)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign sends a signature request to the remote CFSSL server,
|
||||||
|
// receiving a signed certificate or an error in response. The hostname,
|
||||||
|
// csr, and profileName are used as with a local signing operation, and
|
||||||
|
// the label is used to select a signing root in a multi-root CA.
|
||||||
|
func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
|
||||||
|
return s.remoteOp(req, req.Profile, "sign")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info sends an info request to the remote CFSSL server, receiving a signed
|
||||||
|
// certificate or an error in response.
|
||||||
|
func (s *Signer) Info(req client.InfoReq) (cert []byte, err error) {
|
||||||
|
return s.remoteOp(req, req.Profile, "info")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to perform a remote sign or info request.
|
||||||
|
func (s *Signer) remoteOp(req interface{}, profile, target string) (cert []byte, err error) {
|
||||||
|
jsonData, err := json.Marshal(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, cferr.Wrap(cferr.APIClientError, cferr.JSONError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var p *config.SigningProfile
|
||||||
|
if s.policy.Profiles != nil && profile != "" {
|
||||||
|
p = s.policy.Profiles[profile]
|
||||||
|
}
|
||||||
|
|
||||||
|
if p == nil {
|
||||||
|
p = s.policy.Default
|
||||||
|
}
|
||||||
|
|
||||||
|
server := client.NewServer(p.RemoteServer)
|
||||||
|
if server == nil {
|
||||||
|
return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidRequest,
|
||||||
|
errors.New("failed to connect to remote"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's no server-side auth provider for the "info" method
|
||||||
|
// TODO: Revert this change once there is an AuthInfo provider.
|
||||||
|
if p.Provider != nil && target != "info" {
|
||||||
|
cert, err = server.AuthReq(jsonData, nil, p.Provider, target)
|
||||||
|
} else {
|
||||||
|
cert, err = server.Req(jsonData, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte(cert), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SigAlgo returns the RSA signer's signature algorithm.
|
||||||
|
func (s *Signer) SigAlgo() x509.SignatureAlgorithm {
|
||||||
|
// TODO: implement this as a remote info call
|
||||||
|
return x509.UnknownSignatureAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
// Certificate returns the signer's certificate.
|
||||||
|
func (s *Signer) Certificate(label, profile string) (*x509.Certificate, error) {
|
||||||
|
certStr, err := s.Info(client.InfoReq{Label: label, Profile: profile})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cert, err := helpers.ParseCertificatePEM(certStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPolicy sets the signer's signature policy.
|
||||||
|
func (s *Signer) SetPolicy(policy *config.Signing) {
|
||||||
|
s.policy = policy
|
||||||
|
}
|
||||||
|
|
||||||
|
// Policy returns the signer's policy.
|
||||||
|
func (s *Signer) Policy() *config.Signing {
|
||||||
|
return s.policy
|
||||||
|
}
|
||||||
399
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/remote/remote_test.go
generated
vendored
Normal file
399
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/remote/remote_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,399 @@
|
||||||
|
package remote
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api/client"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api/info"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testCaFile = "testdata/ca.pem"
|
||||||
|
testCaKeyFile = "testdata/ca_key.pem"
|
||||||
|
)
|
||||||
|
|
||||||
|
var validMinimalRemoteConfig = `
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"default": {
|
||||||
|
"remote": "localhost"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remotes": {
|
||||||
|
"localhost": "127.0.0.1:80"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
var validMinimalAuthRemoteConfig = `
|
||||||
|
{
|
||||||
|
"signing": {
|
||||||
|
"default": {
|
||||||
|
"auth_key": "sample",
|
||||||
|
"remote": "localhost"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"auth_keys": {
|
||||||
|
"sample": {
|
||||||
|
"type":"standard",
|
||||||
|
"key":"0123456789ABCDEF0123456789ABCDEF"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"remotes": {
|
||||||
|
"localhost": "127.0.0.1:80"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
func TestNewSigner(t *testing.T) {
|
||||||
|
remoteConfig := newConfig(t, []byte(validMinimalRemoteConfig))
|
||||||
|
|
||||||
|
_, err := NewSigner(remoteConfig.Signing)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("fail to init remote signer:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewAuthSigner(t *testing.T) {
|
||||||
|
remoteAuthConfig := newConfig(t, []byte(validMinimalAuthRemoteConfig))
|
||||||
|
|
||||||
|
_, err := NewSigner(remoteAuthConfig.Signing)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("fail to init remote signer:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoteInfo(t *testing.T) {
|
||||||
|
remoteServer := newTestInfoServer(t)
|
||||||
|
defer closeTestServer(t, remoteServer)
|
||||||
|
|
||||||
|
remoteConfig := newConfig(t, []byte(validMinimalRemoteConfig))
|
||||||
|
// override with test server address, ignore url prefix "http://"
|
||||||
|
remoteConfig.Signing.OverrideRemotes(remoteServer.URL[7:])
|
||||||
|
s := newRemoteSigner(t, remoteConfig.Signing)
|
||||||
|
req := client.InfoReq{}
|
||||||
|
certBytes, err := s.Info(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("remote info failed:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
caBytes, err := ioutil.ReadFile(testCaFile)
|
||||||
|
caBytes = bytes.TrimSpace(caBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("fail to read test CA cert:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(caBytes, certBytes) != 0 {
|
||||||
|
t.Fatal("Get a different CA cert through info api.", len(certBytes), len(caBytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type csrTest struct {
|
||||||
|
file string
|
||||||
|
keyAlgo string
|
||||||
|
keyLen int
|
||||||
|
// Error checking function
|
||||||
|
errorCallback func(*testing.T, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var csrTests = []csrTest{
|
||||||
|
{
|
||||||
|
file: "../local/testdata/rsa2048.csr",
|
||||||
|
keyAlgo: "rsa",
|
||||||
|
keyLen: 2048,
|
||||||
|
errorCallback: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "../local/testdata/rsa3072.csr",
|
||||||
|
keyAlgo: "rsa",
|
||||||
|
keyLen: 3072,
|
||||||
|
errorCallback: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "../local/testdata/rsa4096.csr",
|
||||||
|
keyAlgo: "rsa",
|
||||||
|
keyLen: 4096,
|
||||||
|
errorCallback: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "../local/testdata/ecdsa256.csr",
|
||||||
|
keyAlgo: "ecdsa",
|
||||||
|
keyLen: 256,
|
||||||
|
errorCallback: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "../local/testdata/ecdsa384.csr",
|
||||||
|
keyAlgo: "ecdsa",
|
||||||
|
keyLen: 384,
|
||||||
|
errorCallback: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "../local/testdata/ecdsa521.csr",
|
||||||
|
keyAlgo: "ecdsa",
|
||||||
|
keyLen: 521,
|
||||||
|
errorCallback: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoteSign(t *testing.T) {
|
||||||
|
remoteServer := newTestSignServer(t)
|
||||||
|
defer closeTestServer(t, remoteServer)
|
||||||
|
|
||||||
|
remoteConfig := newConfig(t, []byte(validMinimalRemoteConfig))
|
||||||
|
// override with test server address, ignore url prefix "http://"
|
||||||
|
remoteConfig.Signing.OverrideRemotes(remoteServer.URL[7:])
|
||||||
|
s := newRemoteSigner(t, remoteConfig.Signing)
|
||||||
|
|
||||||
|
hosts := []string{"cloudflare.com"}
|
||||||
|
for _, test := range csrTests {
|
||||||
|
csr, err := ioutil.ReadFile(test.file)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("CSR loading error:", err)
|
||||||
|
}
|
||||||
|
certBytes, err := s.Sign(signer.SignRequest{Hosts: hosts, Request: string(csr)})
|
||||||
|
if test.errorCallback != nil {
|
||||||
|
test.errorCallback(t, err)
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no error. Got %s. Param %s %d", err.Error(), test.keyAlgo, test.keyLen)
|
||||||
|
}
|
||||||
|
_, err := helpers.ParseCertificatePEM(certBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Fail to parse returned certificate:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoteSignBadServerAndOverride(t *testing.T) {
|
||||||
|
remoteServer := newTestSignServer(t)
|
||||||
|
defer closeTestServer(t, remoteServer)
|
||||||
|
|
||||||
|
// remoteConfig contains port 80 that no test server will listen on
|
||||||
|
remoteConfig := newConfig(t, []byte(validMinimalRemoteConfig))
|
||||||
|
s := newRemoteSigner(t, remoteConfig.Signing)
|
||||||
|
|
||||||
|
hosts := []string{"cloudflare.com"}
|
||||||
|
csr, err := ioutil.ReadFile("../local/testdata/rsa2048.csr")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("CSR loading error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.Sign(signer.SignRequest{Hosts: hosts, Request: string(csr)})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("Should return error")
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteConfig.Signing.OverrideRemotes(remoteServer.URL[7:])
|
||||||
|
s.SetPolicy(remoteConfig.Signing)
|
||||||
|
certBytes, err := s.Sign(signer.SignRequest{Hosts: hosts, Request: string(csr)})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected no error. Got %s.", err.Error())
|
||||||
|
}
|
||||||
|
_, err = helpers.ParseCertificatePEM(certBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Fail to parse returned certificate:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper functions
|
||||||
|
func newConfig(t *testing.T, configBytes []byte) *config.Config {
|
||||||
|
conf, err := config.LoadConfig([]byte(configBytes))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("config loading error:", err)
|
||||||
|
}
|
||||||
|
if !conf.Valid() {
|
||||||
|
t.Fatal("config is not valid")
|
||||||
|
}
|
||||||
|
return conf
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRemoteSigner(t *testing.T, policy *config.Signing) *Signer {
|
||||||
|
s, err := NewSigner(policy)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("fail to init remote signer:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestSignHandler(t *testing.T) (h http.Handler) {
|
||||||
|
h, err := newHandler(t, testCaFile, testCaKeyFile, "sign")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestInfoHandler(t *testing.T) (h http.Handler) {
|
||||||
|
h, err := newHandler(t, testCaFile, testCaKeyFile, "info")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestSignServer(t *testing.T) *httptest.Server {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.Handle("/api/v1/cfssl/sign", newTestSignHandler(t))
|
||||||
|
ts := httptest.NewUnstartedServer(mux)
|
||||||
|
ts.Start()
|
||||||
|
t.Log(ts.URL)
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestInfoServer(t *testing.T) *httptest.Server {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
mux.Handle("/api/v1/cfssl/info", newTestInfoHandler(t))
|
||||||
|
ts := httptest.NewUnstartedServer(mux)
|
||||||
|
ts.Start()
|
||||||
|
t.Log(ts.URL)
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeTestServer(t *testing.T, ts *httptest.Server) {
|
||||||
|
t.Log("Finalizing test server.")
|
||||||
|
ts.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// newHandler generates a new sign handler (or info handler) using the certificate
|
||||||
|
// authority private key and certficate to sign certificates.
|
||||||
|
func newHandler(t *testing.T, caFile, caKeyFile, op string) (http.Handler, error) {
|
||||||
|
var expiry = 1 * time.Minute
|
||||||
|
var CAConfig = &config.Config{
|
||||||
|
Signing: &config.Signing{
|
||||||
|
Profiles: map[string]*config.SigningProfile{
|
||||||
|
"signature": &config.SigningProfile{
|
||||||
|
Usage: []string{"digital signature"},
|
||||||
|
Expiry: expiry,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Default: &config.SigningProfile{
|
||||||
|
Usage: []string{"cert sign", "crl sign"},
|
||||||
|
ExpiryString: "43800h",
|
||||||
|
Expiry: expiry,
|
||||||
|
CA: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
s, err := local.NewSignerFromFile(testCaFile, testCaKeyFile, CAConfig.Signing)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if op == "sign" {
|
||||||
|
return NewSignHandlerFromSigner(s)
|
||||||
|
} else if op == "info" {
|
||||||
|
return info.NewHandler(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Fatal("Bad op code")
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSignHandlerFromSigner generates a new SignHandler directly from
|
||||||
|
// an existing signer.
|
||||||
|
func NewSignHandlerFromSigner(s signer.Signer) (h http.Handler, err error) {
|
||||||
|
policy := s.Policy()
|
||||||
|
if policy == nil {
|
||||||
|
err = errors.New(errors.PolicyError, errors.InvalidPolicy)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign will only respond for profiles that have no auth provider.
|
||||||
|
// So if all of the profiles require authentication, we return an error.
|
||||||
|
haveUnauth := (policy.Default.Provider == nil)
|
||||||
|
for _, profile := range policy.Profiles {
|
||||||
|
if !haveUnauth {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
haveUnauth = (profile.Provider == nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !haveUnauth {
|
||||||
|
err = errors.New(errors.PolicyError, errors.InvalidPolicy)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return &api.HTTPHandler{
|
||||||
|
Handler: &SignHandler{
|
||||||
|
signer: s,
|
||||||
|
},
|
||||||
|
Method: "POST",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// A SignHandler accepts requests with a hostname and certficate
|
||||||
|
// parameter (which should be PEM-encoded) and returns a new signed
|
||||||
|
// certificate. It includes upstream servers indexed by their
|
||||||
|
// profile name.
|
||||||
|
type SignHandler struct {
|
||||||
|
signer signer.Signer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle responds to requests for the CA to sign the certificate request
|
||||||
|
// present in the "certificate_request" parameter for the host named
|
||||||
|
// in the "hostname" parameter. The certificate should be PEM-encoded. If
|
||||||
|
// provided, subject information from the "subject" parameter will be used
|
||||||
|
// in place of the subject information from the CSR.
|
||||||
|
func (h *SignHandler) Handle(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
log.Info("signature request received")
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r.Body.Close()
|
||||||
|
|
||||||
|
var req signer.SignRequest
|
||||||
|
err = json.Unmarshal(body, &req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.Hosts) == 0 {
|
||||||
|
return errors.NewBadRequestString("missing paratmeter 'hosts'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Request == "" {
|
||||||
|
return errors.NewBadRequestString("missing parameter 'certificate_request'")
|
||||||
|
}
|
||||||
|
|
||||||
|
var cert []byte
|
||||||
|
var profile *config.SigningProfile
|
||||||
|
|
||||||
|
policy := h.signer.Policy()
|
||||||
|
if policy != nil && policy.Profiles != nil && req.Profile != "" {
|
||||||
|
profile = policy.Profiles[req.Profile]
|
||||||
|
}
|
||||||
|
|
||||||
|
if profile == nil && policy != nil {
|
||||||
|
profile = policy.Default
|
||||||
|
}
|
||||||
|
|
||||||
|
if profile.Provider != nil {
|
||||||
|
log.Error("profile requires authentication")
|
||||||
|
return errors.NewBadRequestString("authentication required")
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err = h.signer.Sign(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Warningf("failed to sign request: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := map[string]string{"certificate": string(cert)}
|
||||||
|
log.Info("wrote response")
|
||||||
|
return api.SendResponse(w, result)
|
||||||
|
}
|
||||||
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/remote/testdata/ca.pem
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/remote/testdata/ca.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEmzCCA4OgAwIBAgIMAMSvNBgypwaaSQ5iMA0GCSqGSIb3DQEBBQUAMIGMMQsw
|
||||||
|
CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy
|
||||||
|
YW5jaXNjbzETMBEGA1UEChMKQ0ZTU0wgVEVTVDEbMBkGA1UEAxMSQ0ZTU0wgVEVT
|
||||||
|
VCBSb290IENBMR4wHAYJKoZIhvcNAQkBFg90ZXN0QHRlc3QubG9jYWwwHhcNMTIx
|
||||||
|
MjEyMDIxMDMxWhcNMjIxMDIxMDIxMDMxWjCBjDELMAkGA1UEBhMCVVMxEzARBgNV
|
||||||
|
BAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAoT
|
||||||
|
CkNGU1NMIFRFU1QxGzAZBgNVBAMTEkNGU1NMIFRFU1QgUm9vdCBDQTEeMBwGCSqG
|
||||||
|
SIb3DQEJARYPdGVzdEB0ZXN0LmxvY2FsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||||
|
MIIBCgKCAQEAsRp1xSfIDoD/40Bo4Hls3sFn4dav5NgxbZGpVyGF7dJI9u0eEnL4
|
||||||
|
BUGssPaUFLWC83CZxujUEiEfE0oKX+uOhhGv3+j5xSTNM764m2eSiN53cdZtK05d
|
||||||
|
hwq9uS8LtjKOQeN1mQ5qmiqxBMdjkKgMsVw5lMCgoYKo57kaKFyXzdpNVDzqw+pt
|
||||||
|
HWmuNtDQjK3qT5Ma06mYPmIGYhIZYLY7oJGg9ZEaNR0GIw4zIT5JRsNiaSb5wTLw
|
||||||
|
aa0n/4vLJyVjLJcYmJBvZWj8g+taK+C4INu/jGux+bmsC9hq14tbOaTNAn/NE0qN
|
||||||
|
8oHwcRBEqfOdEYdZkxI5NWPiKNW/Q+AeXQIDAQABo4H6MIH3MB0GA1UdDgQWBBS3
|
||||||
|
0veEuqg51fusEM4p/YuWpBPsvTCBxAYDVR0jBIG8MIG5gBS30veEuqg51fusEM4p
|
||||||
|
/YuWpBPsvaGBkqSBjzCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3Ju
|
||||||
|
aWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAoTCkNGU1NMIFRFU1Qx
|
||||||
|
GzAZBgNVBAMTEkNGU1NMIFRFU1QgUm9vdCBDQTEeMBwGCSqGSIb3DQEJARYPdGVz
|
||||||
|
dEB0ZXN0LmxvY2FsggwAxK80GDKnBppJDmIwDwYDVR0TBAgwBgEB/wIBADANBgkq
|
||||||
|
hkiG9w0BAQUFAAOCAQEAJ7r1EZYDwed6rS0+YKHdkRGRQ5Rz6A9DIVBPXrSMAGj3
|
||||||
|
F5EF2m/GJbhpVbnNJTVlgP9DDyabOZNxzdrCr4cHMkYYnocDdgAodnkw6GZ/GJTc
|
||||||
|
depbVTR4TpihFNzeDEGJePrEwM1DouGswpu97jyuCYZ3z1a60+a+3C1GwWaJ7Aet
|
||||||
|
Uqm+yLTUrMISsfnDPqJdM1NeqW3jiZ4IgcqJkieCCSpag9Xuzrp9q6rjmePvlQkv
|
||||||
|
qz020JGg6VijJ+c6Tf5y0XqbAhkBTqYtVamu9gEth9utn12EhdNjTZMPKMjjgFUd
|
||||||
|
H0N6yOEuQMl4ky7RxZBM0iPyeob6i4z2LEQilgv9MQ==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/remote/testdata/ca_key.pem
generated
vendored
Normal file
28
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/remote/testdata/ca_key.pem
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCxGnXFJ8gOgP/j
|
||||||
|
QGjgeWzewWfh1q/k2DFtkalXIYXt0kj27R4ScvgFQayw9pQUtYLzcJnG6NQSIR8T
|
||||||
|
Sgpf646GEa/f6PnFJM0zvribZ5KI3ndx1m0rTl2HCr25Lwu2Mo5B43WZDmqaKrEE
|
||||||
|
x2OQqAyxXDmUwKChgqjnuRooXJfN2k1UPOrD6m0daa420NCMrepPkxrTqZg+YgZi
|
||||||
|
EhlgtjugkaD1kRo1HQYjDjMhPklGw2JpJvnBMvBprSf/i8snJWMslxiYkG9laPyD
|
||||||
|
61or4Lgg27+Ma7H5uawL2GrXi1s5pM0Cf80TSo3ygfBxEESp850Rh1mTEjk1Y+Io
|
||||||
|
1b9D4B5dAgMBAAECggEAKHhjcSomDSptTwDo9mLI/h40HudwSlsc8GzYxZBjinUD
|
||||||
|
N2n39T9QbeMUE1xFenX/9qFEgq+xxnLLJx1EQacSapCgIAqdCO/f9HMgvGJumdg8
|
||||||
|
c0cMq1i9Bp7tu+OESZ5D48qWlOM2eQRIb08g8W11eRIaFmPuUPoKnuktkQuXpPJc
|
||||||
|
YbS/+JuA8SDwe6sV0cMCQuS+iHFfeGwWCKrDUkhLwcL3waW3od2XFyOeFFWFhl0h
|
||||||
|
HmM/mWKRuRdqR7hrmArTwFZVkB+o/1ywVYXIv+JQm0eNZ5PKLNJGL2f5oxbMR/JI
|
||||||
|
AoK0bAlJmYaFp96h1KpbPwLEL/0hHSWA7sAyJIgQAQKBgQDaEAZor/w4ZUTekT1+
|
||||||
|
cbId0yA+ikDXQOfXaNCSh9Pex+Psjd5zVVOqyVFJ29daRju3d7rmpN4Cm5V4h0l1
|
||||||
|
/2ad207rjCAnpCHtaddJWNyJzF2IL2IaoCZQRp0k7zOjBGQpoWDTwBaEin5CCv3P
|
||||||
|
kkdQkKz6FDP1xskHSLZr21/QCQKBgQDP6jXutEgGjf3yKpMFk/69EamJdon8clbt
|
||||||
|
hl7cOyWtobnZhdOWVZPe00Oo3Jag2aWgFFsm3EtwnUCnR4d4+fXRKS2LkhfIUZcz
|
||||||
|
cKy17Ileggdd8UGhL4RDrF/En9tJL86WcVkcoOrqLcGB2FLWrVhVpHFK74eLMCH/
|
||||||
|
uc/+ioPItQKBgHYoDsD08s7AGMQcoNx90MyWVLduhFnegoFW+wUa8jOZzieka6/E
|
||||||
|
wVQeR5yksZjpy3vLNYu6M83n7eLkM2rrm/fXGHlLcTTpm7SgEBZfPwivotKjEh5p
|
||||||
|
PrlqucWEk082lutz1RqHz+u7e1Rfzk2F7nx6GDBdeBYpw03eGXJx6QW5AoGBAIJq
|
||||||
|
4puyAEAET1fZNtHX7IGCk7sDXTi6LCbgE57HhzHr8V0t4fQ6CABMuvMwM1gATjEk
|
||||||
|
s6yjoLqqGUUUzDipanViBAy5fiuManC868lN7zkWDTLzQ3ytBqVAee4na/DziP27
|
||||||
|
ae9YTSLJwskE/alloLRP6zTbHUXE0n7LelmrX1DFAoGBAMFLl+Lu+WFgCHxBjn43
|
||||||
|
rHpJbQZQmsFhAMhkN4hsj6dJfAGn2gRLRiVRAika+8QF65xMZiVQWUVSUZADWERi
|
||||||
|
0SXGjzN1wYxO3Qzy3LYwws6fxFAq5lo79eb38yFT2lHdqK3x/QgiDSRVl+R6cExV
|
||||||
|
xQB518/lp2eIeMpglWByDwJX
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
320
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/signer.go
generated
vendored
Normal file
320
Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/signer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,320 @@
|
||||||
|
// Package signer implements certificate signature functionality for CF-SSL.
|
||||||
|
package signer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/asn1"
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
|
||||||
|
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/csr"
|
||||||
|
cferr "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MaxPathLen is the default path length for a new CA certificate.
|
||||||
|
var MaxPathLen = 2
|
||||||
|
|
||||||
|
// Subject contains the information that should be used to override the
|
||||||
|
// subject information when signing a certificate.
|
||||||
|
type Subject struct {
|
||||||
|
CN string
|
||||||
|
Names []csr.Name `json:"names"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SignRequest stores a signature request, which contains the hostname,
|
||||||
|
// the CSR, optional subject information, and the signature profile.
|
||||||
|
type SignRequest struct {
|
||||||
|
Hosts []string `json:"hosts"`
|
||||||
|
Request string `json:"certificate_request"`
|
||||||
|
Subject *Subject `json:"subject,omitempty"`
|
||||||
|
Profile string `json:"profile"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendIf appends to a if s is not an empty string.
|
||||||
|
func appendIf(s string, a *[]string) {
|
||||||
|
if s != "" {
|
||||||
|
*a = append(*a, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the PKIX name for the subject.
|
||||||
|
func (s *Subject) Name() pkix.Name {
|
||||||
|
var name pkix.Name
|
||||||
|
name.CommonName = s.CN
|
||||||
|
|
||||||
|
for _, n := range s.Names {
|
||||||
|
appendIf(n.C, &name.Country)
|
||||||
|
appendIf(n.ST, &name.Province)
|
||||||
|
appendIf(n.L, &name.Locality)
|
||||||
|
appendIf(n.O, &name.Organization)
|
||||||
|
appendIf(n.OU, &name.OrganizationalUnit)
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// SplitHosts takes a comma-spearated list of hosts and returns a slice
|
||||||
|
// with the hosts split
|
||||||
|
func SplitHosts(hostList string) []string {
|
||||||
|
return strings.Split(hostList, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Signer contains a CA's certificate and private key for signing
|
||||||
|
// certificates, a Signing policy to refer to and a SignatureAlgorithm.
|
||||||
|
type Signer interface {
|
||||||
|
Certificate(label, profile string) (*x509.Certificate, error)
|
||||||
|
Policy() *config.Signing
|
||||||
|
SetPolicy(*config.Signing)
|
||||||
|
SigAlgo() x509.SignatureAlgorithm
|
||||||
|
Sign(req SignRequest) (cert []byte, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultSigAlgo returns an appropriate X.509 signature algorithm given
|
||||||
|
// the CA's private key.
|
||||||
|
func DefaultSigAlgo(priv crypto.Signer) x509.SignatureAlgorithm {
|
||||||
|
pub := priv.Public()
|
||||||
|
switch pub := pub.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
keySize := pub.N.BitLen()
|
||||||
|
switch {
|
||||||
|
case keySize >= 4096:
|
||||||
|
return x509.SHA512WithRSA
|
||||||
|
case keySize >= 3072:
|
||||||
|
return x509.SHA384WithRSA
|
||||||
|
case keySize >= 2048:
|
||||||
|
return x509.SHA256WithRSA
|
||||||
|
default:
|
||||||
|
return x509.SHA1WithRSA
|
||||||
|
}
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
switch pub.Curve {
|
||||||
|
case elliptic.P256():
|
||||||
|
return x509.ECDSAWithSHA256
|
||||||
|
case elliptic.P384():
|
||||||
|
return x509.ECDSAWithSHA384
|
||||||
|
case elliptic.P521():
|
||||||
|
return x509.ECDSAWithSHA512
|
||||||
|
default:
|
||||||
|
return x509.ECDSAWithSHA1
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return x509.UnknownSignatureAlgorithm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseCertificateRequest takes an incoming certificate request and
|
||||||
|
// builds a certificate template from it. If not nil, the subject
|
||||||
|
// information from subject will be used in place of the information in
|
||||||
|
// the CSR. The same is true for the list of hosts.
|
||||||
|
func ParseCertificateRequest(s Signer, csrBytes []byte, sub *Subject, hosts []string) (template *x509.Certificate, err error) {
|
||||||
|
csr, err := x509.ParseCertificateRequest(csrBytes)
|
||||||
|
if err != nil {
|
||||||
|
err = cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = CheckSignature(csr, csr.SignatureAlgorithm, csr.RawTBSCertificateRequest, csr.Signature)
|
||||||
|
if err != nil {
|
||||||
|
err = cferr.Wrap(cferr.CertificateError, cferr.KeyMismatch, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
template = &x509.Certificate{
|
||||||
|
Subject: csr.Subject,
|
||||||
|
PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
|
||||||
|
PublicKey: csr.PublicKey,
|
||||||
|
SignatureAlgorithm: s.SigAlgo(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if sub != nil {
|
||||||
|
template.Subject = sub.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range hosts {
|
||||||
|
if ip := net.ParseIP(hosts[i]); ip != nil {
|
||||||
|
template.IPAddresses = append(template.IPAddresses, ip)
|
||||||
|
} else {
|
||||||
|
template.DNSNames = append(template.DNSNames, hosts[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckSignature verifies a signature made by the key on a CSR, such
|
||||||
|
// as on the CSR itself.
|
||||||
|
func CheckSignature(csr *x509.CertificateRequest, algo x509.SignatureAlgorithm, signed, signature []byte) error {
|
||||||
|
var hashType crypto.Hash
|
||||||
|
|
||||||
|
switch algo {
|
||||||
|
case x509.SHA1WithRSA, x509.ECDSAWithSHA1:
|
||||||
|
hashType = crypto.SHA1
|
||||||
|
case x509.SHA256WithRSA, x509.ECDSAWithSHA256:
|
||||||
|
hashType = crypto.SHA256
|
||||||
|
case x509.SHA384WithRSA, x509.ECDSAWithSHA384:
|
||||||
|
hashType = crypto.SHA384
|
||||||
|
case x509.SHA512WithRSA, x509.ECDSAWithSHA512:
|
||||||
|
hashType = crypto.SHA512
|
||||||
|
default:
|
||||||
|
return x509.ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hashType.Available() {
|
||||||
|
return x509.ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
h := hashType.New()
|
||||||
|
|
||||||
|
h.Write(signed)
|
||||||
|
digest := h.Sum(nil)
|
||||||
|
|
||||||
|
switch pub := csr.PublicKey.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
return rsa.VerifyPKCS1v15(pub, hashType, digest, signature)
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
ecdsaSig := new(struct{ R, S *big.Int })
|
||||||
|
if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
|
||||||
|
return errors.New("x509: ECDSA signature contained zero or negative values")
|
||||||
|
}
|
||||||
|
if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) {
|
||||||
|
return errors.New("x509: ECDSA verification failure")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return x509.ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
type subjectPublicKeyInfo struct {
|
||||||
|
Algorithm pkix.AlgorithmIdentifier
|
||||||
|
SubjectPublicKey asn1.BitString
|
||||||
|
}
|
||||||
|
|
||||||
|
// ComputeSKI derives an SKI from the certificate's public key in a
|
||||||
|
// standard manner. This is done by computing the SHA-1 digest of the
|
||||||
|
// SubjectPublicKeyInfo component of the certificate.
|
||||||
|
func ComputeSKI(template *x509.Certificate) ([]byte, error) {
|
||||||
|
pub := template.PublicKey
|
||||||
|
encodedPub, err := x509.MarshalPKIXPublicKey(pub)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var subPKI subjectPublicKeyInfo
|
||||||
|
_, err = asn1.Unmarshal(encodedPub, &subPKI)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pubHash := sha1.Sum(subPKI.SubjectPublicKey.Bytes)
|
||||||
|
return pubHash[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillTemplate is a utility function that tries to load as much of
|
||||||
|
// the certificate template as possible from the profiles and current
|
||||||
|
// template. It fills in the key uses, expiration, revocation URLs,
|
||||||
|
// serial number, and SKI.
|
||||||
|
func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.SigningProfile) error {
|
||||||
|
ski, err := ComputeSKI(template)
|
||||||
|
|
||||||
|
var (
|
||||||
|
eku []x509.ExtKeyUsage
|
||||||
|
ku x509.KeyUsage
|
||||||
|
backdate time.Duration
|
||||||
|
expiry time.Duration
|
||||||
|
notBefore time.Time
|
||||||
|
notAfter time.Time
|
||||||
|
crlURL, ocspURL string
|
||||||
|
)
|
||||||
|
|
||||||
|
// The third value returned from Usages is a list of unknown key usages.
|
||||||
|
// This should be used when validating the profile at load, and isn't used
|
||||||
|
// here.
|
||||||
|
ku, eku, _ = profile.Usages()
|
||||||
|
if profile.IssuerURL == nil {
|
||||||
|
profile.IssuerURL = defaultProfile.IssuerURL
|
||||||
|
}
|
||||||
|
|
||||||
|
if ku == 0 && len(eku) == 0 {
|
||||||
|
return cferr.New(cferr.PolicyError, cferr.NoKeyUsages)
|
||||||
|
}
|
||||||
|
|
||||||
|
if expiry = profile.Expiry; expiry == 0 {
|
||||||
|
expiry = defaultProfile.Expiry
|
||||||
|
}
|
||||||
|
|
||||||
|
if crlURL = profile.CRL; crlURL == "" {
|
||||||
|
crlURL = defaultProfile.CRL
|
||||||
|
}
|
||||||
|
if ocspURL = profile.OCSP; ocspURL == "" {
|
||||||
|
ocspURL = defaultProfile.OCSP
|
||||||
|
}
|
||||||
|
if backdate = profile.Backdate; backdate == 0 {
|
||||||
|
backdate = -5 * time.Minute
|
||||||
|
} else {
|
||||||
|
backdate = -1 * profile.Backdate
|
||||||
|
}
|
||||||
|
|
||||||
|
if !profile.NotBefore.IsZero() {
|
||||||
|
notBefore = profile.NotBefore.UTC()
|
||||||
|
} else {
|
||||||
|
notBefore = time.Now().Round(time.Minute).Add(backdate).UTC()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !profile.NotAfter.IsZero() {
|
||||||
|
notAfter = profile.NotAfter.UTC()
|
||||||
|
} else {
|
||||||
|
notAfter = notBefore.Add(expiry).UTC()
|
||||||
|
}
|
||||||
|
|
||||||
|
serialNumber, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
|
||||||
|
if err != nil {
|
||||||
|
return cferr.Wrap(cferr.CertificateError, cferr.Unknown, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
template.SerialNumber = serialNumber
|
||||||
|
template.NotBefore = notBefore
|
||||||
|
template.NotAfter = notAfter
|
||||||
|
template.KeyUsage = ku
|
||||||
|
template.ExtKeyUsage = eku
|
||||||
|
template.BasicConstraintsValid = true
|
||||||
|
template.IsCA = profile.CA
|
||||||
|
template.SubjectKeyId = ski
|
||||||
|
|
||||||
|
if ocspURL != "" {
|
||||||
|
template.OCSPServer = []string{ocspURL}
|
||||||
|
}
|
||||||
|
if crlURL != "" {
|
||||||
|
template.CRLDistributionPoints = []string{crlURL}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(profile.IssuerURL) != 0 {
|
||||||
|
template.IssuingCertificateURL = profile.IssuerURL
|
||||||
|
}
|
||||||
|
if len(profile.Policies) != 0 {
|
||||||
|
template.PolicyIdentifiers = profile.Policies
|
||||||
|
}
|
||||||
|
if profile.OCSPNoCheck {
|
||||||
|
ocspNoCheckExtension := pkix.Extension{
|
||||||
|
Id: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 5},
|
||||||
|
Critical: false,
|
||||||
|
Value: []byte{0x05, 0x00},
|
||||||
|
}
|
||||||
|
template.ExtraExtensions = append(template.ExtraExtensions, ocspNoCheckExtension)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue