This commit is contained in:
Mikaël Cluseau 2018-06-17 18:32:44 +11:00
parent f92c531f5d
commit 4d889632f6
500 changed files with 133832 additions and 0 deletions

8
go.mod Normal file
View File

@ -0,0 +1,8 @@
module novit.nc/direktil/local-server
require (
github.com/cavaliercoder/go-cpio v0.0.0-20180222193108-9caab6ff29df
github.com/cloudflare/cfssl v0.0.0-20180530085446-275fb308ac70
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f
novit.nc/direktil/pkg v0.0.0-20180617064658-ec3736cedc38
)

3
vendor/github.com/cavaliercoder/go-cpio/.gitignore generated vendored Normal file
View File

@ -0,0 +1,3 @@
.fuzz/
*.zip

10
vendor/github.com/cavaliercoder/go-cpio/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,10 @@
language: go
go:
- 1.4.3
- 1.5.4
- 1.6.4
- 1.7.6
- 1.8.3
script: make check

26
vendor/github.com/cavaliercoder/go-cpio/LICENSE generated vendored Normal file
View File

@ -0,0 +1,26 @@
Copyright (c) 2017 Ryan Armstrong. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

18
vendor/github.com/cavaliercoder/go-cpio/Makefile generated vendored Normal file
View File

@ -0,0 +1,18 @@
PACKAGE = github.com/cavaliercoder/go-cpio
all: check
check:
go test -v
cpio-fuzz.zip: *.go
go-fuzz-build $(PACKAGE)
fuzz: cpio-fuzz.zip
go-fuzz -bin=./cpio-fuzz.zip -workdir=.fuzz/
clean-fuzz:
rm -rf cpio-fuzz.zip .fuzz/crashers/* .fuzz/suppressions/*
.PHONY: all check

62
vendor/github.com/cavaliercoder/go-cpio/README.md generated vendored Normal file
View File

@ -0,0 +1,62 @@
# go-cpio [![GoDoc](https://godoc.org/github.com/cavaliercoder/go-cpio?status.svg)](https://godoc.org/github.com/cavaliercoder/go-cpio) [![Build Status](https://travis-ci.org/cavaliercoder/go-cpio.svg?branch=master)](https://travis-ci.org/cavaliercoder/go-cpio) [![Go Report Card](https://goreportcard.com/badge/github.com/cavaliercoder/go-cpio)](https://goreportcard.com/report/github.com/cavaliercoder/go-cpio)
This package provides a Go native implementation of the CPIO archive file
format.
Currently, only the SVR4 (New ASCII) format is supported, both with and without
checksums.
```go
// Create a buffer to write our archive to.
buf := new(bytes.Buffer)
// Create a new cpio archive.
w := cpio.NewWriter(buf)
// Add some files to the archive.
var files = []struct {
Name, Body string
}{
{"readme.txt", "This archive contains some text files."},
{"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
{"todo.txt", "Get animal handling license."},
}
for _, file := range files {
hdr := &cpio.Header{
Name: file.Name,
Mode: 0600,
Size: int64(len(file.Body)),
}
if err := w.WriteHeader(hdr); err != nil {
log.Fatalln(err)
}
if _, err := w.Write([]byte(file.Body)); err != nil {
log.Fatalln(err)
}
}
// Make sure to check the error on Close.
if err := w.Close(); err != nil {
log.Fatalln(err)
}
// Open the cpio archive for reading.
b := bytes.NewReader(buf.Bytes())
r := cpio.NewReader(b)
// Iterate through the files in the archive.
for {
hdr, err := r.Next()
if err == io.EOF {
// end of cpio archive
break
}
if err != nil {
log.Fatalln(err)
}
fmt.Printf("Contents of %s:\n", hdr.Name)
if _, err := io.Copy(os.Stdout, r); err != nil {
log.Fatalln(err)
}
fmt.Println()
}
```

8
vendor/github.com/cavaliercoder/go-cpio/cpio.go generated vendored Normal file
View File

@ -0,0 +1,8 @@
/*
Package cpio implements access to CPIO archives. Currently, only the SVR4 (New
ASCII) format is supported, both with and without checksums.
References:
https://www.freebsd.org/cgi/man.cgi?query=cpio&sektion=5
*/
package cpio

View File

@ -0,0 +1,77 @@
package cpio_test
import (
"bytes"
"fmt"
"io"
"log"
"os"
"github.com/cavaliercoder/go-cpio"
)
func Example() {
// Create a buffer to write our archive to.
buf := new(bytes.Buffer)
// Create a new cpio archive.
w := cpio.NewWriter(buf)
// Add some files to the archive.
var files = []struct {
Name, Body string
}{
{"readme.txt", "This archive contains some text files."},
{"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
{"todo.txt", "Get animal handling license."},
}
for _, file := range files {
hdr := &cpio.Header{
Name: file.Name,
Mode: 0600,
Size: int64(len(file.Body)),
}
if err := w.WriteHeader(hdr); err != nil {
log.Fatalln(err)
}
if _, err := w.Write([]byte(file.Body)); err != nil {
log.Fatalln(err)
}
}
// Make sure to check the error on Close.
if err := w.Close(); err != nil {
log.Fatalln(err)
}
// Open the cpio archive for reading.
b := bytes.NewReader(buf.Bytes())
r := cpio.NewReader(b)
// Iterate through the files in the archive.
for {
hdr, err := r.Next()
if err == io.EOF {
// end of cpio archive
break
}
if err != nil {
log.Fatalln(err)
}
fmt.Printf("Contents of %s:\n", hdr.Name)
if _, err := io.Copy(os.Stdout, r); err != nil {
log.Fatalln(err)
}
fmt.Println()
}
// Output:
// Contents of readme.txt:
// This archive contains some text files.
// Contents of gopher.txt:
// Gopher names:
// George
// Geoffrey
// Gonzo
// Contents of todo.txt:
// Get animal handling license.
}

75
vendor/github.com/cavaliercoder/go-cpio/fileinfo.go generated vendored Normal file
View File

@ -0,0 +1,75 @@
package cpio
import (
"os"
"path"
"time"
)
// headerFileInfo implements os.FileInfo.
type headerFileInfo struct {
h *Header
}
// Name returns the base name of the file.
func (fi headerFileInfo) Name() string {
if fi.IsDir() {
return path.Base(path.Clean(fi.h.Name))
}
return path.Base(fi.h.Name)
}
func (fi headerFileInfo) Size() int64 { return fi.h.Size }
func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
func (fi headerFileInfo) ModTime() time.Time { return fi.h.ModTime }
func (fi headerFileInfo) Sys() interface{} { return fi.h }
func (fi headerFileInfo) Mode() (mode os.FileMode) {
// Set file permission bits.
mode = os.FileMode(fi.h.Mode).Perm()
// Set setuid, setgid and sticky bits.
if fi.h.Mode&ModeSetuid != 0 {
// setuid
mode |= os.ModeSetuid
}
if fi.h.Mode&ModeSetgid != 0 {
// setgid
mode |= os.ModeSetgid
}
if fi.h.Mode&ModeSticky != 0 {
// sticky
mode |= os.ModeSticky
}
// Set file mode bits.
// clear perm, setuid, setgid and sticky bits.
m := os.FileMode(fi.h.Mode) & 0170000
if m == ModeDir {
// directory
mode |= os.ModeDir
}
if m == ModeNamedPipe {
// named pipe (FIFO)
mode |= os.ModeNamedPipe
}
if m == ModeSymlink {
// symbolic link
mode |= os.ModeSymlink
}
if m == ModeDevice {
// device file
mode |= os.ModeDevice
}
if m == ModeCharDevice {
// Unix character device
mode |= os.ModeDevice
mode |= os.ModeCharDevice
}
if m == ModeSocket {
// Unix domain socket
mode |= os.ModeSocket
}
return mode
}

35
vendor/github.com/cavaliercoder/go-cpio/fuzz.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
// +build gofuzz
package cpio
import "bytes"
import "io"
// Fuzz tests the parsing and error handling of random byte arrays using
// https://github.com/dvyukov/go-fuzz.
func Fuzz(data []byte) int {
r := NewReader(bytes.NewReader(data))
h := NewHash()
for {
hdr, err := r.Next()
if err != nil {
if hdr != nil {
panic("hdr != nil on error")
}
if err == io.EOF {
// everything worked with random input... interesting
return 1
}
// error returned for random input. Good!
return -1
}
// hash file
h.Reset()
io.CopyN(h, r, hdr.Size)
h.Sum32()
// convert file header
FileInfoHeader(hdr.FileInfo())
}
}

45
vendor/github.com/cavaliercoder/go-cpio/hash.go generated vendored Normal file
View File

@ -0,0 +1,45 @@
package cpio
import (
"encoding/binary"
"hash"
)
type digest struct {
sum uint32
}
// NewHash returns a new hash.Hash32 computing the SVR4 checksum.
func NewHash() hash.Hash32 {
return &digest{}
}
func (d *digest) Write(p []byte) (n int, err error) {
for _, b := range p {
d.sum += uint32(b & 0xFF)
}
return len(p), nil
}
func (d *digest) Sum(b []byte) []byte {
out := [4]byte{}
binary.LittleEndian.PutUint32(out[:], d.sum)
return append(b, out[:]...)
}
func (d *digest) Sum32() uint32 {
return d.sum
}
func (d *digest) Reset() {
d.sum = 0
}
func (d *digest) Size() int {
return 4
}
func (d *digest) BlockSize() int {
return 1
}

152
vendor/github.com/cavaliercoder/go-cpio/header.go generated vendored Normal file
View File

@ -0,0 +1,152 @@
package cpio
import (
"errors"
"fmt"
"os"
"time"
)
// Mode constants from the cpio spec.
const (
ModeSetuid = 04000 // Set uid
ModeSetgid = 02000 // Set gid
ModeSticky = 01000 // Save text (sticky bit)
ModeDir = 040000 // Directory
ModeNamedPipe = 010000 // FIFO
ModeRegular = 0100000 // Regular file
ModeSymlink = 0120000 // Symbolic link
ModeDevice = 060000 // Block special file
ModeCharDevice = 020000 // Character special file
ModeSocket = 0140000 // Socket
ModeType = 0170000 // Mask for the type bits
ModePerm = 0777 // Unix permission bits
)
const (
// headerEOF is the value of the filename of the last header in a CPIO archive.
headerEOF = "TRAILER!!!"
)
var (
ErrHeader = errors.New("cpio: invalid cpio header")
)
// A FileMode represents a file's mode and permission bits.
type FileMode int64
func (m FileMode) String() string {
return fmt.Sprintf("%#o", m)
}
// IsDir reports whether m describes a directory. That is, it tests for the
// ModeDir bit being set in m.
func (m FileMode) IsDir() bool {
return m&ModeDir != 0
}
// IsRegular reports whether m describes a regular file. That is, it tests for
// the ModeRegular bit being set in m.
func (m FileMode) IsRegular() bool {
return m&^ModePerm == ModeRegular
}
// Perm returns the Unix permission bits in m.
func (m FileMode) Perm() FileMode {
return m & ModePerm
}
// Checksum is the sum of all bytes in the file data. This sum is computed
// treating all bytes as unsigned values and using unsigned arithmetic. Only
// the least-significant 32 bits of the sum are stored. Use NewHash to compute
// the actual checksum of an archived file.
type Checksum uint32
func (c Checksum) String() string {
return fmt.Sprintf("%08X", uint32(c))
}
// A Header represents a single header in a CPIO archive.
type Header struct {
DeviceID int
Inode int64 // inode number
Mode FileMode // permission and mode bits
UID int // user id of the owner
GID int // group id of the owner
Links int // number of inbound links
ModTime time.Time // modified time
Size int64 // size in bytes
Name string // filename
Linkname string // target name of link
Checksum Checksum // computed checksum
pad int64 // bytes to pad before next header
}
// FileInfo returns an os.FileInfo for the Header.
func (h *Header) FileInfo() os.FileInfo {
return headerFileInfo{h}
}
// FileInfoHeader creates a partially-populated Header from fi.
// If fi describes a symlink, FileInfoHeader records link as the link target.
// If fi describes a directory, a slash is appended to the name.
// Because os.FileInfo's Name method returns only the base name of
// the file it describes, it may be necessary to modify the Name field
// of the returned header to provide the full path name of the file.
func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
if fi == nil {
return nil, errors.New("cpio: FileInfo is nil")
}
if sys, ok := fi.Sys().(*Header); ok {
// This FileInfo came from a Header (not the OS). Return a copy of the
// original Header.
h := &Header{}
*h = *sys
return h, nil
}
fm := fi.Mode()
h := &Header{
Name: fi.Name(),
Mode: FileMode(fi.Mode().Perm()), // or'd with Mode* constants later
ModTime: fi.ModTime(),
Size: fi.Size(),
}
switch {
case fm.IsRegular():
h.Mode |= ModeRegular
case fi.IsDir():
h.Mode |= ModeDir
h.Name += "/"
case fm&os.ModeSymlink != 0:
h.Mode |= ModeSymlink
h.Linkname = link
case fm&os.ModeDevice != 0:
if fm&os.ModeCharDevice != 0 {
h.Mode |= ModeCharDevice
} else {
h.Mode |= ModeDevice
}
case fm&os.ModeNamedPipe != 0:
h.Mode |= ModeNamedPipe
case fm&os.ModeSocket != 0:
h.Mode |= ModeSocket
default:
return nil, fmt.Errorf("cpio: unknown file mode %v", fm)
}
if fm&os.ModeSetuid != 0 {
h.Mode |= ModeSetuid
}
if fm&os.ModeSetgid != 0 {
h.Mode |= ModeSetgid
}
if fm&os.ModeSticky != 0 {
h.Mode |= ModeSticky
}
return h, nil
}

72
vendor/github.com/cavaliercoder/go-cpio/reader.go generated vendored Normal file
View File

@ -0,0 +1,72 @@
package cpio
import (
"io"
"io/ioutil"
)
// A Reader provides sequential access to the contents of a CPIO archive. A CPIO
// archive consists of a sequence of files. The Next method advances to the next
// file in the archive (including the first), and then it can be treated as an
// io.Reader to access the file's data.
type Reader struct {
r io.Reader // underlying file reader
hdr *Header // current Header
eof int64 // bytes until the end of the current file
}
// NewReader creates a new Reader reading from r.
func NewReader(r io.Reader) *Reader {
return &Reader{
r: r,
}
}
// Read reads from the current entry in the CPIO archive. It returns 0, io.EOF
// when it reaches the end of that entry, until Next is called to advance to the
// next entry.
func (r *Reader) Read(p []byte) (n int, err error) {
if r.hdr == nil || r.eof == 0 {
return 0, io.EOF
}
rn := len(p)
if r.eof < int64(rn) {
rn = int(r.eof)
}
n, err = r.r.Read(p[0:rn])
r.eof -= int64(n)
return
}
// Next advances to the next entry in the CPIO archive.
// io.EOF is returned at the end of the input.
func (r *Reader) Next() (*Header, error) {
if r.hdr == nil {
return r.next()
}
skp := r.eof + r.hdr.pad
if skp > 0 {
_, err := io.CopyN(ioutil.Discard, r.r, skp)
if err != nil {
return nil, err
}
}
return r.next()
}
func (r *Reader) next() (*Header, error) {
r.eof = 0
hdr, err := readHeader(r.r)
if err != nil {
return nil, err
}
r.hdr = hdr
r.eof = hdr.Size
return hdr, nil
}
// ReadHeader creates a new Header, reading from r.
func readHeader(r io.Reader) (*Header, error) {
// currently only SVR4 format is supported
return readSVR4Header(r)
}

145
vendor/github.com/cavaliercoder/go-cpio/svr4.go generated vendored Normal file
View File

@ -0,0 +1,145 @@
package cpio
import (
"bytes"
"fmt"
"io"
"strconv"
"time"
)
const (
svr4MaxNameSize = 4096 // MAX_PATH
svr4MaxFileSize = 4294967295
)
var svr4Magic = []byte{0x30, 0x37, 0x30, 0x37, 0x30, 0x31} // 07070
func readHex(s string) int64 {
// errors are ignored and 0 returned
i, _ := strconv.ParseInt(s, 16, 64)
return i
}
func writeHex(b []byte, i int64) {
// i needs to be in range of uint32
copy(b, fmt.Sprintf("%08X", i))
}
func readSVR4Header(r io.Reader) (*Header, error) {
var buf [110]byte
if _, err := io.ReadFull(r, buf[:]); err != nil {
return nil, err
}
// TODO: check endianness
// check magic
hasCRC := false
if !bytes.HasPrefix(buf[:], svr4Magic[:5]) {
return nil, ErrHeader
}
if buf[5] == 0x32 { // '2'
hasCRC = true
} else if buf[5] != 0x31 { // '1'
return nil, ErrHeader
}
asc := string(buf[:])
hdr := &Header{}
hdr.Inode = readHex(asc[6:14])
hdr.Mode = FileMode(readHex(asc[14:22]))
hdr.UID = int(readHex(asc[22:30]))
hdr.GID = int(readHex(asc[30:38]))
hdr.Links = int(readHex(asc[38:46]))
hdr.ModTime = time.Unix(readHex(asc[46:54]), 0)
hdr.Size = readHex(asc[54:62])
if hdr.Size > svr4MaxFileSize {
return nil, ErrHeader
}
nameSize := readHex(asc[94:102])
if nameSize < 1 || nameSize > svr4MaxNameSize {
return nil, ErrHeader
}
hdr.Checksum = Checksum(readHex(asc[102:110]))
if !hasCRC && hdr.Checksum != 0 {
return nil, ErrHeader
}
name := make([]byte, nameSize)
if _, err := io.ReadFull(r, name); err != nil {
return nil, err
}
hdr.Name = string(name[:nameSize-1])
if hdr.Name == headerEOF {
return nil, io.EOF
}
// store padding between end of file and next header
hdr.pad = (4 - (hdr.Size % 4)) % 4
// skip to end of header/start of file
pad := (4 - (len(buf)+len(name))%4) % 4
if pad > 0 {
if _, err := io.ReadFull(r, buf[:pad]); err != nil {
return nil, err
}
}
// read link name
if hdr.Mode&^ModePerm == ModeSymlink {
if hdr.Size < 1 || hdr.Size > svr4MaxNameSize {
return nil, ErrHeader
}
b := make([]byte, hdr.Size)
if _, err := io.ReadFull(r, b); err != nil {
return nil, err
}
hdr.Linkname = string(b)
hdr.Size = 0
}
return hdr, nil
}
func writeSVR4Header(w io.Writer, hdr *Header) (pad int64, err error) {
var hdrBuf [110]byte
for i := 0; i < len(hdrBuf); i++ {
hdrBuf[i] = '0'
}
copy(hdrBuf[:], svr4Magic)
writeHex(hdrBuf[6:14], hdr.Inode)
writeHex(hdrBuf[14:22], int64(hdr.Mode))
writeHex(hdrBuf[22:30], int64(hdr.UID))
writeHex(hdrBuf[30:38], int64(hdr.GID))
writeHex(hdrBuf[38:46], int64(hdr.Links))
if !hdr.ModTime.IsZero() {
writeHex(hdrBuf[46:54], hdr.ModTime.Unix())
}
writeHex(hdrBuf[54:62], hdr.Size)
writeHex(hdrBuf[94:102], int64(len(hdr.Name)+1))
// write header
_, err = w.Write(hdrBuf[:])
if err != nil {
return
}
// write filename
_, err = io.WriteString(w, hdr.Name+"\x00")
if err != nil {
return
}
// pad to end of filename
npad := (4 - ((len(hdrBuf) + len(hdr.Name) + 1) % 4)) % 4
_, err = w.Write(zeroBlock[:npad])
if err != nil {
return
}
// compute padding to end of file
pad = (4 - (hdr.Size % 4)) % 4
return
}

121
vendor/github.com/cavaliercoder/go-cpio/svr4_test.go generated vendored Normal file
View File

@ -0,0 +1,121 @@
package cpio
import (
"fmt"
"io"
"log"
"os"
"testing"
)
var files = []struct {
Name, Body string
}{
{"./gophers.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
{"./readme.txt", "This archive contains some text files."},
{"./todo.txt", "Get animal handling license."},
}
func TestRead(t *testing.T) {
f, err := os.Open("testdata/test_svr4_crc.cpio")
if err != nil {
t.Fatalf("error opening test file: %v", err)
}
defer f.Close()
r := NewReader(f)
for {
_, err := r.Next()
if err == io.EOF {
return
}
if err != nil {
t.Errorf("error moving to next header: %v", err)
return
}
// TODO: validate header fields
}
}
func TestSVR4CRC(t *testing.T) {
f, err := os.Open("testdata/test_svr4_crc.cpio")
if err != nil {
t.Fatalf("error opening test file: %v", err)
}
defer f.Close()
w := NewHash()
r := NewReader(f)
for {
hdr, err := r.Next()
if err != nil {
if err != io.EOF {
t.Errorf("error moving to next header: %v", err)
}
return
}
if hdr.Mode.IsRegular() {
w.Reset()
_, err = io.CopyN(w, r, hdr.Size)
if err != nil {
t.Fatalf("error writing to checksum hash: %v", err)
}
sum := Checksum(w.Sum32())
if sum != hdr.Checksum {
t.Errorf("expected checksum %v, got %v for %v", hdr.Checksum, sum, hdr.Name)
}
}
}
}
func ExampleNewHash() {
// Open the cpio archive for reading.
f, err := os.Open("testdata/test_svr4_crc.cpio")
if err != nil {
log.Fatal(err)
}
defer f.Close()
r := NewReader(f)
// create a Hash
h := NewHash()
// Iterate through the files in the archive.
for {
hdr, err := r.Next()
if err == io.EOF {
// end of cpio archive
return
}
if err != nil {
log.Fatal(err)
}
// skip symlinks, directories, etc.
if !hdr.Mode.IsRegular() {
continue
}
// read file into hash
h.Reset()
_, err = io.CopyN(h, r, hdr.Size)
if err != nil {
log.Fatal(err)
}
// check hash matches header checksum
sum := Checksum(h.Sum32())
if sum == hdr.Checksum {
fmt.Printf("Checksum OK: %v (%v)\n", hdr.Name, hdr.Checksum)
} else {
fmt.Printf("Checksum FAIL: %v - expected %v, got %v\n", hdr.Name, hdr.Checksum, sum)
}
}
// Output:
// Checksum OK: gophers.txt (00000C98)
// Checksum OK: readme.txt (00000E3D)
// Checksum OK: todo.txt (00000A52)
}

View File

@ -0,0 +1,26 @@
SOURCES = \
gophers.txt \
readme.txt \
todo.txt \
checklist.txt
ARCHIVES = \
test_odc.cpio \
test_svr4.cpio \
test_svr4_crc.cpio
all: $(ARCHIVES)
test_odc.cpio: $(SOURCES)
echo $(SOURCES) | tr " " "\n" | cpio -o --owner=0:0 --format=odc > $@
test_svr4.cpio: $(SOURCES)
echo $(SOURCES) | tr " " "\n" | cpio -o --owner=0:0 --format=newc > $@
test_svr4_crc.cpio: $(SOURCES)
echo $(SOURCES) | tr " " "\n" | cpio -o --owner=0:0 --format=crc > $@
clean:
rm -f $(ARCHIVES) version.txt
.PHONY: all clean

View File

@ -0,0 +1 @@
todo.txt

View File

@ -0,0 +1,4 @@
Gopher names:
George
Geoffrey
Gonzo

View File

@ -0,0 +1 @@
This archive contains some text files.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
Get animal handling license.

128
vendor/github.com/cavaliercoder/go-cpio/writer.go generated vendored Normal file
View File

@ -0,0 +1,128 @@
package cpio
import (
"errors"
"fmt"
"io"
)
var (
ErrWriteTooLong = errors.New("cpio: write too long")
ErrWriteAfterClose = errors.New("cpio: write after close")
)
var trailer = &Header{
Name: string(headerEOF),
Links: 1,
}
var zeroBlock [4]byte
// A Writer provides sequential writing of a CPIO archive. A CPIO archive
// consists of a sequence of files. Call WriteHeader to begin a new file, and
// then call Write to supply that file's data, writing at most hdr.Size bytes in
// total.
type Writer struct {
w io.Writer
nb int64 // number of unwritten bytes for current file entry
pad int64 // amount of padding to write after current file entry
inode int64
err error
closed bool
}
// NewWriter creates a new Writer writing to w.
func NewWriter(w io.Writer) *Writer {
return &Writer{w: w}
}
// Flush finishes writing the current file (optional).
func (w *Writer) Flush() error {
if w.nb > 0 {
w.err = fmt.Errorf("cpio: missed writing %d bytes", w.nb)
return w.err
}
_, w.err = w.w.Write(zeroBlock[:w.pad])
if w.err != nil {
return w.err
}
w.nb = 0
w.pad = 0
return w.err
}
// WriteHeader writes hdr and prepares to accept the file's contents.
// WriteHeader calls Flush if it is not the first header. Calling after a Close
// will return ErrWriteAfterClose.
func (w *Writer) WriteHeader(hdr *Header) (err error) {
if w.closed {
return ErrWriteAfterClose
}
if w.err == nil {
w.Flush()
}
if w.err != nil {
return w.err
}
if hdr.Name != headerEOF {
// TODO: should we be mutating hdr here?
// ensure all inodes are unique
w.inode++
if hdr.Inode == 0 {
hdr.Inode = w.inode
}
// ensure file type is set
if hdr.Mode&^ModePerm == 0 {
hdr.Mode |= ModeRegular
}
// ensure regular files have at least 1 inbound link
if hdr.Links < 1 && hdr.Mode.IsRegular() {
hdr.Links = 1
}
}
w.nb = hdr.Size
w.pad, w.err = writeSVR4Header(w.w, hdr)
return
}
// Write writes to the current entry in the CPIO archive. Write returns the
// error ErrWriteTooLong if more than hdr.Size bytes are written after
// WriteHeader.
func (w *Writer) Write(p []byte) (n int, err error) {
if w.closed {
err = ErrWriteAfterClose
return
}
overwrite := false
if int64(len(p)) > w.nb {
p = p[0:w.nb]
overwrite = true
}
n, err = w.w.Write(p)
w.nb -= int64(n)
if err == nil && overwrite {
err = ErrWriteTooLong
return
}
w.err = err
return
}
// Close closes the CPIO archive, flushing any unwritten data to the underlying
// writer.
func (w *Writer) Close() error {
if w.err != nil || w.closed {
return w.err
}
w.err = w.WriteHeader(trailer)
if w.err != nil {
return w.err
}
w.Flush()
w.closed = true
return w.err
}

94
vendor/github.com/cloudflare/cfssl/auth/auth.go generated vendored Normal file
View File

@ -0,0 +1,94 @@
// 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"
"fmt"
"io/ioutil"
"os"
"strings"
)
// 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) {
if splitKey := strings.SplitN(key, ":", 2); len(splitKey) == 2 {
switch splitKey[0] {
case "env":
key = os.Getenv(splitKey[1])
case "file":
data, err := ioutil.ReadFile(splitKey[1])
if err != nil {
return nil, err
}
key = string(data)
default:
return nil, fmt.Errorf("unknown key prefix: %s", splitKey[0])
}
}
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
vendor/github.com/cloudflare/cfssl/auth/auth_test.go generated vendored Normal file
View 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")
}
}
}

View File

@ -0,0 +1 @@
{"token": "tSU1WTE/322iXrOBfJSQ9/u1dleqpwUmCj1LXYHw07Y=", "request": "ewoJImhvc3RuYW1lIjogImt5bGVpc29tLm5ldCIsCgkicmVxdWVzdCI6ICItLS0tLUJFR0lOIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLQoJICAgIE1JSUQwVENDQWpzQ0FRQXdZREVMTUFrR0ExVUVCaE1DVlZNeEVqQVFCZ05WQkFvVENXUnliM0J6YjI1a1pURVEKCSAgICBNQTRHQTFVRUN4TUhRMFl0UTJoaGRERVdNQlFHQTFVRUJ4TU5VMkZ1SUVaeVlXNWphWE5qYnpFVE1CRUdBMVVFCgkgICAgQ0JNS1EyRnNhV1p2Y201cFlUQ0NBYUl3RFFZSktvWklodmNOQVFFQkJRQURnZ0dQQURDQ0FZb0NnZ0dCQU1jQwoJICAgIEdCbDVMVHJla0dGV2hvdGtkYlorUjFNbG9hcld4UXY5alA0QWVrdDhVT2ljeXBIdkZPNnhPdFN3SG8rcjMyaUUKCSAgICBxblM1eXYvMDFQMk1KdXlxbmRuY1RTTXNPbFQvN242N1RNMDB1MDFLLzljL3NvZ0tFS2pseXBsVFA3eUZkRy9jCgkgICAgT3UvOXFLYi9KYWxkMndFTEZZRTZ4cTJSREZ5eHlpWk9CM2c3WjdGeGE1ZDZhZGZHUndaek50VUw0LzhzK0x5aQoJICAgIHFkdzlJMWZrUWQ2MDRwb1pGTjB3clFzNGxmaFdUVWZnMHJIdWg1d2dHS1AzVnpacGJ0OEZiMXZOamZiSHRvaHgKCSAgICBHMlBDVTZKeStEYzFiU2ZVeldjUW5lbnA4NThXNEY4ejdwRjV5YmRuRlIzMTNIam9zcVhuRzI4eklUck9hZE1UCgkgICAgSGFKNnpPaGdFYWZVT1dYT3pqTm9mRkJGYTJJdUNBVCtJVFJZMXRDL2dxcHhHd0gveXVWTjE5Qkc4VXBuMCtIQQoJICAgIGllMm1LQ0hmU0JBS1QvWGU0dW1QZWF4U2JJcVdzVzhjaytkM2I0b3I5Ulp2NWNaUmNUM29pa0p0K1NRRzY5cFcKCSAgICA0T0FiYitBQnNzL05JdXJpNnowZTdERWVJTDV6bXlTSnFkdFlIZE5ZTjcrK3Y5eEJOc0w0SXNVNklFeTMrUUlECgkgICAgQVFBQm9DNHdMQVlKS29aSWh2Y05BUWtPTVI4d0hUQWJCZ05WSFJFRUZEQVNnaEJqWmk1a2NtOXdjMjl1WkdVdQoJICAgIGJtVjBNQXNHQ1NxR1NJYjNEUUVCREFPQ0FZRUFoTUFxQmlySStrMWFVM2xmQUdRaVNtOHl0T3paaWozODloSXIKCSAgICBuVXA4K1duVHVWVGI4WFozL1YrTDlFblRJbUY2dTF3ZWFqWGQzU3VlNDk1NzBMYlltSXV4QmtHcDUwL0JkVUR6CgkgICAgdUI2eHNoaEpXczEySnhVYjkxSW1tMGJUUncyek1xZXdnYTZmdHpaL0FLNG1zeFFBMlVJYmNXWmRzS2J1TTdzbwoJICAgIEpUZlZXOWlPd3FIdC82NFpqNHRCWmY5THpPRHI3a051S0tMbndqaXpIMTg3eGZJSWhkcmpGOFdTN0g5QVBCMU8KCSAgICBTdUVVRGZxaDBTV1IzbHRXdUF1VVdlbzZTS2NIVnVzeS9HNFlFK1BCeXcxZVY3RzRTYmVHNVowbytHT1VVSy9GCgkgICAgYjU1R21XMXhhNExBcnMxQSt6ZUZidkovQkFwc2JVMmI2V1ZtTmE3V3BIejdXWElGT0p1WUpnRWtWS1BKbkt1cwoJICAgIHFxczNGZ1VxejBadjdUSzhtTWlFVEpvWFpzNnpDdk15c1FldTNKL29qZ3RBanZNaHpRYzZQUy9udk90SmRJZysKCSAgICBIMHFYNDlmaHAxQnJZeXNsYWx6UUlGMCtIMHFTVWV5b1V5VjJ3YkxCQUxhcHhNZnZUVmxoTnduYWN0Y0tReHE0CgkgICAgK3dUKzJQVEowYk0vNUFWMFRPMVNQVDBBVmlKaAoJICAgIC0tLS0tRU5EIENFUlRJRklDQVRFIFJFUVVFU1QtLS0tLSIsCgkicHJvZmlsZSI6ICIiLAoJInJlbW90ZSI6ICIiLAoJImxhYmVsIjogInByaW1hcnkiCn0KCg=="}

View 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"
}

75
vendor/github.com/cloudflare/cfssl/certdb/README.md generated vendored Normal file
View File

@ -0,0 +1,75 @@
# certdb usage
Using a database enables additional functionality for existing commands when a
db config is provided:
- `sign` and `gencert` add a certificate to the certdb after signing it
- `serve` enables database functionality for the sign and revoke endpoints
A database is required for the following:
- `revoke` marks certificates revoked in the database with an optional reason
- `ocsprefresh` refreshes the table of cached OCSP responses
- `ocspdump` outputs cached OCSP responses in a concatenated base64-encoded format
## Setup/Migration
This directory stores [goose](https://bitbucket.org/liamstask/goose/) db migration scripts for various DB backends.
Currently supported:
- MySQL in mysql
- PostgreSQL in pg
- SQLite in sqlite
### Get goose
go get bitbucket.org/liamstask/goose/cmd/goose
### Use goose to start and terminate a MySQL DB
To start a MySQL using goose:
goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/mysql up
To tear down a MySQL DB using goose
goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/mysql down
Note: the administration of MySQL DB is not included. We assume
the databases being connected to are already created and access control
is properly handled.
### Use goose to start and terminate a PostgreSQL DB
To start a PostgreSQL using goose:
goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/pg up
To tear down a PostgreSQL DB using goose
goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/pg down
Note: the administration of PostgreSQL DB is not included. We assume
the databases being connected to are already created and access control
is properly handled.
### Use goose to start and terminate a SQLite DB
To start a SQLite DB using goose:
goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/sqlite up
To tear down a SQLite DB using goose
goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/sqlite down
## CFSSL Configuration
Several cfssl commands take a -db-config flag. Create a file with a
JSON dictionary:
{"driver":"sqlite3","data_source":"certs.db"}
or
{"driver":"postgres","data_source":"postgres://user:password@host/db"}
or
{"driver":"mysql","data_source":"user:password@tcp(hostname:3306)/db?parseTime=true"}

42
vendor/github.com/cloudflare/cfssl/certdb/certdb.go generated vendored Normal file
View File

@ -0,0 +1,42 @@
package certdb
import (
"time"
)
// CertificateRecord encodes a certificate and its metadata
// that will be recorded in a database.
type CertificateRecord struct {
Serial string `db:"serial_number"`
AKI string `db:"authority_key_identifier"`
CALabel string `db:"ca_label"`
Status string `db:"status"`
Reason int `db:"reason"`
Expiry time.Time `db:"expiry"`
RevokedAt time.Time `db:"revoked_at"`
PEM string `db:"pem"`
}
// OCSPRecord encodes a OCSP response body and its metadata
// that will be recorded in a database.
type OCSPRecord struct {
Serial string `db:"serial_number"`
AKI string `db:"authority_key_identifier"`
Body string `db:"body"`
Expiry time.Time `db:"expiry"`
}
// Accessor abstracts the CRUD of certdb objects from a DB.
type Accessor interface {
InsertCertificate(cr CertificateRecord) error
GetCertificate(serial, aki string) ([]CertificateRecord, error)
GetUnexpiredCertificates() ([]CertificateRecord, error)
GetRevokedAndUnexpiredCertificates() ([]CertificateRecord, error)
GetRevokedAndUnexpiredCertificatesByLabel(label string) ([]CertificateRecord, error)
RevokeCertificate(serial, aki string, reasonCode int) error
InsertOCSP(rr OCSPRecord) error
GetOCSP(serial, aki string) ([]OCSPRecord, error)
GetUnexpiredOCSPs() ([]OCSPRecord, error)
UpdateOCSP(serial, aki, body string, expiry time.Time) error
UpsertOCSP(serial, aki, body string, expiry time.Time) error
}

659
vendor/github.com/cloudflare/cfssl/config/config.go generated vendored Normal file
View File

@ -0,0 +1,659 @@
// Package config contains the configuration logic for CFSSL.
package config
import (
"crypto/tls"
"crypto/x509"
"encoding/asn1"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"regexp"
"strconv"
"strings"
"time"
"github.com/cloudflare/cfssl/auth"
cferr "github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/log"
ocspConfig "github.com/cloudflare/cfssl/ocsp/config"
)
// A CSRWhitelist stores booleans for fields in the CSR. If a CSRWhitelist is
// not present in a SigningProfile, all of these fields may be copied from the
// CSR into the signed certificate. If a CSRWhitelist *is* present in a
// SigningProfile, only those fields with a `true` value in the CSRWhitelist may
// be copied from the CSR to the signed certificate. Note that some of these
// fields, like Subject, can be provided or partially provided through the API.
// Since API clients are expected to be trusted, but CSRs are not, fields
// provided through the API are not subject to whitelisting through this
// mechanism.
type CSRWhitelist struct {
Subject, PublicKeyAlgorithm, PublicKey, SignatureAlgorithm bool
DNSNames, IPAddresses, EmailAddresses bool
}
// OID is our own version of asn1's ObjectIdentifier, so we can define a custom
// JSON marshal / unmarshal.
type OID asn1.ObjectIdentifier
// CertificatePolicy represents the ASN.1 PolicyInformation structure from
// https://tools.ietf.org/html/rfc3280.html#page-106.
// Valid values of Type are "id-qt-unotice" and "id-qt-cps"
type CertificatePolicy struct {
ID OID
Qualifiers []CertificatePolicyQualifier
}
// CertificatePolicyQualifier represents a single qualifier from an ASN.1
// PolicyInformation structure.
type CertificatePolicyQualifier struct {
Type string
Value string
}
// AuthRemote is an authenticated remote signer.
type AuthRemote struct {
RemoteName string `json:"remote"`
AuthKeyName string `json:"auth_key"`
}
// CAConstraint specifies various CA constraints on the signed certificate.
// CAConstraint would verify against (and override) the CA
// extensions in the given CSR.
type CAConstraint struct {
IsCA bool `json:"is_ca"`
MaxPathLen int `json:"max_path_len"`
MaxPathLenZero bool `json:"max_path_len_zero"`
}
// 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"`
CAConstraint CAConstraint `json:"ca_constraint"`
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"`
NameWhitelistString string `json:"name_whitelist"`
AuthRemote AuthRemote `json:"auth_remote"`
CTLogServers []string `json:"ct_log_servers"`
AllowedExtensions []OID `json:"allowed_extensions"`
CertStore string `json:"cert_store"`
Policies []CertificatePolicy
Expiry time.Duration
Backdate time.Duration
Provider auth.Provider
RemoteProvider auth.Provider
RemoteServer string
RemoteCAs *x509.CertPool
ClientCert *tls.Certificate
CSRWhitelist *CSRWhitelist
NameWhitelist *regexp.Regexp
ExtensionWhitelist map[string]bool
ClientProvidesSerialNumbers bool
}
// UnmarshalJSON unmarshals a JSON string into an OID.
func (oid *OID) UnmarshalJSON(data []byte) (err error) {
if data[0] != '"' || data[len(data)-1] != '"' {
return errors.New("OID JSON string not wrapped in quotes." + string(data))
}
data = data[1 : len(data)-1]
parsedOid, err := parseObjectIdentifier(string(data))
if err != nil {
return err
}
*oid = OID(parsedOid)
return
}
// MarshalJSON marshals an oid into a JSON string.
func (oid OID) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`"%v"`, asn1.ObjectIdentifier(oid))), nil
}
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 == "" && p.AuthRemote.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.Policies) > 0 {
for _, policy := range p.Policies {
for _, qualifier := range policy.Qualifiers {
if qualifier.Type != "" && qualifier.Type != "id-qt-unotice" && qualifier.Type != "id-qt-cps" {
return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
errors.New("invalid policy qualifier type"))
}
}
}
}
} else if p.RemoteName != "" {
log.Debug("match remote in profile to remotes section")
if p.AuthRemote.RemoteName != "" {
log.Error("profile has both a remote and an auth remote specified")
return cferr.New(cferr.PolicyError, cferr.InvalidPolicy)
}
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"))
}
} else {
log.Debug("match auth remote in profile to remotes section")
if remote := cfg.Remotes[p.AuthRemote.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"))
}
}
if p.AuthRemote.AuthKeyName != "" {
log.Debug("match auth remote key in profile to auth_keys section")
if key, ok := cfg.AuthKeys[p.AuthRemote.AuthKeyName]; ok == true {
if key.Type == "standard" {
p.RemoteProvider, 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_remote's auth_key in auth_keys section"))
}
}
if p.NameWhitelistString != "" {
log.Debug("compiling whitelist regular expression")
rule, err := regexp.Compile(p.NameWhitelistString)
if err != nil {
return cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
errors.New("failed to compile name whitelist section"))
}
p.NameWhitelist = rule
}
p.ExtensionWhitelist = map[string]bool{}
for _, oid := range p.AllowedExtensions {
p.ExtensionWhitelist[asn1.ObjectIdentifier(oid).String()] = true
}
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
}
// SetClientCertKeyPairFromFile updates the properties to set client certificates for mutual
// authenticated TLS remote requests
func (p *Signing) SetClientCertKeyPairFromFile(certFile string, keyFile string) error {
if certFile != "" && keyFile != "" {
cert, err := helpers.LoadClientCertificate(certFile, keyFile)
if err != nil {
return err
}
for _, profile := range p.Profiles {
profile.ClientCert = cert
}
p.Default.ClientCert = cert
}
return nil
}
// SetRemoteCAsFromFile reads root CAs from file and updates the properties to set remote CAs for TLS
// remote requests
func (p *Signing) SetRemoteCAsFromFile(caFile string) error {
if caFile != "" {
remoteCAs, err := helpers.LoadPEMCertPool(caFile)
if err != nil {
return err
}
p.SetRemoteCAs(remoteCAs)
}
return nil
}
// SetRemoteCAs updates the properties to set remote CAs for TLS
// remote requests
func (p *Signing) SetRemoteCAs(remoteCAs *x509.CertPool) {
for _, profile := range p.Profiles {
profile.RemoteCAs = remoteCAs
}
p.Default.RemoteCAs = remoteCAs
}
// NeedsRemoteSigner returns true if one of the profiles has a remote set
func (p *Signing) NeedsRemoteSigner() bool {
for _, profile := range p.Profiles {
if profile.RemoteServer != "" {
return true
}
}
if p.Default.RemoteServer != "" {
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.RemoteServer == "" {
return true
}
}
if p.Default.RemoteServer == "" {
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.AuthRemote.RemoteName == "" && p.AuthRemote.AuthKeyName != "" {
log.Debugf("invalid auth remote profile: no remote signer specified")
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
}
if p.AuthRemote.RemoteName != "" {
log.Debugf("invalid remote profile: auth remote is also specified")
return false
}
} else if p.AuthRemote.RemoteName != "" {
log.Debugf("validate auth remote profile")
if p.RemoteServer == "" {
log.Debugf("invalid auth remote profile: no remote signer specified")
return false
}
if p.AuthRemote.AuthKeyName == "" || p.RemoteProvider == nil {
log.Debugf("invalid auth remote profile: no auth key is defined")
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
}
// This checks if the SigningProfile object contains configurations that are only effective with a local signer
// which has access to CA private key.
func (p *SigningProfile) hasLocalConfig() bool {
if p.Usage != nil ||
p.IssuerURL != nil ||
p.OCSP != "" ||
p.ExpiryString != "" ||
p.BackdateString != "" ||
p.CAConstraint.IsCA != false ||
!p.NotBefore.IsZero() ||
!p.NotAfter.IsZero() ||
p.NameWhitelistString != "" ||
len(p.CTLogServers) != 0 {
return true
}
return false
}
// warnSkippedSettings prints a log warning message about skipped settings
// in a SigningProfile, usually due to remote signer.
func (p *Signing) warnSkippedSettings() {
const warningMessage = `The configuration value by "usages", "issuer_urls", "ocsp_url", "crl_url", "ca_constraint", "expiry", "backdate", "not_before", "not_after", "cert_store" and "ct_log_servers" are skipped`
if p == nil {
return
}
if (p.Default.RemoteName != "" || p.Default.AuthRemote.RemoteName != "") && p.Default.hasLocalConfig() {
log.Warning("default profile points to a remote signer: ", warningMessage)
}
for name, profile := range p.Profiles {
if (profile.RemoteName != "" || profile.AuthRemote.RemoteName != "") && profile.hasLocalConfig() {
log.Warningf("Profiles[%s] points to a remote signer: %s", name, warningMessage)
}
}
}
// 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"`
OCSP *ocspConfig.Config `json:"ocsp"`
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
}
}
p.warnSkippedSettings()
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 commitment": x509.KeyUsageContentCommitment,
"key encipherment": x509.KeyUsageKeyEncipherment,
"key agreement": x509.KeyUsageKeyAgreement,
"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: "+err.Error()))
}
if cfg.Signing == nil {
return nil, errors.New("No \"signing\" field present")
}
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
}

View File

@ -0,0 +1,537 @@
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 validMinimalRemoteConfig2 = `
{
"signing": {
"default": {
"auth_remote":{
"auth_key": "sample",
"remote": "localhost"
}
}
},
"auth_keys": {
"sample": {
"type":"standard",
"key":"0123456789ABCDEF0123456789ABCDEF"
}
},
"remotes": {
"localhost": "127.0.0.1:8888"
}
}`
var invalidConflictRemoteConfig = `
{
"signing": {
"default": {
"auth_remote":{
"auth_key": "sample",
"remote": "localhost"
},
"remote": "localhost"
}
},
"auth_keys": {
"sample": {
"type":"standard",
"key":"0123456789ABCDEF0123456789ABCDEF"
}
},
"remotes": {
"localhost": "127.0.0.1:8888"
}
}`
var invalidRemoteConfig = `
{
"signing": {
"default": {
"auth_remotes_typos":{
"auth_key": "sample",
"remote": "localhost"
}
}
},
"auth_keys": {
"sample": {
"type":"standard",
"key":"0123456789ABCDEF0123456789ABCDEF"
}
},
"remotes": {
"localhost": "127.0.0.1:8888"
}
}`
var invalidAuthRemoteConfigMissingRemote = `
{
"signing": {
"default": {
"auth_remote":{
"auth_key": "sample"
}
}
},
"auth_keys": {
"sample": {
"type":"standard",
"key":"0123456789ABCDEF0123456789ABCDEF"
}
},
"remotes": {
"localhost": "127.0.0.1:8888"
}
}`
var invalidAuthRemoteConfigMissingKey = `
{
"signing": {
"default": {
"auth_remote":{
"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"
}
}
}`
var validLocalConfigsWithCAConstraint = []string{
`{
"signing": {
"default": {
"usages": ["digital signature", "email protection"],
"ca_constraint": { "is_ca": true },
"expiry": "8000h"
}
}
}`,
`{
"signing": {
"default": {
"usages": ["digital signature", "email protection"],
"ca_constraint": { "is_ca": true, "max_path_len": 1 },
"expiry": "8000h"
}
}
}`,
`{
"signing": {
"default": {
"usages": ["digital signature", "email protection"],
"ca_constraint": { "is_ca": true, "max_path_len_zero": true },
"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().")
}
if err != nil {
t.Fatal(err)
}
}
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")
}
}
}
func TestAuthRemoteConfig(t *testing.T) {
c, err := LoadConfig([]byte(validMinimalRemoteConfig2))
if err != nil {
t.Fatal("load valid config failed:", err)
}
if c.Signing.Default.RemoteServer != "127.0.0.1:8888" {
t.Fatal("load valid config failed: incorrect remote server")
}
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")
}
}
}
func TestDuplicateRemoteConfig(t *testing.T) {
_, err := LoadConfig([]byte(invalidConflictRemoteConfig))
if err == nil {
t.Fatal("fail to reject invalid config")
}
}
func TestBadAuthRemoteConfig(t *testing.T) {
_, err := LoadConfig([]byte(invalidRemoteConfig))
if err == nil {
t.Fatal("load invalid config should failed")
}
_, err = LoadConfig([]byte(invalidAuthRemoteConfigMissingRemote))
if err == nil {
t.Fatal("load invalid config should failed")
}
_, err = LoadConfig([]byte(invalidAuthRemoteConfigMissingKey))
if err == nil {
t.Fatal("load invalid config should failed")
}
var p *Signing
if p.Valid() {
t.Fatal("nil Signing config should be invalid")
}
}
func TestValidCAConstraint(t *testing.T) {
for _, config := range validLocalConfigsWithCAConstraint {
_, err := LoadConfig([]byte(config))
if err != nil {
t.Fatal("can't parse valid ca constraint")
}
}
}

View 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"
}
}

View 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"
}
}

View 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"],
}
}
}

View 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"
}
}
}

View 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"
}
}

View 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"
}
}
}

View 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"
}
}
}

View 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"
}
}

View 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"
}
}
}

View 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"
}
}
}

View 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"
}
}

View 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"
}
}

View File

@ -0,0 +1,14 @@
{
"signing": {
"profiles": {
"CA": {
"usages": ["cert sign"],
"expiry": "720h"
},
"email": {
"usages": ["s/mime"],
"expiry": "720h"
}
}
}
}

View File

@ -0,0 +1,188 @@
// Package pkcs7 implements the subset of the CMS PKCS #7 datatype that is typically
// used to package certificates and CRLs. Using openssl, every certificate converted
// to PKCS #7 format from another encoding such as PEM conforms to this implementation.
// reference: https://www.openssl.org/docs/man1.1.0/apps/crl2pkcs7.html
//
// PKCS #7 Data type, reference: https://tools.ietf.org/html/rfc2315
//
// The full pkcs#7 cryptographic message syntax allows for cryptographic enhancements,
// for example data can be encrypted and signed and then packaged through pkcs#7 to be
// sent over a network and then verified and decrypted. It is asn1, and the type of
// PKCS #7 ContentInfo, which comprises the PKCS #7 structure, is:
//
// ContentInfo ::= SEQUENCE {
// contentType ContentType,
// content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
// }
//
// There are 6 possible ContentTypes, data, signedData, envelopedData,
// signedAndEnvelopedData, digestedData, and encryptedData. Here signedData, Data, and encrypted
// Data are implemented, as the degenerate case of signedData without a signature is the typical
// format for transferring certificates and CRLS, and Data and encryptedData are used in PKCS #12
// formats.
// The ContentType signedData has the form:
//
//
// signedData ::= SEQUENCE {
// version Version,
// digestAlgorithms DigestAlgorithmIdentifiers,
// contentInfo ContentInfo,
// certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL
// crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
// signerInfos SignerInfos
// }
//
// As of yet signerInfos and digestAlgorithms are not parsed, as they are not relevant to
// this system's use of PKCS #7 data. Version is an integer type, note that PKCS #7 is
// recursive, this second layer of ContentInfo is similar ignored for our degenerate
// usage. The ExtendedCertificatesAndCertificates type consists of a sequence of choices
// between PKCS #6 extended certificates and x509 certificates. Any sequence consisting
// of any number of extended certificates is not yet supported in this implementation.
//
// The ContentType Data is simply a raw octet string and is parsed directly into a Go []byte slice.
//
// The ContentType encryptedData is the most complicated and its form can be gathered by
// the go type below. It essentially contains a raw octet string of encrypted data and an
// algorithm identifier for use in decrypting this data.
package pkcs7
import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
cferr "github.com/cloudflare/cfssl/errors"
)
// Types used for asn1 Unmarshaling.
type signedData struct {
Version int
DigestAlgorithms asn1.RawValue
ContentInfo asn1.RawValue
Certificates asn1.RawValue `asn1:"optional" asn1:"tag:0"`
Crls asn1.RawValue `asn1:"optional"`
SignerInfos asn1.RawValue
}
type initPKCS7 struct {
Raw asn1.RawContent
ContentType asn1.ObjectIdentifier
Content asn1.RawValue `asn1:"tag:0,explicit,optional"`
}
// Object identifier strings of the three implemented PKCS7 types.
const (
ObjIDData = "1.2.840.113549.1.7.1"
ObjIDSignedData = "1.2.840.113549.1.7.2"
ObjIDEncryptedData = "1.2.840.113549.1.7.6"
)
// PKCS7 represents the ASN1 PKCS #7 Content type. It contains one of three
// possible types of Content objects, as denoted by the object identifier in
// the ContentInfo field, the other two being nil. SignedData
// is the degenerate SignedData Content info without signature used
// to hold certificates and crls. Data is raw bytes, and EncryptedData
// is as defined in PKCS #7 standard.
type PKCS7 struct {
Raw asn1.RawContent
ContentInfo string
Content Content
}
// Content implements three of the six possible PKCS7 data types. Only one is non-nil.
type Content struct {
Data []byte
SignedData SignedData
EncryptedData EncryptedData
}
// SignedData defines the typical carrier of certificates and crls.
type SignedData struct {
Raw asn1.RawContent
Version int
Certificates []*x509.Certificate
Crl *pkix.CertificateList
}
// Data contains raw bytes. Used as a subtype in PKCS12.
type Data struct {
Bytes []byte
}
// EncryptedData contains encrypted data. Used as a subtype in PKCS12.
type EncryptedData struct {
Raw asn1.RawContent
Version int
EncryptedContentInfo EncryptedContentInfo
}
// EncryptedContentInfo is a subtype of PKCS7EncryptedData.
type EncryptedContentInfo struct {
Raw asn1.RawContent
ContentType asn1.ObjectIdentifier
ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
EncryptedContent []byte `asn1:"tag:0,optional"`
}
// ParsePKCS7 attempts to parse the DER encoded bytes of a
// PKCS7 structure.
func ParsePKCS7(raw []byte) (msg *PKCS7, err error) {
var pkcs7 initPKCS7
_, err = asn1.Unmarshal(raw, &pkcs7)
if err != nil {
return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
}
msg = new(PKCS7)
msg.Raw = pkcs7.Raw
msg.ContentInfo = pkcs7.ContentType.String()
switch {
case msg.ContentInfo == ObjIDData:
msg.ContentInfo = "Data"
_, err = asn1.Unmarshal(pkcs7.Content.Bytes, &msg.Content.Data)
if err != nil {
return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
}
case msg.ContentInfo == ObjIDSignedData:
msg.ContentInfo = "SignedData"
var signedData signedData
_, err = asn1.Unmarshal(pkcs7.Content.Bytes, &signedData)
if err != nil {
return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
}
if len(signedData.Certificates.Bytes) != 0 {
msg.Content.SignedData.Certificates, err = x509.ParseCertificates(signedData.Certificates.Bytes)
if err != nil {
return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
}
}
if len(signedData.Crls.Bytes) != 0 {
msg.Content.SignedData.Crl, err = x509.ParseDERCRL(signedData.Crls.Bytes)
if err != nil {
return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
}
}
msg.Content.SignedData.Version = signedData.Version
msg.Content.SignedData.Raw = pkcs7.Content.Bytes
case msg.ContentInfo == ObjIDEncryptedData:
msg.ContentInfo = "EncryptedData"
var encryptedData EncryptedData
_, err = asn1.Unmarshal(pkcs7.Content.Bytes, &encryptedData)
if err != nil {
return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err)
}
if encryptedData.Version != 0 {
return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, errors.New("Only support for PKCS #7 encryptedData version 0"))
}
msg.Content.EncryptedData = encryptedData
default:
return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, errors.New("Attempt to parse PKCS# 7 Content not of type data, signed data or encrypted data"))
}
return msg, nil
}

432
vendor/github.com/cloudflare/cfssl/csr/csr.go generated vendored Normal file
View File

@ -0,0 +1,432 @@
// Package csr implements certificate requests for CFSSL.
package csr
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"errors"
"net"
"net/mail"
"strings"
cferr "github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/helpers"
"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
SerialNumber string
}
// A KeyRequest is a generic request for a new key.
type KeyRequest interface {
Algo() string
Size() int
Generate() (crypto.PrivateKey, error)
SigAlgo() x509.SignatureAlgorithm
}
// A BasicKeyRequest contains the algorithm and key size for a new private key.
type BasicKeyRequest struct {
A string `json:"algo" yaml:"algo"`
S int `json:"size" yaml:"size"`
}
// NewBasicKeyRequest returns a default BasicKeyRequest.
func NewBasicKeyRequest() *BasicKeyRequest {
return &BasicKeyRequest{"ecdsa", curveP256}
}
// Algo returns the requested key algorithm represented as a string.
func (kr *BasicKeyRequest) Algo() string {
return kr.A
}
// Size returns the requested key size.
func (kr *BasicKeyRequest) Size() int {
return kr.S
}
// Generate generates a key as specified in the request. Currently,
// only ECDSA and RSA are supported.
func (kr *BasicKeyRequest) Generate() (crypto.PrivateKey, 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")
}
if kr.Size() > 8192 {
return nil, errors.New("RSA key size too large")
}
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 *BasicKeyRequest) 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 kr.Size() {
case curveP521:
return x509.ECDSAWithSHA512
case curveP384:
return x509.ECDSAWithSHA384
case 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" yaml:"pathlen"`
PathLenZero bool `json:"pathlenzero" yaml:"pathlenzero"`
Expiry string `json:"expiry" yaml:"expiry"`
Backdate string `json:"backdate" yaml:"backdate"`
}
// A CertificateRequest encapsulates the API interface to the
// certificate request functionality.
type CertificateRequest struct {
CN string
Names []Name `json:"names" yaml:"names"`
Hosts []string `json:"hosts" yaml:"hosts"`
KeyRequest KeyRequest `json:"key,omitempty" yaml:"key,omitempty"`
CA *CAConfig `json:"ca,omitempty" yaml:"ca,omitempty"`
SerialNumber string `json:"serialnumber,omitempty" yaml:"serialnumber,omitempty"`
}
// New returns a new, empty CertificateRequest with a
// BasicKeyRequest.
func New() *CertificateRequest {
return &CertificateRequest{
KeyRequest: NewBasicKeyRequest(),
}
}
// 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)
}
name.SerialNumber = cr.SerialNumber
return name
}
// BasicConstraints CSR information RFC 5280, 4.2.1.9
type BasicConstraints struct {
IsCA bool `asn1:"optional"`
MaxPathLen int `asn1:"optional,default:-1"`
}
// 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 = NewBasicKeyRequest()
}
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.")
}
csr, err = Generate(priv.(crypto.Signer), req)
if err != nil {
log.Errorf("failed to generate a CSR: %v", err)
err = cferr.Wrap(cferr.CSRError, cferr.BadRequest, err)
}
return
}
// ExtractCertificateRequest extracts a CertificateRequest from
// x509.Certificate. It is aimed to used for generating a new certificate
// from an existing certificate. For a root certificate, the CA expiry
// length is calculated as the duration between cert.NotAfter and cert.NotBefore.
func ExtractCertificateRequest(cert *x509.Certificate) *CertificateRequest {
req := New()
req.CN = cert.Subject.CommonName
req.Names = getNames(cert.Subject)
req.Hosts = getHosts(cert)
req.SerialNumber = cert.Subject.SerialNumber
if cert.IsCA {
req.CA = new(CAConfig)
// CA expiry length is calculated based on the input cert
// issue date and expiry date.
req.CA.Expiry = cert.NotAfter.Sub(cert.NotBefore).String()
req.CA.PathLength = cert.MaxPathLen
req.CA.PathLenZero = cert.MaxPathLenZero
}
return req
}
func getHosts(cert *x509.Certificate) []string {
var hosts []string
for _, ip := range cert.IPAddresses {
hosts = append(hosts, ip.String())
}
for _, dns := range cert.DNSNames {
hosts = append(hosts, dns)
}
for _, email := range cert.EmailAddresses {
hosts = append(hosts, email)
}
return hosts
}
// getNames returns an array of Names from the certificate
// It onnly cares about Country, Organization, OrganizationalUnit, Locality, Province
func getNames(sub pkix.Name) []Name {
// anonymous func for finding the max of a list of interger
max := func(v1 int, vn ...int) (max int) {
max = v1
for i := 0; i < len(vn); i++ {
if vn[i] > max {
max = vn[i]
}
}
return max
}
nc := len(sub.Country)
norg := len(sub.Organization)
nou := len(sub.OrganizationalUnit)
nl := len(sub.Locality)
np := len(sub.Province)
n := max(nc, norg, nou, nl, np)
names := make([]Name, n)
for i := range names {
if i < nc {
names[i].C = sub.Country[i]
}
if i < norg {
names[i].O = sub.Organization[i]
}
if i < nou {
names[i].OU = sub.OrganizationalUnit[i]
}
if i < nl {
names[i].L = sub.Locality[i]
}
if i < np {
names[i].ST = sub.Province[i]
}
}
return names
}
// 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 nil, nil, err
}
csr, key, err = ParseRequest(req)
if err != nil {
return nil, nil, err
}
return
}
// IsNameEmpty returns true if the name has no identifying information in it.
func IsNameEmpty(n Name) bool {
empty := func(s string) bool { return strings.TrimSpace(s) == "" }
if empty(n.C) && empty(n.ST) && empty(n.L) && empty(n.O) && empty(n.OU) {
return true
}
return false
}
// Regenerate uses the provided CSR as a template for signing a new
// CSR using priv.
func Regenerate(priv crypto.Signer, csr []byte) ([]byte, error) {
req, extra, err := helpers.ParseCSR(csr)
if err != nil {
return nil, err
} else if len(extra) > 0 {
return nil, errors.New("csr: trailing data in certificate request")
}
return x509.CreateCertificateRequest(rand.Reader, req, priv)
}
// Generate creates a new CSR from a CertificateRequest structure and
// an existing key. The KeyRequest field is ignored.
func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err error) {
sigAlgo := helpers.SignerAlgo(priv)
if sigAlgo == x509.UnknownSignatureAlgorithm {
return nil, cferr.New(cferr.PrivateKeyError, cferr.Unavailable)
}
var tpl = x509.CertificateRequest{
Subject: req.Name(),
SignatureAlgorithm: sigAlgo,
}
for i := range req.Hosts {
if ip := net.ParseIP(req.Hosts[i]); ip != nil {
tpl.IPAddresses = append(tpl.IPAddresses, ip)
} else if email, err := mail.ParseAddress(req.Hosts[i]); err == nil && email != nil {
tpl.EmailAddresses = append(tpl.EmailAddresses, email.Address)
} else {
tpl.DNSNames = append(tpl.DNSNames, req.Hosts[i])
}
}
if req.CA != nil {
err = appendCAInfoToCSR(req.CA, &tpl)
if err != nil {
err = cferr.Wrap(cferr.CSRError, cferr.GenerationFailed, err)
return
}
}
csr, err = x509.CreateCertificateRequest(rand.Reader, &tpl, priv)
if err != nil {
log.Errorf("failed to generate a CSR: %v", err)
err = cferr.Wrap(cferr.CSRError, cferr.BadRequest, err)
return
}
block := pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: csr,
}
log.Info("encoded CSR")
csr = pem.EncodeToMemory(&block)
return
}
// appendCAInfoToCSR appends CAConfig BasicConstraint extension to a CSR
func appendCAInfoToCSR(reqConf *CAConfig, csr *x509.CertificateRequest) error {
pathlen := reqConf.PathLength
if pathlen == 0 && !reqConf.PathLenZero {
pathlen = -1
}
val, err := asn1.Marshal(BasicConstraints{true, pathlen})
if err != nil {
return err
}
csr.ExtraExtensions = []pkix.Extension{
{
Id: asn1.ObjectIdentifier{2, 5, 29, 19},
Value: val,
Critical: true,
},
}
return nil
}

744
vendor/github.com/cloudflare/cfssl/csr/csr_test.go generated vendored Normal file
View File

@ -0,0 +1,744 @@
package csr
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"io/ioutil"
"testing"
"github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/helpers"
)
//TestNew validate the CertificateRequest created to return with a BasicKeyRequest
//in KeyRequest field
func TestNew(t *testing.T) {
if cr := New(); cr.KeyRequest == nil {
t.Fatalf("Should create a new, empty certificate request with BasicKeyRequest")
}
}
// TestBasicKeyRequest ensures that key generation returns the same type of
// key specified in the BasicKeyRequest.
func TestBasicKeyRequest(t *testing.T) {
kr := NewBasicKeyRequest()
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 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: NewBasicKeyRequest(),
}
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 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", "192.168.0.1", "jdoe@example.com"},
KeyRequest: NewBasicKeyRequest(),
}
_, _, err := ParseRequest(cr)
if err != nil {
t.Fatalf("%v", err)
}
}
// TestParseRequestCA ensures that a valid CA certificate request does not
// error and the resulting CSR includes the BasicConstraint extension
func TestParseRequestCA(t *testing.T) {
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",
},
},
CA: &CAConfig{
PathLength: 0,
PathLenZero: true,
},
KeyRequest: NewBasicKeyRequest(),
}
csrBytes, _, err := ParseRequest(cr)
if err != nil {
t.Fatalf("%v", err)
}
block, _ := pem.Decode(csrBytes)
if block == nil {
t.Fatalf("%v", err)
}
if block.Type != "CERTIFICATE REQUEST" {
t.Fatalf("Incorrect block type: %s", block.Type)
}
csr, err := x509.ParseCertificateRequest(block.Bytes)
if err != nil {
t.Fatalf("%v", err)
}
found := false
for _, ext := range csr.Extensions {
if ext.Id.Equal(asn1.ObjectIdentifier{2, 5, 29, 19}) {
found = true
break
}
}
if !found {
t.Fatalf("CSR did not include BasicConstraint Extension")
}
}
// TestParseRequestCANoPathlen ensures that a valid CA certificate request
// with an unspecified pathlen does not error and the resulting CSR includes
// the BasicConstraint extension
func TestParseRequestCANoPathlen(t *testing.T) {
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",
},
},
CA: &CAConfig{
PathLength: 0,
PathLenZero: false,
},
KeyRequest: NewBasicKeyRequest(),
}
csrBytes, _, err := ParseRequest(cr)
if err != nil {
t.Fatalf("%v", err)
}
block, _ := pem.Decode(csrBytes)
if block == nil {
t.Fatalf("%v", err)
}
if block.Type != "CERTIFICATE REQUEST" {
t.Fatalf("Incorrect block type: %s", block.Type)
}
csr, err := x509.ParseCertificateRequest(block.Bytes)
if err != nil {
t.Fatalf("%v", err)
}
found := false
for _, ext := range csr.Extensions {
if ext.Id.Equal(asn1.ObjectIdentifier{2, 5, 29, 19}) {
bc := &BasicConstraints{}
asn1.Unmarshal(ext.Value, bc)
if bc.IsCA == true && bc.MaxPathLen == -1 {
found = true
break
}
}
}
if !found {
t.Fatalf("CSR did not include BasicConstraint Extension")
}
}
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 := &BasicKeyRequest{"ecdsa", 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 := &BasicKeyRequest{"rsa", 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!")
}
}
}
// TestBadBasicKeyRequest ensures that generating a key from a BasicKeyRequest
// 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 TestBadBasicKeyRequest(t *testing.T) {
kr := &BasicKeyRequest{"yolocrypto", 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.A = "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.A = "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!")
}
kr = &BasicKeyRequest{"tobig", 9216}
kr.A = "rsa"
if _, err := kr.Generate(); err == nil {
t.Fatal("Key generation should fail with invalid key size")
} else if sa := kr.SigAlgo(); sa != x509.SHA512WithRSA {
t.Fatal("The wrong signature algorithm was returned from SigAlgo!")
}
}
// TestDefaultBasicKeyRequest makes sure that certificate requests without
// explicit key requests fall back to the default key request.
func TestDefaultBasicKeyRequest(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", "jdoe@example.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!")
}
DefaultKeyRequest := NewBasicKeyRequest()
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", "jdoe@example.com"},
KeyRequest: &BasicKeyRequest{"rsa", 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: &BasicKeyRequest{"yolo-crypto", 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", "192.168.0.1", "jdoe@example.com"},
KeyRequest: &BasicKeyRequest{"rsa", 2048},
}
csrBytes, _, err := g.ProcessRequest(req)
if err != nil {
t.Fatal(err)
}
block, _ := pem.Decode([]byte(csrBytes))
if block == nil {
t.Fatalf("bad CSR in PEM")
}
if block.Type != "CERTIFICATE REQUEST" {
t.Fatalf("bad CSR in PEM")
}
csr, err := x509.ParseCertificateRequest(block.Bytes)
if err != nil {
t.Fatal(err)
}
if len(csr.DNSNames) != 2 {
t.Fatal("SAN parsing error")
}
if len(csr.IPAddresses) != 1 {
t.Fatal("SAN parsing error")
}
if len(csr.EmailAddresses) != 1 {
t.Fatal("SAN parsing error")
}
}
// 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: &BasicKeyRequest{"rsa", 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", "jdoe@example.com"},
KeyRequest: &BasicKeyRequest{"rsa", 1024},
}
g := &Generator{testValidator}
_, _, err := g.ProcessRequest(weakKey)
if err == nil {
t.Fatalf("Request should have failed.")
}
}
var testEmpty = []struct {
name Name
ok bool
}{
{
Name{},
true,
},
{
Name{C: "OK"},
false,
},
{
Name{ST: "OK"},
false,
},
{
Name{L: "OK"},
false,
},
{
Name{O: "OK"},
false,
},
{
Name{OU: "OK"},
false,
},
}
func TestIsNameEmpty(t *testing.T) {
for i, c := range testEmpty {
if IsNameEmpty(c.name) != c.ok {
t.Fatalf("%d: expected IsNameEmpty to return %v, but have %v", i, c.ok, !c.ok)
}
}
}
func TestGenerate(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", "192.168.0.1", "jdoe@example.com"},
KeyRequest: &BasicKeyRequest{"ecdsa", 256},
}
key, err := req.KeyRequest.Generate()
if err != nil {
t.Fatalf("%v", err)
}
priv, ok := key.(crypto.Signer)
if !ok {
t.Fatal("Private key is not a signer.")
}
csrPEM, err := Generate(priv, req)
if err != nil {
t.Fatalf("%v", err)
}
csr, _, err := helpers.ParseCSR(csrPEM)
if err != nil {
t.Fatalf("%v", err)
}
if len(csr.DNSNames) != 2 {
t.Fatal("SAN parsing error")
}
if len(csr.IPAddresses) != 1 {
t.Fatal("SAN parsing error")
}
if len(csr.EmailAddresses) != 1 {
t.Fatal("SAN parsing error")
}
}
// TestReGenerate ensures Regenerate() is abel to use the provided CSR as a template for signing a new
// CSR using priv.
func TestReGenerate(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", "192.168.0.1"},
KeyRequest: &BasicKeyRequest{"ecdsa", 256},
}
_, key, err := ParseRequest(req)
if err != nil {
t.Fatalf("%v", err)
}
priv, err := helpers.ParsePrivateKeyPEM(key)
if err != nil {
t.Fatalf("%v", err)
}
csr, err := Generate(priv, req)
if err != nil {
t.Fatalf("%v", err)
}
if _, _, err = helpers.ParseCSR(csr); err != nil {
t.Fatalf("%v", err)
}
_, err = Regenerate(priv, csr)
if err != nil {
t.Fatalf("%v", err)
}
}
// TestBadReGenerator ensures that a request that fails the ParseCSR is
// not processed.
func TestBadReGenerate(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", "192.168.0.1"},
KeyRequest: &BasicKeyRequest{"ecdsa", 256},
}
_, key, err := ParseRequest(req)
if err != nil {
t.Fatalf("%v", err)
}
priv, err := helpers.ParsePrivateKeyPEM(key)
if err != nil {
t.Fatalf("%v", err)
}
csr, err := Generate(priv, req)
if err != nil {
t.Fatalf("%v", err)
}
block := pem.Block{
Type: "CERTIFICATE REQUEST",
Headers: map[string]string{
"Location": "UCSD",
},
Bytes: csr,
}
csr = pem.EncodeToMemory(&block)
_, err = Regenerate(priv, csr)
if err == nil {
t.Fatalf("%v", err)
}
}
var testECDSACertificateFile = "testdata/test-ecdsa-ca.pem"
func TestExtractCertificateRequest(t *testing.T) {
certPEM, err := ioutil.ReadFile(testECDSACertificateFile)
if err != nil {
t.Fatal(err)
}
// must parse ok
cert, err := helpers.ParseCertificatePEM(certPEM)
if err != nil {
t.Fatal(err)
}
req := ExtractCertificateRequest(cert)
if req.CN != "" {
t.Fatal("Bad Certificate Request!")
}
if len(req.Names) != 1 {
t.Fatal("Bad Certificate Request!")
}
name := req.Names[0]
if name.C != "US" || name.ST != "California" || name.O != "CloudFlare, Inc." ||
name.OU != "Test Certificate Authority" || name.L != "San Francisco" {
t.Fatal("Bad Certificate Request!")
}
if req.CA == nil || req.CA.PathLength != 2 {
t.Fatal("Bad Certificate Request!")
}
}

View File

@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICUDCCAfagAwIBAgIIec5PjdpJcNYwCgYIKoZIzj0EAwIwejELMAkGA1UEBhMC
VVMxGTAXBgNVBAoTEENsb3VkRmxhcmUsIEluYy4xIzAhBgNVBAsTGlRlc3QgQ2Vy
dGlmaWNhdGUgQXV0aG9yaXR5MRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYD
VQQIEwpDYWxpZm9ybmlhMB4XDTE1MTAwODIzMDEwMFoXDTE1MTAwODIzMDYwMFow
ejELMAkGA1UEBhMCVVMxGTAXBgNVBAoTEENsb3VkRmxhcmUsIEluYy4xIzAhBgNV
BAsTGlRlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMFkwEwYHKoZIzj0CAQYIKoZIzj0D
AQcDQgAEoCV+bVOLTJMy38j50sc3vE5k41GMRgriFJt0g0OVX8yaOZ93CZTI7Lzf
GbMU+KqWTgOwGhrPvpusep3fjw+dAaNmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1Ud
EwEB/wQIMAYBAf8CAQIwHQYDVR0OBBYEFDpLhSKBN3njfb6cXQCdRLzCZt0ZMB8G
A1UdIwQYMBaAFDpLhSKBN3njfb6cXQCdRLzCZt0ZMAoGCCqGSM49BAMCA0gAMEUC
IFU3BmzntGGeXZu2qWZx249nYn37S0AkCnQ3rUtI31bdAiEAsPICnZ+GB8yCN26N
OL+N8dHvXiOvZ9/Vl488pyWOccY=
-----END CERTIFICATE-----

46
vendor/github.com/cloudflare/cfssl/errors/doc.go generated vendored Normal file
View File

@ -0,0 +1,46 @@
/*
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
5400: UnknownProfile
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

438
vendor/github.com/cloudflare/cfssl/errors/error.go generated vendored Normal file
View File

@ -0,0 +1,438 @@
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
// CSRError indicates a problem with CSR parsing
CSRError // 9XXX
// CTError indicates a problem with the certificate transparency process
CTError // 10XXX
// CertStoreError indicates a problem with the certificate store
CertStoreError // 11XXX
)
// 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
// MissingSerial indicates that the profile specified
// 'ClientProvidesSerialNumbers', but the SignRequest did not include a serial
// number.
MissingSerial // Code 14XX
)
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
// UnknownProfile indicates that the profile does not exist.
UnknownProfile // 54XX
UnmatchedWhitelist // 55xx
)
// 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
)
// Certificate transparency related errors specified with CTError
const (
// PrecertSubmissionFailed occurs when submitting a precertificate to
// a log server fails
PrecertSubmissionFailed = 100 * (iota + 1)
// CTClientConstructionFailed occurs when the construction of a new
// github.com/google/certificate-transparency client fails.
CTClientConstructionFailed
// PrecertMissingPoison occurs when a precert is passed to SignFromPrecert
// and is missing the CT poison extension.
PrecertMissingPoison
// PrecertInvalidPoison occurs when a precert is passed to SignFromPrecert
// and has a invalid CT poison extension value or the extension is not
// critical.
PrecertInvalidPoison
)
// Certificate persistence related errors specified with CertStoreError
const (
// InsertionFailed occurs when a SQL insert query failes to complete.
InsertionFailed = 100 * (iota + 1)
// RecordNotFound occurs when a SQL query targeting on one unique
// record failes to update the specified row in the table.
RecordNotFound
)
// 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"
case MissingSerial:
msg = "Missing serial number in request"
default:
panic(fmt.Sprintf("Unsupported CFSSL 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 CFSSL 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 CFSSL 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 CFSSL 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"
case UnknownProfile:
msg = "Unknown policy profile"
case UnmatchedWhitelist:
msg = "Request does not match policy whitelist"
default:
panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category PolicyError.",
reason))
}
case DialError:
switch reason {
case Unknown:
msg = "Failed to dial remote server"
default:
panic(fmt.Sprintf("Unsupported CFSSL 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 CFSSL error reason %d under category APIClientError.",
reason))
}
case CSRError:
switch reason {
case Unknown:
msg = "CSR parsing failed due to unknown error"
case ReadFailed:
msg = "CSR file read failed"
case ParseFailed:
msg = "CSR Parsing failed"
case DecodeFailed:
msg = "CSR Decode failed"
case BadRequest:
msg = "CSR Bad request"
default:
panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category APIClientError.", reason))
}
case CTError:
switch reason {
case Unknown:
msg = "Certificate transparency parsing failed due to unknown error"
case PrecertSubmissionFailed:
msg = "Certificate transparency precertificate submission failed"
case PrecertMissingPoison:
msg = "Precertificate is missing CT poison extension"
case PrecertInvalidPoison:
msg = "Precertificate contains an invalid CT poison extension"
default:
panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category CTError.", reason))
}
case CertStoreError:
switch reason {
case Unknown:
msg = "Certificate store action failed due to unknown error"
default:
panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category CertStoreError.", reason))
}
default:
panic(fmt.Sprintf("Unsupported CFSSL 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, CSRError, CTError, CertStoreError, OCSPError:
// no-op, just use the error
default:
panic(fmt.Sprintf("Unsupported CFSSL error type: %d.",
category))
}
return &Error{ErrorCode: errorCode, Message: err.Error()}
}

338
vendor/github.com/cloudflare/cfssl/errors/error_test.go generated vendored Normal file
View File

@ -0,0 +1,338 @@
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.")
}
code := New(OCSPError, ReadFailed).ErrorCode
if code != 8001 {
t.Fatal("Improper error code")
}
code = New(OCSPError, IssuerMismatch).ErrorCode
if code != 8100 {
t.Fatal("Improper error code")
}
code = New(OCSPError, InvalidStatus).ErrorCode
if code != 8200 {
t.Fatal("Improper error code")
}
code = New(CertificateError, Unknown).ErrorCode
if code != 1000 {
t.Fatal("Improper error code")
}
code = New(CertificateError, ReadFailed).ErrorCode
if code != 1001 {
t.Fatal("Improper error code")
}
code = New(CertificateError, DecodeFailed).ErrorCode
if code != 1002 {
t.Fatal("Improper error code")
}
code = New(CertificateError, ParseFailed).ErrorCode
if code != 1003 {
t.Fatal("Improper error code")
}
code = New(CertificateError, SelfSigned).ErrorCode
if code != 1100 {
t.Fatal("Improper error code")
}
code = New(CertificateError, VerifyFailed).ErrorCode
if code != 1200 {
t.Fatal("Improper error code")
}
code = New(CertificateError, BadRequest).ErrorCode
if code != 1300 {
t.Fatal("Improper error code")
}
code = New(CertificateError, MissingSerial).ErrorCode
if code != 1400 {
t.Fatal("Improper error code")
}
code = New(PrivateKeyError, Unknown).ErrorCode
if code != 2000 {
t.Fatal("Improper error code")
}
code = New(PrivateKeyError, ReadFailed).ErrorCode
if code != 2001 {
t.Fatal("Improper error code")
}
code = New(PrivateKeyError, DecodeFailed).ErrorCode
if code != 2002 {
t.Fatal("Improper error code")
}
code = New(PrivateKeyError, ParseFailed).ErrorCode
if code != 2003 {
t.Fatal("Improper error code")
}
code = New(PrivateKeyError, Encrypted).ErrorCode
if code != 2100 {
t.Fatal("Improper error code")
}
code = New(PrivateKeyError, NotRSAOrECC).ErrorCode
if code != 2200 {
t.Fatal("Improper error code")
}
code = New(PrivateKeyError, KeyMismatch).ErrorCode
if code != 2300 {
t.Fatal("Improper error code")
}
code = New(PrivateKeyError, GenerationFailed).ErrorCode
if code != 2400 {
t.Fatal("Improper error code")
}
code = New(PrivateKeyError, Unavailable).ErrorCode
if code != 2500 {
t.Fatal("Improper error code")
}
code = New(IntermediatesError, Unknown).ErrorCode
if code != 3000 {
t.Fatal("Improper error code")
}
code = New(IntermediatesError, ReadFailed).ErrorCode
if code != 3001 {
t.Fatal("Improper error code")
}
code = New(IntermediatesError, DecodeFailed).ErrorCode
if code != 3002 {
t.Fatal("Improper error code")
}
code = New(IntermediatesError, ParseFailed).ErrorCode
if code != 3003 {
t.Fatal("Improper error code")
}
code = New(RootError, Unknown).ErrorCode
if code != 4000 {
t.Fatal("Improper error code")
}
code = New(RootError, ReadFailed).ErrorCode
if code != 4001 {
t.Fatal("Improper error code")
}
code = New(RootError, DecodeFailed).ErrorCode
if code != 4002 {
t.Fatal("Improper error code")
}
code = New(RootError, ParseFailed).ErrorCode
if code != 4003 {
t.Fatal("Improper error code")
}
code = New(PolicyError, Unknown).ErrorCode
if code != 5000 {
t.Fatal("Improper error code")
}
code = New(PolicyError, NoKeyUsages).ErrorCode
if code != 5100 {
t.Fatal("Improper error code")
}
code = New(PolicyError, InvalidPolicy).ErrorCode
if code != 5200 {
t.Fatal("Improper error code")
}
code = New(PolicyError, InvalidRequest).ErrorCode
if code != 5300 {
t.Fatal("Improper error code")
}
code = New(PolicyError, UnknownProfile).ErrorCode
if code != 5400 {
t.Fatal("Improper error code")
}
code = New(DialError, Unknown).ErrorCode
if code != 6000 {
t.Fatal("Improper error code")
}
code = New(APIClientError, AuthenticationFailure).ErrorCode
if code != 7100 {
t.Fatal("Improper error code")
}
code = New(APIClientError, JSONError).ErrorCode
if code != 7200 {
t.Fatal("Improper error code")
}
code = New(APIClientError, ClientHTTPError).ErrorCode
if code != 7400 {
t.Fatal("Improper error code")
}
code = New(APIClientError, IOError).ErrorCode
if code != 7300 {
t.Fatal("Improper error code")
}
code = New(APIClientError, ServerRequestFailed).ErrorCode
if code != 7500 {
t.Fatal("Improper error code")
}
code = New(CSRError, Unknown).ErrorCode
if code != 9000 {
t.Fatal("Improper error code")
}
code = New(CSRError, ReadFailed).ErrorCode
if code != 9001 {
t.Fatal("Improper error code")
}
code = New(CSRError, DecodeFailed).ErrorCode
if code != 9002 {
t.Fatal("Improper error code")
}
code = New(CSRError, ParseFailed).ErrorCode
if code != 9003 {
t.Fatal("Improper error code")
}
code = New(CSRError, KeyMismatch).ErrorCode
if code != 9300 {
t.Fatal("Improper error code")
}
code = New(CSRError, BadRequest).ErrorCode
if code != 9300 {
t.Fatal("Improper error code")
}
code = New(CTError, Unknown).ErrorCode
if code != 10000 {
t.Fatal("Improper error code")
}
code = New(CTError, PrecertSubmissionFailed).ErrorCode
if code != 10100 {
t.Fatal("Improper error code")
}
}
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.")
}
err = Wrap(CertificateError, VerifyFailed, x509.UnknownAuthorityError{})
if err == nil {
t.Fatal("Error creation failed.")
}
err = Wrap(RootError, Unknown, errors.New(msg))
if err == nil {
t.Fatal("Error creation failed.")
}
if err.ErrorCode != int(RootError)+int(Unknown) {
t.Fatal("Error code construction failed.")
}
if err.Message != msg {
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)
}
}
func TestHTTP(t *testing.T) {
err := NewMethodNotAllowed("GET")
if err == nil {
t.Fatal("New Mathod Check failed")
}
err = NewBadRequest(errors.New("Bad Request"))
if err == nil {
t.Fatal("New Bad Request Check failed")
}
if err.StatusCode != 400 {
t.Fatal("New Bad Request error code construction failed")
}
err = NewBadRequestString("Bad Request String")
if err == nil {
t.Fatal("New Bad Request String Check failed")
}
if err.StatusCode != 400 {
t.Fatal("New Bad Request String error code construction failed")
}
err = NewBadRequestMissingParameter("Request Missing Parameter")
if err == nil {
t.Fatal("New Bad Request Missing Parameter Check failed")
}
if err.StatusCode != 400 {
t.Fatal("New Bad Request Missing Parameter error code construction failed")
}
err = NewBadRequestUnwantedParameter("Unwanted Parameter Present In Request")
if err == nil {
t.Fatal("New Bad Request Unwanted Parameter Check failed")
}
if err.StatusCode != 400 {
t.Fatal("New Bad Request Unwanted Parameter error code construction failed")
}
}
func TestHTTPErrorString(t *testing.T) {
method := "GET"
err := NewMethodNotAllowed(method)
str := err.Error()
if str != `Method is not allowed:"`+method+`"` {
t.Fatal("Incorrect Error():", str)
}
}

47
vendor/github.com/cloudflare/cfssl/errors/http.go generated vendored Normal file
View File

@ -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 + `"`)
}

View File

@ -0,0 +1,42 @@
// Package derhelpers implements common functionality
// on DER encoded data
package derhelpers
import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
cferr "github.com/cloudflare/cfssl/errors"
)
// 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
}
// should never reach here
return nil, cferr.New(cferr.PrivateKeyError, cferr.ParseFailed)
}

577
vendor/github.com/cloudflare/cfssl/helpers/helpers.go generated vendored Normal file
View File

@ -0,0 +1,577 @@
// Package helpers implements utility functionality common to many
// CFSSL packages.
package helpers
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
"os"
"github.com/google/certificate-transparency-go"
cttls "github.com/google/certificate-transparency-go/tls"
ctx509 "github.com/google/certificate-transparency-go/x509"
"golang.org/x/crypto/ocsp"
"strings"
"time"
"github.com/cloudflare/cfssl/crypto/pkcs7"
cferr "github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/helpers/derhelpers"
"github.com/cloudflare/cfssl/log"
"golang.org/x/crypto/pkcs12"
)
// 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
// InclusiveDate returns the time.Time representation of a date - 1
// nanosecond. This allows time.After to be used inclusively.
func InclusiveDate(year int, month time.Month, day int) time.Time {
return time.Date(year, month, day, 0, 0, 0, 0, time.UTC).Add(-1 * time.Nanosecond)
}
// Jul2012 is the July 2012 CAB Forum deadline for when CAs must stop
// issuing certificates valid for more than 5 years.
var Jul2012 = InclusiveDate(2012, time.July, 01)
// Apr2015 is the April 2015 CAB Forum deadline for when CAs must stop
// issuing certificates valid for more than 39 months.
var Apr2015 = InclusiveDate(2015, time.April, 01)
// 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) (notAfter time.Time) {
if len(chain) == 0 {
return
}
notAfter = chain[0].NotAfter
for _, cert := range chain {
if notAfter.After(cert.NotAfter) {
notAfter = cert.NotAfter
}
}
return
}
// MonthsValid returns the number of months for which a certificate is valid.
func MonthsValid(c *x509.Certificate) int {
issued := c.NotBefore
expiry := c.NotAfter
years := (expiry.Year() - issued.Year())
months := years*12 + int(expiry.Month()) - int(issued.Month())
// Round up if valid for less than a full month
if expiry.Day() > issued.Day() {
months++
}
return months
}
// ValidExpiry determines if a certificate is valid for an acceptable
// length of time per the CA/Browser Forum baseline requirements.
// See https://cabforum.org/wp-content/uploads/CAB-Forum-BR-1.3.0.pdf
func ValidExpiry(c *x509.Certificate) bool {
issued := c.NotBefore
var maxMonths int
switch {
case issued.After(Apr2015):
maxMonths = 39
case issued.After(Jul2012):
maxMonths = 60
case issued.Before(Jul2012):
maxMonths = 120
}
if MonthsValid(c) > maxMonths {
return false
}
return true
}
// 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"
}
}
// EncodeCertificatesPEM encodes a number of x509 certificates to PEM
func EncodeCertificatesPEM(certs []*x509.Certificate) []byte {
var buffer bytes.Buffer
for _, cert := range certs {
pem.Encode(&buffer, &pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Raw,
})
}
return buffer.Bytes()
}
// EncodeCertificatePEM encodes a single x509 certificates to PEM
func EncodeCertificatePEM(cert *x509.Certificate) []byte {
return EncodeCertificatesPEM([]*x509.Certificate{cert})
}
// ParseCertificatesPEM parses a sequence of PEM-encoded certificate and returns them,
// can handle PEM encoded PKCS #7 structures.
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
}
// ParseCertificatesDER parses a DER encoding of a certificate object and possibly private key,
// either PKCS #7, PKCS #12, or raw x509.
func ParseCertificatesDER(certsDER []byte, password string) (certs []*x509.Certificate, key crypto.Signer, err error) {
certsDER = bytes.TrimSpace(certsDER)
pkcs7data, err := pkcs7.ParsePKCS7(certsDER)
if err != nil {
var pkcs12data interface{}
certs = make([]*x509.Certificate, 1)
pkcs12data, certs[0], err = pkcs12.Decode(certsDER, password)
if err != nil {
certs, err = x509.ParseCertificates(certsDER)
if err != nil {
return nil, nil, cferr.New(cferr.CertificateError, cferr.DecodeFailed)
}
} else {
key = pkcs12data.(crypto.Signer)
}
} else {
if pkcs7data.ContentInfo != "SignedData" {
return nil, nil, cferr.Wrap(cferr.CertificateError, cferr.DecodeFailed, errors.New("can only extract certificates from signed data content info"))
}
certs = pkcs7data.Content.SignedData.Certificates
}
if certs == nil {
return nil, key, cferr.New(cferr.CertificateError, cferr.DecodeFailed)
}
return certs, key, 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,
// can handle PEM encoded PKCS #7 structures.
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 object"))
} else if len(cert) > 1 {
return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, errors.New("the PKCS7 object in the PEM file should contain only one certificate"))
}
return cert[0], nil
}
// ParseOneCertificateFromPEM attempts to parse one PEM encoded certificate object,
// either a raw x509 certificate or a PKCS #7 structure possibly containing
// multiple certificates, from the top of certsPEM, which itself may
// contain multiple PEM encoded certificate objects.
func ParseOneCertificateFromPEM(certsPEM []byte) ([]*x509.Certificate, []byte, error) {
block, rest := pem.Decode(certsPEM)
if block == nil {
return nil, rest, nil
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
pkcs7data, err := pkcs7.ParsePKCS7(block.Bytes)
if err != nil {
return nil, rest, err
}
if pkcs7data.ContentInfo != "SignedData" {
return nil, rest, errors.New("only PKCS #7 Signed Data Content Info supported for certificate parsing")
}
certs := pkcs7data.Content.SignedData.Certificates
if certs == nil {
return nil, rest, errors.New("PKCS #7 structure contains no certificates")
}
return certs, rest, nil
}
var certs = []*x509.Certificate{cert}
return certs, rest, nil
}
// LoadPEMCertPool loads a pool of PEM certificates from file.
func LoadPEMCertPool(certsFile string) (*x509.CertPool, error) {
if certsFile == "" {
return nil, nil
}
pemCerts, err := ioutil.ReadFile(certsFile)
if err != nil {
return nil, err
}
return PEMToCertPool(pemCerts)
}
// PEMToCertPool concerts PEM certificates to a CertPool.
func PEMToCertPool(pemCerts []byte) (*x509.CertPool, error) {
if len(pemCerts) == 0 {
return nil, nil
}
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(pemCerts) {
return nil, errors.New("failed to load cert pool")
}
return certPool, nil
}
// 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) {
return ParsePrivateKeyPEMWithPassword(keyPEM, nil)
}
// ParsePrivateKeyPEMWithPassword parses and returns a PEM-encoded private
// key. The private key may be a potentially encrypted PKCS#8, PKCS#1,
// or elliptic private key.
func ParsePrivateKeyPEMWithPassword(keyPEM []byte, password []byte) (key crypto.Signer, err error) {
keyDER, err := GetKeyDERFromPEM(keyPEM, password)
if err != nil {
return nil, err
}
return derhelpers.ParsePrivateKeyDER(keyDER)
}
// GetKeyDERFromPEM parses a PEM-encoded private key and returns DER-format key bytes.
func GetKeyDERFromPEM(in []byte, password []byte) ([]byte, error) {
keyDER, _ := pem.Decode(in)
if keyDER != nil {
if procType, ok := keyDER.Headers["Proc-Type"]; ok {
if strings.Contains(procType, "ENCRYPTED") {
if password != nil {
return x509.DecryptPEMBlock(keyDER, password)
}
return nil, cferr.New(cferr.PrivateKeyError, cferr.Encrypted)
}
}
return keyDER.Bytes, nil
}
return nil, cferr.New(cferr.PrivateKeyError, cferr.DecodeFailed)
}
// ParseCSR parses a PEM- or DER-encoded PKCS #10 certificate signing request.
func ParseCSR(in []byte) (csr *x509.CertificateRequest, rest []byte, err error) {
in = bytes.TrimSpace(in)
p, rest := pem.Decode(in)
if p != nil {
if p.Type != "NEW CERTIFICATE REQUEST" && p.Type != "CERTIFICATE REQUEST" {
return nil, rest, cferr.New(cferr.CSRError, cferr.BadRequest)
}
csr, err = x509.ParseCertificateRequest(p.Bytes)
} else {
csr, err = x509.ParseCertificateRequest(in)
}
if err != nil {
return nil, rest, err
}
err = csr.CheckSignature()
if err != nil {
return nil, rest, err
}
return csr, rest, nil
}
// ParseCSRPEM parses a PEM-encoded certificate signing request.
// It does not check the signature. This is useful for dumping data from a CSR
// locally.
func ParseCSRPEM(csrPEM []byte) (*x509.CertificateRequest, error) {
block, _ := pem.Decode([]byte(csrPEM))
if block == nil {
return nil, cferr.New(cferr.CSRError, cferr.DecodeFailed)
}
csrObject, err := x509.ParseCertificateRequest(block.Bytes)
if err != nil {
return nil, err
}
return csrObject, nil
}
// SignerAlgo returns an X.509 signature algorithm from a crypto.Signer.
func SignerAlgo(priv crypto.Signer) x509.SignatureAlgorithm {
switch pub := priv.Public().(type) {
case *rsa.PublicKey:
bitLength := pub.N.BitLen()
switch {
case bitLength >= 4096:
return x509.SHA512WithRSA
case bitLength >= 3072:
return x509.SHA384WithRSA
case bitLength >= 2048:
return x509.SHA256WithRSA
default:
return x509.SHA1WithRSA
}
case *ecdsa.PublicKey:
switch pub.Curve {
case elliptic.P521():
return x509.ECDSAWithSHA512
case elliptic.P384():
return x509.ECDSAWithSHA384
case elliptic.P256():
return x509.ECDSAWithSHA256
default:
return x509.ECDSAWithSHA1
}
default:
return x509.UnknownSignatureAlgorithm
}
}
// LoadClientCertificate load key/certificate from pem files
func LoadClientCertificate(certFile string, keyFile string) (*tls.Certificate, error) {
if certFile != "" && keyFile != "" {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
log.Critical("Unable to read client certificate from file: %s or key from file: %s", certFile, keyFile)
return nil, err
}
log.Debug("Client certificate loaded ")
return &cert, nil
}
return nil, nil
}
// CreateTLSConfig creates a tls.Config object from certs and roots
func CreateTLSConfig(remoteCAs *x509.CertPool, cert *tls.Certificate) *tls.Config {
var certs []tls.Certificate
if cert != nil {
certs = []tls.Certificate{*cert}
}
return &tls.Config{
Certificates: certs,
RootCAs: remoteCAs,
}
}
// SerializeSCTList serializes a list of SCTs.
func SerializeSCTList(sctList []ct.SignedCertificateTimestamp) ([]byte, error) {
list := ctx509.SignedCertificateTimestampList{}
for _, sct := range sctList {
sctBytes, err := cttls.Marshal(sct)
if err != nil {
return nil, err
}
list.SCTList = append(list.SCTList, ctx509.SerializedSCT{Val: sctBytes})
}
return cttls.Marshal(list)
}
// DeserializeSCTList deserializes a list of SCTs.
func DeserializeSCTList(serializedSCTList []byte) ([]ct.SignedCertificateTimestamp, error) {
var sctList ctx509.SignedCertificateTimestampList
rest, err := cttls.Unmarshal(serializedSCTList, &sctList)
if err != nil {
return nil, err
}
if len(rest) != 0 {
return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, errors.New("serialized SCT list contained trailing garbage"))
}
list := make([]ct.SignedCertificateTimestamp, len(sctList.SCTList))
for i, serializedSCT := range sctList.SCTList {
var sct ct.SignedCertificateTimestamp
rest, err := cttls.Unmarshal(serializedSCT.Val, &sct)
if err != nil {
return nil, err
}
if len(rest) != 0 {
return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, errors.New("serialized SCT contained trailing garbage"))
}
list[i] = sct
}
return list, nil
}
// SCTListFromOCSPResponse extracts the SCTList from an ocsp.Response,
// returning an empty list if the SCT extension was not found or could not be
// unmarshalled.
func SCTListFromOCSPResponse(response *ocsp.Response) ([]ct.SignedCertificateTimestamp, error) {
// This loop finds the SCTListExtension in the OCSP response.
var SCTListExtension, ext pkix.Extension
for _, ext = range response.Extensions {
// sctExtOid is the ObjectIdentifier of a Signed Certificate Timestamp.
sctExtOid := asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 5}
if ext.Id.Equal(sctExtOid) {
SCTListExtension = ext
break
}
}
// This code block extracts the sctList from the SCT extension.
var sctList []ct.SignedCertificateTimestamp
var err error
if numBytes := len(SCTListExtension.Value); numBytes != 0 {
var serializedSCTList []byte
rest := make([]byte, numBytes)
copy(rest, SCTListExtension.Value)
for len(rest) != 0 {
rest, err = asn1.Unmarshal(rest, &serializedSCTList)
if err != nil {
return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, err)
}
}
sctList, err = DeserializeSCTList(serializedSCTList)
}
return sctList, err
}
// ReadBytes reads a []byte either from a file or an environment variable.
// If valFile has a prefix of 'env:', the []byte is read from the environment
// using the subsequent name. If the prefix is 'file:' the []byte is read from
// the subsequent file. If no prefix is provided, valFile is assumed to be a
// file path.
func ReadBytes(valFile string) ([]byte, error) {
switch splitVal := strings.SplitN(valFile, ":", 2); len(splitVal) {
case 1:
return ioutil.ReadFile(valFile)
case 2:
switch splitVal[0] {
case "env":
return []byte(os.Getenv(splitVal[1])), nil
case "file":
return ioutil.ReadFile(splitVal[1])
default:
return nil, fmt.Errorf("unknown prefix: %s", splitVal[0])
}
default:
return nil, fmt.Errorf("multiple prefixes: %s",
strings.Join(splitVal[:len(splitVal)-1], ", "))
}
}

View File

@ -0,0 +1,629 @@
package helpers
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"io/ioutil"
"math"
"testing"
"time"
"golang.org/x/crypto/ocsp"
"github.com/google/certificate-transparency-go"
)
const (
testCertFile = "testdata/cert.pem"
testCertDERFile = "testdata/cert.der"
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"
testPrivateRSAKey = "testdata/priv_rsa_key.pem"
testPrivateECDSAKey = "testdata/private_ecdsa_key.pem"
testUnsupportedECDSAKey = "testdata/secp256k1-key.pem"
testMessedUpPrivateKey = "testdata/messed_up_priv_key.pem"
testEncryptedPrivateKey = "testdata/enc_priv_key.pem"
testEmptyPem = "testdata/empty.pem"
testNoHeaderCert = "testdata/noheadercert.pem"
testSinglePKCS7 = "testdata/cert_pkcs7.pem" // openssl crl2pkcs7 -nocrl -out cert_pkcs7.pem -in cert.pem
testEmptyPKCS7DER = "testdata/empty_pkcs7.der" // openssl crl2pkcs7 -nocrl -out empty_pkcs7.der -outform der
testEmptyPKCS7PEM = "testdata/empty_pkcs7.pem" // openssl crl2pkcs7 -nocrl -out empty_pkcs7.pem -outform pem
testMultiplePKCS7 = "testdata/bundle_pkcs7.pem"
testPKCS12EmptyPswd = "testdata/emptypasswordpkcs12.p12"
testPKCS12Passwordispassword = "testdata/passwordpkcs12.p12"
testPKCS12MultipleCerts = "testdata/multiplecerts.p12"
testCSRPEM = "testdata/test.csr.pem"
testCSRPEMBad = "testdata/test.bad.csr.pem"
)
func TestParseCertificatesDER(t *testing.T) {
var password = []string{"password", "", ""}
for i, testFile := range []string{testPKCS12Passwordispassword, testPKCS12EmptyPswd, testCertDERFile} {
testDER, err := ioutil.ReadFile(testFile)
if err != nil {
t.Fatal(err)
}
if _, _, err := ParseCertificatesDER(testDER, password[i]); err != nil {
t.Fatal(err)
}
// Incorrect Password for PKCS12 formatted files
if _, _, err := ParseCertificatesDER(testDER, "incorrectpassword"); err == nil && i != 2 {
t.Fatal(err)
}
}
testDER, err := ioutil.ReadFile(testEmptyPKCS7DER)
if err != nil {
t.Fatal(err)
}
// PKCS7 with no certificates
if _, _, err := ParseCertificatesDER(testDER, ""); err == nil {
t.Fatal(err)
}
}
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 != expected {
t.Fatalf("Expected %v, got %v", expected, out)
}
}
func TestMonthsValid(t *testing.T) {
var cert = &x509.Certificate{
NotBefore: time.Date(2015, time.April, 01, 0, 0, 0, 0, time.UTC),
NotAfter: time.Date(2015, time.April, 01, 0, 0, 0, 0, time.UTC),
}
if MonthsValid(cert) != 0 {
t.Fail()
}
cert.NotAfter = time.Date(2016, time.April, 01, 0, 0, 0, 0, time.UTC)
if MonthsValid(cert) != 12 {
t.Fail()
}
// extra days should be rounded up to 1 month
cert.NotAfter = time.Date(2016, time.April, 02, 0, 0, 0, 0, time.UTC)
if MonthsValid(cert) != 13 {
t.Fail()
}
}
func TestHasValidExpiry(t *testing.T) {
// Issue period > April 1, 2015
var cert = &x509.Certificate{
NotBefore: time.Date(2015, time.April, 01, 0, 0, 0, 0, time.UTC),
NotAfter: time.Date(2016, time.April, 01, 0, 0, 0, 0, time.UTC),
}
if !ValidExpiry(cert) {
t.Fail()
}
cert.NotAfter = time.Date(2019, time.April, 01, 01, 0, 0, 0, time.UTC)
if ValidExpiry(cert) {
t.Fail()
}
// Issue period < July 1, 2012
cert.NotBefore = time.Date(2009, time.March, 01, 0, 0, 0, 0, time.UTC)
if ValidExpiry(cert) {
t.Fail()
}
// Issue period July 1, 2012 - April 1, 2015
cert.NotBefore = time.Date(2012, time.July, 01, 0, 0, 0, 0, time.UTC)
cert.NotAfter = time.Date(2017, time.July, 01, 0, 0, 0, 0, time.UTC)
if !ValidExpiry(cert) {
t.Fail()
}
}
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, testSinglePKCS7} {
certPEM, err := ioutil.ReadFile(testFile)
if err != nil {
t.Fatal(err)
}
if _, err := ParseCertificatePEM(certPEM); err != nil {
t.Log(testFile)
t.Fatal(err)
}
}
for _, testFile := range []string{testBundleFile, testMessedUpCertFile, testEmptyPKCS7PEM, testEmptyCertFile, testMultiplePKCS7} {
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, testSinglePKCS7, testMultiplePKCS7} {
bundlePEM, err := ioutil.ReadFile(testFile)
if err != nil {
t.Fatal(err)
}
if _, err := ParseCertificatesPEM(bundlePEM); err != nil {
t.Log(testFile)
t.Fatal(err)
}
}
// test failure cases
// few lines deleted, then headers removed
for _, testFile := range []string{testMessedUpBundleFile, testEmptyPKCS7PEM, 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 cases
testRSAPEM, _ := ioutil.ReadFile(testPrivateRSAKey)
_, err := ParsePrivateKeyPEM(testRSAPEM)
if err != nil {
t.Fatal(err)
}
testECDSAPEM, _ := ioutil.ReadFile(testPrivateECDSAKey)
_, err = ParsePrivateKeyPEM(testECDSAPEM)
if err != nil {
t.Fatal(err)
}
// error cases
errCases := []string{
testMessedUpPrivateKey, // a few lines deleted
testEmptyPem, // empty file
testEncryptedPrivateKey, // encrypted key
testUnsupportedECDSAKey, // ECDSA curve not currently supported by Go standard library
}
for _, fname := range errCases {
testPEM, _ := ioutil.ReadFile(fname)
_, err = ParsePrivateKeyPEM(testPEM)
if err == nil {
t.Fatal("Incorrect private key failed to produce an error")
}
}
}
// Imported from signers/local/testdata/
const ecdsaTestCSR = "testdata/ecdsa256.csr"
func TestParseCSRPEM(t *testing.T) {
in, err := ioutil.ReadFile(ecdsaTestCSR)
if err != nil {
t.Fatalf("%v", err)
}
_, _, err = ParseCSR(in)
if err != nil {
t.Fatalf("%v", err)
}
in[12]++
_, _, err = ParseCSR(in)
if err == nil {
t.Fatalf("Expected an invalid CSR.")
}
in[12]--
}
func TestParseCSRPEMMore(t *testing.T) {
csrPEM, err := ioutil.ReadFile(testCSRPEM)
if err != nil {
t.Fatal(err)
}
if _, err := ParseCSRPEM(csrPEM); err != nil {
t.Fatal(err)
}
csrPEM, err = ioutil.ReadFile(testCSRPEMBad)
if err != nil {
t.Fatal(err)
}
if _, err := ParseCSRPEM(csrPEM); err == nil {
t.Fatal(err)
}
if _, err := ParseCSRPEM([]byte("not even pem")); err == nil {
t.Fatal("Expected an invalid CSR.")
}
}
// Imported from signers/local/testdata/
const rsaOldTestCSR = "testdata/rsa-old.csr"
func TestParseOldCSR(t *testing.T) {
in, err := ioutil.ReadFile(rsaOldTestCSR)
if err != nil {
t.Fatalf("%v", err)
}
_, _, err = ParseCSR(in)
if err != nil {
t.Fatalf("%v", err)
}
}
// Imported from signers/local/testdata/
const clientCertFile = "testdata/ca.pem"
const clientKeyFile = "testdata/ca_key.pem"
func TestClientCertParams(t *testing.T) {
_, err := LoadClientCertificate(testCertFile, testPrivateRSAKey)
if err == nil {
t.Fatal("Unmatched cert/key should generate error")
}
cert, err := LoadClientCertificate("", "")
if err != nil || cert != nil {
t.Fatal("Certificate atempted to loaded with missing key and cert")
}
cert, err = LoadClientCertificate(clientCertFile, "")
if err != nil || cert != nil {
t.Fatal("Certificate atempted to loaded with missing key")
}
cert, err = LoadClientCertificate("", clientKeyFile)
if err != nil || cert != nil {
t.Fatal("Certificate atempted to loaded with missing cert")
}
cert, err = LoadClientCertificate(clientCertFile, clientKeyFile)
if err != nil {
t.Fatal(err)
}
if cert == nil {
t.Fatal("cert not created")
}
}
func TestLoadPEMCertPool(t *testing.T) {
certPool, err := PEMToCertPool([]byte{})
if certPool != nil || err != nil {
t.Fatal("Empty file name should not generate error or a cert pool")
}
in, err := ioutil.ReadFile(testEmptyPem)
if err != nil {
t.Fatalf("%v", err)
}
certPool, err = PEMToCertPool(in)
if certPool != nil {
t.Fatal("Empty file should not generate a cert pool")
} else if err == nil {
t.Fatal("Expected error for empty file")
}
in, err = ioutil.ReadFile(testEmptyCertFile)
if err != nil {
t.Fatalf("%v", err)
}
certPool, err = PEMToCertPool(in)
if certPool != nil {
t.Fatal("Empty cert should not generate a cert pool")
} else if err == nil {
t.Fatal("Expected error for empty cert")
}
in, err = ioutil.ReadFile(clientCertFile)
if err != nil {
t.Fatalf("%v", err)
}
certPool, err = PEMToCertPool(in)
if err != nil {
t.Fatalf("%v", err)
} else if certPool == nil {
t.Fatal("cert pool not created")
}
}
// sctEquals returns true if all fields of both SCTs are equivalent.
func sctEquals(sctA, sctB ct.SignedCertificateTimestamp) bool {
if sctA.SCTVersion == sctB.SCTVersion &&
sctA.LogID == sctB.LogID &&
sctA.Timestamp == sctB.Timestamp &&
bytes.Equal(sctA.Extensions, sctB.Extensions) &&
sctA.Signature.Algorithm == sctB.Signature.Algorithm &&
bytes.Equal(sctA.Signature.Signature, sctA.Signature.Signature) {
return true
}
return false
}
// NOTE: TestDeserializeSCTList tests both DeserializeSCTList and
// SerializeSCTList.
func TestDeserializeSCTList(t *testing.T) {
// Here we make sure that empty SCT lists return an error
emptyLists := [][]byte{nil, {}}
for _, emptyList := range emptyLists {
_, err := DeserializeSCTList(emptyList)
if err == nil {
t.Fatalf("DeserializeSCTList(%v) should raise an error\n", emptyList)
}
}
// Here we make sure that an SCT list with a zero SCT is deserialized
// correctly
var zeroSCT ct.SignedCertificateTimestamp
serializedSCT, err := SerializeSCTList([]ct.SignedCertificateTimestamp{zeroSCT})
if err != nil {
t.Fatal(err)
}
deserializedSCTList, err := DeserializeSCTList(serializedSCT)
if err != nil {
t.Fatal(err)
}
if !sctEquals(zeroSCT, (deserializedSCTList)[0]) {
t.Fatal("SCTs don't match")
}
// Here we verify that an error is raised when the SCT list length
// field is greater than its actual length
serializedSCT, err = SerializeSCTList([]ct.SignedCertificateTimestamp{zeroSCT})
if err != nil {
t.Fatal(err)
}
serializedSCT[0] = 15
_, err = DeserializeSCTList(serializedSCT)
if err == nil {
t.Fatalf("DeserializeSCTList should raise an error when " +
"the SCT list length field and the list length don't match\n")
}
// Here we verify that an error is raised when the SCT list length
// field is less than its actual length
serializedSCT[0] = 0
serializedSCT[1] = 0
_, err = DeserializeSCTList(serializedSCT)
if err == nil {
t.Fatalf("DeserializeSCTList should raise an error when " +
"the SCT list length field and the list length don't match\n")
}
// Here we verify that an error is raised when the SCT length field is
// greater than its actual length
serializedSCT[0] = 0
serializedSCT[1] = 49
serializedSCT[2] = 1
_, err = DeserializeSCTList(serializedSCT)
if err == nil {
t.Fatalf("DeserializeSCTList should raise an error when " +
"the SCT length field and the SCT length don't match\n")
}
// Here we verify that an error is raised when the SCT length field is
// less than its actual length
serializedSCT[2] = 0
serializedSCT[3] = 0
_, err = DeserializeSCTList(serializedSCT)
if err == nil {
t.Fatalf("DeserializeSCTList should raise an error when " +
"the SCT length field and the SCT length don't match\n")
}
}
func TestSCTListFromOCSPResponse(t *testing.T) {
var response ocsp.Response
lst, err := SCTListFromOCSPResponse(&response)
if err != nil {
t.Fatal(err)
}
if len(lst) != 0 {
t.Fatal("SCTListFromOCSPResponse should return an empty SCT list for an empty extension")
}
var zeroSCT ct.SignedCertificateTimestamp
serializedSCTList, err := SerializeSCTList([]ct.SignedCertificateTimestamp{zeroSCT})
if err != nil {
t.Fatal("failed to serialize SCT list")
}
serializedSCTList, err = asn1.Marshal(serializedSCTList)
if err != nil {
t.Fatal("failed to serialize SCT list")
}
// The value of Id below is the object identifier of the OCSP Stapling
// SCT extension (see section 3.3. of RFC 6962).
response.Extensions = []pkix.Extension{{
Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 5},
Critical: false,
Value: serializedSCTList,
}}
lst, err = SCTListFromOCSPResponse(&response)
if err != nil {
t.Fatal(err)
}
if !sctEquals(zeroSCT, lst[0]) {
t.Fatal("SCTs don't match")
}
}

View 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-----

View File

@ -0,0 +1,52 @@
-----BEGIN PKCS7-----
MIIJOAYJKoZIhvcNAQcCoIIJKTCCCSUCAQExADALBgkqhkiG9w0BBwGgggkLMIIE
czCCAl2gAwIBAgIIDARj8BWNsscwCwYJKoZIhvcNAQELMIGMMQswCQYDVQQGEwJV
UzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVl
cmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5p
YTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwMzAyMDAwMDAw
WhcNMTkwNDAxMDAwMDAwWjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNsb3Vk
RmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcTDVNh
biBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNsb3Vk
ZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGwf3F0
XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73PIXGy
fNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo4GIMIGFMA4G
A1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBRB+Yoi
UjIm34/wBwHdJGE4Wufs/DAfBgNVHSMEGDAWgBTXXUgpaSwO9HOrQBxGqOOSFHsH
EDAfBgNVHREEGDAWghRjbG91ZGZsYXJlLWludGVyLmNvbTALBgkqhkiG9w0BAQsD
ggIBACRqAC5EJEe+8ihv1WzCUMEMb7KtS0BqoNbdXE32ia66PgJSQmHcmeJdFI1U
jL0DlljTM2tc+8KxR/1/qnKiI+W/D4wFTWOY/JWFOd15q7lXuKGl+8PMkAHFA145
JCr6oZoO9G9wUwVUrbmXAbyPCOfzsEQ2+mD9F1ZpoEjzVhtGf0R+vnYrRw8j4WCv
5AIcYRAf7HZxbhMILF1bccNlqyUtdH+/MTHXpjkjJjA5KbsHBrAEfjAXkD7cWWOa
y6m7mVWb3PPFmGorP6t29baEETK9ZTZSrfD9rnExjjUCftWJEn0M4Pp98DvTbr6+
bg8jwtq73qdyOfNsC/Sod18UuHH7MTQA22yqAF5jIlcYtAHGlNnl+sDPZACs369/
Z9rOL9vPFL+Z3F/uJtqZzvN1QiCkj8jWzR0u9fh3eQwZADM2RwgwS4Gs2YghPsyp
Do33sFOwfX93KqKBsTHssn8SSDDaSnZ8bu1ATEdshbVieecuQx40UadPuJpwEPVq
TR5AhviXQ9bKrTnU5T7EgkW9vNydkpLQQlMg3QE8hsndv4loGZbZGfNtqQHS/mg1
t07S+7OEa4YaMW+wVOBOqTdW7OXlZFLfCcF5SYLM0SnlTMklRMxiqI4JqZXH0thn
UGD0JjfLX4rTaZUzT3lrXXWzpS2jzutXQkjGv4nhGGprIDuTMIIEkDCCA/ugAwIB
AgIIWnP9jF/2nogwCwYJKoZIhvcNAQELMH0xCzAJBgNVBAYTAlVTMRMwEQYDVQQI
DApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQKDApD
bG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQGA1UEAwwNQ0ZTU0xf
VEVTVF9DQTAeFw0xNDAzMDEwMDAwMDBaFw0xNDA0MTUwMDAwMDBaMIGMMQswCQYD
VQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBF
bmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2Fs
aWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQDlCnV+vj0sVPy8SqHLAlI+xwnPhWgzj2Ve
vD6Nz1Zu1BeQ5m5y4CWCf+GmRGTP7+a/C510Fw6rpmInB0NgxxwQ2rC08fJtCnij
lGH/VjEPIHY5lRaAomcM8Rgx6JOuv9BpZJKpr9pyUMV53JeWRbWuLH5nEMdyk9Np
etS2gWxt4/D20QlhK/tHkROrcLmEUddwIGdwE8JzI88c77Fuu6pgMtHKvl4GGH0y
vb4T7PvCdH8V2tCH7bt8roXd9MSyFVy7uORkfouip7EsVREUmlcY5EvpR141KXbZ
qiOQiusJ+u76mEUQNk8wCR1/CW/ii9v1BKOVjXwCfEtIXjg0APJx1VNSSH6XoDpU
ETL+eQ4J0FL9XNbsDuYar7+zD0N1/5vSo3HLNRQR9f0lbsyssWBEN+CxK19xyPum
r21Z0bU0f1B5H52VSF0q3I1Ju9wRo994a7YipdGcmZ2lChmT7r3mzlBTYl3poU26
q34v8wG9U7Jv4fsZJ+RGebDI+TR3QG6Yod06l9oEYZxWXBY7STOs8wuTu3huSnan
/IpWnV017Vsc61D5G+QrqcxZdXckt3anZKCF75JpUnJ7vuowTmmHlb8KIMa9mOvc
uGX4P6mz8gTi2arl/aL27kj9Q0Jgv/y1ebe2Bx2P9TF6+VNDDL3J/vSVlFeqLt2r
eAIBKnytLwIDAQABo4GIMIGFMA4GA1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAG
AQH/AgEBMB0GA1UdDgQWBBTXXUgpaSwO9HOrQBxGqOOSFHsHEDAfBgNVHSMEGDAW
gBS4Xu+uZ1C31vMH5Wq+VbNnOg2SPjAfBgNVHREEGDAWghRjbG91ZGZsYXJlLWlu
dGVyLmNvbTALBgkqhkiG9w0BAQsDgYEAfPLKCAHnPzgMYLX/fWznVvOEFAAYZByP
Fx4QdMBbDZUtxHyvJIBs6PdxrdSuDwSiMqE7qQIi+jzzwGl9fC7vf45B2zCX0OW5
1QL2oWNBdKlGgB+b2pwyME82lX/Pr7V1GY10u+ep1xdZDnchDaMsXjQQTJu0iuG3
qKEuCmUwOmehADEA
-----END PKCS7-----

View 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-----

View 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-----

View 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-----

Binary file not shown.

View File

@ -0,0 +1,13 @@
-----BEGIN CERTIFICATE-----
MIIB7DCCAZKgAwIBAgIIE/Qz49ebG7kwCgYIKoZIzj0EAwIwTDELMAkGA1UEBhMC
VVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28x
EDAOBgNVBAoTB2FjbWUuY28wHhcNMTcwNTIzMTk1MTQ0WhcNMTcwODIzMDE1NjQ0
WjBMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzEQMA4GA1UEChMHYWNtZS5jbzBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABEW8F+k/avvdBm/KRsuDnTZ3p+VuVdsqDF+aD9nIYeOhx5sj574y
hEIZOpgbEsi3BvqY63y2jYyPFodf25+CA9GjXjBcMA4GA1UdDwEB/wQEAwIFoDAd
BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNV
HQ4EFgQUzDpu+HN89EC1M8aNl7f0Ln5JnnIwCgYIKoZIzj0EAwIDSAAwRQIgC4/r
urbw/pzE3LDA6GpIts6TVyzgftLLEfU2BzQsjp0CIQDo+sn8t7XC6JN4KKRr2ABl
ZI+JifgG+2KCy9ln2LxGJQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,14 @@
-----BEGIN PKCS7-----
MIICHQYJKoZIhvcNAQcCoIICDjCCAgoCAQExADALBgkqhkiG9w0BBwGgggHwMIIB
7DCCAZKgAwIBAgIIE/Qz49ebG7kwCgYIKoZIzj0EAwIwTDELMAkGA1UEBhMCVVMx
EzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEDAO
BgNVBAoTB2FjbWUuY28wHhcNMTcwNTIzMTk1MTQ0WhcNMTcwODIzMDE1NjQ0WjBM
MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2Fu
IEZyYW5jaXNjbzEQMA4GA1UEChMHYWNtZS5jbzBZMBMGByqGSM49AgEGCCqGSM49
AwEHA0IABEW8F+k/avvdBm/KRsuDnTZ3p+VuVdsqDF+aD9nIYeOhx5sj574yhEIZ
OpgbEsi3BvqY63y2jYyPFodf25+CA9GjXjBcMA4GA1UdDwEB/wQEAwIFoDAdBgNV
HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4E
FgQUzDpu+HN89EC1M8aNl7f0Ln5JnnIwCgYIKoZIzj0EAwIDSAAwRQIgC4/rurbw
/pzE3LDA6GpIts6TVyzgftLLEfU2BzQsjp0CIQDo+sn8t7XC6JN4KKRr2ABlZI+J
ifgG+2KCy9ln2LxGJaEAMQA=
-----END PKCS7-----

View File

@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIIB7DCCAZKgAwIBAgIIE/Qz49ebG7kwCgYIKoZIzj0EAwIwTDELMAkGA1UEBhMC
VVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28x
EDAOBgNVBAoTB2FjbWUuY28wHhcNMTcwNTIzMTk1MTQ0WhcNMTcwODIzMDE1NjQ0
WjBMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzEQMA4GA1UEChMHYWNtZS5jbzBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABEW8F+k/avvdBm/KRsuDnTZ3p+VuVdsqDF+aD9nIYeOhx5sj574y
hEIZOpgbEsi3BvqY63y2jYyPFodf25+CA9GjXjBcMA4GA1UdDwEB/wQEAwIFoDAd
BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNV
HQ4EFgQUzDpu+HN89EC1M8aNl7f0Ln5JnnIwCgYIKoZIzj0EAwIDSAAwRQIgC4/r
urbw/pzE3LDA6GpIts6TVyzgftLLEfU2BzQsjp0CIQDo+sn8t7XC6JN4KKRr2ABl
ZI+JifgG+2KCy9ln2LxGJQ==
-----END CERTIFICATE-----

View 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-----

View File

@ -0,0 +1 @@

Binary file not shown.

View File

@ -0,0 +1,3 @@
-----BEGIN PKCS7-----
MCcGCSqGSIb3DQEHAqAaMBgCAQExADALBgkqhkiG9w0BBwGgAKEAMQA=
-----END PKCS7-----

View File

@ -0,0 +1,2 @@
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----LSKFSKLF

Binary file not shown.

View 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-----

View 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

View 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-----

View 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-----

Binary file not shown.

View 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

Binary file not shown.

View 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-----

View File

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MGgCAQEEHCGXsrNo2xfy8+zd4Pzj8rcQ5KqQO43au1t/7nugBwYFK4EEACGhPAM6
AASJodCTtj5aYXnWxMiYhwjEgNQJJbNzJFEbsGJX9pCWZC673ammTWFHMjnMPkS/
9eU5YeW40BHqfw==
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,19 @@
-----BEGIN NEW 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 NEW CERTIFICATE REQUEST-----

View File

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIJLKycmoCAk4HqlJGdsuFyHsxfIheKsLH91tS/TNP5OOoAcGBSuBBAAK
oUQDQgAEBkmL7cvC2cgchzfSuUZPGnzH0FqBtf3kGhSllQiIzGDn4envPXNqp+93
V2NZ8VT+Aba4ln2Vbp9gYrKquut5Zg==
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICzDCCAbQCAQAwgYYxCzAJBgNVBAYTAkVOMQ0wCwYDVQQIDARub25lMQ0wCwYD
VQQHDARub25lMRIwEAYDVQQKDAlXaWtpcGVkaWExDTALBgNVBAsMBG5vbmUxGDAW
BgNVBAMMDyoud2lraXBlZGlhLm9yZzEcMBoGCSqGSIb3DQEJARYNbm9uZUBub25l
LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMP/U8RlcCD6E8AL
PT8LLUR9ygyygPCaSmIEC8zXGJung3ykElXFRz/Jc/bu0hxCxi2YDz5IjxBBOpB/
kieG83HsSmZZtR+drZIQ6vOsr/ucvpnB9z4XzKuabNGZ5ZiTSQ9L7Mx8FzvUTq5y
57HhA7ECAwEAAaAAMA0GCSqGSIb3DQEBBAUAA4IBAQBn8OCVOIx+n0AS6WbEmYDR
SspR9xOCoOwYfamB+2Bpmt82R01zJ/kaqzUtZUjaGvQvAaz5lUwoMdaO0X7I5Xfl
sllMFDaYoGD4Rru4s8gz2qG/QHWA8uPXzJVAj6X0olbIdLTEqTKsnBj4Zr1AJCNy
/YcG4ouLJr140o26MhwBpoCRpPjAgdYMH60BYfnc4/DILxMVqR9xqK1s98d6Ob/+
3wHFK+S7BRWrJQXcM8veAexXuk9lHQ+FgGfD0eSYGz0kyP26Qa2pLTwumjt+nBPl
rfJxaLHwTQ/1988G0H35ED0f9Md5fzoKi5evU1wG5WRxdEUPyt3QUXxdQ69i0C+7
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICzDCCAbQCAQAwgYYxCzAJBgNVBAYTAkVOMQ0wCwYDVQQIDARub25lMQ0wCwYD
VQQHDARub25lMRIwEAYDVQQKDAlXaWtpcGVkaWExDTALBgNVBAsMBG5vbmUxGDAW
BgNVBAMMDyoud2lraXBlZGlhLm9yZzEcMBoGCSqGSIb3DQEJARYNbm9uZUBub25l
LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMP/U8RlcCD6E8AL
PT8LLUR9ygyygPCaSmIEC8zXGJung3ykElXFRz/Jc/bu0hxCxi2YDz5IjxBBOpB/
kieG83HsSmZZtR+drZIQ6vOsr/ucvpnB9z4XzKuabNGZ5ZiTSQ9L7Mx8FzvUTq5y
/ArIuM+FBeuno/IV8zvwAe/VRa8i0QjFXT9vBBp35aeatdnJ2ds50yKCsHHcjvtr
9/8zPVqqmhl2XFS3Qdqlsprzbgksom67OobJGjaV+fNHNQ0o/rzP//Pl3i7vvaEG
7Ff8tQhEwR9nJUR1T6Z7ln7S6cOr23YozgWVkEJ/dSr6LAopb+cZ88FzW5NszU6i
57HhA7ECAwEAAaAAMA0GCSqGSIb3DQEBBAUAA4IBAQBn8OCVOIx+n0AS6WbEmYDR
SspR9xOCoOwYfamB+2Bpmt82R01zJ/kaqzUtZUjaGvQvAaz5lUwoMdaO0X7I5Xfl
sllMFDaYoGD4Rru4s8gz2qG/QHWA8uPXzJVAj6X0olbIdLTEqTKsnBj4Zr1AJCNy
/YcG4ouLJr140o26MhwBpoCRpPjAgdYMH60BYfnc4/DILxMVqR9xqK1s98d6Ob/+
3wHFK+S7BRWrJQXcM8veAexXuk9lHQ+FgGfD0eSYGz0kyP26Qa2pLTwumjt+nBPl
rfJxaLHwTQ/1988G0H35ED0f9Md5fzoKi5evU1wG5WRxdEUPyt3QUXxdQ69i0C+7
-----END CERTIFICATE REQUEST-----

15
vendor/github.com/cloudflare/cfssl/info/info.go generated vendored Normal file
View File

@ -0,0 +1,15 @@
// Package info contains the definitions for the info endpoint
package info
// Req is the request struct for an info API request.
type Req struct {
Label string `json:"label"`
Profile string `json:"profile"`
}
// Resp is the response for an Info API request.
type Resp struct {
Certificate string `json:"certificate"`
Usage []string `json:"usages"`
ExpiryString string `json:"expiry"`
}

249
vendor/github.com/cloudflare/cfssl/initca/initca.go generated vendored Normal file
View File

@ -0,0 +1,249 @@
// Package initca contains code to initialise a certificate authority,
// generating a new root key and certificate.
package initca
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"time"
"github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/csr"
cferr "github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/log"
"github.com/cloudflare/cfssl/signer"
"github.com/cloudflare/cfssl/signer/local"
)
// validator contains the default validation logic for certificate
// authority certificates. The only requirement here is that the
// certificate have a non-empty subject field.
func validator(req *csr.CertificateRequest) error {
if req.CN != "" {
return nil
}
if len(req.Names) == 0 {
return cferr.Wrap(cferr.PolicyError, cferr.InvalidRequest, errors.New("missing subject information"))
}
for i := range req.Names {
if csr.IsNameEmpty(req.Names[i]) {
return cferr.Wrap(cferr.PolicyError, cferr.InvalidRequest, errors.New("missing subject information"))
}
}
return nil
}
// New creates a new root certificate from the certificate request.
func New(req *csr.CertificateRequest) (cert, csrPEM, key []byte, err error) {
policy := CAPolicy()
if req.CA != nil {
if req.CA.Expiry != "" {
policy.Default.ExpiryString = req.CA.Expiry
policy.Default.Expiry, err = time.ParseDuration(req.CA.Expiry)
if err != nil {
return
}
}
if req.CA.Backdate != "" {
policy.Default.Backdate, err = time.ParseDuration(req.CA.Backdate)
if err != nil {
return
}
}
policy.Default.CAConstraint.MaxPathLen = req.CA.PathLength
if req.CA.PathLength != 0 && req.CA.PathLenZero {
log.Infof("ignore invalid 'pathlenzero' value")
} else {
policy.Default.CAConstraint.MaxPathLenZero = req.CA.PathLenZero
}
}
g := &csr.Generator{Validator: validator}
csrPEM, key, err = g.ProcessRequest(req)
if err != nil {
log.Errorf("failed to process request: %v", err)
key = nil
return
}
priv, err := helpers.ParsePrivateKeyPEM(key)
if err != nil {
log.Errorf("failed to parse private key: %v", err)
return
}
s, err := local.NewSigner(priv, nil, signer.DefaultSigAlgo(priv), policy)
if err != nil {
log.Errorf("failed to create signer: %v", err)
return
}
signReq := signer.SignRequest{Hosts: req.Hosts, Request: string(csrPEM)}
cert, err = s.Sign(signReq)
return
}
// NewFromPEM creates a new root certificate from the key file passed in.
func NewFromPEM(req *csr.CertificateRequest, keyFile string) (cert, csrPEM []byte, err error) {
privData, err := helpers.ReadBytes(keyFile)
if err != nil {
return nil, nil, err
}
priv, err := helpers.ParsePrivateKeyPEM(privData)
if err != nil {
return nil, nil, err
}
return NewFromSigner(req, priv)
}
// RenewFromPEM re-creates a root certificate from the CA cert and key
// files. The resulting root certificate will have the input CA certificate
// as the template and have the same expiry length. E.g. the existing CA
// is valid for a year from Jan 01 2015 to Jan 01 2016, the renewed certificate
// will be valid from now and expire in one year as well.
func RenewFromPEM(caFile, keyFile string) ([]byte, error) {
caBytes, err := helpers.ReadBytes(caFile)
if err != nil {
return nil, err
}
ca, err := helpers.ParseCertificatePEM(caBytes)
if err != nil {
return nil, err
}
keyBytes, err := helpers.ReadBytes(keyFile)
if err != nil {
return nil, err
}
key, err := helpers.ParsePrivateKeyPEM(keyBytes)
if err != nil {
return nil, err
}
return RenewFromSigner(ca, key)
}
// NewFromSigner creates a new root certificate from a crypto.Signer.
func NewFromSigner(req *csr.CertificateRequest, priv crypto.Signer) (cert, csrPEM []byte, err error) {
policy := CAPolicy()
if req.CA != nil {
if req.CA.Expiry != "" {
policy.Default.ExpiryString = req.CA.Expiry
policy.Default.Expiry, err = time.ParseDuration(req.CA.Expiry)
if err != nil {
return nil, nil, err
}
}
policy.Default.CAConstraint.MaxPathLen = req.CA.PathLength
if req.CA.PathLength != 0 && req.CA.PathLenZero == true {
log.Infof("ignore invalid 'pathlenzero' value")
} else {
policy.Default.CAConstraint.MaxPathLenZero = req.CA.PathLenZero
}
}
csrPEM, err = csr.Generate(priv, req)
if err != nil {
return nil, nil, err
}
s, err := local.NewSigner(priv, nil, signer.DefaultSigAlgo(priv), policy)
if err != nil {
log.Errorf("failed to create signer: %v", err)
return
}
signReq := signer.SignRequest{Request: string(csrPEM)}
cert, err = s.Sign(signReq)
return
}
// RenewFromSigner re-creates a root certificate from the CA cert and crypto.Signer.
// The resulting root certificate will have ca certificate
// as the template and have the same expiry length. E.g. the existing CA
// is valid for a year from Jan 01 2015 to Jan 01 2016, the renewed certificate
// will be valid from now and expire in one year as well.
func RenewFromSigner(ca *x509.Certificate, priv crypto.Signer) ([]byte, error) {
if !ca.IsCA {
return nil, errors.New("input certificate is not a CA cert")
}
// matching certificate public key vs private key
switch {
case ca.PublicKeyAlgorithm == x509.RSA:
var rsaPublicKey *rsa.PublicKey
var ok bool
if rsaPublicKey, ok = priv.Public().(*rsa.PublicKey); !ok {
return nil, cferr.New(cferr.PrivateKeyError, cferr.KeyMismatch)
}
if ca.PublicKey.(*rsa.PublicKey).N.Cmp(rsaPublicKey.N) != 0 {
return nil, cferr.New(cferr.PrivateKeyError, cferr.KeyMismatch)
}
case ca.PublicKeyAlgorithm == x509.ECDSA:
var ecdsaPublicKey *ecdsa.PublicKey
var ok bool
if ecdsaPublicKey, ok = priv.Public().(*ecdsa.PublicKey); !ok {
return nil, cferr.New(cferr.PrivateKeyError, cferr.KeyMismatch)
}
if ca.PublicKey.(*ecdsa.PublicKey).X.Cmp(ecdsaPublicKey.X) != 0 {
return nil, cferr.New(cferr.PrivateKeyError, cferr.KeyMismatch)
}
default:
return nil, cferr.New(cferr.PrivateKeyError, cferr.NotRSAOrECC)
}
req := csr.ExtractCertificateRequest(ca)
cert, _, err := NewFromSigner(req, priv)
return cert, err
}
// CAPolicy contains the CA issuing policy as default policy.
var CAPolicy = func() *config.Signing {
return &config.Signing{
Default: &config.SigningProfile{
Usage: []string{"cert sign", "crl sign"},
ExpiryString: "43800h",
Expiry: 5 * helpers.OneYear,
CAConstraint: config.CAConstraint{IsCA: true},
},
}
}
// Update copies the CA certificate, updates the NotBefore and
// NotAfter fields, and then re-signs the certificate.
func Update(ca *x509.Certificate, priv crypto.Signer) (cert []byte, err error) {
copy, err := x509.ParseCertificate(ca.Raw)
if err != nil {
return
}
validity := ca.NotAfter.Sub(ca.NotBefore)
copy.NotBefore = time.Now().Round(time.Minute).Add(-5 * time.Minute)
copy.NotAfter = copy.NotBefore.Add(validity)
cert, err = x509.CreateCertificate(rand.Reader, copy, copy, priv.Public(), priv)
if err != nil {
return
}
cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert})
return
}

View File

@ -0,0 +1,385 @@
package initca
import (
"bytes"
"crypto/ecdsa"
"crypto/rsa"
"io/ioutil"
"strings"
"testing"
"time"
"github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/csr"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/signer"
"github.com/cloudflare/cfssl/signer/local"
)
var validKeyParams = []csr.BasicKeyRequest{
{A: "rsa", S: 2048},
{A: "rsa", S: 3072},
{A: "rsa", S: 4096},
{A: "ecdsa", S: 256},
{A: "ecdsa", S: 384},
{A: "ecdsa", S: 521},
}
var validCAConfigs = []csr.CAConfig{
{PathLength: 0, PathLenZero: true},
{PathLength: 0, PathLenZero: false},
{PathLength: 2},
{PathLength: 2, Expiry: "1h"},
// invalid PathLenZero value will be ignored
{PathLength: 2, PathLenZero: true},
}
var invalidCAConfig = csr.CAConfig{
PathLength: 2,
// Expiry must be a duration string
Expiry: "2116/12/31",
}
var csrFiles = []string{
"testdata/rsa2048.csr",
"testdata/rsa3072.csr",
"testdata/rsa4096.csr",
"testdata/ecdsa256.csr",
"testdata/ecdsa384.csr",
"testdata/ecdsa521.csr",
}
var testRSACAFile = "testdata/5min-rsa.pem"
var testRSACAKeyFile = "testdata/5min-rsa-key.pem"
var testECDSACAFile = "testdata/5min-ecdsa.pem"
var testECDSACAKeyFile = "testdata/5min-ecdsa-key.pem"
var invalidCryptoParams = []csr.BasicKeyRequest{
// Weak Key
{A: "rsa", S: 1024},
// Bad param
{A: "rsaCrypto", S: 2048},
{A: "ecdsa", S: 2000},
}
func TestInitCA(t *testing.T) {
var req *csr.CertificateRequest
hostname := "cloudflare.com"
for _, param := range validKeyParams {
for _, caconfig := range validCAConfigs {
req = &csr.CertificateRequest{
Names: []csr.Name{
{
C: "US",
ST: "California",
L: "San Francisco",
O: "CloudFlare",
OU: "Systems Engineering",
},
},
CN: hostname,
Hosts: []string{hostname, "www." + hostname},
KeyRequest: &param,
CA: &caconfig,
}
certBytes, _, keyBytes, err := New(req)
if err != nil {
t.Fatal("InitCA failed:", err)
}
key, err := helpers.ParsePrivateKeyPEM(keyBytes)
if err != nil {
t.Fatal("InitCA private key parsing failed:", err)
}
cert, err := helpers.ParseCertificatePEM(certBytes)
if err != nil {
t.Fatal("InitCA cert parsing failed:", err)
}
// Verify key parameters.
switch req.KeyRequest.Algo() {
case "rsa":
if cert.PublicKey.(*rsa.PublicKey).N.BitLen() != param.Size() {
t.Fatal("Cert key length mismatch.")
}
if key.(*rsa.PrivateKey).N.BitLen() != param.Size() {
t.Fatal("Private key length mismatch.")
}
case "ecdsa":
if cert.PublicKey.(*ecdsa.PublicKey).Curve.Params().BitSize != param.Size() {
t.Fatal("Cert key length mismatch.")
}
if key.(*ecdsa.PrivateKey).Curve.Params().BitSize != param.Size() {
t.Fatal("Private key length mismatch.")
}
}
// Verify CA MaxPathLen
if caconfig.PathLength == 0 && cert.MaxPathLenZero != caconfig.PathLenZero {
t.Fatalf("fail to init a CA cert with specified CA pathlen zero: expect %v, got %v", caconfig.PathLenZero, cert.MaxPathLenZero)
}
if caconfig.PathLength != 0 {
if cert.MaxPathLen != caconfig.PathLength {
t.Fatalf("fail to init a CA cert with specified CA pathlen: expect %d, got %d", caconfig.PathLength, cert.MaxPathLen)
}
if cert.MaxPathLenZero != false {
t.Fatalf("fail to init a CA cert with specified CA pathlen zero: expect false, got %t", cert.MaxPathLenZero)
}
}
// Replace the default CAPolicy with a test (short expiry) version.
CAPolicy = func() *config.Signing {
return &config.Signing{
Default: &config.SigningProfile{
Usage: []string{"cert sign", "crl sign"},
ExpiryString: "300s",
Expiry: 300 * time.Second,
CAConstraint: config.CAConstraint{IsCA: true},
},
}
}
// Start a signer
s, err := local.NewSigner(key, cert, signer.DefaultSigAlgo(key), nil)
if err != nil {
t.Fatal("Signer Creation error:", err)
}
s.SetPolicy(CAPolicy())
// Sign RSA and ECDSA customer CSRs.
for _, csrFile := range csrFiles {
csrBytes, err := ioutil.ReadFile(csrFile)
if err != nil {
t.Fatal("CSR loading error:", err)
}
req := signer.SignRequest{
Request: string(csrBytes),
Hosts: signer.SplitHosts(hostname),
Profile: "",
Label: "",
}
bytes, err := s.Sign(req)
if err != nil {
t.Fatal(err)
}
customerCert, _ := helpers.ParseCertificatePEM(bytes)
if customerCert.SignatureAlgorithm != s.SigAlgo() {
t.Fatal("Signature Algorithm mismatch")
}
err = customerCert.CheckSignatureFrom(cert)
if err != nil {
t.Fatal("Signing CSR failed.", err)
}
}
}
}
}
func TestInvalidCAConfig(t *testing.T) {
hostname := "example.com"
req := &csr.CertificateRequest{
Names: []csr.Name{
{
C: "US",
ST: "California",
L: "San Francisco",
O: "CloudFlare",
OU: "Systems Engineering",
},
},
CN: hostname,
Hosts: []string{hostname, "www." + hostname},
KeyRequest: &validKeyParams[0],
CA: &invalidCAConfig,
}
_, _, _, err := New(req)
if err == nil {
t.Fatal("InitCA with bad CAConfig should fail:", err)
}
}
func TestInvalidCryptoParams(t *testing.T) {
var req *csr.CertificateRequest
hostname := "cloudflare.com"
for _, invalidParam := range invalidCryptoParams {
req = &csr.CertificateRequest{
Names: []csr.Name{
{
C: "US",
ST: "California",
L: "San Francisco",
O: "CloudFlare",
OU: "Systems Engineering",
},
},
CN: hostname,
Hosts: []string{hostname, "www." + hostname},
KeyRequest: &invalidParam,
}
_, _, _, err := New(req)
if err == nil {
t.Fatal("InitCA with bad params should fail:", err)
}
if !strings.Contains(err.Error(), `"code":2400`) {
t.Fatal(err)
}
}
}
type validation struct {
r *csr.CertificateRequest
v bool
}
var testValidations = []validation{
{&csr.CertificateRequest{}, false},
{&csr.CertificateRequest{
CN: "test CA",
}, true},
{&csr.CertificateRequest{
Names: []csr.Name{{}},
}, false},
{&csr.CertificateRequest{
Names: []csr.Name{
{O: "Example CA"},
},
}, true},
}
func TestValidations(t *testing.T) {
for i, tv := range testValidations {
err := validator(tv.r)
if tv.v && err != nil {
t.Fatalf("%v", err)
}
if !tv.v && err == nil {
t.Fatalf("%d: expected error, but no error was reported", i)
}
}
}
func TestRenewRSA(t *testing.T) {
certPEM, err := RenewFromPEM(testRSACAFile, testRSACAKeyFile)
if err != nil {
t.Fatal(err)
}
// must parse ok
cert, err := helpers.ParseCertificatePEM(certPEM)
if err != nil {
t.Fatal(err)
}
if !cert.IsCA {
t.Fatal("renewed CA certificate is not CA")
}
// cert expiry must be 5 minutes
expiry := cert.NotAfter.Sub(cert.NotBefore).Seconds()
if expiry >= 301 || expiry <= 299 {
t.Fatal("expiry is not correct:", expiry)
}
// check subject
if cert.Subject.CommonName != "" {
t.Fatal("Bad CommonName")
}
if len(cert.Subject.Country) != 1 || cert.Subject.Country[0] != "US" {
t.Fatal("Bad Subject")
}
if len(cert.Subject.Organization) != 1 || cert.Subject.Organization[0] != "CloudFlare, Inc." {
t.Fatal("Bad Subject")
}
}
func TestRenewECDSA(t *testing.T) {
certPEM, err := RenewFromPEM(testECDSACAFile, testECDSACAKeyFile)
if err != nil {
t.Fatal(err)
}
// must parse ok
cert, err := helpers.ParseCertificatePEM(certPEM)
if err != nil {
t.Fatal(err)
}
if !cert.IsCA {
t.Fatal("renewed CA certificate is not CA")
}
// cert expiry must be 5 minutes
expiry := cert.NotAfter.Sub(cert.NotBefore).Seconds()
if expiry >= 301 || expiry <= 299 {
t.Fatal("expiry is not correct:", expiry)
}
// check subject
if cert.Subject.CommonName != "" {
t.Fatal("Bad CommonName")
}
if len(cert.Subject.Country) != 1 || cert.Subject.Country[0] != "US" {
t.Fatal("Bad Subject")
}
if len(cert.Subject.Organization) != 1 || cert.Subject.Organization[0] != "CloudFlare, Inc." {
t.Fatal("Bad Subject")
}
}
func TestRenewMismatch(t *testing.T) {
_, err := RenewFromPEM(testECDSACAFile, testRSACAKeyFile)
if err == nil {
t.Fatal("Fail to detect cert/key mismatch")
}
}
func TestRenew(t *testing.T) {
in, err := ioutil.ReadFile(testECDSACAFile)
if err != nil {
t.Fatal(err)
}
cert, err := helpers.ParseCertificatePEM(in)
if err != nil {
t.Fatal(err)
}
in, err = ioutil.ReadFile(testECDSACAKeyFile)
if err != nil {
t.Fatal(err)
}
priv, err := helpers.ParsePrivateKeyPEM(in)
if err != nil {
t.Fatal(err)
}
renewed, err := Update(cert, priv)
if err != nil {
t.Fatal(err)
}
newCert, err := helpers.ParseCertificatePEM(renewed)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(newCert.RawSubjectPublicKeyInfo, cert.RawSubjectPublicKeyInfo) {
t.Fatal("Update returned a certificate with different subject public key info")
}
if !bytes.Equal(newCert.RawSubject, cert.RawSubject) {
t.Fatal("Update returned a certificate with different subject info")
}
if !bytes.Equal(newCert.RawIssuer, cert.RawIssuer) {
t.Fatal("Update returned a certificate with different issuer info")
}
}

View File

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIA8OzPeVZT0cXTAPdcXYefLRIqyUXa0f0SgYMJ2J1AVcoAoGCCqGSM49
AwEHoUQDQgAEoCV+bVOLTJMy38j50sc3vE5k41GMRgriFJt0g0OVX8yaOZ93CZTI
7LzfGbMU+KqWTgOwGhrPvpusep3fjw+dAQ==
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICUDCCAfagAwIBAgIIec5PjdpJcNYwCgYIKoZIzj0EAwIwejELMAkGA1UEBhMC
VVMxGTAXBgNVBAoTEENsb3VkRmxhcmUsIEluYy4xIzAhBgNVBAsTGlRlc3QgQ2Vy
dGlmaWNhdGUgQXV0aG9yaXR5MRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYD
VQQIEwpDYWxpZm9ybmlhMB4XDTE1MTAwODIzMDEwMFoXDTE1MTAwODIzMDYwMFow
ejELMAkGA1UEBhMCVVMxGTAXBgNVBAoTEENsb3VkRmxhcmUsIEluYy4xIzAhBgNV
BAsTGlRlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMFkwEwYHKoZIzj0CAQYIKoZIzj0D
AQcDQgAEoCV+bVOLTJMy38j50sc3vE5k41GMRgriFJt0g0OVX8yaOZ93CZTI7Lzf
GbMU+KqWTgOwGhrPvpusep3fjw+dAaNmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1Ud
EwEB/wQIMAYBAf8CAQIwHQYDVR0OBBYEFDpLhSKBN3njfb6cXQCdRLzCZt0ZMB8G
A1UdIwQYMBaAFDpLhSKBN3njfb6cXQCdRLzCZt0ZMAoGCCqGSM49BAMCA0gAMEUC
IFU3BmzntGGeXZu2qWZx249nYn37S0AkCnQ3rUtI31bdAiEAsPICnZ+GB8yCN26N
OL+N8dHvXiOvZ9/Vl488pyWOccY=
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAtrYWs9ao2CpLWWLMyJJr3Bw7eJu3vSImzoqsBuhAREMaeuHm
vAwqbByVpdxu1o+t0u6cMp/1M4YwDSxD4Ny3zEUUGse6yZpyph0+whdHSn1LOCxY
KVwMtcYaEswenm0a+s/b9BYpbLv6lPoJ8+6bQNDuyyracDzlvGgk/HabemqDly4+
W64tlrMUDHBuHHIm5EMF1sqVcinLCS8KsVDVfg4qKfzsZbTw0dDo5GZh1lPslkk9
y8NzRltZjfJ3y5acv7SvIlETpy41VxScplR+Ot/6sXNJY3aEBT10smPXPABDeWjx
FbnU5xacL/pC7pnKy734sL4lkvzKPDWZPsNMEwIDAQABAoIBAHIFHBHKib+sVS1I
7MbWKR1JOQvBEV6kK1eFTmlZEpIG1kWNJ/J+HRMum2zQLRMUwsL5SNyG2fv3Z5Ew
6IMw+joteahkr/oTuixT39A7uq+PlRtPAQ1+digRoj/MxebT65xNjtO56MwEWxIR
H5jsdFJ0kDCVY4/bUPrMexhZ5Bj1xM3j8wpCPlVv2b9Ic/FUD9p6tOZDFhfSluiE
87VsFHUImNvu4p/BAKUuKiz58cPNDHPAABsPrJR2SVU59roC4QtEmaxbmDkXUtB1
+o+ypJQ0saqoffzHq7URebrJU9u+AV51UWaqHjg5OAe8eElOou6MHYX8R9cWZmJX
UQKPyVECgYEAyLqstNHtA7R7+r4bW8Tr/kF7z+VvCfV9wB6TPT+ycuv3aU5+HYgR
YRs2RBRtwI625hPk7AXEdbMt3SKoKjcMNMSD3qUK+fJFEyvOqRXiMJ2pLg04GlYZ
cOInJd0T1q3O2cNLZwcWB1L0/KiV0dYHc4p+p5hisai3T9w7QthTUr8CgYEA6QVW
jcsSBRFCokf/GKpTCVXIeqDSwrcEwoZh/RN6PlvgDwjw08G2IxKdAFs3/wxbKWHT
xss+LQiMyBL8aRJvBUfotj5e5ZYESaSDqdeYv0Sydl1vfxcknHpTBRUdbyDtsOQn
4X1ZEmfa9vFWS5P9fTFBC0BU2zzrhSlfQb6g360CgYBmnT+zBGo07aw/p7XWuRmn
lhRUWEbmgXAyqa69rfVs2IJXfD/umuO/j6izLvpYaNzJS7xIiD5BqUK1/ISZaCC+
TQPY6uhslFSJk2iHed9y2PZmy2010XQaCBLZQWZl5d6L5lGCrtWtEtSY4RoN9mtC
vrc2uCkkB0sG8V/+MRaPgwKBgBiML2oQkn1mLBbcbssyZjz9hHkmqA1LKn0zmu8G
NkKLezcaQgSMy5s2QsPe2C9OJexeGek/T/V+iRYqqdyHzJpJ0QIh3+1fuGPpqNUj
mTvNCN/fR/ejgH/bgxNt/gPO/Ds+TdU7Vz7RIggRtH2RwYqGvctpo4bVDBqjGR3b
7yahAoGAAgH97uN2FU1ffK0OAfMA1N58ikq/bg07KnJxO2CP5hrgsWK2ZVfeHUmU
3k+xqQHCIuew55yO0tARTrFAh3Rj+zarA+PrtnzqW82wCIn8Fym3PFzbK2qrIMie
yp0p4nBXsRmzinrPWKUYlFyRNY3Tcbstm5gUw2S4czSwwQeM/No=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIID3DCCAsSgAwIBAgIIfbm2I1hwBa8wDQYJKoZIhvcNAQELBQAwejELMAkGA1UE
BhMCVVMxGTAXBgNVBAoTEENsb3VkRmxhcmUsIEluYy4xIzAhBgNVBAsTGlRlc3Qg
Q2VydGlmaWNhdGUgQXV0aG9yaXR5MRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMw
EQYDVQQIEwpDYWxpZm9ybmlhMB4XDTE1MTAwODIwMjEwMFoXDTE1MTAwODIwMjYw
MFowejELMAkGA1UEBhMCVVMxGTAXBgNVBAoTEENsb3VkRmxhcmUsIEluYy4xIzAh
BgNVBAsTGlRlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRYwFAYDVQQHEw1TYW4g
RnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAtrYWs9ao2CpLWWLMyJJr3Bw7eJu3vSImzoqsBuhAREMa
euHmvAwqbByVpdxu1o+t0u6cMp/1M4YwDSxD4Ny3zEUUGse6yZpyph0+whdHSn1L
OCxYKVwMtcYaEswenm0a+s/b9BYpbLv6lPoJ8+6bQNDuyyracDzlvGgk/HabemqD
ly4+W64tlrMUDHBuHHIm5EMF1sqVcinLCS8KsVDVfg4qKfzsZbTw0dDo5GZh1lPs
lkk9y8NzRltZjfJ3y5acv7SvIlETpy41VxScplR+Ot/6sXNJY3aEBT10smPXPABD
eWjxFbnU5xacL/pC7pnKy734sL4lkvzKPDWZPsNMEwIDAQABo2YwZDAOBgNVHQ8B
Af8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBAjAdBgNVHQ4EFgQUCHoGEI1RZ8JN
7UZ4zcTRll8nnnAwHwYDVR0jBBgwFoAUCHoGEI1RZ8JN7UZ4zcTRll8nnnAwDQYJ
KoZIhvcNAQELBQADggEBAHRcbd6cSXV6IuT4jLV8k6OUUlxzobbiRnXJrLjy9Anx
tyIUWv2XSh/4IEJa+/MLNIb28gU9Sa2y4GV1qAgOM5qUM2iQJyLem0pTg0WTVKlj
ytEK1kUwQCNkc/xpDrPo5CbN3aDuW/VPntOJL1GSQzS7jzK3NeQ9sah9YYhk4Wsk
jzHVI1sX+qzcuUqCIPhqmGR0JE8ZI5YzbMTZ4/B+oWxZ7EyzB8O+v6HVD4eQFBSq
tyGhGbh7mUvuMpVJ8FIX4BA7QL+RwqNNtAMZKcxPjhy5I23nVclbTCz/NC2Dgp8H
13uQsEpUZ65clgiTo4LuPzPiIouZh5cBWP4gGqbyyS4=
-----END CERTIFICATE-----

View File

@ -0,0 +1,11 @@
1. To generate 5min-rsa.pem and 5min-rsa-key.pem
```
$ GOPATH/bin/cfssl gencert -initca ca_csr_rsa.json | GOPATH/bin/cfssljson -bare 5min-rsa
```
2. To generate 5min-ecdsa.pem and 5min-ecdsa-key.pem
```
$ GOPATH/bin/cfssl gencert -initca ca_csr_ecdsa.json | GOPATH/bin/cfssljson -bare 5min-ecdsa
```
The above commands will generate 5min-rsa.csr and 5min-ecdsa.csr as well, but those
files can be ignored.

View File

@ -0,0 +1,18 @@
{
"key": {
"algo": "ecdsa",
"size": 256
},
"names": [
{
"C": "US",
"L": "San Francisco",
"ST": "California",
"O": "CloudFlare, Inc.",
"OU": "Test Certificate Authority"
}
],
"ca": {
"expiry": "5m"
}
}

View File

@ -0,0 +1,18 @@
{
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "San Francisco",
"ST": "California",
"O": "CloudFlare, Inc.",
"OU": "Test Certificate Authority"
}
],
"ca": {
"expiry": "5m"
}
}

View 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-----

View 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-----

View 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-----

View 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-----

View 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-----

View 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-----

Some files were not shown because too many files have changed in this diff Show More