fix conflict

This commit is contained in:
Roland Shoemaker 2015-04-12 21:55:01 -07:00
commit ed4a147737
136 changed files with 31870 additions and 32 deletions

32
Godeps/Godeps.json generated
View File

@ -18,6 +18,18 @@
"ImportPath": "github.com/cloudflare/cfssl/auth",
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
},
{
"ImportPath": "github.com/cloudflare/cfssl/bundler",
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
},
{
"ImportPath": "github.com/cloudflare/cfssl/cli",
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
},
{
"ImportPath": "github.com/cloudflare/cfssl/cmd/cfssl",
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
},
{
"ImportPath": "github.com/cloudflare/cfssl/config",
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
@ -34,14 +46,30 @@
"ImportPath": "github.com/cloudflare/cfssl/helpers",
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
},
{
"ImportPath": "github.com/cloudflare/cfssl/initca",
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
},
{
"ImportPath": "github.com/cloudflare/cfssl/log",
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
},
{
"ImportPath": "github.com/cloudflare/cfssl/ocsp",
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
},
{
"ImportPath": "github.com/cloudflare/cfssl/selfsign",
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
},
{
"ImportPath": "github.com/cloudflare/cfssl/signer",
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
},
{
"ImportPath": "github.com/cloudflare/cfssl/ubiquity",
"Rev": "1415724f395ffd7aa29176066765cabc68193453"
},
{
"ImportPath": "github.com/codegangsta/cli",
"Comment": "1.2.0-64-ge1712f3",
@ -59,6 +87,10 @@
{
"ImportPath": "github.com/streadway/amqp",
"Rev": "150b7f24d6ad507e6026c13d85ce1f1391ac7400"
},
{
"ImportPath": "golang.org/x/crypto/ocsp",
"Rev": "c57d4a71915a248dbad846d60825145062b4c18e"
}
]
}

View File

@ -3,8 +3,8 @@ package bundle
import (
"net/http"
"github.com/cloudflare/cfssl/bundler"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/bundler"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
)

View File

@ -6,9 +6,9 @@ import (
"io/ioutil"
"net/http"
"github.com/cloudflare/cfssl/bundler"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api/client"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/bundler"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"

View File

@ -5,10 +5,10 @@ import (
"io/ioutil"
"net/http"
"github.com/cloudflare/cfssl/initca"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/csr"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/initca"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
)

View File

@ -0,0 +1,180 @@
package bundler
import (
"bytes"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"time"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
)
// A Bundle contains a certificate and its trust chain. It is intended
// to store the most widely applicable chain, with shortness an
// explicit goal.
type Bundle struct {
Chain []*x509.Certificate
Cert *x509.Certificate
Root *x509.Certificate
Key interface{}
Issuer *pkix.Name
Subject *pkix.Name
Expires *time.Time
Hostnames []string
Status *BundleStatus
}
// BundleStatus is designated for various status reporting.
type BundleStatus struct {
// A flag on whether a new bundle is generated
IsRebundled bool `json:"rebundled"`
// A list of SKIs of expiring certificates
ExpiringSKIs []string `json:"expiring_SKIs"`
// A list of untrusted root store names
Untrusted []string `json:"untrusted_root_stores"`
// A list of human readable warning messages based on the bundle status.
Messages []string `json:"messages"`
// A status code consists of binary flags
Code int `json:"code"`
}
type chain []*x509.Certificate
func (c chain) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
for _, cert := range c {
buf.Write(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}))
}
ret := bytes.TrimSpace(buf.Bytes())
return json.Marshal(string(ret))
}
// PemBlockToString turns a pem.Block into the string encoded form.
func PemBlockToString(block *pem.Block) string {
if block.Bytes == nil || block.Type == "" {
return ""
}
return string(bytes.TrimSpace(pem.EncodeToMemory(block)))
}
var typeToName = map[int]string{
3: "CommonName",
5: "SerialNumber",
6: "Country",
7: "Locality",
8: "Province",
9: "StreetAddress",
10: "Organization",
11: "OrganizationalUnit",
17: "PostalCode",
}
type names []pkix.AttributeTypeAndValue
func (n names) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
for _, name := range n {
buf.WriteString(fmt.Sprintf("/%s=%s", typeToName[name.Type[3]], name.Value))
}
return json.Marshal(buf.String())
}
// MarshalJSON serialises the bundle to JSON. The resulting JSON
// structure contains the bundle (as a sequence of PEM-encoded
// certificates), the certificate, the private key, the size of they
// key, the issuer(s), the subject name(s), the expiration, the
// hostname(s), the OCSP server, and the signature on the certificate.
func (b *Bundle) MarshalJSON() ([]byte, error) {
if b == nil || b.Cert == nil {
return nil, errors.New("no certificate in bundle")
}
var keyBytes, rootBytes []byte
var keyLength int
var typeString string
var keyType string
keyLength = helpers.KeyLength(b.Cert.PublicKey)
switch b.Cert.PublicKeyAlgorithm {
case x509.ECDSA:
keyType = fmt.Sprintf("%d-bit ECDSA", keyLength)
case x509.RSA:
keyType = fmt.Sprintf("%d-bit RSA", keyLength)
case x509.DSA:
keyType = "DSA"
default:
keyType = "Unknown"
}
if rsaKey, ok := b.Key.(*rsa.PrivateKey); ok {
keyBytes = x509.MarshalPKCS1PrivateKey(rsaKey)
typeString = "RSA PRIVATE KEY"
} else if ecdsaKey, ok := b.Key.(*ecdsa.PrivateKey); ok {
keyBytes, _ = x509.MarshalECPrivateKey(ecdsaKey)
typeString = "EC PRIVATE KEY"
}
if len(b.Hostnames) == 0 {
b.buildHostnames()
}
var ocspSupport = false
if b.Cert.OCSPServer != nil {
ocspSupport = true
}
var crlSupport = false
if b.Cert.CRLDistributionPoints != nil {
crlSupport = true
}
if b.Root != nil {
rootBytes = b.Root.Raw
}
return json.Marshal(map[string]interface{}{
"bundle": chain(b.Chain),
"root": PemBlockToString(&pem.Block{Type: "CERTIFICATE", Bytes: rootBytes}),
"crt": PemBlockToString(&pem.Block{Type: "CERTIFICATE", Bytes: b.Cert.Raw}),
"key": PemBlockToString(&pem.Block{Type: typeString, Bytes: keyBytes}),
"key_type": keyType,
"key_size": keyLength,
"issuer": names(b.Issuer.Names),
"subject": names(b.Subject.Names),
"expires": b.Expires,
"hostnames": b.Hostnames,
"ocsp_support": ocspSupport,
"crl_support": crlSupport,
"ocsp": b.Cert.OCSPServer,
"signature": helpers.SignatureString(b.Cert.SignatureAlgorithm),
"status": b.Status,
})
}
// buildHostnames sets bundle.Hostnames by the x509 cert's subject CN and DNS names
// Since the subject CN may overlap with one of the DNS names, it needs to handle
// the duplication by a set.
func (b *Bundle) buildHostnames() {
if b.Cert == nil {
return
}
// hset keeps a set of unique hostnames.
hset := make(map[string]bool)
// insert CN into hset
if b.Cert.Subject.CommonName != "" {
hset[b.Cert.Subject.CommonName] = true
}
// insert all DNS names into hset
for _, h := range b.Cert.DNSNames {
hset[h] = true
}
// convert hset to an array of hostnames
b.Hostnames = make([]string, len(hset))
i := 0
for h := range hset {
b.Hostnames[i] = h
i++
}
}

View File

@ -0,0 +1,393 @@
package bundler
// This test file contains tests on checking the correctness of BundleFromFile and Bundle.
// We simulate various scenarios for Bundle and funnel the tests through BundleFromFile.
import (
"encoding/json"
"testing"
)
// A helper structure that defines a BundleFromFile test case.
type fileTest struct {
// PEM cert file to be bundled
cert string
// PEM private key file to be bundled
key string
// Root CA bundle
caBundleFile string
// Trust intermediate bundle
intBundleFile string
// Additional PEM intermediate certificates to be added into the bundler
extraIntermediates string
// Bundler creation function
bundlerConstructor func(*testing.T) (b *Bundler)
// Error checking function
errorCallback func(*testing.T, error)
// Bundle checking function
bundleChecking func(*testing.T, *Bundle)
}
/* ========== BundleFromFile Test Setup =============
For each pair of crypto algorithm X and key size Y, a CA chain is constructed:
Test_root_CA -> inter-L1 -> inter-L2--> cfssl-leaf-ecdsa256
|-> cfssl-leaf-ecdsa384
|-> cfssl-leaf-ecdsa521
|-> cfssl-leaf-rsa2048
|-> cfssl-leaf-rsa3072
|-> cfssl-leaf-rsa4096
Test_root_CA is a RSA cert, inter-L1 is RSA 4096 cert, inter-L2 is ecdsa-384 cert.
In addition, we construct another mixed chain of RSA->ECDSA->RSA->ECDSA
Test_root_CA -> inter-L1-v2 -> inter-L2-v2 --> cfssl-leaf-ecdsa256-v2
|-> cfssl-leaf-ecdsa384-v2
|-> cfssl-leaf-ecdsa521-v2
The max path length is set to be 1 for non-root CAs.
Two inter-* certs are assembled in intermediates.crt
There is also an expired L1 cert, sharing the same CSR with inter-L1. Also the
root CA processes the inter-L2 CSR directly to generate inter-L2-direct cert.
* Test_root_CA--> inter-L1-expired
|-> inter-L2-direct
Using inter-L2-direct as additional intermediate cert should shorten the
bundle chain.
*/
const (
leafECDSA256 = "testdata/cfssl-leaf-ecdsa256.pem"
leafECDSA384 = "testdata/cfssl-leaf-ecdsa384.pem"
leafECDSA521 = "testdata/cfssl-leaf-ecdsa521.pem"
leafRSA2048 = "testdata/cfssl-leaf-rsa2048.pem"
leafRSA3072 = "testdata/cfssl-leaf-rsa3072.pem"
leafRSA4096 = "testdata/cfssl-leaf-rsa4096.pem"
leafKeyECDSA256 = "testdata/cfssl-leaf-ecdsa256.key"
leafKeyECDSA384 = "testdata/cfssl-leaf-ecdsa384.key"
leafKeyECDSA521 = "testdata/cfssl-leaf-ecdsa521.key"
leafKeyRSA2048 = "testdata/cfssl-leaf-rsa2048.key"
leafKeyRSA3072 = "testdata/cfssl-leaf-rsa3072.key"
leafKeyRSA4096 = "testdata/cfssl-leaf-rsa4096.key"
leafletRSA4096 = "testdata/cfssl-leaflet-rsa4096.pem"
interL1 = "testdata/inter-L1.pem"
interL1Expired = "testdata/inter-L1-expired.pem"
interL1CSR = "testdata/inter-L1.csr"
interL2 = "testdata/inter-L2.pem"
interL1v2 = "testdata/inter-L1-v2.pem"
interL2v2 = "testdata/inter-L2-v2.pem"
leafECDSA256v2 = "testdata/cfssl-leaf-ecdsa256-v2.pem"
leafECDSA384v2 = "testdata/cfssl-leaf-ecdsa384-v2.pem"
leafECDSA521v2 = "testdata/cfssl-leaf-ecdsa521-v2.pem"
interL2Direct = "testdata/inter-L2-direct.pem"
partialBundle = "testdata/partial-bundle.pem" // partialBundle is a partial cert chain {leaf-ecds256, inter-L2}
rpBundle = "testdata/reverse-partial-bundle.pem" // partialBundle is a partial cert chain in the reverse order {inter-L2, leaf-ecdsa256}
badBundle = "testdata/bad-bundle.pem" // badBundle is a non-verifying partial bundle {leaf-ecdsa256, leaf-ecdsa384}
interL2CSR = "testdata/inter-L2.csr"
certDSA2048 = "testdata/dsa2048.pem"
keyDSA2048 = "testdata/dsa2048.key"
)
// BundleFromFile test cases.
var fileTests = []fileTest{
// Input verification
{
cert: "not_such_cert.pem",
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: ExpectErrorMessage(`"code":1001`),
},
{
cert: emptyPEM,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: ExpectErrorMessage(`"code":1002`),
},
// Normal Keyless bundling for all supported public key types
{
cert: leafECDSA256,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
{
cert: leafECDSA384,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
{
cert: leafECDSA521,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
{
cert: leafRSA2048,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
{
cert: leafRSA3072,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
{
cert: leafRSA4096,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
{
cert: leafECDSA256v2,
caBundleFile: testCFSSLRootBundle,
intBundleFile: interL1v2,
extraIntermediates: interL2v2,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
{
cert: leafECDSA384v2,
caBundleFile: testCFSSLRootBundle,
intBundleFile: interL1v2,
extraIntermediates: interL2v2,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
{
cert: leafECDSA521v2,
caBundleFile: testCFSSLRootBundle,
intBundleFile: interL1v2,
extraIntermediates: interL2v2,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
// Normal bundling with private key for all supported key types
{
cert: leafECDSA256,
key: leafKeyECDSA256,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
{
cert: leafECDSA384,
key: leafKeyECDSA384,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
{
cert: leafECDSA521,
key: leafKeyECDSA521,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
{
cert: leafRSA2048,
key: leafKeyRSA2048,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
{
cert: leafRSA3072,
key: leafKeyRSA3072,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
{
cert: leafRSA4096,
key: leafKeyRSA4096,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
// Bundling with errors
// leaflet cert is signed by a leaf cert which is not included the intermediate bundle.
// So an UnknownAuthority error is expected.
{
cert: leafletRSA4096,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: ExpectErrorMessage(`"code":1220`),
},
// Expect TooManyIntermediates error because max path length is 1 for
// inter-L1 but the leaflet cert is 2 CA away from inter-L1.
{
cert: leafletRSA4096,
extraIntermediates: leafRSA4096,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: ExpectErrorMessage(`"code":1213`),
},
// Bundle with expired inter-L1 intermediate cert only, expect error 1211 VerifyFailed:Expired.
{
cert: interL2,
extraIntermediates: interL1Expired,
caBundleFile: testCFSSLRootBundle,
intBundleFile: emptyPEM,
errorCallback: ExpectErrorMessage(`"code":1211`),
},
// Bundle with private key mismatch
// RSA cert, ECC private key
{
cert: leafRSA4096,
key: leafKeyECDSA256,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: ExpectErrorMessages([]string{`"code":2300,`, `"message":"Private key does not match public key"`}),
},
// ECC cert, RSA private key
{
cert: leafECDSA256,
key: leafKeyRSA4096,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: ExpectErrorMessages([]string{`"code":2300,`, `"message":"Private key does not match public key"`}),
},
// RSA 2048 cert, RSA 4096 private key
{
cert: leafRSA2048,
key: leafKeyRSA4096,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: ExpectErrorMessages([]string{`"code":2300,`, `"message":"Private key does not match public key"`}),
},
// ECDSA 256 cert, ECDSA 384 private key
{
cert: leafECDSA256,
key: leafKeyECDSA384,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: ExpectErrorMessages([]string{`"code":2300,`, `"message":"Private key does not match public key"`}),
},
// DSA is NOT supported.
// Keyless bundling, expect private key error "NotRSAOrECC"
{
cert: certDSA2048,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: ExpectErrorMessages([]string{`"code":2200,`, `"message":"Private key algorithm is not RSA or ECC"`}),
},
// Bundling with DSA private key, expect error "Failed to parse private key"
{
cert: certDSA2048,
key: keyDSA2048,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: ExpectErrorMessages([]string{`"code":2003,`, `"message":"Failed to parse private key"`}),
},
// Bundle with partial chain less some intermediates, expected error 1220: UnknownAuthority
{
cert: badBundle,
caBundleFile: testCFSSLRootBundle,
intBundleFile: interL1,
errorCallback: ExpectErrorMessage(`"code":1220`),
},
// Bundle with misplaced key as cert
{
cert: leafKeyECDSA256,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: ExpectErrorMessages([]string{`"code":1003,`, `"message":"Failed to parse certificate"`}),
},
// Bundle with misplaced cert as key
{
cert: leafECDSA256,
key: leafECDSA256,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: ExpectErrorMessages([]string{`"code":2003,`, `"message":"Failed to parse private key"`}),
},
// Smart Bundling
// Bundling with a partial bundle should work the same as bundling the leaf.
{
cert: partialBundle,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
// Bundle with a partial bundle such that the intermediate provided in the
// partial bundle is verify by an intermediate. Yet itself is not in the intermediate
// pool. In such cases, the bundling should be able to store the new intermediate
// and return a correct bundle.
{
cert: partialBundle,
caBundleFile: testCFSSLRootBundle,
intBundleFile: interL1,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
// Bundle with a reverse-ordered partial bundle.
// Bundler should be able to detect it and return a correct bundle.
{
cert: rpBundle,
caBundleFile: testCFSSLRootBundle,
intBundleFile: interL1,
errorCallback: nil,
bundleChecking: ExpectBundleLength(3),
},
// Bundle with a L2 cert direct signed by root, expect a shorter chain of length 2.
{
cert: leafECDSA256,
extraIntermediates: interL2Direct,
caBundleFile: testCFSSLRootBundle,
intBundleFile: testCFSSLIntBundle,
errorCallback: nil,
bundleChecking: ExpectBundleLength(2),
},
}
// TestBundleFromFile goes through test cases defined in fileTests. See below for test cases definition and details.
func TestBundleFromFile(t *testing.T) {
for _, test := range fileTests {
b := newCustomizedBundlerFromFile(t, test.caBundleFile, test.intBundleFile, test.extraIntermediates)
bundle, err := b.BundleFromFile(test.cert, test.key, Optimal)
if test.errorCallback != nil {
test.errorCallback(t, err)
} else {
if err != nil {
t.Fatalf("expected no error. but an error occurred: %v", err)
}
if test.bundleChecking != nil {
test.bundleChecking(t, bundle)
}
}
if bundle != nil {
bundle.Cert = nil
if _, err = json.Marshal(bundle); err == nil {
t.Fatal("bundle should fail with no cert")
}
}
}
}

View File

@ -0,0 +1,284 @@
package bundler
// This test file contains tests on checking the correctness of BundleFromPEM
import (
"testing"
)
// A helper structure that defines a BundleFromPEM test case.
type pemTest struct {
// PEM cert to be bundled
cert []byte
// PEM private key to be bundled
key []byte
// PEM intermediate certificates to be considered when bundling
inters []byte
// Bundler creation function
bundlerConstructor func(*testing.T) (b *Bundler)
// Error checking function
errorCallback func(*testing.T, error)
// Bundle checking function
bundleChecking func(*testing.T, *Bundle)
}
// BundleFromPEM test cases.
var pemTests = []pemTest{
{
cert: GoDaddyIntermediateCert,
bundlerConstructor: newBundler,
errorCallback: nil,
bundleChecking: ExpectBundleLength(1),
},
{
cert: []byte(""),
bundlerConstructor: newBundler,
errorCallback: ExpectErrorMessage("\"code\":1002"),
},
{
cert: corruptCert,
bundlerConstructor: newBundler,
errorCallback: ExpectErrorMessage("\"code\":1002"),
},
{
cert: garbageCert,
bundlerConstructor: newBundler,
errorCallback: ExpectErrorMessage("\"code\":1003"),
},
{
cert: selfSignedCert,
bundlerConstructor: newBundler,
errorCallback: ExpectErrorMessage("\"code\":1100"),
},
// 121X errors are X509.CertificateInvalidError. This test
// covers the code path leads to all 121X errors.
{
cert: expiredCert,
bundlerConstructor: newBundler,
errorCallback: ExpectErrorMessage("\"code\":1211"),
},
// With a empty root cert pool, the valid root cert
// is seen as issued by an unknown authority.
{
cert: GoDaddyIntermediateCert,
bundlerConstructor: newBundlerWithoutRoots,
errorCallback: ExpectErrorMessage("\"code\":1220"),
},
}
// TestBundleFromPEM goes through the test cases defined in pemTests and run them through. See below for test case definitions.
func TestBundleFromPEM(t *testing.T) {
for _, test := range pemTests {
b := test.bundlerConstructor(t)
bundle, err := b.BundleFromPEM(test.cert, test.key, Optimal)
if test.errorCallback != nil {
test.errorCallback(t, err)
} else {
if err != nil {
t.Errorf("expected no error. but an error occurred: %s", err.Error())
}
if test.bundleChecking != nil {
test.bundleChecking(t, bundle)
}
}
}
}
// GoDaddy intermeidate cert valid until year 2034
var GoDaddyRootCert = []byte(`-----BEGIN CERTIFICATE-----
MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
ReYNnyicsbkqWletNw+vHX/bvZ8=
-----END CERTIFICATE-----`)
// GoDaddy intermeidate cert valid until year 2026
var GoDaddyIntermediateCert = []byte(`-----BEGIN CERTIFICATE-----
MIIE3jCCA8agAwIBAgICAwEwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMCVVMx
ITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMTYw
MTU0MzdaFw0yNjExMTYwMTU0MzdaMIHKMQswCQYDVQQGEwJVUzEQMA4GA1UECBMH
QXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5j
b20sIEluYy4xMzAxBgNVBAsTKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5j
b20vcmVwb3NpdG9yeTEwMC4GA1UEAxMnR28gRGFkZHkgU2VjdXJlIENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5MREwDwYDVQQFEwgwNzk2OTI4NzCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAMQt1RWMnCZM7DI161+4WQFapmGBWTtwY6vj3D3H
KrjJM9N55DrtPDAjhI6zMBS2sofDPZVUBJ7fmd0LJR4h3mUpfjWoqVTr9vcyOdQm
VZWt7/v+WIbXnvQAjYwqDL1CBM6nPwT27oDyqu9SoWlm2r4arV3aLGbqGmu75RpR
SgAvSMeYddi5Kcju+GZtCpyz8/x4fKL4o/K1w/O5epHBp+YlLpyo7RJlbmr2EkRT
cDCVw5wrWCs9CHRK8r5RsL+H0EwnWGu1NcWdrxcx+AuP7q2BNgWJCJjPOq8lh8BJ
6qf9Z/dFjpfMFDniNoW1fho3/Rb2cRGadDAW/hOUoz+EDU8CAwEAAaOCATIwggEu
MB0GA1UdDgQWBBT9rGEyk2xF1uLuhV+auud2mWjM5zAfBgNVHSMEGDAWgBTSxLDS
kdRMEXGzYcs9of7dqGrU4zASBgNVHRMBAf8ECDAGAQH/AgEAMDMGCCsGAQUFBwEB
BCcwJTAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZ29kYWRkeS5jb20wRgYDVR0f
BD8wPTA7oDmgN4Y1aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBv
c2l0b3J5L2dkcm9vdC5jcmwwSwYDVR0gBEQwQjBABgRVHSAAMDgwNgYIKwYBBQUH
AgEWKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeTAO
BgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBANKGwOy9+aG2Z+5mC6IG
OgRQjhVyrEp0lVPLN8tESe8HkGsz2ZbwlFalEzAFPIUyIXvJxwqoJKSQ3kbTJSMU
A2fCENZvD117esyfxVgqwcSeIaha86ykRvOe5GPLL5CkKSkB2XIsKd83ASe8T+5o
0yGPwLPk9Qnt0hCqU7S+8MxZC9Y7lhyVJEnfzuz9p0iRFEUOOjZv2kWzRaJBydTX
RE4+uXR21aITVSzGh6O1mawGhId/dQb8vxRMDsxuxN89txJx9OjxUUAiKEngHUuH
qDTMBqLdElrRhjZkAzVvb3du6/KFUJheqwNTrZEjYx8WnM25sgVjOuH0aBsXBTWV
U+4=
-----END CERTIFICATE-----`)
// This is the same GoDaddy cert above except the last line is corrupted.
var corruptCert = []byte(`-----BEGIN CERTIFICATE-----
MIIE3jCCA8agAwIBAgICAwEwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMCVVMx
ITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMTYw
MTU0MzdaFw0yNjExMTYwMTU0MzdaMIHKMQswCQYDVQQGEwJVUzEQMA4GA1UECBMH
QXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5j
b20sIEluYy4xMzAxBgNVBAsTKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5j
b20vcmVwb3NpdG9yeTEwMC4GA1UEAxMnR28gRGFkZHkgU2VjdXJlIENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5MREwDwYDVQQFEwgwNzk2OTI4NzCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAMQt1RWMnCZM7DI161+4WQFapmGBWTtwY6vj3D3H
KrjJM9N55DrtPDAjhI6zMBS2sofDPZVUBJ7fmd0LJR4h3mUpfjWoqVTr9vcyOdQm
VZWt7/v+WIbXnvQAjYwqDL1CBM6nPwT27oDyqu9SoWlm2r4arV3aLGbqGmu75RpR
SgAvSMeYddi5Kcju+GZtCpyz8/x4fKL4o/K1w/O5epHBp+YlLpyo7RJlbmr2EkRT
cDCVw5wrWCs9CHRK8r5RsL+H0EwnWGu1NcWdrxcx+AuP7q2BNgWJCJjPOq8lh8BJ
6qf9Z/dFjpfMFDniNoW1fho3/Rb2cRGadDAW/hOUoz+EDU8CAwEAAaOCATIwggEu
MB0GA1UdDgQWBBT9rGEyk2xF1uLuhV+auud2mWjM5zAfBgNVHSMEGDAWgBTSxLDS
kdRMEXGzYcs9of7dqGrU4zASBgNVHRMBAf8ECDAGAQH/AgEAMDMGCCsGAQUFBwEB
BCcwJTAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZ29kYWRkeS5jb20wRgYDVR0f
BD8wPTA7oDmgN4Y1aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBv
c2l0b3J5L2dkcm9vdC5jcmwwSwYDVR0gBEQwQjBABgRVHSAAMDgwNgYIKwYBBQUH
AgEWKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeTAO
BgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBANKGwOy9+aG2Z+5mC6IG
OgRQjhVyrEp0lVPLN8tESe8HkGsz2ZbwlFalEzAFPIUyIXvJxwqoJKSQ3kbTJSMU
A2fCENZvD117esyfxVgqwcSeIaha86ykRvOe5GPLL5CkKSkB2XIsKd83ASe8T+5o
0yGPwLPk9Qnt0hCqU7S+8MxZC9Y7lhyVJEnfzuz9p0iRFEUOOjZv2kWzRaJBydTX
RE4+uXR21aITVSzGh6O1mawGhId/dQb8vxRMDsxuxN89txJx9OjxUUAiKEngHUuH
qDTMBqLdElrRhjZkAzVvb3du6/KFUJheqwNTrZEjYx8WnM25sgVjOuH0aBsXBTWV
CORRUPTED
-----END CERTIFICATE-----`)
// A garbage cert, which can be decoded into ill-formed cert
var garbageCert = []byte(`-----BEGIN CERTIFICATE-----
MIICATCCAWoCCQDidF+uNJR6czANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
cyBQdHkgTHRkMB4XDTEyMDUwMTIyNTUxN1oXDTEzMDUwMTIyNTUxN1owRTELMAkG
A1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0
nodhz31kLEJoeLSkRmrv8l7exkGtO0REtIbirj9BBy64ZXVBE7khKGO2cnM8U7yj
w7Ntfh+IvCjZVA3d2XqHS3Pjrt4HmU/cGCONE8+NEXoqdzLUDPOix1qDDRBvXs81
IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtpjl
KAV2qh6CYHZbdqixhDerjvJcD4Nsd7kExEZfHuECAwEAATANBgkqhkiG9w0BAQUF
AAOBgQCyOqs7+qpMrYCgL6OamDeCVojLoEp036PsnaYWf2NPmsVXdpYW40Foyyjp
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
iv5otkxO5rxtGPv7o2J1eMBpCuSkydvoz3Ey/QwGqbBwEXQ4xYCgra336gqW2KQt
+LnDCkE8f5oBhCIisExc2i8PDvsRsY70g/2gs983ImJjVR8sDw==
-----END CERTIFICATE-----`)
// A expired cert
var expiredCert = []byte(`-----BEGIN CERTIFICATE-----
MIIB7jCCAVmgAwIBAgIBADALBgkqhkiG9w0BAQUwJjEQMA4GA1UEChMHQWNtZSBD
bzESMBAGA1UEAxMJMTI3LjAuMC4xMB4XDTEyMDkwNzIyMDAwNFoXDTEzMDkwNzIy
MDUwNFowJjEQMA4GA1UEChMHQWNtZSBDbzESMBAGA1UEAxMJMTI3LjAuMC4xMIGd
MAsGCSqGSIb3DQEBAQOBjQAwgYkCgYEAm6f+jkP2t5q/vM0YAUZZkhq/EAYD+L1C
MS59jJOLomfDnKUWOGKi/k7URBg1HNL3vm7/ESDazZWFy9l/nibWxNkSUPkQIrvr
GsNivkRUzXkwgNX8IN8LOYAQ3BWxAqitXTpLjf4FeCTB6G59v9eYlAX3kicXRdY+
cqhEvLFbu3MCAwEAAaMyMDAwDgYDVR0PAQH/BAQDAgCgMA0GA1UdDgQGBAQBAgME
MA8GA1UdIwQIMAaABAECAwQwCwYJKoZIhvcNAQEFA4GBABndWRIcfi+QB9Sakr+m
dYnXTgYCnFio53L2Z+6EHTGG+rEhWtUEGhL4p4pzXX4siAnjWvwcgXTo92cafcfi
uB7wRfK+NL9CTJdpN6cdL+fiNHzH8hsl3bj1nL0CSmdn2hkUWVLbLhSgWlib/I8O
aq+K7aVrgHkPnWeRiG6tl+ZA
-----END CERTIFICATE-----`)
// A self-signed cert
var selfSignedCert = []byte(`-----BEGIN CERTIFICATE-----
MIIERTCCAy2gAwIBAgIJAORAsvx6MZO7MA0GCSqGSIb3DQEBBQUAMHQxCzAJBgNV
BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEXMBUG
A1UEChMOQ2xvdWRGbGFyZSBMTEMxETAPBgNVBAsTCFNlY3VyaXR5MRQwEgYDVQQD
Ewt0ZXN0c3NsLmxvbDAeFw0xNDA0MDQyMjM4MzhaFw0yNDA0MDEyMjM4MzhaMHQx
CzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNj
bzEXMBUGA1UEChMOQ2xvdWRGbGFyZSBMTEMxETAPBgNVBAsTCFNlY3VyaXR5MRQw
EgYDVQQDEwt0ZXN0c3NsLmxvbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAMKNLxpsnT37jSYkP9LVjw050Nmt1YMcEPwLe8zGUr8QzTWQ194Z9Ik/qYS0
UpQlx7+8UBoCanDTYuNKarHhmj4nZp+gc3mWWlaJKRnCJZ+Ru18x2lg9BzG4MwPQ
63ve0WxZ69/6J3lx53ertDgcD7S4v71BaeE10miBeJLK3JkV6fgGGfGRAGwU9vfm
OBbPTAw2SRdB1AaYTHaT4ANwUI7vvkIPrNuneTjOqlN9DAroUNIkXhV+fSmncRxi
RCAfP8/4BZdZ9C4TTKUpdAVUe1LUcHygK2f3YtOx8qJLCRMTRMYccSI1Y1idhX1s
SKIDDrOuELb+pGgno5PCe6i6MWcCAwEAAaOB2TCB1jAdBgNVHQ4EFgQUCkxuIVbR
+I8Z0A547Xj1R57ceXUwgaYGA1UdIwSBnjCBm4AUCkxuIVbR+I8Z0A547Xj1R57c
eXWheKR2MHQxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2Fu
IEZyYW5jaXNjbzEXMBUGA1UEChMOQ2xvdWRGbGFyZSBMTEMxETAPBgNVBAsTCFNl
Y3VyaXR5MRQwEgYDVQQDEwt0ZXN0c3NsLmxvbIIJAORAsvx6MZO7MAwGA1UdEwQF
MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAKFaOjVRXCNsOznpZe0478mIFK6mNwwi
ZrLcrEUZ0FIOcPwsnQXd/HmrR4MVj3z3U62mE6qFo+07yJnnXdKBJ9ThjmNu6c4S
dk2xPbKTuACF7UhMgPlac0tEp/KSJTaMcjl23H+ol80LZ/t1113XSAZYHWsAgTjC
905kp66Gcq7c+GBgrBqR4e6Z2GYCeAk5aMy5f5s90teW2bIZE0hG1mFz1e25l9lI
SkAp0gZusX4yxqoSBqKmKXBkjrW5vkKJZjP51c7fuhfuAyNfxZF4Cz9SS0YSG8eh
H5kVbpLP+eSYMqF110qqjAo4tkgBquF6IppA+HQ66DN64+TeiXb3f2Y=
-----END CERTIFICATE-----`)
// An expired bundle
var expiredBundlePEM = []byte(`-----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,194 @@
package bundler
// This test file contains tests on checking the correctness of BundleFromRemote
import (
"flag"
"testing"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ubiquity"
)
var shouldTestSNI bool
func init() {
flag.BoolVar(&shouldTestSNI, "test-sni", false, "run the SNI tests")
flag.Parse()
}
// remoteTest defines a test case for BundleFromRemote. Hostname and ip are the test inputs.
// bundlerConstructor points the bundler ctor and errorCallback handles the error checking.
type remoteTest struct {
hostname string
ip string
bundlerConstructor func(*testing.T) (b *Bundler)
errorCallback func(*testing.T, error)
bundleCallback func(*testing.T, *Bundle)
}
const (
ValidSSLSite = "google.com"
SelfSignedSSLSite = "cacert.org"
MismatchedHostnameSite = "www.capitol.state.tx.us"
ExpiredCertSite = "testssl-expire.disig.sk"
ECCCertSite = "benflare.us"
InvalidSite = "cloudflare1337.com"
ValidSNI = "alice.sni.velox.ch"
ValidSNIWildcard = "cloudflare.sni.velox.ch"
SNISANWildcard = "*.sni.velox.ch"
ValidSNIIP = "85.25.46.13"
InvalidIP = "300.300.300.300"
)
func getBundleHostnameChecker(hostname string) func(*testing.T, *Bundle) {
return func(t *testing.T, bundle *Bundle) {
if bundle == nil {
t.Fatalf("Nil bundle returned")
}
var found = false
for _, h := range bundle.Hostnames {
if h == hostname {
found = true
}
}
if !found {
t.Errorf("hostname expected but not found: %s", hostname)
}
}
}
// test cases of BundleFromRemote
var remoteTests = []remoteTest{
{
hostname: ValidSSLSite,
bundlerConstructor: newBundler,
errorCallback: nil,
},
{
hostname: SelfSignedSSLSite,
bundlerConstructor: newBundler,
errorCallback: ExpectErrorMessages([]string{`"code":1220`, "x509: certificate signed by unknown authority"}),
},
{
hostname: MismatchedHostnameSite,
bundlerConstructor: newBundler,
errorCallback: ExpectErrorMessages([]string{`"code":1200`, "x509: certificate is valid for"}),
},
{
hostname: ExpiredCertSite,
bundlerConstructor: newBundler,
errorCallback: ExpectErrorMessages([]string{`"code":1211`, "x509: certificate has expired or is not yet valid"}),
},
{
hostname: InvalidSite,
bundlerConstructor: newBundler,
errorCallback: ExpectErrorMessages([]string{`"code":6000`, "dial tcp: lookup cloudflare1337.com: no such host"}),
},
{
hostname: InvalidIP,
bundlerConstructor: newBundler,
errorCallback: ExpectErrorMessages([]string{`"code":6000`, "dial tcp: lookup 300.300.300.300: no such host"}),
},
{
ip: InvalidIP,
bundlerConstructor: newBundler,
errorCallback: ExpectErrorMessages([]string{`"code":6000`, "dial tcp: lookup 300.300.300.300: no such host"}),
},
}
// TestBundleFromRemote goes through the test cases defined in remoteTests and run them through. See above for test case definitions.
func TestBundleFromRemote(t *testing.T) {
for _, bf := range []BundleFlavor{Ubiquitous, Optimal} {
for _, test := range remoteTests {
b := test.bundlerConstructor(t)
bundle, err := b.BundleFromRemote(test.hostname, test.ip, bf)
if test.errorCallback != nil {
test.errorCallback(t, err)
} else {
if err != nil {
t.Errorf("expected no error. but an error occurred: %s", err.Error())
}
if test.bundleCallback != nil {
test.bundleCallback(t, bundle)
}
}
}
}
}
var remoteSNITests = []remoteTest{
{
hostname: ValidSNI,
bundlerConstructor: newBundler,
errorCallback: nil,
bundleCallback: getBundleHostnameChecker(ValidSNI),
},
{
hostname: ValidSNIWildcard,
bundlerConstructor: newBundler,
errorCallback: nil,
bundleCallback: getBundleHostnameChecker(SNISANWildcard),
},
{
hostname: ValidSNI,
ip: ValidSNIIP,
bundlerConstructor: newBundler,
errorCallback: nil,
bundleCallback: getBundleHostnameChecker(ValidSNI),
},
{
hostname: ValidSNIWildcard,
ip: ValidSNIIP,
bundlerConstructor: newBundler,
errorCallback: nil,
bundleCallback: getBundleHostnameChecker(SNISANWildcard),
},
}
// TestBundleFromRemoteSNI goes through the test cases defined in remoteSNITests and run them through. See above for test case definitions.
func TestBundleFromRemoteSNI(t *testing.T) {
if !shouldTestSNI {
t.Skip()
}
for _, bf := range []BundleFlavor{Ubiquitous, Optimal} {
for _, test := range remoteSNITests {
b := test.bundlerConstructor(t)
bundle, err := b.BundleFromRemote(test.hostname, test.ip, bf)
if test.errorCallback != nil {
test.errorCallback(t, err)
} else {
if err != nil {
t.Errorf("expected no error. but an error occurred: %s", err.Error())
}
if test.bundleCallback != nil {
test.bundleCallback(t, bundle)
}
}
}
}
}
func TestBundleFromRemoteFlavor(t *testing.T) {
b := newBundler(t)
ubiquity.Platforms = nil
ubiquity.LoadPlatforms(testMetadata)
bundle, err := b.BundleFromRemote(ECCCertSite, "", Ubiquitous)
if err != nil {
t.Errorf("expected no error. but an error occurred: %s", err.Error())
}
if len(bundle.Chain) != 3 {
t.Error("expected 3-cert bundle. Got ", len(bundle.Chain))
}
if len(bundle.Status.Untrusted) != 0 {
t.Error("expected no untrusted platforms. Got ", bundle.Status.Untrusted)
}
bundle, err = b.BundleFromRemote(ECCCertSite, "", Optimal)
if err != nil {
t.Errorf("expected no error. but an error occurred: %s", err.Error())
}
if len(bundle.Chain) != 2 {
t.Error("expected 2-cert bundle. Got ", len(bundle.Chain))
}
}

View File

@ -0,0 +1,803 @@
// Package bundler implements certificate bundling functionality for
// CF-SSL.
package bundler
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ubiquity"
)
// intermediateStash contains the path to the directory where
// downloaded intermediates should be saved.
var IntermediateStash = "intermediates"
// BundleFlavor is named optimization strategy on certificate chain selection when bundling.
type BundleFlavor string
const (
// Optimal means the shortest chain with newest intermediates and
// the most advanced crypto.
Optimal BundleFlavor = "optimal"
// Ubiquitous is aimed to provide the chain which is accepted
// by the most platforms.
Ubiquitous BundleFlavor = "ubiquitous"
// Force means the bundler only verfiies the input as a valid bundle, not optimization is done.
Force BundleFlavor = "force"
)
const (
sha2Warning = "The bundle contains certs signed with advanced hash functions such as SHA2, which are problematic at certain operating systems, e.g. Windows XP SP2."
ecdsaWarning = "The bundle contains ECDSA signatures, which are problematic at certain operating systems, e.g. Windows XP SP2, Android 2.2 and Android 2.3."
expiringWarningStub = "The bundle is expiring within 30 days. "
untrustedWarningStub = "The bundle may not be trusted by the following platform(s):"
ubiquityWarning = "The bundle trust ubiquity is not guaranteed: No platform metadata found."
deprecateSHA1WarningStub = "Due to SHA-1 deprecation, the bundle may not be trusted by the following platform(s):"
)
// A Bundler contains the certificate pools for producing certificate
// bundles. It contains any intermediates and root certificates that
// should be used.
type Bundler struct {
RootPool *x509.CertPool
IntermediatePool *x509.CertPool
KnownIssuers map[string]bool
}
// NewBundler creates a new Bundler from the files passed in; these
// files should contain a list of valid root certificates and a list
// of valid intermediate certificates, respectively.
func NewBundler(caBundleFile, intBundleFile string) (*Bundler, error) {
log.Debug("Loading CA bundle: ", caBundleFile)
caBundlePEM, err := ioutil.ReadFile(caBundleFile)
if err != nil {
log.Errorf("root bundle failed to load: %v", err)
return nil, errors.Wrap(errors.RootError, errors.ReadFailed, err)
}
log.Debug("Loading Intermediate bundle: ", intBundleFile)
intBundlePEM, err := ioutil.ReadFile(intBundleFile)
if err != nil {
log.Errorf("intermediate bundle failed to load: %v", err)
return nil, errors.Wrap(errors.IntermediatesError, errors.ReadFailed, err)
}
if _, err := os.Stat(IntermediateStash); err != nil && os.IsNotExist(err) {
log.Infof("intermediate stash directory %s doesn't exist, creating", IntermediateStash)
err = os.MkdirAll(IntermediateStash, 0755)
if err != nil {
log.Errorf("failed to create intermediate stash directory %s: %v",
IntermediateStash, err)
return nil, err
}
log.Infof("intermediate stash directory %s created", IntermediateStash)
}
return NewBundlerFromPEM(caBundlePEM, intBundlePEM)
}
// NewBundlerFromPEM creates a new Bundler from PEM-encoded root certificates and
// intermediate certificates.
func NewBundlerFromPEM(caBundlePEM, intBundlePEM []byte) (*Bundler, error) {
b := &Bundler{
RootPool: x509.NewCertPool(),
IntermediatePool: x509.NewCertPool(),
KnownIssuers: map[string]bool{},
}
log.Debug("parsing root certificates from PEM")
roots, err := helpers.ParseCertificatesPEM(caBundlePEM)
if err != nil {
log.Errorf("failed to parse root bundle: %v", err)
return nil, errors.New(errors.RootError, errors.ParseFailed)
}
log.Debug("parse intermediate certificates from PEM")
var intermediates []*x509.Certificate
if intermediates, err = helpers.ParseCertificatesPEM(intBundlePEM); err != nil {
log.Errorf("failed to parse intermediate bundle: %v", err)
return nil, errors.New(errors.IntermediatesError, errors.ParseFailed)
}
log.Debug("building certificate pools")
for _, c := range roots {
b.RootPool.AddCert(c)
b.KnownIssuers[string(c.Signature)] = true
}
for _, c := range intermediates {
b.IntermediatePool.AddCert(c)
b.KnownIssuers[string(c.Signature)] = true
}
log.Debug("bundler set up")
return b, nil
}
// VerifyOptions generates an x509 VerifyOptions structure that can be
// used for verifying certificates.
func (b *Bundler) VerifyOptions() x509.VerifyOptions {
return x509.VerifyOptions{
Roots: b.RootPool,
Intermediates: b.IntermediatePool,
KeyUsages: []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
x509.ExtKeyUsageMicrosoftServerGatedCrypto,
x509.ExtKeyUsageNetscapeServerGatedCrypto,
},
}
}
// BundleFromFile takes a set of files containing the PEM-encoded leaf certificate
// (optionally along with some intermediate certs), the PEM-encoded private key
// and returns the bundle built from that key and the certificate(s).
func (b *Bundler) BundleFromFile(bundleFile, keyFile string, flavor BundleFlavor) (*Bundle, error) {
log.Debug("Loading Certificate: ", bundleFile)
certsPEM, err := ioutil.ReadFile(bundleFile)
if err != nil {
return nil, errors.Wrap(errors.CertificateError, errors.ReadFailed, err)
}
var keyPEM []byte
// Load private key PEM only if a file is given
if keyFile != "" {
log.Debug("Loading private key: ", keyFile)
keyPEM, err = ioutil.ReadFile(keyFile)
if err != nil {
log.Debugf("failed to read private key: ", err)
return nil, errors.Wrap(errors.PrivateKeyError, errors.ReadFailed, err)
}
if len(keyPEM) == 0 {
log.Debug("key is empty")
return nil, errors.Wrap(errors.PrivateKeyError, errors.DecodeFailed, err)
}
}
return b.BundleFromPEM(certsPEM, keyPEM, flavor)
}
// BundleFromPEM builds a certificate bundle from the set of byte
// slices containing the PEM-encoded certificate(s), private key.
func (b *Bundler) BundleFromPEM(certsPEM, keyPEM []byte, flavor BundleFlavor) (*Bundle, error) {
log.Debug("bundling from PEM files")
var key crypto.Signer
var err error
if len(keyPEM) != 0 {
key, err = helpers.ParsePrivateKeyPEM(keyPEM)
if err != nil {
log.Debugf("failed to parse private key: %v", err)
return nil, err
}
}
certs, err := helpers.ParseCertificatesPEM(certsPEM)
if err != nil {
log.Debugf("failed to parse certificates: %v", err)
return nil, err
} else if len(certs) == 0 {
log.Debugf("no certificates found")
return nil, errors.New(errors.CertificateError, errors.DecodeFailed)
}
log.Debugf("bundle ready")
return b.Bundle(certs, key, flavor)
}
// BundleFromRemote fetches the certificate served by the server at
// serverName (or ip, if the ip argument is not the empty string). It
// is expected that the method will be able to make a connection at
// port 443. The certificate used by the server in this connection is
// used to build the bundle, which will necessarily be keyless.
func (b *Bundler) BundleFromRemote(serverName, ip string, flavor BundleFlavor) (*Bundle, error) {
config := &tls.Config{
RootCAs: b.RootPool,
ServerName: serverName,
}
// Dial by IP if present
var dialName string
if ip != "" {
dialName = ip + ":443"
} else {
dialName = serverName + ":443"
}
log.Debugf("bundling from remote %s", dialName)
dialer := &net.Dialer{Timeout: time.Duration(5) * time.Second}
conn, err := tls.DialWithDialer(dialer, "tcp", dialName, config)
var dialError string
// If there's an error in tls.Dial, try again with
// InsecureSkipVerify to fetch the remote bundle to (re-)bundle
// with. If the bundle is indeed not usable (expired, mismatched
// hostnames, etc.), report the error. Otherwise, create a
// working bundle and insert the tls error in the bundle.Status.
if err != nil {
log.Debugf("dial failed: %v", err)
// record the error msg
dialError = fmt.Sprintf("Failed rigid TLS handshake with %s: %v", dialName, err)
// dial again with InsecureSkipVerify
log.Debugf("try again with InsecureSkipVerify.")
config.InsecureSkipVerify = true
conn, err = tls.DialWithDialer(dialer, "tcp", dialName, config)
if err != nil {
log.Debugf("dial with InsecureSkipVerify failed: %v", err)
return nil, errors.Wrap(errors.DialError, errors.Unknown, err)
}
}
connState := conn.ConnectionState()
certs := connState.PeerCertificates
err = conn.VerifyHostname(serverName)
if err != nil {
log.Debugf("failed to verify hostname: %v", err)
return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
}
// Bundle with remote certs. Inject the initial dial error, if any, to the status reporting.
bundle, err := b.Bundle(certs, nil, flavor)
if err != nil {
return nil, err
} else if dialError != "" {
bundle.Status.Messages = append(bundle.Status.Messages, dialError)
}
return bundle, err
}
type fetchedIntermediate struct {
Cert *x509.Certificate
Name string
}
// fetchRemoteCertificate retrieves a single URL pointing to a certificate
// and attempts to first parse it as a DER-encoded certificate; if
// this fails, it attempts to decode it as a PEM-encoded certificate.
func fetchRemoteCertificate(certURL string) (fi *fetchedIntermediate, err error) {
log.Debugf("fetching remote certificate: %s", certURL)
var resp *http.Response
resp, err = http.Get(certURL)
if err != nil {
log.Debugf("failed HTTP get: %v", err)
return
}
defer resp.Body.Close()
var certData []byte
certData, err = ioutil.ReadAll(resp.Body)
if err != nil {
log.Debugf("failed to read response body: %v", err)
return
}
log.Debugf("attempting to parse certificate as DER")
crt, err := x509.ParseCertificate(certData)
if err != nil {
log.Debugf("attempting to parse certificate as PEM")
crt, err = helpers.ParseCertificatePEM(certData)
if err != nil {
log.Debugf("failed to parse certificate: %v", err)
return
}
}
log.Debugf("certificate fetch succeeds")
fi = &fetchedIntermediate{Cert: crt, Name: constructCertFileName(crt)}
return
}
func reverse(certs []*x509.Certificate) []*x509.Certificate {
n := len(certs)
if n == 0 {
return certs
}
rcerts := []*x509.Certificate{}
for i := n - 1; i >= 0; i-- {
rcerts = append(rcerts, certs[i])
}
return rcerts
}
// Check if the certs form a partial cert chain: every cert verifies
// the signature of the one in front of it.
func partialVerify(certs []*x509.Certificate) bool {
n := len(certs)
if n == 0 {
return false
}
for i := 0; i < n-1; i++ {
if certs[i].CheckSignatureFrom(certs[i+1]) != nil {
return false
}
}
return true
}
func isSelfSigned(cert *x509.Certificate) bool {
return cert.CheckSignatureFrom(cert) == nil
}
func isChainRootNode(cert *x509.Certificate) bool {
if isSelfSigned(cert) {
return true
}
return false
}
func (b *Bundler) verifyChain(chain []*fetchedIntermediate) bool {
// This process will verify if the root of the (partial) chain is in our root pool,
// and will fail otherwise.
log.Debugf("verifying chain")
for vchain := chain[:]; len(vchain) > 0; vchain = vchain[1:] {
cert := vchain[0]
// If this is a certificate in one of the pools, skip it.
if b.KnownIssuers[string(cert.Cert.Signature)] {
log.Debugf("certificate is known")
continue
}
_, err := cert.Cert.Verify(b.VerifyOptions())
if err != nil {
log.Debugf("certificate failed verification: %v", err)
return false
} else if len(chain) == len(vchain) && isChainRootNode(cert.Cert) {
// The first certificate in the chain is a root; it shouldn't be stored.
log.Debug("looking at root certificate, will not store")
continue
}
// leaf cert has an empty name, don't store leaf cert.
if cert.Name == "" {
continue
}
log.Debug("add certificate to intermediate pool:", cert.Name)
b.IntermediatePool.AddCert(cert.Cert)
b.KnownIssuers[string(cert.Cert.Signature)] = true
fileName := filepath.Join(IntermediateStash, cert.Name)
var block = pem.Block{Type: "CERTIFICATE", Bytes: cert.Cert.Raw}
log.Debugf("write intermediate to stash directory: %s", fileName)
// If the write fails, verification should not fail.
err = ioutil.WriteFile(fileName, pem.EncodeToMemory(&block), 0644)
if err != nil {
log.Errorf("failed to write new intermediate: %v", err)
} else {
log.Info("stashed new intermediate ", cert.Name)
}
}
return true
}
// constructCertFileName returns a uniquely identifying file name for a certificate
func constructCertFileName(cert *x509.Certificate) string {
// construct the filename as the CN with no period and space
name := strings.Replace(cert.Subject.CommonName, ".", "", -1)
name = strings.Replace(name, " ", "", -1)
// add SKI and serial number as extra identifier
name += fmt.Sprintf("_%x", cert.SubjectKeyId)
name += fmt.Sprintf("_%x", cert.SerialNumber.Bytes())
name += ".crt"
return name
}
// fetchIntermediates goes through each of the URLs in the AIA "Issuing
// CA" extensions and fetches those certificates. If those
// certificates are not present in either the root pool or
// intermediate pool, the certificate is saved to file and added to
// the list of intermediates to be used for verification. This will
// not add any new certificates to the root pool; if the ultimate
// issuer is not trusted, fetching the certicate here will not change
// that.
func (b *Bundler) fetchIntermediates(certs []*x509.Certificate) (err error) {
log.Debugf("searching intermediates")
if _, err := os.Stat(IntermediateStash); err != nil && os.IsNotExist(err) {
log.Infof("intermediate stash directory %s doesn't exist, creating", IntermediateStash)
err = os.MkdirAll(IntermediateStash, 0755)
if err != nil {
log.Errorf("failed to create intermediate stash directory %s: %v", IntermediateStash, err)
return err
}
log.Infof("intermediate stash directory %s created", IntermediateStash)
}
// stores URLs and certificate signatures that have been seen
seen := map[string]bool{}
var foundChains int
// Construct a verify chain as a reversed partial bundle,
// such that the certs are ordered by promxity to the root CAs.
var chain []*fetchedIntermediate
for i, cert := range certs {
var name string
// Only construct filenames for non-leaf intermediate certs
// so they will be saved to disk if necessary.
// Leaf cert gets a empty name and will be skipped.
if i > 0 {
name = constructCertFileName(cert)
}
chain = append([]*fetchedIntermediate{&fetchedIntermediate{cert, name}}, chain...)
seen[string(cert.Signature)] = true
}
// Verify the chain and store valid intermediates in the chain.
// If it doesn't verify, fetch the intermediates and extend the chain
// in a DFS manner and verify each time we hit a root.
for {
if len(chain) == 0 {
log.Debugf("search complete")
if foundChains == 0 {
return x509.UnknownAuthorityError{}
}
return nil
}
current := chain[0]
var advanced bool
if b.verifyChain(chain) {
foundChains++
}
log.Debugf("walk AIA issuers")
for _, url := range current.Cert.IssuingCertificateURL {
if seen[url] {
log.Debugf("url %s has been seen", url)
continue
}
crt, err := fetchRemoteCertificate(url)
if err != nil {
continue
} else if seen[string(crt.Cert.Signature)] {
log.Debugf("fetched certificate is known")
continue
}
seen[url] = true
seen[string(crt.Cert.Signature)] = true
chain = append([]*fetchedIntermediate{crt}, chain...)
advanced = true
break
}
if !advanced {
log.Debugf("didn't advance, stepping back")
chain = chain[1:]
}
}
}
// Bundle takes an X509 certificate (already in the
// Certificate structure), a private key as crypto.Signer in one of the appropriate
// formats (i.e. *rsa.PrivateKey or *ecdsa.PrivateKey, or even a opaque key), using them to
// build a certificate bundle.
func (b *Bundler) Bundle(certs []*x509.Certificate, key crypto.Signer, flavor BundleFlavor) (*Bundle, error) {
log.Infof("bundling certificate for %+v", certs[0].Subject)
if len(certs) == 0 {
return nil, nil
}
// Detect reverse ordering of the cert chain.
if len(certs) > 1 && !partialVerify(certs) {
rcerts := reverse(certs)
if partialVerify(rcerts) {
certs = rcerts
}
}
var ok bool
cert := certs[0]
if key != nil {
switch {
case cert.PublicKeyAlgorithm == x509.RSA:
var rsaPublicKey *rsa.PublicKey
if rsaPublicKey, ok = key.Public().(*rsa.PublicKey); !ok {
return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch)
}
if cert.PublicKey.(*rsa.PublicKey).N.Cmp(rsaPublicKey.N) != 0 {
return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch)
}
case cert.PublicKeyAlgorithm == x509.ECDSA:
var ecdsaPublicKey *ecdsa.PublicKey
if ecdsaPublicKey, ok = key.Public().(*ecdsa.PublicKey); !ok {
return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch)
}
if cert.PublicKey.(*ecdsa.PublicKey).X.Cmp(ecdsaPublicKey.X) != 0 {
return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch)
}
default:
return nil, errors.New(errors.PrivateKeyError, errors.NotRSAOrECC)
}
} else {
switch {
case cert.PublicKeyAlgorithm == x509.RSA:
case cert.PublicKeyAlgorithm == x509.ECDSA:
default:
return nil, errors.New(errors.PrivateKeyError, errors.NotRSAOrECC)
}
}
if cert.CheckSignatureFrom(cert) == nil {
return nil, errors.New(errors.CertificateError, errors.SelfSigned)
}
bundle := new(Bundle)
bundle.Cert = cert
bundle.Key = key
bundle.Issuer = &cert.Issuer
bundle.Subject = &cert.Subject
bundle.buildHostnames()
// verify and store input intermediates to the intermediate pool.
// Ignore the returned error here, will treat it in the second call.
b.fetchIntermediates(certs)
chains, err := cert.Verify(b.VerifyOptions())
if err != nil {
log.Debugf("verification failed: %v", err)
// If the error was an unknown authority, try to fetch
// the intermediate specified in the AIA and add it to
// the intermediates bundle.
switch err := err.(type) {
case x509.UnknownAuthorityError:
// Do nothing -- have the default case return out.
default:
return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
}
log.Debugf("searching for intermediates via AIA issuer")
err = b.fetchIntermediates(certs)
if err != nil {
log.Debugf("search failed: %v", err)
return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
}
log.Debugf("verifying new chain")
chains, err = cert.Verify(b.VerifyOptions())
if err != nil {
log.Debugf("failed to verify chain: %v", err)
return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
}
log.Debugf("verify ok")
}
var matchingChains [][]*x509.Certificate
switch flavor {
case Optimal:
matchingChains = optimalChains(chains)
case Ubiquitous:
if len(ubiquity.Platforms) == 0 {
log.Warning("No metadata, Ubiquitous falls back to Optimal.")
}
matchingChains = ubiquitousChains(chains)
case Force:
matchingChains = forceChains(certs, chains)
default:
matchingChains = ubiquitousChains(chains)
}
bundle.Chain = matchingChains[0]
// Include at least one intermediate if the leaf has enabled OCSP and is not CA.
if bundle.Cert.OCSPServer != nil && !bundle.Cert.IsCA && len(bundle.Chain) <= 2 {
// No op. Return one intermediate if there is one.
} else {
// do not include the root.
bundle.Chain = bundle.Chain[:len(bundle.Chain)-1]
}
statusCode := int(errors.Success)
var messages []string
// Check if bundle is expiring.
expiringCerts := checkExpiringCerts(bundle.Chain)
bundle.Expires = helpers.ExpiryTime(bundle.Chain)
if len(expiringCerts) > 0 {
statusCode |= errors.BundleExpiringBit
messages = append(messages, expirationWarning(expiringCerts))
}
// Check if bundle contains SHA2 certs.
if ubiquity.ChainHashUbiquity(matchingChains[0]) <= ubiquity.SHA2Ubiquity {
statusCode |= errors.BundleNotUbiquitousBit
messages = append(messages, sha2Warning)
}
// Check if bundle contains ECDSA signatures.
if ubiquity.ChainKeyAlgoUbiquity(matchingChains[0]) <= ubiquity.ECDSA256Ubiquity {
statusCode |= errors.BundleNotUbiquitousBit
messages = append(messages, ecdsaWarning)
}
// Add root store presence info
root := matchingChains[0][len(matchingChains[0])-1]
bundle.Root = root
log.Infof("the anchoring root is %v", root.Subject)
// Check if there is any platform that doesn't trust the chain.
// Also, an warning will be generated if ubiquity.Platforms is nil,
untrusted := ubiquity.UntrustedPlatforms(root)
untrustedMsg := untrustedPlatformsWarning(untrusted)
if len(untrustedMsg) > 0 {
log.Debug("Populate untrusted platform warning.")
statusCode |= errors.BundleNotUbiquitousBit
messages = append(messages, untrustedMsg)
}
// Check if there is any platform that rejects the chain because of SHA1 deprecation.
deprecated := ubiquity.DeprecatedSHA1Platforms(matchingChains[0])
if len(deprecated) > 0 {
log.Debug("Populate SHA1 deprecation warning.")
statusCode |= errors.BundleNotUbiquitousBit
messages = append(messages, deprecateSHA1Warning(deprecated))
}
bundle.Status = &BundleStatus{ExpiringSKIs: getSKIs(bundle.Chain, expiringCerts), Code: statusCode, Messages: messages, Untrusted: untrusted}
bundle.Status.IsRebundled = diff(bundle.Chain, certs)
log.Debugf("bundle complete")
return bundle, nil
}
// checkExpiringCerts returns indices of certs that are expiring within 30 days.
func checkExpiringCerts(chain []*x509.Certificate) (expiringIntermediates []int) {
now := time.Now()
for i, cert := range chain {
if cert.NotAfter.Sub(now).Hours() < 720 {
expiringIntermediates = append(expiringIntermediates, i)
}
}
return
}
// getSKIs returns a list of cert subject key id in the bundle chain with matched indices.
func getSKIs(chain []*x509.Certificate, indices []int) (skis []string) {
for _, index := range indices {
ski := fmt.Sprintf("%X", chain[index].SubjectKeyId)
skis = append(skis, ski)
}
return
}
// expirationWarning generates a warning message with expiring certs.
func expirationWarning(expiringIntermediates []int) (ret string) {
if len(expiringIntermediates) == 0 {
return
}
ret = expiringWarningStub
if len(expiringIntermediates) > 1 {
ret = ret + "The expiring certs are"
} else {
ret = ret + "The expiring cert is"
}
for _, index := range expiringIntermediates {
ret = ret + " #" + strconv.Itoa(index+1)
}
ret = ret + " in the chain."
return
}
// untrustedPlatformsWarning generates a warning message with untrusted platform names.
func untrustedPlatformsWarning(platforms []string) string {
if len(ubiquity.Platforms) == 0 {
return ubiquityWarning
}
if len(platforms) == 0 {
return ""
}
msg := untrustedWarningStub
for i, platform := range platforms {
if i > 0 {
msg += ","
}
msg += " " + platform
}
msg += "."
return msg
}
// deprecateSHA1Warning generates a warning message with platform names which deprecates SHA1.
func deprecateSHA1Warning(platforms []string) string {
if len(platforms) == 0 {
return ""
}
msg := deprecateSHA1WarningStub
for i, platform := range platforms {
if i > 0 {
msg += ","
}
msg += " " + platform
}
msg += "."
return msg
}
// Optimal chains are the shortest chains, with newest intermediates and most advanced crypto suite being the tie breaker.
func optimalChains(chains [][]*x509.Certificate) [][]*x509.Certificate {
// Find shortest chains
chains = ubiquity.Filter(chains, ubiquity.CompareChainLength)
// Find the chains with longest expiry.
chains = ubiquity.Filter(chains, ubiquity.CompareChainExpiry)
// Find the chains with more advanced crypto suite
chains = ubiquity.Filter(chains, ubiquity.CompareChainCryptoSuite)
return chains
}
// Ubiquitous chains are the chains with highest platform coverage and break ties with the optimal strategy.
func ubiquitousChains(chains [][]*x509.Certificate) [][]*x509.Certificate {
// Filter out chains with highest cross platform ubiquity.
chains = ubiquity.Filter(chains, ubiquity.ComparePlatformUbiquity)
// Prefer that all intermediates are SHA-2 certs if the leaf is a SHA-2 cert, in order to improve ubiquity.
chains = ubiquity.Filter(chains, ubiquity.CompareSHA2Homogeneity)
// Filter shortest chains
chains = ubiquity.Filter(chains, ubiquity.CompareChainLength)
// Filter chains with highest signature hash ubiquity.
chains = ubiquity.Filter(chains, ubiquity.CompareChainHashUbiquity)
// Filter chains with highest keyAlgo ubiquity.
chains = ubiquity.Filter(chains, ubiquity.CompareChainKeyAlgoUbiquity)
// Filter chains with intermediates that last longer.
chains = ubiquity.Filter(chains, ubiquity.CompareExpiryUbiquity)
// Use the optimal strategy as final tie breaker.
return optimalChains(chains)
}
// Force chains returns the input bundle (plus one verified root CA) as the highest ranked ones if possible.
// If there doesn't exist such bundle, fall back to the most ubiquitous bundle.
func forceChains(input []*x509.Certificate, chains [][]*x509.Certificate) [][]*x509.Certificate {
// Filter out chains that are the same as the input certs.
var candidateChains [][]*x509.Certificate
for _, chain := range chains {
if !diff(chain[:len(chain)-1], input) {
candidateChains = append(candidateChains, chain)
}
}
if len(candidateChains) == 0 {
candidateChains = chains
}
// Filter out chains with highest cross platform ubiquity.
return ubiquity.Filter(candidateChains, ubiquity.ComparePlatformUbiquity)
}
// diff checkes if two input cert chains are not identical
func diff(chain1, chain2 []*x509.Certificate) bool {
// Check if bundled one is different from the input.
diff := false
if len(chain1) != len(chain2) {
diff = true
} else {
for i := 0; i < len(chain1); i++ {
cert1 := chain1[i]
cert2 := chain2[i]
// Use signature to differentiate.
if !bytes.Equal(cert1.Signature, cert2.Signature) {
diff = true
break
}
}
}
return diff
}

View File

@ -0,0 +1,115 @@
package bundler
// This test file contains tests on checking Bundle.Status with SHA-1 deprecation warning.
import (
"testing"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ubiquity"
)
const (
sha1CA = "testdata/ca.pem"
testChromeMetadata = "testdata/ca.pem.metadata"
sha1Intermediate = "testdata/inter-L1-sha1.pem"
sha2Intermediate = "testdata/inter-L1.pem"
sha2LeafExp2015Jun2nd = "testdata/inter-L2-1.pem"
sha2LeafExp2016Jan2nd = "testdata/inter-L2-2.pem"
sha2LeafExp2016Jun2nd = "testdata/inter-L2-3.pem"
sha2LeafExp2017Jan2nd = "testdata/inter-L2-4.pem"
)
func TestChromeWarning(t *testing.T) {
b := newCustomizedBundlerFromFile(t, sha1CA, sha1Intermediate, "")
// The metadata contains Chrome M39, M40 and M41. The effective date for their SHA1 deprecation
// is pushed to 2014-09-01 to enable unit testing.
ubiquity.Platforms = nil
ubiquity.LoadPlatforms(testChromeMetadata)
// Bundle a leaf cert with expiration on 2015-06-02.
// Expect no SHA-1 deprecation warnings but a SHA2 warning.
bundle, err := b.BundleFromFile(sha2LeafExp2015Jun2nd, "", Ubiquitous)
if err != nil {
t.Fatal("bundling failed: ", err)
}
if bundle.Status.Code|errors.BundleNotUbiquitousBit != errors.BundleNotUbiquitousBit {
t.Fatal("Incorrect bundle status code. Bundle status:", bundle.Status)
}
if len(bundle.Status.Messages) != 2 ||
bundle.Status.Messages[0] != sha2Warning ||
bundle.Status.Messages[1] != ecdsaWarning {
t.Fatal("Incorrect bundle status messages. Bundle status messages:", bundle.Status.Messages)
}
// Bundle a leaf cert with expiration on 2016-01-02.
// Expect one SHA-1 deprecation warning from Chrome M41 and a SHA2 warning.
bundle, err = b.BundleFromFile(sha2LeafExp2016Jan2nd, "", Ubiquitous)
if err != nil {
t.Fatal("bundling failed: ", err)
}
if bundle.Status.Code|errors.BundleNotUbiquitousBit != errors.BundleNotUbiquitousBit {
t.Fatal("Incorrect bundle status code. Bundle status:", bundle.Status)
}
if len(bundle.Status.Messages) != 3 ||
bundle.Status.Messages[0] != sha2Warning ||
bundle.Status.Messages[1] != ecdsaWarning ||
bundle.Status.Messages[2] != deprecateSHA1WarningStub+" Chrome Browser M41." {
t.Fatal("Incorrect bundle status messages. Bundle status messages:", bundle.Status.Messages)
}
// Bundle a leaf cert with expiration on 2016-06-02.
// Expect SHA-1 deprecation warnings from Chrome M40, M41 and a SHA2 warning.
bundle, err = b.BundleFromFile(sha2LeafExp2016Jun2nd, "", Ubiquitous)
if err != nil {
t.Fatal("bundling failed: ", err)
}
if bundle.Status.Code|errors.BundleNotUbiquitousBit != errors.BundleNotUbiquitousBit {
t.Fatal("Incorrect bundle status code. Bundle status:", bundle.Status)
}
if len(bundle.Status.Messages) != 3 ||
bundle.Status.Messages[0] != sha2Warning ||
bundle.Status.Messages[1] != ecdsaWarning ||
bundle.Status.Messages[2] != deprecateSHA1WarningStub+" Chrome Browser M40, Chrome Browser M41." {
t.Fatal("Incorrect bundle status messages. Bundle status messages:", bundle.Status.Messages)
}
// Bundle a leaf cert with expiration on 2017-01-02.
// Expect SHA-1 deprecation warnings from Chrome M39, M40, M41 and a SHA2 warning.
bundle, err = b.BundleFromFile(sha2LeafExp2017Jan2nd, "", Ubiquitous)
if err != nil {
t.Fatal("bundling failed: ", err)
}
if bundle.Status.Code|errors.BundleNotUbiquitousBit != errors.BundleNotUbiquitousBit {
t.Fatal("Incorrect bundle status code. Bundle status:", bundle.Status)
}
if len(bundle.Status.Messages) != 3 ||
bundle.Status.Messages[0] != sha2Warning ||
bundle.Status.Messages[1] != ecdsaWarning ||
bundle.Status.Messages[2] != deprecateSHA1WarningStub+" Chrome Browser M39, Chrome Browser M40, Chrome Browser M41." {
t.Fatal("Incorrect bundle status messages. Bundle status messages:", bundle.Status.Messages)
}
}
func TestUbiquitySHA2Preference(t *testing.T) {
// Add a SHA-2 L1 cert to the intermediate pool.
b := newCustomizedBundlerFromFile(t, sha1CA, sha1Intermediate, sha2Intermediate)
ubiquity.Platforms = nil
ubiquity.LoadPlatforms(testChromeMetadata)
bundle, err := b.BundleFromFile(sha2LeafExp2017Jan2nd, "", Ubiquitous)
if err != nil {
t.Fatal("bundling failed: ", err)
}
// With same ubiquity score, the chain with SHA2 L1 will be chosen. And so there is no Chrome warning.
if len(bundle.Status.Messages) != 2 ||
bundle.Status.Messages[0] != sha2Warning ||
bundle.Status.Messages[1] != ecdsaWarning {
t.Fatal("Incorrect bundle status messages. Bundle status messages:", bundle.Status.Messages)
}
}

View File

@ -0,0 +1,809 @@
package bundler
// This test file contains mostly tests on checking Bundle.Status when bundling under different circumstances.
import (
"bytes"
"crypto/x509"
"encoding/json"
"io/ioutil"
"strings"
"testing"
"time"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ubiquity"
)
const (
testCaBundle = "testdata/ca-bundle.pem"
testIntCaBundle = "testdata/int-bundle.pem"
testNSSRootBundle = "testdata/nss.pem"
testMetadata = "testdata/ca-bundle.crt.metadata"
firstdataPEM = "testdata/firstdata.pem"
forcebundlePEM = "testdata/forcebundle.pem"
draftkingsPEM = "testdata/draftkings.pem" // expires in July 2015
riotPEM = "testdata/riot.pem"
bunningsPEM = "testdata/bunnings.pem"
testCFSSLRootBundle = "testdata/ca.pem"
testCAFile = "testdata/ca.pem"
testCAKeyFile = "testdata/ca.key"
testCFSSLIntBundle = "testdata/intermediates.crt"
emptyPEM = "testdata/empty.pem"
interL1SHA1 = "testdata/inter-L1-sha1.pem"
interL2SHA1 = "testdata/inter-L2-sha1.pem"
)
// Simply create a bundler
func TestNewBundler(t *testing.T) {
newBundler(t)
}
func TestNewBundlerMissingCA(t *testing.T) {
badFile := "testdata/no_such_file.pem"
_, err := NewBundler(badFile, testIntCaBundle)
if err == nil {
t.Fatal("Should fail with error code 4001")
}
// generate a function checking error content
errorCheck := ExpectErrorMessage(`"code":4001`)
errorCheck(t, err)
}
func TestNewBundlerMissingIntermediate(t *testing.T) {
badFile := "testdata/no_such_file.pem"
_, err := NewBundler(testCaBundle, badFile)
if err == nil {
t.Fatal("Should fail with error code 3001")
}
// generate a function checking error content
errorCheck := ExpectErrorMessage(`"code":3001`)
errorCheck(t, err)
}
// JSON object of a bundle
type bundleObject struct {
Bundle string `json:"bundle"`
Root string `json:"root"`
Cert string `json:"crt"`
Key string `json:"key"`
KeyType string `json:"key_type"`
KeySize int `json:"key_size"`
Issuer string `json:"issuer"`
Subject string `json:"subject"`
Expires string `json:"expires"`
Hostnames []string `json:"hostnames"`
OCSPSupport bool `json:"ocsp_support"`
CRLSupport bool `json:"crl_support"`
OCSP []string `json:"ocsp"`
Signature string `json:"signature"`
Status BundleStatus
}
var godaddyIssuerString = `/Country=US/Organization=The Go Daddy Group, Inc./OrganizationalUnit=Go Daddy Class 2 Certification Authority`
var godaddySubjectString = `/Country=US/Province=Arizona/Locality=Scottsdale/Organization=GoDaddy.com, Inc./OrganizationalUnit=http://certificates.godaddy.com/repository/CommonName=Go Daddy Secure Certification Authority/SerialNumber=07969287`
// Test marshal to JSON
// Also serves as a JSON format regression test.
func TestBundleMarshalJSON(t *testing.T) {
b := newBundler(t)
bundle, _ := b.BundleFromPEM(GoDaddyIntermediateCert, nil, Optimal)
bytes, err := json.Marshal(bundle)
if err != nil {
t.Fatal(err)
}
var obj bundleObject
err = json.Unmarshal(bytes, &obj)
if err != nil {
t.Fatal(err)
}
if obj.Bundle == "" {
t.Fatal("bundle is empty.")
}
if obj.Bundle != string(GoDaddyIntermediateCert) {
t.Fatal("bundle is incorrect:", obj.Bundle)
}
if obj.Key != "" {
t.Fatal("key is not empty:", obj.Key)
}
if obj.Root != string(GoDaddyRootCert) {
t.Fatal("Root is not recovered")
}
if obj.Cert != string(GoDaddyIntermediateCert) {
t.Fatal("Cert is not recovered")
}
if obj.KeyType != "2048-bit RSA" {
t.Fatal("Incorrect key type:", obj.KeyType)
}
if obj.KeySize != 2048 {
t.Fatal("Incorrect key size:", obj.KeySize)
}
if obj.Issuer != godaddyIssuerString {
t.Fatal("Incorrect issuer:", obj.Issuer)
}
if obj.Subject != godaddySubjectString {
t.Fatal("Incorrect subject:", obj.Subject)
}
if obj.Expires != "2026-11-16T01:54:37Z" {
t.Fatal("Incorrect expiration time:", obj.Expires)
}
if len(obj.Hostnames) != 1 || obj.Hostnames[0] != "Go Daddy Secure Certification Authority" {
t.Fatal("Incorrect hostnames:", obj.Hostnames)
}
if obj.OCSPSupport != true {
t.Fatal("Incorrect OCSP support flag:", obj.OCSPSupport)
}
if obj.CRLSupport != true {
t.Fatal("Incorrect CRL support flag:", obj.CRLSupport)
}
if len(obj.OCSP) != 1 || obj.OCSP[0] != `http://ocsp.godaddy.com` {
t.Fatal("Incorrect ocsp server list:", obj.OCSP)
}
if obj.Signature != "SHA1WithRSA" {
t.Fatal("Incorrect cert signature method:", obj.Signature)
}
}
func TestBundleWithECDSAKeyMarshalJSON(t *testing.T) {
b := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, testCFSSLIntBundle, "")
bundle, _ := b.BundleFromFile(leafECDSA256, leafKeyECDSA256, Optimal)
jsonBytes, err := json.Marshal(bundle)
if err != nil {
t.Fatal(err)
}
var obj map[string]interface{}
err = json.Unmarshal(jsonBytes, &obj)
if err != nil {
t.Fatal(err)
}
key := obj["key"].(string)
keyBytes, _ := ioutil.ReadFile(leafKeyECDSA256)
keyBytes = bytes.Trim(keyBytes, " \n")
if key != string(keyBytes) {
t.Fatal("key is not recovered.")
}
cert := obj["crt"].(string)
certBytes, _ := ioutil.ReadFile(leafECDSA256)
certBytes = bytes.Trim(certBytes, " \n")
if cert != string(certBytes) {
t.Fatal("cert is not recovered.")
}
keyType := obj["key_type"]
if keyType != "256-bit ECDSA" {
t.Fatal("Incorrect key type:", keyType)
}
}
func TestBundleWithRSAKeyMarshalJSON(t *testing.T) {
b := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, testCFSSLIntBundle, "")
bundle, _ := b.BundleFromFile(leafRSA2048, leafKeyRSA2048, Optimal)
jsonBytes, err := json.Marshal(bundle)
if err != nil {
t.Fatal(err)
}
var obj map[string]interface{}
err = json.Unmarshal(jsonBytes, &obj)
if err != nil {
t.Fatal(err)
}
key := obj["key"].(string)
keyBytes, _ := ioutil.ReadFile(leafKeyRSA2048)
keyBytes = bytes.Trim(keyBytes, " \n")
if key != string(keyBytes) {
t.Error("key is", key)
t.Error("keyBytes is", string(keyBytes))
t.Fatal("key is not recovered.")
}
cert := obj["crt"].(string)
certBytes, _ := ioutil.ReadFile(leafRSA2048)
certBytes = bytes.Trim(certBytes, " \n")
if cert != string(certBytes) {
t.Fatal("cert is not recovered.")
}
keyType := obj["key_type"]
if keyType != "2048-bit RSA" {
t.Fatal("Incorrect key type:", keyType)
}
}
// Test marshal to JSON on hostnames
func TestBundleHostnamesMarshalJSON(t *testing.T) {
b := newBundler(t)
bundle, _ := b.BundleFromRemote("www.cloudflare.com", "", Ubiquitous)
hostnames, _ := json.Marshal(bundle.Hostnames)
expectedOne := []byte(`["www.cloudflare.com","cloudflare.com"]`)
expectedTheOther := []byte(`["cloudflare.com","www.cloudflare.com"]`)
if !bytes.Equal(hostnames, expectedOne) && !bytes.Equal(hostnames, expectedTheOther) {
t.Fatal("Hostnames construction failed for cloudflare.com.", string(hostnames))
}
bundle, _ = b.BundleFromPEM(GoDaddyIntermediateCert, nil, Optimal)
expected := []byte(`["Go Daddy Secure Certification Authority"]`)
hostnames, _ = json.Marshal(bundle.Hostnames)
if !bytes.Equal(hostnames, expected) {
t.Fatal("Hostnames construction failed for godaddy root cert.", string(hostnames))
}
}
// Tests on verifying the rebundle flag and error code in Bundle.Status when rebundling.
func TestRebundleFromPEM(t *testing.T) {
newBundler := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, interL1, "")
newBundle, err := newBundler.BundleFromPEM(expiredBundlePEM, nil, Optimal)
if err != nil {
t.Fatalf("Re-bundle failed. %s", err.Error())
}
newChain := newBundle.Chain
if len(newChain) != 2 {
t.Fatalf("Expected bundle chain length is 2. Got %d.", len(newChain))
}
expiredChain, _ := helpers.ParseCertificatesPEM(expiredBundlePEM)
for i, cert := range newChain {
old := expiredChain[i]
if i == 0 {
if !bytes.Equal(old.Signature, cert.Signature) {
t.Fatal("Leaf cert should be the same.")
}
} else {
if bytes.Equal(old.Signature, cert.Signature) {
t.Fatal("Intermediate cert should be different.")
}
}
}
// The status must be {Code: ExpiringBit is not set, IsRebundled:true, ExpiringSKIs:{}}
if len(newBundle.Status.ExpiringSKIs) != 0 || !newBundle.Status.IsRebundled || newBundle.Status.Code&errors.BundleExpiringBit != 0 {
t.Fatal("Rebundle Status is incorrect.")
}
}
func TestRebundleExpiring(t *testing.T) {
// make a policy that generate a cert expires in one hour.
expiry := 1 * time.Hour
policy := &config.Signing{
Profiles: map[string]*config.SigningProfile{
"expireIn1Hour": &config.SigningProfile{
Usage: []string{"cert sign"},
Expiry: expiry,
CA: true,
},
},
Default: config.DefaultConfig(),
}
// Generate a intermediate cert that expires in one hour.
expiringPEM := createInterCert(t, interL1CSR, policy, "expireIn1Hour")
rootBundlePEM, _ := ioutil.ReadFile(testCFSSLRootBundle)
// Use the expiring intermediate to initiate a bundler.
bundler, err := NewBundlerFromPEM(rootBundlePEM, expiringPEM)
if err != nil {
t.Fatalf("bundle failed. %s", err.Error())
}
newBundle, err := bundler.BundleFromPEM(expiredBundlePEM, nil, Optimal)
if err != nil {
t.Fatalf("Re-bundle failed. %s", err.Error())
}
// Check the bundle content.
newChain := newBundle.Chain
if len(newChain) != 2 {
t.Fatalf("Expected bundle chain length is 2. Got %d.", len(newChain))
}
// The status must be {Code: ExpiringBit is set, IsRebundled:true, ExpiringSKIs:{"8860BA18A477B841041BD5EF7751C25B14BA203F"}}
if len(newBundle.Status.ExpiringSKIs) != 1 || !newBundle.Status.IsRebundled || newBundle.Status.Code&errors.BundleExpiringBit == 0 {
t.Fatal("Rebundle Status is incorrect.")
}
expectedSKI := "8860BA18A477B841041BD5EF7751C25B14BA203F"
if newBundle.Status.ExpiringSKIs[0] != expectedSKI {
t.Fatalf("Expected expiring cert SKI is %s, got %s\n", expectedSKI, newBundle.Status.ExpiringSKIs[0])
}
}
// Test on verifying ubiquitous messaging in Bundle.Status.
func TestUbiquitousBundle(t *testing.T) {
L1Cert := readCert(interL1)
// Simulate the case that L1Cert is added to trust store by one platform but not yet in another.
b := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, testCFSSLIntBundle, "")
b.RootPool.AddCert(L1Cert)
// Prepare Platforms.
platformA := ubiquity.Platform{Name: "MacroSoft", Weight: 100, HashAlgo: "SHA2", KeyAlgo: "ECDSA256", KeyStoreFile: testCFSSLRootBundle}
platformA.ParseAndLoad()
platformB := ubiquity.Platform{Name: "Godzilla", Weight: 100, HashAlgo: "SHA2", KeyAlgo: "ECDSA256", KeyStoreFile: testCFSSLRootBundle}
platformB.ParseAndLoad()
platformA.KeyStore.Add(L1Cert)
ubiquity.Platforms = []ubiquity.Platform{platformA, platformB}
// Optimal bundle algorithm will picks up the new root and shorten the chain.
optimalBundle, err := b.BundleFromFile(leafECDSA256, "", Optimal)
if err != nil {
t.Fatal("Optimal bundle failed:", err)
}
if len(optimalBundle.Chain) != 2 {
t.Fatal("Optimal bundle failed the chain length test. Chain length:", len(optimalBundle.Chain))
}
// The only trust platform is "Macrosoft".
if len(optimalBundle.Status.Untrusted) != 1 {
t.Fatal("Optimal bundle status has incorrect untrusted platforms", optimalBundle.Status.Untrusted)
}
checkUbiquityWarningAndCode(t, optimalBundle, true)
// Ubiquitous bundle will remain the same.
ubiquitousBundle, err := b.BundleFromFile(leafECDSA256, "", Ubiquitous)
if err != nil {
t.Fatal("Ubiquitous bundle failed")
}
if len(ubiquitousBundle.Chain) != 3 {
t.Fatal("Ubiquitous bundle failed")
}
// Should be trusted by both platforms.
if len(ubiquitousBundle.Status.Untrusted) != 0 {
t.Fatal("Ubiquitous bundle status has incorrect untrusted platforms", len(ubiquitousBundle.Status.Untrusted))
}
checkUbiquityWarningAndCode(t, ubiquitousBundle, false)
}
func TestUbiquityBundleWithoutMetadata(t *testing.T) {
b := newCustomizedBundlerFromFile(t, testCFSSLRootBundle, testCFSSLIntBundle, "")
L1Cert := readCert(interL1)
b.RootPool.AddCert(L1Cert)
// Without platform info, ubiquitous bundling falls back to optimal bundling.
ubiquity.Platforms = nil
nuBundle, err := b.BundleFromFile(leafECDSA256, "", Ubiquitous)
if err != nil {
t.Fatal("Ubiquitous-fall-back-to-optimal bundle failed: ", err)
}
if len(nuBundle.Chain) != 2 {
t.Fatal("Ubiquitous-fall-back-to-optimal bundle failed")
}
// Should be trusted by all (i.e. zero) platforms.
if len(nuBundle.Status.Untrusted) != 0 {
t.Fatal("Ubiquitous-fall-back-to-optimal bundle status has incorrect untrusted platforms", len(nuBundle.Status.Untrusted))
}
checkUbiquityWarningAndCode(t, nuBundle, true)
}
func checkUbiquityWarningAndCode(t *testing.T, bundle *Bundle, expected bool) {
found := false
for _, msg := range bundle.Status.Messages {
if strings.Contains(msg, untrustedWarningStub) || strings.Contains(msg, ubiquityWarning) {
found = true
}
}
if found != expected {
t.Fatal("Expected ubiquity warning: ", expected, " Found ubiquity warning:", found)
}
// check status code
if expected && bundle.Status.Code&errors.BundleNotUbiquitousBit == 0 {
t.Fatal("Bundle status doesn't set BundleNotUbiquitousBit :", bundle.Status.Code)
}
}
// FIXME: test case expires in July 2015
// Regression test on ubiquity.
// It is to make sure ubiquitous bundles are generated so they can be trusted by Android 2.2
// and its variants.
//
// Leaf cert from DraftKings.com is issued by a GoDaddy intermediate cert,
// which in turn is issued by a GoDaddy Root Certificate (CN: Go Daddy Root Certificate Authority
// G2). The NSS library includes this root cert. So optimal bundle should only have two certs.
// However, that root cert is not present in trust stores of Android <= 2.2. Ubiquitous bundling
// should be able to recognize this scenario and produces a bundle that includes the GoDaddy Root
// cert as an intermediate, which is verified by older trust roots.
func TestAndroidUbiquitousBundle(t *testing.T) {
leafs := []string{draftkingsPEM}
for _, leaf := range leafs {
b := newCustomizedBundlerFromFile(t, testNSSRootBundle, testIntCaBundle, "")
ubiquity.Platforms = nil
ubiquity.LoadPlatforms(testMetadata)
// Optimal bundle algorithm will use the Godaddy Root/GeoTrust CA.
optimalBundle, err := b.BundleFromFile(leaf, "", Optimal)
if err != nil {
t.Fatal("Optimal bundle failed:", err)
}
if len(optimalBundle.Chain) != 2 {
t.Fatal("Optimal bundle failed")
}
checkUbiquityWarningAndCode(t, optimalBundle, true)
// Ubiquitous bundle will include a 2nd intermediate CA.
ubiquitousBundle, err := b.BundleFromFile(leaf, "", Ubiquitous)
if err != nil {
t.Fatal("Ubiquitous bundle failed")
}
if len(ubiquitousBundle.Chain) != 3 {
t.Fatal("Ubiquitous bundle failed")
}
if len(ubiquitousBundle.Status.Untrusted) != 0 {
t.Fatal("Regression: Ubiquitous bundle has untrusted platforms: ", ubiquitousBundle.Status.Untrusted)
}
checkUbiquityWarningAndCode(t, ubiquitousBundle, false)
}
}
// Regression test on bundle with flavor 'Force'.
// Compare to ubiquitous bundle which will optimize bundle length given the platform ubiquity is the same, force bundle
// with return the same bundle as long as the input bundle is verified.
func TestForceBundle(t *testing.T) {
b := newCustomizedBundlerFromFile(t, testNSSRootBundle, testIntCaBundle, "")
ubiquity.Platforms = nil
ubiquity.LoadPlatforms(testMetadata)
bundle, err := b.BundleFromFile(firstdataPEM, "", Ubiquitous)
if err != nil {
t.Fatal("ubiquitous bundling failed.", err)
}
if len(bundle.Chain) != 2 {
t.Fatal("ubiquitous bundling failed. Bundle length:", len(bundle.Chain))
}
if bundle.Status.IsRebundled == false {
t.Fatal("force bundling failed, incorrect bundle.Status", bundle.Status)
}
bundle, err = b.BundleFromFile(firstdataPEM, "", Force)
if err != nil {
t.Fatal("force bundling failed.", err)
}
if len(bundle.Chain) != 3 {
t.Fatal("force bundling failed. Bundle length:", len(bundle.Chain))
}
if bundle.Status.IsRebundled == true {
t.Fatal("force bundling failed, incorrect bundle.Status", bundle.Status)
}
}
// TODO(nick): re-enable with non-expired certificate
func testUpdateIntermediate(t *testing.T) {
b := newCustomizedBundlerFromFile(t, testNSSRootBundle, testIntCaBundle, "")
ubiquity.Platforms = nil
ubiquity.LoadPlatforms(testMetadata)
// forcebundle.pem contains a newer intermediate, which should be used when bundling.
ub, err := b.BundleFromFile(forcebundlePEM, "", Ubiquitous)
if err != nil {
t.Fatal("ubiquitous bundling failed.", err)
}
// Ubiquitous bundle should use the intermediate from NSS since it will score higher.
if len(ub.Chain) != 2 {
t.Fatal("force bundling failed. Bundle length:", len(ub.Chain))
}
if ub.Status.IsRebundled == false {
t.Fatal("force bundling failed, incorrect bundle.Status", ub.Status)
}
fb, err := b.BundleFromFile(forcebundlePEM, "", Force)
if err != nil {
t.Fatal("force bundling failed.", err)
}
// Force bundle should use the intermediate from input, indicating intermediate pool is updated.
if len(fb.Chain) != 2 {
t.Fatal("force bundling failed. Bundle length:", len(fb.Chain))
}
if fb.Status.IsRebundled == true {
t.Fatal("force bundling failed, incorrect bundle.Status", fb.Status)
}
}
// FIXME: test case expires in July 2015
func TestForceBundleFallback(t *testing.T) {
leafs := []string{draftkingsPEM}
for _, leaf := range leafs {
b := newCustomizedBundlerFromFile(t, testNSSRootBundle, testIntCaBundle, "")
ubiquity.Platforms = nil
ubiquity.LoadPlatforms(testMetadata)
forceBundle, err := b.BundleFromFile(leaf, "", Force)
if err != nil {
t.Fatal("Force bundle failed:", err)
}
ubiquitousBundle, err := b.BundleFromFile(leaf, "", Ubiquitous)
if err != nil {
t.Fatal("Ubiquitous bundle failed")
}
if diff(ubiquitousBundle.Chain, forceBundle.Chain) {
t.Fatal("Force bundle fallback failed.")
}
}
}
// Regression test: ubiquity bundle test with SHA2-homogeneous preference should not override root ubiquity.
func TestSHA2Homogeneity(t *testing.T) {
b := newCustomizedBundlerFromFile(t, testNSSRootBundle, testIntCaBundle, "")
ubiquity.Platforms = nil
ubiquity.LoadPlatforms(testMetadata)
// The input PEM bundle is 3-cert chain with a cross-signed GeoTrust certificate,
// aimed to provide cert ubiquity to Android 2.2
bundle, err := b.BundleFromFile(bunningsPEM, "", Force)
if err != nil {
t.Fatal("Force bundle failed:", err)
}
if len(bundle.Chain) != 3 {
t.Fatal("Force bundle failed:")
}
if len(bundle.Status.Untrusted) != 0 {
t.Fatal("Force bundle failed:")
}
// With ubiquity flavor, we should not sacrifice Android 2.2 and rebundle with a shorter chain.
bundle, err = b.BundleFromFile(bunningsPEM, "", Ubiquitous)
if err != nil {
t.Fatal("Ubiquitous bundle failed:", err)
}
if len(bundle.Chain) != 3 {
t.Fatal("Ubiquitous bundle failed:")
}
if len(bundle.Status.Untrusted) != 0 {
t.Fatal("Ubiquitous bundle failed:")
}
// With optimal flavor, we should have a shorter chain with only SHA-2 intermediates. But Android 2.2
// is untrusted.
bundle, err = b.BundleFromFile(bunningsPEM, "", Optimal)
if err != nil {
t.Fatal("Optimal bundle failed:", err)
}
if len(bundle.Chain) != 2 {
t.Fatal("Optimal bundle failed:")
}
if len(bundle.Status.Untrusted) == 0 {
t.Fatal("Optimal bundle failed:")
}
}
func checkSHA2WarningAndCode(t *testing.T, bundle *Bundle, expected bool) {
found := false
for _, msg := range bundle.Status.Messages {
if strings.Contains(msg, sha2Warning) {
found = true
}
}
if found != expected {
t.Fatal("Expected ubiquity warning: ", expected, " Found ubiquity warning:", found)
}
// check status code
if bundle.Status.Code&errors.BundleNotUbiquitousBit == 0 {
t.Fatal("Bundle status code is incorrect:", bundle.Status.Code)
}
}
func checkECDSAWarningAndCode(t *testing.T, bundle *Bundle, expected bool) {
found := false
for _, msg := range bundle.Status.Messages {
if strings.Contains(msg, ecdsaWarning) {
found = true
}
}
if found != expected {
t.Fatal("Expected ubiquity warning: ", expected, " Found ubiquity warning:", found)
}
// check status code
if bundle.Status.Code&errors.BundleNotUbiquitousBit == 0 {
t.Fatal("Bundle status code is incorrect:", bundle.Status.Code)
}
}
// Regression test on SHA-2 Warning
// Riot Games once bundle a cert issued by DigiCert SHA2 High Assurance Server CA. The resulting
// bundle uses SHA-256 which is not supported in Windows XP SP2. We should present a warning
// on this.
func TestSHA2Warning(t *testing.T) {
b := newCustomizedBundlerFromFile(t, testNSSRootBundle, testIntCaBundle, "")
// Optimal bundle algorithm will use the Godaddy Root/GeoTrust CA.
optimalBundle, err := b.BundleFromFile(riotPEM, "", Optimal)
if err != nil {
t.Fatal("Optimal bundle failed:", err)
}
checkSHA2WarningAndCode(t, optimalBundle, true)
// Ubiquitous bundle will include a 2nd intermediate CA.
ubiquitousBundle, err := b.BundleFromFile(riotPEM, "", Ubiquitous)
if err != nil {
t.Fatal("Ubiquitous bundle failed")
}
checkSHA2WarningAndCode(t, ubiquitousBundle, true)
}
// Regression test on ECDSA Warning
// A test bundle that contains ECDSA384 but no SHA1. Expect ECDSA warning and no SHA-2 warning.
func TestECDSAWarning(t *testing.T) {
b := newCustomizedBundlerFromFile(t, testCAFile, interL1SHA1, "")
// Optimal
optimalBundle, err := b.BundleFromFile(interL2SHA1, "", Optimal)
if err != nil {
t.Fatal("Optimal bundle failed:", err)
}
checkSHA2WarningAndCode(t, optimalBundle, false)
checkECDSAWarningAndCode(t, optimalBundle, true)
}
// === Helper function block ===
// readCert read a PEM file and returns a cert.
func readCert(filename string) *x509.Certificate {
bytes, _ := ioutil.ReadFile(filename)
cert, _ := helpers.ParseCertificatePEM(bytes)
return cert
}
// newBundler is a helper function that returns a new Bundler. If it fails to do so,
// it fails the test suite immediately.
func newBundler(t *testing.T) (b *Bundler) {
b, err := NewBundler(testCaBundle, testIntCaBundle)
if err != nil {
t.Fatal(err)
}
return
}
// create a test intermediate cert in PEM
func createInterCert(t *testing.T, csrFile string, policy *config.Signing, profileName string) (certPEM []byte) {
s, err := local.NewSignerFromFile(testCAFile, testCAKeyFile, policy)
if err != nil {
t.Fatal(err)
}
csr, err := ioutil.ReadFile(csrFile)
if err != nil {
t.Fatal(err)
}
req := signer.SignRequest{
Hosts: []string{"cloudflare-inter.com"},
Request: string(csr),
Profile: profileName,
Label: "",
}
certPEM, err = s.Sign(req)
if err != nil {
t.Fatal(err)
}
return
}
func newBundlerFromPEM(t *testing.T, caBundlePEM, intBundlePEM []byte) (b *Bundler) {
b, err := NewBundlerFromPEM(caBundlePEM, intBundlePEM)
if err != nil {
t.Fatal(err)
}
return
}
// newCustomizedBundleCreator is a helper function that returns a new Bundler
// takes specified CA bundle, intermediate bundle, and any additional intermdiate certs to generate a bundler.
func newCustomizedBundlerFromFile(t *testing.T, caBundle, intBundle, adhocInters string) (b *Bundler) {
b, err := NewBundler(caBundle, intBundle)
if err != nil {
t.Fatal(err)
}
if adhocInters != "" {
moreIntersPEM, err := ioutil.ReadFile(adhocInters)
if err != nil {
t.Fatalf("Read additional intermediates failed. %v",
err)
}
intermediates, err := helpers.ParseCertificatesPEM(moreIntersPEM)
if err != nil {
t.Fatalf("Parsing additional intermediates failed. %s", err.Error())
}
for _, c := range intermediates {
b.IntermediatePool.AddCert(c)
}
}
return
}
// newBundlerWithoutInters is a helper function that returns a bundler with an empty
// intermediate cert pool. Such bundlers can help testing error handling in cert
// bundling.
func newBundlerWithoutInters(t *testing.T) (b *Bundler) {
b = newBundler(t)
// Re-assign an empty intermediate cert pool
b.IntermediatePool = x509.NewCertPool()
return
}
// newBundlerWithoutRoots is a helper function that returns a bundler with an empty
// root cert pool. Such bundlers can help testing error handling in cert
// bundling.
func newBundlerWithoutRoots(t *testing.T) (b *Bundler) {
b = newBundler(t)
// Re-assign an empty root cert pool
b.RootPool = x509.NewCertPool()
return
}
// A helper function that returns a errorCallback function which expects certain error content in
// an error message.
func ExpectErrorMessage(expectedErrorContent string) func(*testing.T, error) {
return func(t *testing.T, err error) {
if err == nil {
t.Fatalf("Expected error has %s. Got nothing.", expectedErrorContent)
} else if !strings.Contains(err.Error(), expectedErrorContent) {
t.Fatalf("Expected error has %s. Got %s", expectedErrorContent, err.Error())
}
}
}
// A helper function that returns a errorCallback function which inspect error message for
// all expected messages.
func ExpectErrorMessages(expectedContents []string) func(*testing.T, error) {
return func(t *testing.T, err error) {
if err == nil {
t.Fatalf("Expected error has %s. Got nothing.", expectedContents)
} else {
for _, expected := range expectedContents {
if !strings.Contains(err.Error(), expected) {
t.Fatalf("Expected error has %s. Got %s", expected, err.Error())
}
}
}
}
}
// A helper function that returns a bundle chain length checking function
func ExpectBundleLength(expectedLen int) func(*testing.T, *Bundle) {
return func(t *testing.T, bundle *Bundle) {
if bundle == nil {
t.Fatalf("Cert bundle should have a chain of length %d. Got nil.",
expectedLen)
} else if len(bundle.Chain) != expectedLen {
t.Fatalf("Cert bundle should have a chain of length %d. Got chain length %d.",
expectedLen, len(bundle.Chain))
}
}
}

View File

@ -0,0 +1,16 @@
// Package bundler provides an API for creating certificate bundles,
// which contain a trust chain of certificates. Generally, the bundles
// will also include the private key (but this is not strictly
// required). In this package, a bundle refers to a certificate with
// full trust chain -- all certificates in the chain in one file or
// buffer.
//
// The first step in creating a certificate bundle is to create a
// Bundler. A Bundler must be created from a pre-existing certificate
// authority bundle and an intermediate certificate bundle. Once the
// Bundler is initialised, bundles may be created using a variety of
// methods: from PEM- or DER-encoded files, directly from the relevant
// Go structures, or by starting with the certificate from a remote
// system. These functions return a Bundle value, which may be
// serialised to JSON.
package bundler

View File

@ -0,0 +1,35 @@
-----BEGIN CERTIFICATE-----
MIICsDCCAjegAwIBAgIIDmHBNS+T0F8wCgYIKoZIzj0EAwMwgYwxCzAJBgNVBAYT
AlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJlMRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2lu
ZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMR0wGwYDVQQDExRjbG91ZGZsYXJlLWludGVyLmNvbTAeFw0xNDA0MTEyMTIy
MzhaFw0xOTA0MTEyMTI3MzhaMIGLMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xv
dWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEcMBoGA1UEAxMTY2xv
dWRmbGFyZS1sZWFmLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIxG/fG9
y/gjlAXvB77beERLbBooN98FGFAxVUA5IglylvgmfNxUmI8mM2Uw9tzOLm9vORAr
aSSM4/6iSpCJreCjgYEwfzAOBgNVHQ8BAf8EBAMCAKQwEgYDVR0TAQH/BAgwBgEB
/wIBATAdBgNVHQ4EFgQU4t+cr91ma5IxOPeiezgN8W9FBNowHwYDVR0jBBgwFoAU
QfmKIlIyJt+P8AcB3SRhOFrn7PwwGQYDVR0RBBIwEIIOY2Zzc2wtbGVhZi5jb20w
CgYIKoZIzj0EAwMDZwAwZAIwYQWcWr79DPrIBnphpHZPuxnGust6NtD0aSffB1cF
NlYtggjJZDbLijAgD0Bwi3THAjA639xrNxVgc/LkJcHfSRhs8Jhv9cxQxIVf3g8w
6tBymEgJ6L8aIPGgXNRJGs7FmPs=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICzzCCAlSgAwIBAgIIbOxERQylZJMwCgYIKoZIzj0EAwMwgYwxCzAJBgNVBAYT
AlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJlMRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2lu
ZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMR0wGwYDVQQDExRjbG91ZGZsYXJlLWludGVyLmNvbTAeFw0xNDA0MTEyMTIy
MzhaFw0xOTA0MTEyMTI3MzhaMIGLMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xv
dWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEcMBoGA1UEAxMTY2xv
dWRmbGFyZS1sZWFmLmNvbTB2MBAGByqGSM49AgEGBSuBBAAiA2IABNYivDDh3Iik
kb+3/Oocity4JQXmxLP2njZThYNtR4y7Bxixp05KLoq8gtazyccDklueu4OWFnpm
kjyqPQ+0MIf/BJKoA4Q4iNiCN/ZfF690LR/pZPrMRZuWSGVb2890L6OBgTB/MA4G
A1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTiTQoJ
uFODtNnEnbYaxy+He8lO+DAfBgNVHSMEGDAWgBRB+YoiUjIm34/wBwHdJGE4Wufs
/DAZBgNVHREEEjAQgg5jZnNzbC1sZWFmLmNvbTAKBggqhkjOPQQDAwNpADBmAjEA
q/sUd8AQAornMMiLZ5spBu+g6x6qx66wNPw9WE5a+T0hndHJsAqads5ndW7/5fuo
AjEAiQ9wR1ugYaY56mj9UfjCZbwvo19unlB+CTLr48fh/RhvX6xjnpWXxJeXzU3G
GhTH
-----END CERTIFICATE-----

View File

@ -0,0 +1,75 @@
-----BEGIN CERTIFICATE-----
MIIE2jCCA8KgAwIBAgIQB0GfyZ37ARHtj7oFnxJVqDANBgkqhkiG9w0BAQsFADBE
MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMU
R2VvVHJ1c3QgU1NMIENBIC0gRzMwHhcNMTQwNTA2MDAwMDAwWhcNMTYwNjIxMjM1
OTU5WjCBiTELMAkGA1UEBhMCQVUxETAPBgNVBAgMCFZpY3RvcmlhMRYwFAYDVQQH
DA1IYXd0aG9ybiBFYXN0MR8wHQYDVQQKDBZCdW5uaW5ncyBHcm91cCBMaW1pdGVk
MRIwEAYDVQQLDAlFY29tbWVyY2UxGjAYBgNVBAMMESouYnVubmluZ3MuY29tLmF1
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqpFpWh6yMFhX6O24zyjD
/RKi3qwQeCokwLf2mkEIkcZPvrm/aDQ26e+ZieUJeXHTK9zwxibHCRqYZYAkMvuS
k5ZuB6bdA9ipdPLQN9sG4dsJGNRV3MdbXRK6534BWc8LbbG3O+1p6X8Fm3tpYC5u
C5H+Ek2Gm2uf3hQys+O8mYieEONg5Xf3SqBzGyT3yPztPZQXthjRJfMcBF9UJe5b
a1MB0IYYefph3Bo7HHb3pX9ywqiC8zffoIoowsysg+gU9tKoPRUgAbUcV/IjM037
sy+qe4BK++cifD9UN7R2to6F373MtOl5ldMG7fXdOhf3cALPiwYe5P3XAHC3UkXA
aQIDAQABo4IBgDCCAXwwLQYDVR0RBCYwJIIRKi5idW5uaW5ncy5jb20uYXWCD2J1
bm5pbmdzLmNvbS5hdTAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIFoDAtBgNVHR8E
JjAkMCKgIKAehhxodHRwOi8vZ24xLnN5bWNiLmNvbS9nbjEuY3JsMGUGA1UdIARe
MFwwWgYKYIZIAYb4RQEHNjBMMCMGCCsGAQUFBwIBFhdodHRwczovL2Quc3ltY2Iu
Y29tL2NwczAlBggrBgEFBQcCAjAZFhdodHRwczovL2Quc3ltY2IuY29tL3JwYTAd
BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHwYDVR0jBBgwFoAU0m/3lvSF
P3I8MH0j2oV4m6N8WnwwWgYIKwYBBQUHAQEETjBMMCAGCCsGAQUFBzABhhRodHRw
Oi8vZ24yLnN5bWNiLmNvbTAoBggrBgEFBQcwAoYcaHR0cDovL2duMS5zeW1jYi5j
b20vZ24xLmNydDANBgkqhkiG9w0BAQsFAAOCAQEA1jfVww4Vm/aXvfev4UOgg+7G
LWuJDm7j0F7oB48Xjah9iuGFWSfmq0Z8nTfM+d5X+BOH1fmJ+O1zLs8S8IBUTgVk
hbdWXy8NA+r8ufaF2MDJiL1tfHwn3WgAnc1T+w+HMIRMPNlRs+ZEBXEbpXUJmHOW
rSbFlLN4fwWvN8icuNWO4TuJDqvh5WjQvIFdytygJbMeb5FjLEHFmYftAW5co2cN
2+FRA+WZ8lfebr5/rObxUoZs8PYCNp7BkXoYZRSI49TEEe+QmO9Q1OmbfPzflJfJ
fIR/XQZ2zHvuZTd3nfbIkDZoHYnyJwKj29UUKWwHRL+6celDM9/kDwIXbIem2g==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIETzCCAzegAwIBAgIDAjpvMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
YWwgQ0EwHhcNMTMxMTA1MjEzNjUwWhcNMjIwNTIwMjEzNjUwWjBEMQswCQYDVQQG
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg
U1NMIENBIC0gRzMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDjvn4K
hqPPa209K6GXrUkkTdd3uTR5CKWeop7eRxKSPX7qGYax6E89X/fQp3eaWx8KA7UZ
U9ulIZRpY51qTJEMEEe+EfpshiW3qwRoQjgJZfAU2hme+msLq2LvjafvY3AjqK+B
89FuiGdT7BKkKXWKp/JXPaKDmJfyCn3U50NuMHhiIllZuHEnRaoPZsZVP/oyFysx
j0ag+mkUfJ2fWuLrM04QprPtd2PYw5703d95mnrU7t7dmszDt6ldzBE6B7tvl6QB
I0eVH6N3+liSxsfQvc+TGEK3fveeZerVO8rtrMVwof7UEJrwEgRErBpbeFBFV0xv
vYDLgVwts7x2oR5lAgMBAAGjggFKMIIBRjAfBgNVHSMEGDAWgBTAephojYn7qwVk
DBF9qn1luMrMTjAdBgNVHQ4EFgQU0m/3lvSFP3I8MH0j2oV4m6N8WnwwEgYDVR0T
AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwNgYDVR0fBC8wLTAroCmgJ4Yl
aHR0cDovL2cxLnN5bWNiLmNvbS9jcmxzL2d0Z2xvYmFsLmNybDAvBggrBgEFBQcB
AQQjMCEwHwYIKwYBBQUHMAGGE2h0dHA6Ly9nMi5zeW1jYi5jb20wTAYDVR0gBEUw
QzBBBgpghkgBhvhFAQc2MDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly93d3cuZ2VvdHJ1
c3QuY29tL3Jlc291cmNlcy9jcHMwKQYDVR0RBCIwIKQeMBwxGjAYBgNVBAMTEVN5
bWFudGVjUEtJLTEtNTM5MA0GCSqGSIb3DQEBCwUAA4IBAQCg1Pcs+3QLf2TxzUNq
n2JTHAJ8mJCi7k9o1CAacxI+d7NQ63K87oi+fxfqd4+DYZVPhKHLMk9sIb7SaZZ9
Y73cK6gf0BOEcP72NZWJ+aZ3sEbIu7cT9clgadZM/tKO79NgwYCA4ef7i28heUrg
3Kkbwbf7w0lZXLV3B0TUl/xJAIlvBk4BcBmsLxHA4uYPL4ZLjXvDuacu9PGsFj45
SVGeF0tPEDpbpaiSb/361gsDTUdWVxnzy2v189bPsPX1oxHSIFMTNDcFLENaY9+N
QNaFHlHpURceA1bJ8TCt55sRornQMYGbaLHZ6PPmlH7HrhMvh+3QJbBo+d4IWvMp
zNSS
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw
WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE
AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m
OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu
T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c
JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR
Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz
PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm
aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM
TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g
LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO
BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv
dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB
AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL
NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W
b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S
-----END CERTIFICATE-----

View File

@ -0,0 +1,56 @@
[
{
"name":"Chrome Browser M39",
"weight": 0,
"hash_algo": "SHA2",
"key_algo": "ECDSA256",
"hash_algo_expiry": {
"target": "SHA1",
"effective_date": "2014-09-26T00:00:00Z",
"expiry_deadline": "2017-01-01T00:00:00Z"
}
},
{
"name":"Chrome Browser M40",
"weight": 0,
"hash_algo": "SHA2",
"key_algo": "ECDSA256",
"hash_algo_expiry": {
"target": "SHA1",
"effective_date": "2014-09-26T00:00:00Z",
"expiry_deadline": "2016-06-01T00:00:00Z"
}
},
{
"name":"Chrome Browser M41 and later",
"weight": 0,
"hash_algo": "SHA2",
"key_algo": "ECDSA256",
"hash_algo_expiry": {
"target": "SHA1",
"effective_date": "2014-09-26T00:00:00Z",
"expiry_deadline": "2016-01-01T00:00:00Z"
}
},
{
"name":"Mozilla",
"weight": 99,
"hash_algo": "SHA2",
"key_algo": "ECDSA256",
"keystore": "nss.pem"
},
{
"name":"OSX",
"weight": 99,
"hash_algo": "SHA2",
"key_algo": "ECDSA256",
"keystore": "osx.pem"
},
{
"name":"Android 2.2 Froyo",
"weight": 1,
"hash_algo": "SHA2",
"key_algo": "RSA",
"keystore": "froyo.pem"
}
]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCbp/6OQ/a3mr+8zRgBRlmSGr8QBgP4vUIxLn2Mk4uiZ8OcpRY4
YqL+TtREGDUc0ve+bv8RINrNlYXL2X+eJtbE2RJQ+RAiu+saw2K+RFTNeTCA1fwg
3ws5gBDcFbECqK1dOkuN/gV4JMHobn2/15iUBfeSJxdF1j5yqES8sVu7cwIDAQAB
AoGBALZOnnBV3aLRlnw04kar9MCQnvLPeNteHyanQtjg/oxqZ8sR9+J2dFzSSv6u
M5bc6Nmb+xY+msZqt9g3l6bN6n+qCvNnLauIY/YPjd577uMTpx/QTOQSK8oc5Dhi
WgdU8GCtUmY+LE8qYx2NFitKCN4hubdrI76c+rnezIPVncZRAkEA9T5+vlfwk/Zl
DOte+JtbXx3RtXKFJPMirOFqNVp1qnIlUm8XtBW6760ugiNYbVbGHgbd8JsZnkPH
NC17TNLVJwJBAKJ7pDlJ2mvVr0cLrFhjAibz45dOipt8B4+dKtDIEuqbtKzJCGuP
SCk4X2SgYz0gC5kH62S7rn6Bsa9lM98dztUCQASdLWNFYkhWXWZV006YFar/c5+X
TPv5+xAHmajxT79qMFuRrX983Sx/NJ3MLnC4LjgIZwqM0HmSyt+nb2dtnAcCQCKi
nIUhuw+Vg0FvuZM1t7W581/DfERckfgJFqFepLmh60eRqtvStR0kSSFYFw9mj1JV
n9XfM/j/iHLM7du3rOkCQAw9R64yjcIBwcoSQxW/dr0Q9j+SnYgt+EhyXYXT30DS
DdOJ06GXtb/P0peFBp26BnQU4CSS75yseZ1TdB4ZqaA=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICyDCCAjGgAwIBAgIJAPCgd7rafQZGMA0GCSqGSIb3DQEBBQUAMH0xCzAJBgNV
BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNp
c2NvMRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEW
MBQGA1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDA0MTExNjQyMjBaFw0yNDA0MDgx
NjQyMjBaMH0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYD
VQQHDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQL
DAtERVZfVEVTVElORzEWMBQGA1UEAwwNQ0ZTU0xfVEVTVF9DQTCBnzANBgkqhkiG
9w0BAQEFAAOBjQAwgYkCgYEAm6f+jkP2t5q/vM0YAUZZkhq/EAYD+L1CMS59jJOL
omfDnKUWOGKi/k7URBg1HNL3vm7/ESDazZWFy9l/nibWxNkSUPkQIrvrGsNivkRU
zXkwgNX8IN8LOYAQ3BWxAqitXTpLjf4FeCTB6G59v9eYlAX3kicXRdY+cqhEvLFb
u3MCAwEAAaNQME4wHQYDVR0OBBYEFLhe765nULfW8wflar5Vs2c6DZI+MB8GA1Ud
IwQYMBaAFLhe765nULfW8wflar5Vs2c6DZI+MAwGA1UdEwQFMAMBAf8wDQYJKoZI
hvcNAQEFBQADgYEABYqqOUq3ZrtMYaTAoeA7Cr/OBMjBV+/TiOe8fRNoPZ7+aKSg
E1baohCGqougm+/XOtBXeLv5tVQihz/2iKdwHmX4HjkxzevAXyazjxeW4IDA21Jl
fKd7xUJHM0Du/opoDkXWr/vRVztOB33ndlAK7ruSLfTR3E9HoUe3aRH7ceQ=
-----END CERTIFICATE-----

View File

@ -0,0 +1,42 @@
[
{
"name":"Chrome Browser M39",
"weight": 0,
"hash_algo": "SHA2",
"key_algo": "ECDSA256",
"hash_algo_expiry": {
"target": "SHA1",
"effective_date": "2014-09-01T00:00:00Z",
"expiry_deadline": "2017-01-01T00:00:00Z"
}
},
{
"name":"Chrome Browser M40",
"weight": 0,
"hash_algo": "SHA2",
"key_algo": "ECDSA256",
"hash_algo_expiry": {
"target": "SHA1",
"effective_date": "2014-09-01T00:00:00Z",
"expiry_deadline": "2016-06-01T00:00:00Z"
}
},
{
"name":"Chrome Browser M41",
"weight": 0,
"hash_algo": "SHA2",
"key_algo": "ECDSA256",
"hash_algo_expiry": {
"target": "SHA1",
"effective_date": "2014-09-01T00:00:00Z",
"expiry_deadline": "2016-01-01T00:00:00Z"
}
},
{
"name":"Test Platform",
"weight": 1,
"hash_algo": "SHA2",
"key_algo": "ECDSA521",
"keystore": "ca.pem"
}
]

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDTTCCAjegAwIBAgIIaqaClruOQWMwCwYJKoZIhvcNAQELMIGGMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEXMBUGA1UEAxMOY2xvdWRmbGFyZS5jb20wHhcNMTQwNTI3MTgwMzA4WhcN
MTUwNTI3MTgwODA4WjCBizELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNsb3VkRmxh
cmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHDAaBgNVBAMTE2Nsb3VkZmxh
cmUtbGVhZi5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASMRv3xvcv4I5QF
7we+23hES2waKDffBRhQMVVAOSIJcpb4JnzcVJiPJjNlMPbczi5vbzkQK2kkjOP+
okqQia3go4GGMIGDMA4GA1UdDwEB/wQEAwIApjAdBgNVHSUEFjAUBggrBgEFBQcD
AQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQU4t+cr91m
a5IxOPeiezgN8W9FBNowHwYDVR0jBBgwFoAUfb7i6rIruh9UuqhvSZlEoevO5qgw
CwYJKoZIhvcNAQELA4IBAQDXW01KmbTDyOcVvF+gU9tIUs0xjrEaMEmm5vwlukSH
AmliEUEAieRdsJsPgFxwzifBksZpa4J8FTUNs7tS1JIievAgtTj08wY/sdBhF8pX
s0JmfTyW93IJYOqJrgkpqHRH0bOfuNidxjzEeqg06GL1rnqD3wS7q4wKpJ0jR8Ws
iYK9URC6KB8WMLqJTOt4GwRSHfhink1iAc7Nlw2Id26o2kFDGHHyB4S5XsSc0YrB
hUuzKOkC1hHsvR07OBolpvzXsGR1S77UVOKvgpU5DiTbyjX2iycJGtBdhil+w0U/
Gq99AYLLfFKG67C1lSM5BLincp0AX2wYrY/hf8MTDdag
-----END CERTIFICATE-----

View File

@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBkTCCATcCAQAwgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRwwGgYDVQQDExNjbG91ZGZsYXJl
LWxlYWYuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjEb98b3L+COUBe8H
vtt4REtsGig33wUYUDFVQDkiCXKW+CZ83FSYjyYzZTD23M4ub285ECtpJIzj/qJK
kImt4KBJMEcGCSqGSIb3DQEJDjE6MDgwNgYDVR0RBC8wLYITY2xvdWRmbGFyZS1s
ZWFmLmNvbYIWd3d3Y2xvdWRmbGFyZS1sZWFmLmNvbTAKBggqhkjOPQQDAgNIADBF
AiEA+hlls8mNtLv47Rr8B7dGGKCDa1/qLHectmhdAnyrTVwCIFnAgTgiPAerNAct
KjOJZdHDuaBGeu5o+5SLD232m/2E
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIC2qaVydr67HuwWMrPQ3ljCVSsnbV7HbN78KqEX6a0GuoAoGCCqGSM49
AwEHoUQDQgAEjEb98b3L+COUBe8Hvtt4REtsGig33wUYUDFVQDkiCXKW+CZ83FSY
jyYzZTD23M4ub285ECtpJIzj/qJKkImt4A==
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICsDCCAjegAwIBAgIIDmHBNS+T0F8wCgYIKoZIzj0EAwMwgYwxCzAJBgNVBAYT
AlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJlMRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2lu
ZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMR0wGwYDVQQDExRjbG91ZGZsYXJlLWludGVyLmNvbTAeFw0xNDA0MTEyMTIy
MzhaFw0xOTA0MTEyMTI3MzhaMIGLMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xv
dWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEcMBoGA1UEAxMTY2xv
dWRmbGFyZS1sZWFmLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIxG/fG9
y/gjlAXvB77beERLbBooN98FGFAxVUA5IglylvgmfNxUmI8mM2Uw9tzOLm9vORAr
aSSM4/6iSpCJreCjgYEwfzAOBgNVHQ8BAf8EBAMCAKQwEgYDVR0TAQH/BAgwBgEB
/wIBATAdBgNVHQ4EFgQU4t+cr91ma5IxOPeiezgN8W9FBNowHwYDVR0jBBgwFoAU
QfmKIlIyJt+P8AcB3SRhOFrn7PwwGQYDVR0RBBIwEIIOY2Zzc2wtbGVhZi5jb20w
CgYIKoZIzj0EAwMDZwAwZAIwYQWcWr79DPrIBnphpHZPuxnGust6NtD0aSffB1cF
NlYtggjJZDbLijAgD0Bwi3THAjA639xrNxVgc/LkJcHfSRhs8Jhv9cxQxIVf3g8w
6tBymEgJ6L8aIPGgXNRJGs7FmPs=
-----END CERTIFICATE-----

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDajCCAlSgAwIBAgIIKkKT9A/MDD4wCwYJKoZIhvcNAQELMIGGMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEXMBUGA1UEAxMOY2xvdWRmbGFyZS5jb20wHhcNMTQwNTI3MTgwNDEyWhcN
MTUwNTI3MTgwOTEyWjCBizELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNsb3VkRmxh
cmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHDAaBgNVBAMTE2Nsb3VkZmxh
cmUtbGVhZi5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATWIrww4dyIpJG/t/zq
HIrcuCUF5sSz9p42U4WDbUeMuwcYsadOSi6KvILWs8nHA5JbnruDlhZ6ZpI8qj0P
tDCH/wSSqAOEOIjYgjf2XxevdC0f6WT6zEWblkhlW9vPdC+jgYYwgYMwDgYDVR0P
AQH/BAQDAgCmMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMB
Af8ECDAGAQH/AgEBMB0GA1UdDgQWBBTiTQoJuFODtNnEnbYaxy+He8lO+DAfBgNV
HSMEGDAWgBR9vuLqsiu6H1S6qG9JmUSh687mqDALBgkqhkiG9w0BAQsDggEBAJIu
Gg+4SJYA1vwlP6di/cXrctbfuFmxkHb+2+xMnJhRGzWZSk26ZRSfJmfyl4VR8x5y
oYFpvIV1JsihIKoVWt34gZJrtUM5yZLIqEpaIzQd+yxg3NtUZ+Br0YDYKJOJ+GOl
/uk5Nz10I5qqGZgCLTxW2riQH2z8hY8Bow7eh1SfazSVzuqOHGobaspbrxzkbqYE
OlJeGyg1YUsf6X2SlaGgO3uYon6gVanPj3e/I3yUciCLouD72PP2IY5O/bllVp1R
7VEgbrJAsfMtVywH5KV9R8A+VuxGj8nIh3JrckBlCfj5U9sicJbWIQbcjlBXn4/Y
EHD1hD0LRPKdq7eZ1GI=
-----END CERTIFICATE-----

View File

@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBzjCCAVQCAQAwgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRwwGgYDVQQDExNjbG91ZGZsYXJl
LWxlYWYuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE1iK8MOHciKSRv7f86hyK
3LglBebEs/aeNlOFg21HjLsHGLGnTkouiryC1rPJxwOSW567g5YWemaSPKo9D7Qw
h/8EkqgDhDiI2II39l8Xr3QtH+lk+sxFm5ZIZVvbz3QvoEkwRwYJKoZIhvcNAQkO
MTowODA2BgNVHREELzAtghNjbG91ZGZsYXJlLWxlYWYuY29tghZ3d3djbG91ZGZs
YXJlLWxlYWYuY29tMAoGCCqGSM49BAMDA2gAMGUCMF4FEJtaKJXcrj6ZHxtFGWp2
IIBmMKRctjcQLm46S6toh9oT/TQGvIYBTiyYmxWhVgIxANsA3GzCIPSiwhKiBFxv
026lKuw4Ci9mlH4pJ7cJnCgSmxHP6jr8O+XovT7SzN1zag==
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDAEwBewBsRvgqvyy/aJ0NsoTqkbwFeu3bL6rLxLGcxCfKzlOYz5te8j
BR4cPZbv5WOgBwYFK4EEACKhZANiAATWIrww4dyIpJG/t/zqHIrcuCUF5sSz9p42
U4WDbUeMuwcYsadOSi6KvILWs8nHA5JbnruDlhZ6ZpI8qj0PtDCH/wSSqAOEOIjY
gjf2XxevdC0f6WT6zEWblkhlW9vPdC8=
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIICzzCCAlSgAwIBAgIIbOxERQylZJMwCgYIKoZIzj0EAwMwgYwxCzAJBgNVBAYT
AlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJlMRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2lu
ZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMR0wGwYDVQQDExRjbG91ZGZsYXJlLWludGVyLmNvbTAeFw0xNDA0MTEyMTIy
MzhaFw0xOTA0MTEyMTI3MzhaMIGLMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xv
dWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEcMBoGA1UEAxMTY2xv
dWRmbGFyZS1sZWFmLmNvbTB2MBAGByqGSM49AgEGBSuBBAAiA2IABNYivDDh3Iik
kb+3/Oocity4JQXmxLP2njZThYNtR4y7Bxixp05KLoq8gtazyccDklueu4OWFnpm
kjyqPQ+0MIf/BJKoA4Q4iNiCN/ZfF690LR/pZPrMRZuWSGVb2890L6OBgTB/MA4G
A1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTiTQoJ
uFODtNnEnbYaxy+He8lO+DAfBgNVHSMEGDAWgBRB+YoiUjIm34/wBwHdJGE4Wufs
/DAZBgNVHREEEjAQgg5jZnNzbC1sZWFmLmNvbTAKBggqhkjOPQQDAwNpADBmAjEA
q/sUd8AQAornMMiLZ5spBu+g6x6qx66wNPw9WE5a+T0hndHJsAqads5ndW7/5fuo
AjEAiQ9wR1ugYaY56mj9UfjCZbwvo19unlB+CTLr48fh/RhvX6xjnpWXxJeXzU3G
GhTH
-----END CERTIFICATE-----

View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDkDCCAnqgAwIBAgIIQafPjiBzOXkwCwYJKoZIhvcNAQELMIGGMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEXMBUGA1UEAxMOY2xvdWRmbGFyZS5jb20wHhcNMTQwNTI3MTgwNDI2WhcN
MTUwNTI3MTgwOTI2WjCBizELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNsb3VkRmxh
cmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcTDVNhbiBG
cmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHDAaBgNVBAMTE2Nsb3VkZmxh
cmUtbGVhZi5jb20wgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABAEp3UqqoJpe6UV7
y9YR3a8SgMpoRotSKNqeA9JKi+3LyE6p5w3WsENvych1DQc4Tq7nvH4gjRPqO752
1eXsbmrAYwEK3F1RdexFJJ53DaKuUnYOqiI5pcHKjTDqkr2CABh3cE11Hg2vPHf4
3vpMp82ojKFzNtSJBSnnV7ab9+g+ziveqKOBhjCBgzAOBgNVHQ8BAf8EBAMCAKYw
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1UdEwEB/wQIMAYBAf8C
AQEwHQYDVR0OBBYEFDYrMNrJaqxCJjszyculXPsseU/yMB8GA1UdIwQYMBaAFH2+
4uqyK7ofVLqob0mZRKHrzuaoMAsGCSqGSIb3DQEBCwOCAQEAmytI+1j8r9KmCGEF
9iq60KycOXKlVIsEH890WrpvMLBspr0+NGDHCZA5BcVB0aOpyno8Vj/9y6qlr2Wr
+CNrChF3UmGTzROqJeAXH4N8gnOoZGSUVYBcNJnGgji/ZURsrE5IPw5d9oMGRRvb
q+JdUVlr1G7YGo6mtVHHUCG3okeUj7ddjLMCDGaprIPk1teqR/94jvcG+2cLgrge
riJMqtOI8EFckcvHfcA3bZ4G2mXmikWje2od0UaWT5VS1htCKJhlxAFTcaz3qg2o
T1hNaWSM72HiBCZERI+iwI0CmBBRkOIBOxznh1/boa5O0tWN4o55Ureh9rbF3xep
q2GQSw==
-----END CERTIFICATE-----

View File

@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE REQUEST-----
MIICGDCCAXoCAQAwgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRwwGgYDVQQDExNjbG91ZGZsYXJl
LWxlYWYuY29tMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBKd1KqqCaXulFe8vW
Ed2vEoDKaEaLUijangPSSovty8hOqecN1rBDb8nIdQ0HOE6u57x+II0T6ju+dtXl
7G5qwGMBCtxdUXXsRSSedw2irlJ2DqoiOaXByo0w6pK9ggAYd3BNdR4Nrzx3+N76
TKfNqIyhczbUiQUp51e2m/foPs4r3qigSTBHBgkqhkiG9w0BCQ4xOjA4MDYGA1Ud
EQQvMC2CE2Nsb3VkZmxhcmUtbGVhZi5jb22CFnd3d2Nsb3VkZmxhcmUtbGVhZi5j
b20wCgYIKoZIzj0EAwQDgYsAMIGHAkFlyII6rIxYiv7S5RwwMi8G0qACjrbb1SMa
oZA9vG+3G/SRcr5WmzKYgG09OjLT61KYfXu4mybdXXlXzHbx07llRgJCAKRuWU3O
3elclbkZvAGduasj3sj0Uee3nLG0YmDvz95sZPIp5JH54naeF4KKF6NJQF/rl9TW
BHa3MZqM3JM7vMkI
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,7 @@
-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIBnn+dzn3tVUMj9s3nRs8I7waob9iLi/QhsIj5leFRj44hbWGwfymm
OHLJR1jIG8VzyYaNssSPo7ioMpgOpX+R14+gBwYFK4EEACOhgYkDgYYABAEp3Uqq
oJpe6UV7y9YR3a8SgMpoRotSKNqeA9JKi+3LyE6p5w3WsENvych1DQc4Tq7nvH4g
jRPqO7521eXsbmrAYwEK3F1RdexFJJ53DaKuUnYOqiI5pcHKjTDqkr2CABh3cE11
Hg2vPHf43vpMp82ojKFzNtSJBSnnV7ab9+g+ziveqA==
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC9TCCAnqgAwIBAgIIUbwCGeeEj4AwCgYIKoZIzj0EAwMwgYwxCzAJBgNVBAYT
AlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJlMRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2lu
ZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMR0wGwYDVQQDExRjbG91ZGZsYXJlLWludGVyLmNvbTAeFw0xNDA0MTEyMTIy
MzhaFw0xOTA0MTEyMTI3MzhaMIGLMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xv
dWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEcMBoGA1UEAxMTY2xv
dWRmbGFyZS1sZWFmLmNvbTCBmzAQBgcqhkjOPQIBBgUrgQQAIwOBhgAEASndSqqg
ml7pRXvL1hHdrxKAymhGi1Io2p4D0kqL7cvITqnnDdawQ2/JyHUNBzhOrue8fiCN
E+o7vnbV5exuasBjAQrcXVF17EUknncNoq5Sdg6qIjmlwcqNMOqSvYIAGHdwTXUe
Da88d/je+kynzaiMoXM21IkFKedXtpv36D7OK96oo4GBMH8wDgYDVR0PAQH/BAQD
AgCkMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFDYrMNrJaqxCJjszycul
XPsseU/yMB8GA1UdIwQYMBaAFEH5iiJSMibfj/AHAd0kYTha5+z8MBkGA1UdEQQS
MBCCDmNmc3NsLWxlYWYuY29tMAoGCCqGSM49BAMDA2kAMGYCMQCKWeIUGeuvt9kb
5DtYw3++X5m7Nxf8CE67BuyoLV/3OpmTpo0Qp2LnapyXP63hAY8CMQCm1P3S/6+S
U6oMFvMrpAcIFm6B1TtuTnSRGx89eZqoCdEJHVZuBWRyFABBnkKSf0Q=
-----END CERTIFICATE-----

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIDGDCCAgICAQAwgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRwwGgYDVQQDExNjbG91ZGZsYXJl
LWxlYWYuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0C6SSsXf
use2IV8+6hSYqSPQdoQwZ5BYQnSxuKylArCrMXx8JGHrJP6Pj7GxRmH40v9u9VwZ
vcrQOm8yUTuzAEf2Kd3uvXmVKJb2vc0BopsflpSEOLEuddTSHlHgdVHylqpbzB7Z
rmyXXuWTtTFEaGmPVUmWcOBOy6pc/7hZv7HkTjaHLQu/uohic/NjO0oJaaUwds6m
uwTCNSmMvtvoP51pyQJeuZjYIoWnnu+/DbtZYmH44VbHD0U+uSNKLZa4beWqDq5Z
DwQvEVkuLqL331awzgIf0a4bhP+uc1kdWXZ8V+8aBbqtq6g6o9HdrzgNRR+9S3Ev
EelCrxuWw9FQ3QIDAQABoEkwRwYJKoZIhvcNAQkOMTowODA2BgNVHREELzAtghNj
bG91ZGZsYXJlLWxlYWYuY29tghZ3d3djbG91ZGZsYXJlLWxlYWYuY29tMAsGCSqG
SIb3DQEBCwOCAQEAguCRmg2XzRlcq6neK/IdHZb+EeXSPo1BXsXrhzZZTpDTw4pC
Kp+L9tG97t46rnlhRpwqY8zL/sXxBAlRB3G+VpsgLQzt18Gq0ZGBTjAHZBOeraKS
/GMzig241SNvvvqEQR540TAZnzRgJzGJxCGQkhaXKIrGoh6yqiiTUkn5iu+K737U
wX5xa09OdUnOc6MBbHFaynyWHZYjXzKv7zuZE+0VKjyKnLuHtRw8AS7zX/TkRf39
mgIp/hg3ZjWKTKDzudfMRVYS6nsbufViDTsOd7jMJa393H/wtKN2F+GyN8EIvuNt
eVECUulWhbugcCAv3qgpiTgyx0eDSLBu9Ct/Kg==
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA0C6SSsXfuse2IV8+6hSYqSPQdoQwZ5BYQnSxuKylArCrMXx8
JGHrJP6Pj7GxRmH40v9u9VwZvcrQOm8yUTuzAEf2Kd3uvXmVKJb2vc0BopsflpSE
OLEuddTSHlHgdVHylqpbzB7ZrmyXXuWTtTFEaGmPVUmWcOBOy6pc/7hZv7HkTjaH
LQu/uohic/NjO0oJaaUwds6muwTCNSmMvtvoP51pyQJeuZjYIoWnnu+/DbtZYmH4
4VbHD0U+uSNKLZa4beWqDq5ZDwQvEVkuLqL331awzgIf0a4bhP+uc1kdWXZ8V+8a
Bbqtq6g6o9HdrzgNRR+9S3EvEelCrxuWw9FQ3QIDAQABAoIBAQDFQ5vzplQ9lIgM
T0g6XpHZk8oww0lqmOhI8HKG33Dsf6N4HNE1WGOMhnpaWrH0U1mH9eqaLE9n/Aob
lMpFFyCin42uVlGm0NJ5x7K+Xsex4POpp8kyPxIbLTJ88HCUOrZ39a1OWd1C3jsA
/OFdy/VaSsw6sKQRCTsg2amN1o2UibDJYVW47ycv9cwjk/GEzzOSq32a9o6g6Gwd
g3ycroIaxhDlGjS5l0IZ/ozhN+AS5dYcPgJRsYD/jTBqTSzIW2ePrcheznoRcgLK
bb+UVQC+PZX8kycCcerPbcGc2YcBpZgmIkCj85+ITFt/BhH7+TSH9G7F8LTKAaJg
qlYKF14BAoGBAPz8Jx0vAcv/4zIfCckuNy3kVu4PHBTMTBO5+tUg6CZgktRrroiV
+Zq1lCuj2/Px3Lx9oaUie52iV5xgmEEax77xa1rVezY1PhGSFmngHqfumUJf8EEB
snlAUpwBHvWU9B9OxKOHRrD9Y9ptXcBK30ZHLJT4t5JvbHVrKZF2J82hAoGBANKp
ue+dOafhgc1F/ThD2VLuIi6Garf1pqNG3OMugMfieHAmr1RRYWwFErLoijt9dpe9
gXVecUm1KO4/0ZkR+7YDzUSifXvcizaw+XqjrtFerrz+Yao4gZssFnw/sLc2pbWm
1DHWxRnmh6MyHEEiA0KxElgutswhP8GIKN7INOG9AoGAR1sD2Upp8lVBiuCQTQtZ
CvutvUXLwN4C00mQw06dzD1PDNU2jFXo6kcu/MQiBQOCJDQ3RLGeNk8U8QmZyDs6
fdPwWNWABEEuOZx/7+sEGo/E8KDIzj0hTuvioRf72H7kAHSiKBG+0asW4AQa/mLf
6R2oKHiipo4BBHluZxXxkiECgYEAuYXnzfH0+LhMi+77VjXKipJVYAvYqDGak2iw
1xH5MA9uabZn6iXRWkQNd6n7MvEHJBMsk6ScuIDmjwt9FwUTW/R1LeC8CfzsTToG
O88zAggUczTD5hjlazakhr/AbVmfDh7h+RJferPe+AYFhAbkQDOZKDfbnGIbt+Cl
va0rhTECgYAFb38TvJmEIzB1/nZ7sKbFmr2pYgzBqspQcprws6gZlWydd4OoTZiv
QzSBDi3tGt07yJuntVlbuI6qejhFMmonGZuntNTvTZMmx2+W/F8EGByfWpLtB9W5
S+tx5/0d4MhOYHlt0EcdC7j881swY9LCrc/EOqg1O4BlTJ5+UJer+Q==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDfDCCAwKgAwIBAgIIUYJhG37C300wCgYIKoZIzj0EAwMwgYwxCzAJBgNVBAYT
AlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJlMRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2lu
ZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMR0wGwYDVQQDExRjbG91ZGZsYXJlLWludGVyLmNvbTAeFw0xNDA0MTEyMTIy
MzhaFw0xOTA0MTEyMTI3MzhaMIGLMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xv
dWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEcMBoGA1UEAxMTY2xv
dWRmbGFyZS1sZWFmLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
ANAukkrF37rHtiFfPuoUmKkj0HaEMGeQWEJ0sbispQKwqzF8fCRh6yT+j4+xsUZh
+NL/bvVcGb3K0DpvMlE7swBH9ind7r15lSiW9r3NAaKbH5aUhDixLnXU0h5R4HVR
8paqW8we2a5sl17lk7UxRGhpj1VJlnDgTsuqXP+4Wb+x5E42hy0Lv7qIYnPzYztK
CWmlMHbOprsEwjUpjL7b6D+dackCXrmY2CKFp57vvw27WWJh+OFWxw9FPrkjSi2W
uG3lqg6uWQ8ELxFZLi6i999WsM4CH9GuG4T/rnNZHVl2fFfvGgW6rauoOqPR3a84
DUUfvUtxLxHpQq8blsPRUN0CAwEAAaOBgTB/MA4GA1UdDwEB/wQEAwIApDASBgNV
HRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBShnoK2Oquaq/XjlNBMxs5yPTSJvjAf
BgNVHSMEGDAWgBRB+YoiUjIm34/wBwHdJGE4Wufs/DAZBgNVHREEEjAQgg5jZnNz
bC1sZWFmLmNvbTAKBggqhkjOPQQDAwNoADBlAjAhMWEJzBwuN5bVACPCAoVPSWI2
+0DQi4Tu6sBNQl+dsyO+FPyA3+aYc0NgnBwcj+0CMQC7JOdfdWJPZj6rOAXvGV3I
jGJRHZmu5q5K+9teIK1b9mustpnDJgniKAHtBGecXy4=
-----END CERTIFICATE-----

View File

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIEGDCCAoICAQAwgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRwwGgYDVQQDExNjbG91ZGZsYXJl
LWxlYWYuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA9xYBDoV2
tPx8lqZ/bH/wLvoPsg1/CXeknvRcNuxw1gu6c3IJBrKZlkFtiU6Y8FADiUBOVab/
Y0cQ/9EdeB2srPH4M5KNiPdWZPgxARWnRq5Ez8pvVASP2E2Zya1UnH5iJBau8e6S
wBl8UaXnGwcA+CUv+FXcZtdoFh0Lqt3AdItQOkHVjSE6Cfiv5lsSW0ikMcoHFOHN
ps4/9A4A/griT5lRDqQIycN7WD2k4+aKVreCWxbSteU35yIDJV6PGUtw8k41arJ+
kwuwYM3+YklR0Dsj0RxXn07oLqnf6IeNUogGhNVO7RvLdpfvrhlevHVXmmYj40fk
GjU15KkZOKigMw/gDInI6Sc2jp8oPX9tjkaQYkF2t7AWOq01lh5TleMIoBFUqVcy
+X/qejla0JaKCEyt/fiPUo7/SgucyFl8GrKfSdELUOKx5Vr2ZZ48QSfIlXle+tGt
FD0AYUsO0ud0wclW5C+g8E27raTuR4RaZOj8/pmB7XNDszwxQ/97dBRpAgMBAAGg
STBHBgkqhkiG9w0BCQ4xOjA4MDYGA1UdEQQvMC2CE2Nsb3VkZmxhcmUtbGVhZi5j
b22CFnd3d2Nsb3VkZmxhcmUtbGVhZi5jb20wCwYJKoZIhvcNAQEMA4IBgQAKrSiJ
qfeYzFQgCx+lj2rTDdGbiB9JoIamyTULWoN4WCxwS8KJWFQXOf4SkibHNLMMqBFY
RpU/5mvjXVrKboNgzp6+QoWpdN/AHu6ldFz+o3Imna1yEscGZA7Qfie5hrf9kePe
PCPEqnsG8j9qyip3W3p9/SsM2xUaei+YGVmAyzpXlYq0WZGsz+wVJ2zc6ZcxzTsC
HN8cYafVR0ZmhruRUjhRM9mI+XXFYjk11lNo907Hue5n1acvqofz4RID2rx4e2nq
2DH4HZ1UvPDx93FJmMu/c8vLyMz17wPXCaC2M1SeVdXQeGg7JETvL95hJ++o3vLL
/QJehGooK7Rcht4lc1logn6tQYNyRpIKiN6Bb+lBujTzVT461yPTk8D9xY0+jHIO
nKXIXKVkoXXiL70aR0ZCviHx4sNOSZyqwhwiUedNP0rAacbk7AY4cdJWSHvcVH3/
qKlTkwOyr5AGX/SK/JTDvVjQWW95OI4a1xqEMlCN5jMOrQFwa181JMx4cmM=
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,39 @@
-----BEGIN RSA PRIVATE KEY-----
MIIG5AIBAAKCAYEA9xYBDoV2tPx8lqZ/bH/wLvoPsg1/CXeknvRcNuxw1gu6c3IJ
BrKZlkFtiU6Y8FADiUBOVab/Y0cQ/9EdeB2srPH4M5KNiPdWZPgxARWnRq5Ez8pv
VASP2E2Zya1UnH5iJBau8e6SwBl8UaXnGwcA+CUv+FXcZtdoFh0Lqt3AdItQOkHV
jSE6Cfiv5lsSW0ikMcoHFOHNps4/9A4A/griT5lRDqQIycN7WD2k4+aKVreCWxbS
teU35yIDJV6PGUtw8k41arJ+kwuwYM3+YklR0Dsj0RxXn07oLqnf6IeNUogGhNVO
7RvLdpfvrhlevHVXmmYj40fkGjU15KkZOKigMw/gDInI6Sc2jp8oPX9tjkaQYkF2
t7AWOq01lh5TleMIoBFUqVcy+X/qejla0JaKCEyt/fiPUo7/SgucyFl8GrKfSdEL
UOKx5Vr2ZZ48QSfIlXle+tGtFD0AYUsO0ud0wclW5C+g8E27raTuR4RaZOj8/pmB
7XNDszwxQ/97dBRpAgMBAAECggGAcWoWPhYg8N5cScJPBvyKwOVjQvVS9IOIerXr
hgJtoLJteQRFBGACg6ewobAEH3p6xQtRaZtn6qf6M5JHFpV4Z0ICDZodgVsWuu35
gGfyCk1/pGllRIl7hWvJRXtcNSEF507KKp65mZeZKtkeBZfnZ/+Zz0GKE2KYkl3u
txVme5he0P7bCRbRTzZpdzEicegcBgaXzYwAG6rcTCgJaJKSYrsbK787kXE7MrvI
7hsqMLe3DByjx35ZdKx2CTcoNBId9RODWnPpANVrlNv7kbaZRqd5OI8b7JfblFsq
F6vCzvDq+Quc8ID1zxRZv761pexejtDzghgQy7X2EVvMlHh4//wErgq6WfPjwyvU
/zZczO0L/c1XwwkfBU6Yf6UuYCKngwifgvb7aGU4/aGNcD5SHRITwCHK/E9JrkR8
pkqerMxsf9uP5FxGdwOm1k77Lkap7Kx2Utt5l7stOY0fFUFz1YQdAHJUzhmbP3Zy
C+TeX2/9+CudXM1parW7HQRlZeMJAoHBAP545khACfRvUWpxdQohp1Ol0FuDosYg
NC75q12T8ovllx8Qly3aafJdd0NTvFmrBkBPTL3pCUWCyGZh6/E00fUL4dtD3zwz
QUbm6hWGTgKHdeLLdae2wxcZ/NqmTvpY9o/p4jS9+StRKQtdsftLKCmRv7wfYkju
UT7O+gRyGat/Rqpr9cTSKBXHUT+WJlITDrwk5QdydF7eKzLT8DROgcRRE1+FMJkj
pO5ChuAxZr0Q0fISRm9Lu7aJ3H8QFfboGwKBwQD4kcCkZvRdz8BQsOsyHQ3SlGhx
5nwA7SPadXtfnpoW0ZlEdHwkPJzU1Z50z1ulEQymBTARPUQ4s28MQt8NXuRzHBrW
PMUGgsspzT6FjiskhUc8k9PAZbEJE/axLKK2qSKktGuZj+VFih/9XPPTX4xSzlOg
ntJEr2tc3TIv+JEOuJX6VT2URFLXgdOHXxAejS0DTGIg1aB4VGQpWzfbcJ6Cyf11
YyoyYWA25wdw7sB9kDHsd0Ej0mld5+l8JOd8hcsCgcA9jCpOcUa3GzF66EQhljAt
WB6D89urxeA5OGPNN1pjob0iY1XdXkVfvGF7JEaa/XV+mm96Q2HdsRsdQDPb3CWn
+h6/dLQKkG8KYhFd8WTu0aqelw026kpXTQ7OJ4lUna3M8wmmLgiVBIVD3X6NxAjL
vRe9vW19LD70TQVFi/9PbnI+B+yilR3i3pl1IrDUCw32TYojefhRdbTHD2G6lP5n
6CAia0ls0KU0h1yt3uT1d5r/zJHCm3OkW8W76b0WQd8CgcEAh0czk4WgiomtPXz7
k3tycV9pdEuewxZMQ/FaIpD7hV2uzy2h/kqqg756jVHoq24a9yOtpEQ2o7Erx32B
TRKOvALYrC3IgKGgFfDojODxo9+RBGvjezsc3TbrNEN5jnWAMCkswhcpDO5+OHJl
FG1UviAiLTEieFUL1i9fx/G8aEmW/fV0HQQOHdE/INZgvG/Sxo/Ee+AnhDVRiZxm
StwAuGdbtI4ygday+U5Eo3acdfmK4gmI/wjdZUj4riKbhQ5/AoHBAI0yzo+PIFi6
HjNYVoC7rZ39oQ0YCrEWrui+DRdEjnjec31Jw02AtKnv5swpDDHjgnIcd9ciQY48
rk7eC6IkVrL9hOxUzC9YQZX/2MBiOLjUkDkSLt+d5PL0OXiSg1O4fGJdGiVPF0Fc
sF9p1UNEfGvXjzUB3ay0kMyCLitNe1BCvJlYXdSV9YmAMNvguE7TNU3OPiVv65PK
6OndznX41Pw7OlnLaq1sFQcYBmf5E7QSKYP+4HeV89Sc824VlCNxwA==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID/DCCA4KgAwIBAgIIFVfMGJwEBdcwCgYIKoZIzj0EAwMwgYwxCzAJBgNVBAYT
AlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJlMRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2lu
ZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMR0wGwYDVQQDExRjbG91ZGZsYXJlLWludGVyLmNvbTAeFw0xNDA0MTEyMTIy
MzhaFw0xOTA0MTEyMTI3MzhaMIGLMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xv
dWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEcMBoGA1UEAxMTY2xv
dWRmbGFyZS1sZWFmLmNvbTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGB
APcWAQ6FdrT8fJamf2x/8C76D7INfwl3pJ70XDbscNYLunNyCQaymZZBbYlOmPBQ
A4lATlWm/2NHEP/RHXgdrKzx+DOSjYj3VmT4MQEVp0auRM/Kb1QEj9hNmcmtVJx+
YiQWrvHuksAZfFGl5xsHAPglL/hV3GbXaBYdC6rdwHSLUDpB1Y0hOgn4r+ZbEltI
pDHKBxThzabOP/QOAP4K4k+ZUQ6kCMnDe1g9pOPmila3glsW0rXlN+ciAyVejxlL
cPJONWqyfpMLsGDN/mJJUdA7I9EcV59O6C6p3+iHjVKIBoTVTu0by3aX764ZXrx1
V5pmI+NH5Bo1NeSpGTiooDMP4AyJyOknNo6fKD1/bY5GkGJBdrewFjqtNZYeU5Xj
CKARVKlXMvl/6no5WtCWighMrf34j1KO/0oLnMhZfBqyn0nRC1DiseVa9mWePEEn
yJV5XvrRrRQ9AGFLDtLndMHJVuQvoPBNu62k7keEWmTo/P6Zge1zQ7M8MUP/e3QU
aQIDAQABo4GBMH8wDgYDVR0PAQH/BAQDAgCkMBIGA1UdEwEB/wQIMAYBAf8CAQEw
HQYDVR0OBBYEFFRI3nOBTv9Gq7OQNv8dWdCiysNQMB8GA1UdIwQYMBaAFEH5iiJS
Mibfj/AHAd0kYTha5+z8MBkGA1UdEQQSMBCCDmNmc3NsLWxlYWYuY29tMAoGCCqG
SM49BAMDA2gAMGUCMQDAZV84hdNMZORoY35qBjTBSDfgZH2RN7EQHBr01G3rRfrr
0pfr7IGqmUfC8ca/Dc0CMDM0Gk9ulfiXhBg/Ewzpru8UVX6/hgbhPnH9GiGq/8XZ
5HC9JXjnDj10F8BHD11QzQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIFGDCCAwICAQAwgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRwwGgYDVQQDExNjbG91ZGZsYXJl
LWxlYWYuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtY3sRFA2
cmwm4bEttx1TVRENJnd1re3fiR8YccUPcnmZ3uNY1sfnaEiHfTsxk10hbLOo24de
YAZSC6w4W1ErGZnPO21kTrdlKUysmqfwLcjLGvTj7/3HKnbpfFQx3sV91+InI5HP
141mn78/Zgw22SZizysbn1x0QpnjK9WFZLdY6o7hNkAp53Jx9g85PiRROcLh+EH6
WMkxUUKx9zie0MPydFkiBlR+nGZ9SO5DGPKJGPVk7YF5n2XQNBWUXUq+cLqisAkS
Op7FB1AryMiQCLVp6FATt/CRXA0O3d0hd9HirnLU6QMf6SgguYzFw/VjWF7AoCdX
gNhtAo8hB5wR3/srInRhDz2YKhcTC8F6gUErCXKY6QF8QV8I8H10/Drp2MJwlxW1
9AfmSFogIs/Y5KPn0kMmcUhtMtMtx1xa21OdmbgD0vbMFE6cqoKdSYfImhK5tKfa
xgQu/jPlshBztYp6jXtlfcYVQ4rcnHM/hqm6HJO4hh55U6wrw3OGv/HSfwjs63oS
JJgqLzs8WWVJKahWozCotyAGrIF+/mCcsciMm7NsWWWsPizB275nDWh1t4zhUq3W
P4A46klZqF2UuNCkxJsh4Dgz8C2xMReRmPkDN/hTE9iOPAunk8xL1dqtooLxGSKf
oO4YLlBgEqYottodEFG3LUEycps65m4eIAkCAwEAAaBJMEcGCSqGSIb3DQEJDjE6
MDgwNgYDVR0RBC8wLYITY2xvdWRmbGFyZS1sZWFmLmNvbYIWd3d3Y2xvdWRmbGFy
ZS1sZWFmLmNvbTALBgkqhkiG9w0BAQ0DggIBAIry/y2+Q9mLxlNZz7mKemrqj5Iz
b+0IyaM6uReys6O1YcRf2KfnZ4TtURRa1ehjqOJsyYpLFEtzvATS9SktrcL/YnvL
kWctJWEGJ0PJvhMpAy0uZy9uwI7moltcDtr1HdOG2riqfbhxTY+/g8mFhqWl5vFj
S+ok7sSnztN0NQmDpXfuAVZIQQwEioeSDrcT2EcCf4ltuB23wzTMYqhflHZucEB6
eDr+7n8zcv4pXHvING6yR5G8eklR79zjlxO9QadNBjCWVllz9c37FMk6CvANfGLs
YbIJTXYVPdmbyKMuYzIzgL1RiqTX4WNUVI1AputdGXytGkNPl7KbVvHsyp/A6tLR
fZu6WW2NwjDC2s6HBYseo8huEIwG8zXV2Et1+yGZB6YHsw5Jv6W4UHD7pgpMiacg
G4FOex7h66tTPGGHKmuouumCoGf2Zyr7oVeixx8OHwrl/tzoiyRzcmqZ+wW7IJA1
Nx8exenau8Kq+lCkj8dJObYNZNEQ7Hljo5w2ATChTXYaJYc49KOoSKd3d/YyVqIQ
Qoeq+MTmxQuDcWbNZCgDnQZEPqW4Imv1cFApPH0t5JCRSutd7WNnNtC+kSPcXYhc
NV71ovaHrio/7bXeNYKxJoYrfTe2mxMwrsnfuCwK2TgrfAL9yVQ+pfn2StopJOaN
iznpt2q5PEkYOJEj
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJJwIBAAKCAgEAtY3sRFA2cmwm4bEttx1TVRENJnd1re3fiR8YccUPcnmZ3uNY
1sfnaEiHfTsxk10hbLOo24deYAZSC6w4W1ErGZnPO21kTrdlKUysmqfwLcjLGvTj
7/3HKnbpfFQx3sV91+InI5HP141mn78/Zgw22SZizysbn1x0QpnjK9WFZLdY6o7h
NkAp53Jx9g85PiRROcLh+EH6WMkxUUKx9zie0MPydFkiBlR+nGZ9SO5DGPKJGPVk
7YF5n2XQNBWUXUq+cLqisAkSOp7FB1AryMiQCLVp6FATt/CRXA0O3d0hd9HirnLU
6QMf6SgguYzFw/VjWF7AoCdXgNhtAo8hB5wR3/srInRhDz2YKhcTC8F6gUErCXKY
6QF8QV8I8H10/Drp2MJwlxW19AfmSFogIs/Y5KPn0kMmcUhtMtMtx1xa21OdmbgD
0vbMFE6cqoKdSYfImhK5tKfaxgQu/jPlshBztYp6jXtlfcYVQ4rcnHM/hqm6HJO4
hh55U6wrw3OGv/HSfwjs63oSJJgqLzs8WWVJKahWozCotyAGrIF+/mCcsciMm7Ns
WWWsPizB275nDWh1t4zhUq3WP4A46klZqF2UuNCkxJsh4Dgz8C2xMReRmPkDN/hT
E9iOPAunk8xL1dqtooLxGSKfoO4YLlBgEqYottodEFG3LUEycps65m4eIAkCAwEA
AQKCAgAAn5EdFu1o7SghBDu08jvUAe/6ntRfmX53+Qxbb6LC8NnvYvZuHleUCxO0
AV0FNX+k4OUGg+t2Bu+HLLswzRGJz/ZfLNv4TTbismmxSjxP2+2elRKnQ0bIxYm9
rIhTTHhHInah76E4Czs79ysfjZEuo6wZK/u3S1j21ZJrFxuTIfIDNCRfzE6YhdMQ
VjMLHJLO0PV3pbpXTbGGpuT3hVE+RD3z2k58mROqM8vgUTkXv9VqqYUEL7qcKnxR
gXV18IjA2FMwqYdPfjYM9WCBGvcroHvRmVzH9+J332+aoWS5BZZypOBIQIN+iG28
VVhkeNYzenfM1PW+8n9FT/p8DTQegEJItmWOUsf0eNE6beHEOJZAuqnmLoKpD/je
4DXmcWaBxKTyCjLdG6BHPjAoF/XkNIW5gMXO1b3iGDYBWl9ChWfpzwuqVWDJerAy
9UH3VLPEHpZOahs6GKxMkYr3dAQRUDGW/Cj+a28VaAoeI99mszP5t95rUjq9YDx4
FI29BOwQOjTiF8qtjsJujrwjnbYbWV8xDcho4oNPGskzOTIxL5lOzzhWCMdo2qg5
skG6qOmJo1Ccezd2m54njHi46vj0tFePPnYcFtUZjnCxWmh1N4olc5pcmQA+y1j0
516JACIyd1VQ5OzFL/DwYYR5aUekZoDFqr3AEKadMgvC/WTXAQKCAQEA2NWpbe6K
tYNvVk4XNyG9IzT/KfJ6uVAbwG5gebVbmNF87H20l9gbzPlZs0hmFw8sYPDVUVx3
OuWD0/ertCrKANX/9cfWa5fk8gmC9ESxOR7qMdiKF/9WUGfgQEfcyXms3L6N/xIv
Ds/WOZMBnODHT7Y+th95J3sQqHy/CwYw5k4JIDXjaEVC8LawdkUjIzjnIJN+Rbou
CD/5HFOmlg9vRaIXi5UY8lG5g0J0wLNXISF6SS+ROEOD6PKS9+M49vhTcbpOzKOL
DkMqnRM8qFw58afX7/g3jNLUZLRRVd4xXsIhTnLe4+117m76cyaqPV7Hq0Ss5ZyD
f+oBiv7KTusxqQKCAQEA1ljrKo0BTbzVLlZPb/SGRmHE+W23xYH3Y4P635gzEIRB
fqptIvRAWTTC4rC7c2aLat3tlmm2TG2zcOAiDeP1QltEeOCIcOXXNkGjDd0LmaVW
utkzMdRhBmK3u0GteZWI8z/G4ZyaddhhzLV9RB5hXcw0g46YLGY/oN+U4VAeypHx
nqI181dK+D7L8tJnWkWqqUfHpiNAXPXI56rDbetCv8tU5/WuryaZEPHqbLhbqhdP
ovvxcRXG4FzoPc/gY1db7fm46WXQHBJ2cZ0NWc5Clk27VxLzxsptzesCYE+juSKS
zldIww9WkAPbvGS5eKgqLqYh/CCSiwjmpDHUQ803YQKCAQBUoVgGsyLqY4lSCxqe
hwmWMzogOibSK0UZnzsCZdmBVMpIV2vkFBINt4jeI7TM7Twp/fWUUt2qXChO1Azt
PgInv16upDe5OMi/+xxkkGcHX1yS5exIH32l1lU9YY74CAiDA9DSLFu3kUEQqaLo
gwbnwr7JQJF96ld/G2lJOTpeuThwnPfMG7Rb1UIcdzGWrr/vBAI13svWpnlpJ/EO
AqowaGp+LUxWT7VzWL3O9HBeWv2qkOlCJ3/VrM/V9pamNhgDfG8DChXdFDQOqJJ1
N8HZ4uOyIpQz35nMUGCqfhWQ4X40azs5hNYRoLkZto6dc1/FJgHBgIwGoePGR1nY
4Y05AoIBAGjH/OXHGj0LM1c8gAaljUI4pxabiPt3Bh1Stj+5YjUPwgiOfV1Z817o
SOCSLoBCP6MVLACcWq5P7ikel+ccaZdvkDBa6rft01/FhFTRmssYJSaf6MPSI0AA
3/odKBVIgTMQGUPOzz8OcPimO78h7szwdzdcbI0/ypj00w21oee6ole+ygrTwGVM
JVzld/qMFdo8qZ9QmjUXPYfqVTCVkgK5/h6KXkNm5ep/p+5PzRd/38E30hZK4/Zn
1GvrA7DsUpcpvTfzOmGOsPHnKVCeYLSi+RKexCaIbFb+LCIyrEFjYkqWJo6cH9+0
0yTsRDJ0WnKFahWbQqfOyRi49x/R4OECggEAGPyBEn6253gPawT87wIEvTlaoowp
Of3aqpCVuksb4GinD/jGPmGwUOqrHGD3YEuIPSMQ2oBeEaSMPgMutUwTMt36J8rG
L2zG5N4jvj5gv1zStdV03OEAae2QgTwNX6vI5NbQLT4R9yd0CNVsbRJrC0PJjEoi
d+SOCU8jiYx7/h68n1xbgtkS7sFzXc4DMc+uUKoprTloZj7pKLX/NqpUKZKiWwAe
Ss4mRqFIywC7uyazyr5PwOyuPE02PgUadeAJOsHTVspADf+gMG8R1OppjPs6Lqrj
m2k2JUCh7tjMGyePf/HqILgyByWSXXuTlvUXD6i9qDThg+QbyZhGcPOtAA==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEezCCBAKgAwIBAgIIZP3PePNium4wCgYIKoZIzj0EAwMwgYwxCzAJBgNVBAYT
AlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJlMRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2lu
ZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMR0wGwYDVQQDExRjbG91ZGZsYXJlLWludGVyLmNvbTAeFw0xNDA0MTEyMTIy
MzhaFw0xOTA0MTEyMTI3MzhaMIGLMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xv
dWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEcMBoGA1UEAxMTY2xv
dWRmbGFyZS1sZWFmLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
ALWN7ERQNnJsJuGxLbcdU1URDSZ3da3t34kfGHHFD3J5md7jWNbH52hIh307MZNd
IWyzqNuHXmAGUgusOFtRKxmZzzttZE63ZSlMrJqn8C3Iyxr04+/9xyp26XxUMd7F
fdfiJyORz9eNZp+/P2YMNtkmYs8rG59cdEKZ4yvVhWS3WOqO4TZAKedycfYPOT4k
UTnC4fhB+ljJMVFCsfc4ntDD8nRZIgZUfpxmfUjuQxjyiRj1ZO2BeZ9l0DQVlF1K
vnC6orAJEjqexQdQK8jIkAi1aehQE7fwkVwNDt3dIXfR4q5y1OkDH+koILmMxcP1
Y1hewKAnV4DYbQKPIQecEd/7KyJ0YQ89mCoXEwvBeoFBKwlymOkBfEFfCPB9dPw6
6djCcJcVtfQH5khaICLP2OSj59JDJnFIbTLTLcdcWttTnZm4A9L2zBROnKqCnUmH
yJoSubSn2sYELv4z5bIQc7WKeo17ZX3GFUOK3JxzP4apuhyTuIYeeVOsK8Nzhr/x
0n8I7Ot6EiSYKi87PFllSSmoVqMwqLcgBqyBfv5gnLHIjJuzbFllrD4swdu+Zw1o
dbeM4VKt1j+AOOpJWahdlLjQpMSbIeA4M/AtsTEXkZj5Azf4UxPYjjwLp5PMS9Xa
raKC8Rkin6DuGC5QYBKmKLbaHRBRty1BMnKbOuZuHiAJAgMBAAGjgYEwfzAOBgNV
HQ8BAf8EBAMCAKQwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUliwSq0YZ
SDgIUZ7+1t5Ntzb8sYkwHwYDVR0jBBgwFoAUQfmKIlIyJt+P8AcB3SRhOFrn7Pww
GQYDVR0RBBIwEIIOY2Zzc2wtbGVhZi5jb20wCgYIKoZIzj0EAwMDZwAwZAIwGTkD
/FuSQ+VDGKZ8UM6kYAFS30rvi5/vScTIkAFmAISfyJF63Puk7gesDzkzV0uNAjAZ
QPl9/aXIud70gp7SRmTEWtqc2sohR2UT2OBw6neTVxxM6GWZqAGTZCu7++Z2fDw=
-----END CERTIFICATE-----

View File

@ -0,0 +1,35 @@
-----BEGIN CERTIFICATE-----
MIIGITCCBAugAwIBAgIIem6puVxiMz8wCwYJKoZIhvcNAQELMIGLMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEcMBoGA1UEAxMTY2xvdWRmbGFyZS1sZWFmLmNvbTAeFw0xNDA0MTExNzI4
NTlaFw0xOTA0MTExNzMzNTlaMIGPMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xv
dWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEgMB4GA1UEAxMXY2xv
dWRmbGFyZS1jdXN0b21lci5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQDPVV24keFMsRxmPwskbKfN6WcCeRTBvzUpkz3wUcpae7HY7Mzb6Ah+RMWr
Z4ufbDJfvoM5mTra8dKWMAeWplGmDDqNeGDEYHz5k1NlB/WIIY/TdwL79idjP+m1
nwk8h4MoXIlQmw4OUFXZQPYgCsa3R6skxSr44n91gqhdvImiOnUZeiuo55oKe8nZ
uw4JbKRraEJCDJdFKL1Pu1g6QxNenGZvQxsolLZIjg8VbBFbhjlXAS467drHByPM
MHl0QZFAAvOuGNXwWQDFB9K2ow8iE0P1MFnOCGUgj36rjTkpQJ3Ppl2/MZm6ZAFe
bHcT//lA/8h+3cD3XW6WCcg1tWwVTPYqTVjU1KdXk5gGWfim4PRPRzd9vagmasdw
1Wo5xFgkzciHVgGXYeBUK8k1nLS3CT1ws4VIOfib6TsR5mbZxzc2IuJXGZr4QjBL
69qYoF+nTLN9vcGz4gYYx2bIwioMPA9fEG37Au8sKhzZnczcU9DfLnfFHwn9qJBw
zmAqRpv1XGyNj2sCUjopNFvOaF701+AiWQ3s80N0imeD6qYIVoCBp4DrgI0ICOwk
F/XGvATQvqlOiHz7uS/yUvTcOArNTlpsYxHj/ZMn9W5pmAtTMDv1LgVLTYvtyQH3
EEmZwHSK4FNv0+lURj9C91zkoMUg7en7Fdym4VToZOnP5TGyqQIDAQABo4GGMIGD
MA4GA1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBS0
Cf35SFvopXmp4Wk2vaCMnyzYBjAfBgNVHSMEGDAWgBSWLBKrRhlIOAhRnv7W3k23
NvyxiTAdBgNVHREEFjAUghJjZnNzbC1jdXN0b21lci5jb20wCwYJKoZIhvcNAQEL
A4ICAQBiHHSRGe8Rw0/5uHQwPaLtEYhGL2pTET+8MPOd0XRk2LBsNq2T5S2q6qPw
ccwkOSKjGYqGZZ8aIGXcMSNbkY7H15T25l1zyGYBl2C0+5fqXBLu/StJPg836Q6f
25FguKlAg0v6NAtWQnpTS0CmmC6yp2d5usufUsiBDxqALquMiaVPPpYt8AIYQz0F
3KJuURgVNzzOT+kedJKmxyJyCwUzcKvDVlwUWK39ws1Z61FcJ7baRASIDj9cfJmf
pXOGqzKccHi1Sy5KuZBGsZ6f/a0cIB6n2J6Ww6pDDnRrWeh7pgdT8eBsxzacLOSi
EjKy8CSWzqkY0RM0HOjIn6meHQWY/SUWgfZSv2Ro3rdZrNN3K4nTz44z8ZYl3oxy
NGuJM4kcWCN/A3gRPUXwihOsvX697ebzK3FAHB8gFv9Eqtkq1OV9OkO5BkPyFJqQ
nr/4ajm+ZOeZTwENGZUBCmuMM0q6W7jV8eib/UVetQ6gHYLlARZGv6NXlHatKBIV
3vnmv0YRYfLzV/uqYjXN+53VY0WE/iyk9UTyPtuONUEtFRWCyWPk3oM7ONWLBJRk
hZvc2WbKpzzH7jnuTsVyixHXJmtxJtP9kiegd6JfzxMQ8zZf4BATi45h0xUYNl4u
R1WiNAwSy8rUyCI39Tb4l70fi6O/K/VmzzsOVoPkTJmyu8S36A==
-----END CERTIFICATE-----

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFLDCCBBSgAwIBAgIHBEK/bqLmmjANBgkqhkiG9w0BAQsFADCBtDELMAkGA1UE
BhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAY
BgNVBAoTEUdvRGFkZHkuY29tLCBJbmMuMS0wKwYDVQQLEyRodHRwOi8vY2VydHMu
Z29kYWRkeS5jb20vcmVwb3NpdG9yeS8xMzAxBgNVBAMTKkdvIERhZGR5IFNlY3Vy
ZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjAeFw0xNDA0MjgxODIyMDlaFw0x
NTA3MTMyMDI0MDlaMD4xITAfBgNVBAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRl
ZDEZMBcGA1UEAwwQKi5kcmFmdGtpbmdzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBANGdW/XXPUTqrbTkDwOEKD4aN7t6qdo6kbyVeozX0WXfSuVG
JoFPnHYRc1NBlgRpz+2fcWJaAm61IRg2OGVbgM86JqI1B4/TnjHTgHqtxiu8oyKF
mAJFV15ZEqdKa4sDgx+HWUgLIljsF4BjSgXLdFKY05pBnNOxMEgrypZUE3sAKRcZ
WUOJUsL7fLi+mjtbCtzvaO5zpMpmsSlodvt481i1nhQauh4mpGimxWXuvJp49uuz
OnVwmaB3JiBk8QeOTbIfzjvbW0yO/8nTL3z75oZRll/mKhxhYruRLH2s/Fnfad3c
KRZTHVAwGeU75CHeu5EhCwIDYWrOrF33P5ShR/kCAwEAAaOCAbYwggGyMA8GA1Ud
EwEB/wQFMAMBAQAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA4GA1Ud
DwEB/wQEAwIFoDA2BgNVHR8ELzAtMCugKaAnhiVodHRwOi8vY3JsLmdvZGFkZHku
Y29tL2dkaWcyczEtNDkuY3JsMFMGA1UdIARMMEowSAYLYIZIAYb9bQEHFwEwOTA3
BggrBgEFBQcCARYraHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBv
c2l0b3J5LzB2BggrBgEFBQcBAQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3Nw
LmdvZGFkZHkuY29tLzBABggrBgEFBQcwAoY0aHR0cDovL2NlcnRpZmljYXRlcy5n
b2RhZGR5LmNvbS9yZXBvc2l0b3J5L2dkaWcyLmNydDAfBgNVHSMEGDAWgBRAwr0n
jsw0gzCiM9f7bLPwtCyAzjArBgNVHREEJDAighAqLmRyYWZ0a2luZ3MuY29tgg5k
cmFmdGtpbmdzLmNvbTAdBgNVHQ4EFgQUkF4a4BpBmRLNfTDDir3K9m++N3AwDQYJ
KoZIhvcNAQELBQADggEBAGU/LaXNXEyagwZxxDOTJHRH6CUofPwvJJiIIfF3ANLz
ulFb4H9ap38X/rOnIZHJHUjYVGBhVfwFfhhJHXjQHm+V9ixHYNIVPeCifAQw0xE5
V6M79+9BbuRvrKNF7mI60gtY2JL/L1L+Uj1R2VX2velw2GSMr6fyJdwq5N4cfmwf
QP0LRkyHOn6wMX74mYzPQ/51OQi9pzvKPXJE8vwUi1rKvAiViqivXbCLiisdx13h
5/BOx8fUjLSew66QJLjU8laZDK2bIzEf2JczLuY92yyS2ifgLyalKieY6y10y/O+
4TWjhNY5msjyIqXeLXwAO1CjRZzFIv/li+HXka6qAFQ=
-----END CERTIFICATE-----

View File

@ -0,0 +1,20 @@
-----BEGIN DSA PRIVATE KEY-----
MIIDPQIBAAKCAQEA27xa+d5kAGDnxWkmZON9rNHw73/M4cwKpKGMpxGEdMt+u7wB
Nt6tCH0v6dHo6726L6YUopxSzKahtzngxmT8G/P2dcbiVUm6r2N1T7zX5+9tnwWY
PcpexdX/mXUnoB1yNHSckDiG0k5EGlQTTFXmg22aChvINIFaoEdR5IW3fOdiIX0z
NWUBQ6eezsFuoy1anIb9WjOcCtmdvjPFtWdmZwGVfUp/CmJ+720GijTmsRB3dCqp
QoxsFC+BtbtOtgX7pKPPsmICaYTgDqaY6Oc2HyWvS6xnl5uaHa33sFz9EisIy48n
UbajWnLN8+bqSb+iIbR9xKxe1NRUO5rvJtXCmQIVAK2dU+z5hzWPAnuHp19T9y8J
Km8JAoIBABk907ebpqMBTGcJ6kQiJshgmao2zN3uUWiA3GCrdnq8JxumqoRTbsLQ
sxh+nvw24U8bK94NhhoUmQHfhl1GWb4seSUygoN7NUOC9wDH9QfrEi9S9eUS07gs
LQ4QEYJPbxC1Wu8MIXJ2RpuaSFh+TClsasaGK54JOwNp4Nvh3CXYfwYL1Jtt9vOc
tN2tF8Rr9zQrSgZDdsJvr/cIprxhY8JB4D54Bq77D4zzULz792TKTHXyjhObL4XQ
cXz8tWloYF/wC8ME64CpVOx6GveN/cy6rINLG4T9epmheVDVmM33Mg2KgY+L+V3l
l3QxBX/uygjuzCmK489u+OrP4cnXxJYCggEAVl000S2oxe2zAnt+oaeHc8QUO5B4
pb4k9MoLgM5AXGQQMmZcMwUaiSDe7q7FsM47ARXBI8jZkR/ZEAZuhoK/7qgo9VQV
tW95SpMjesaj7LK0ocHU2djvUMzxZDWU+zkd2aJTusnbwWKwTXK64WAv97aKbf+O
Avnjln3MkqfMzqR24w0ccdr8pZ9yTRyRyC6tf9G0/vnvSbZEALSsLXjuB6FIrpma
30S5KL4IR6cBIKlUHC9rf6ET3lLDFlM3B7YCVw/8VpENATd+sEez8f96lgQNcWSH
8Us611d7wGOjB6pDe7FueX+CeLFUzBEJ2YdiMRnQMVZ9nFY8i+s/KH2FFgIUeuC2
1y9hgnFoPYic5nnISNkQKP4=
-----END DSA PRIVATE KEY-----

View File

@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFdTCCBTWgAwIBAgIJAJfyK94Nz1yPMAkGByqGSM44BAMwcDELMAkGA1UEBhMC
VVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQK
EwpDbG91ZEZsYXJlMRQwEgYDVQQLEwtFbmdpbmVlcmluZzERMA8GA1UEAxMIVEVT
VCBEU0EwHhcNMTQwNDEyMDUwMTUyWhcNMjQwNDA5MDUwMTUyWjBwMQswCQYDVQQG
EwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNV
BAoTCkNsb3VkRmxhcmUxFDASBgNVBAsTC0VuZ2luZWVyaW5nMREwDwYDVQQDEwhU
RVNUIERTQTCCAzowggItBgcqhkjOOAQBMIICIAKCAQEA27xa+d5kAGDnxWkmZON9
rNHw73/M4cwKpKGMpxGEdMt+u7wBNt6tCH0v6dHo6726L6YUopxSzKahtzngxmT8
G/P2dcbiVUm6r2N1T7zX5+9tnwWYPcpexdX/mXUnoB1yNHSckDiG0k5EGlQTTFXm
g22aChvINIFaoEdR5IW3fOdiIX0zNWUBQ6eezsFuoy1anIb9WjOcCtmdvjPFtWdm
ZwGVfUp/CmJ+720GijTmsRB3dCqpQoxsFC+BtbtOtgX7pKPPsmICaYTgDqaY6Oc2
HyWvS6xnl5uaHa33sFz9EisIy48nUbajWnLN8+bqSb+iIbR9xKxe1NRUO5rvJtXC
mQIVAK2dU+z5hzWPAnuHp19T9y8JKm8JAoIBABk907ebpqMBTGcJ6kQiJshgmao2
zN3uUWiA3GCrdnq8JxumqoRTbsLQsxh+nvw24U8bK94NhhoUmQHfhl1GWb4seSUy
goN7NUOC9wDH9QfrEi9S9eUS07gsLQ4QEYJPbxC1Wu8MIXJ2RpuaSFh+TClsasaG
K54JOwNp4Nvh3CXYfwYL1Jtt9vOctN2tF8Rr9zQrSgZDdsJvr/cIprxhY8JB4D54
Bq77D4zzULz792TKTHXyjhObL4XQcXz8tWloYF/wC8ME64CpVOx6GveN/cy6rINL
G4T9epmheVDVmM33Mg2KgY+L+V3ll3QxBX/uygjuzCmK489u+OrP4cnXxJYDggEF
AAKCAQBWXTTRLajF7bMCe36hp4dzxBQ7kHilviT0yguAzkBcZBAyZlwzBRqJIN7u
rsWwzjsBFcEjyNmRH9kQBm6Ggr/uqCj1VBW1b3lKkyN6xqPssrShwdTZ2O9QzPFk
NZT7OR3ZolO6ydvBYrBNcrrhYC/3topt/44C+eOWfcySp8zOpHbjDRxx2vyln3JN
HJHILq1/0bT++e9JtkQAtKwteO4HoUiumZrfRLkovghHpwEgqVQcL2t/oRPeUsMW
UzcHtgJXD/xWkQ0BN36wR7Px/3qWBA1xZIfxSzrXV3vAY6MHqkN7sW55f4J4sVTM
EQnZh2IxGdAxVn2cVjyL6z8ofYUWo4HVMIHSMB0GA1UdDgQWBBTdWYYdSWrZr5eD
pf3QoSWZz0AbCDCBogYDVR0jBIGaMIGXgBTdWYYdSWrZr5eDpf3QoSWZz0AbCKF0
pHIwcDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRMwEQYDVQQKEwpDbG91ZEZsYXJlMRQwEgYDVQQLEwtFbmdpbmVlcmlu
ZzERMA8GA1UEAxMIVEVTVCBEU0GCCQCX8iveDc9cjzAMBgNVHRMEBTADAQH/MAkG
ByqGSM44BAMDLwAwLAIUP2uvD9JJpn1e7YZ/5QJIjlXhFl8CFGfNcNS49a0bN4Md
2HTcWtoMC+5k
-----END CERTIFICATE-----

View File

@ -0,0 +1,94 @@
-----BEGIN CERTIFICATE-----
MIIFjDCCBHSgAwIBAgIQTeNLe9jcoRyAtFeMsEB3/DANBgkqhkiG9w0BAQUFADCB
tTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2Ug
YXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMm
VmVyaVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwHhcNMTQwOTA0
MDAwMDAwWhcNMTYwOTA0MjM1OTU5WjCBsjELMAkGA1UEBhMCVVMxEDAOBgNVBAgT
B0dlb3JnaWExEDAOBgNVBAcUB0F0bGFudGExHzAdBgNVBAoUFkZpcnN0IERhdGEg
Q29ycG9yYXRpb24xNjA0BgNVBAsULUZpcnN0IERhdGEgUkFTIChSZXRhaWwgYW5k
IEFsbGlhbmNlIFNlcnZpY2VzKTEmMCQGA1UEAxQdZ2xvYmFsZ2F0ZXdheWU0LmZp
cnN0ZGF0YS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCunuir
5P0VQDraZ+iHqz1XItS2sEKCNoG23eWz1Xh87GSPYtrBgOXznNo079ruf48vY5da
KKs6YhjMsOXhJrA0kGfsq3/0WPLZjlWzFdM/0NMdV/Z9iYzBPkrR6Hz64sNGBnEP
BWYvpblmchtyUWhuy58W69VoufYUJMnmbBj1OxPufI97AXdoaoFapMYbaHKdqBJy
2lLmqDalR7oYb4dnZvZY/arEAstLULCAn36X1O7/dKQQXxtmUkUS4XZj26pHEmIJ
5ld4ERZJU67AnKimk5nXmlGWIJPpOo9Aw/0Lwx3BCDN4eC7hPiL+atgtLBKHxy1m
HaWLSdXOLCvrqZKnAgMBAAGjggGXMIIBkzBJBgNVHREEQjBAgh8qLmdsb2JhbGdh
dGV3YXlFNC5maXJzdGRhdGEuY29tgh1nbG9iYWxnYXRld2F5ZTQuZmlyc3RkYXRh
LmNvbTAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEF
BQcDAQYIKwYBBQUHAwIwZQYDVR0gBF4wXDBaBgpghkgBhvhFAQc2MEwwIwYIKwYB
BQUHAgEWF2h0dHBzOi8vZC5zeW1jYi5jb20vY3BzMCUGCCsGAQUFBwICMBkaF2h0
dHBzOi8vZC5zeW1jYi5jb20vcnBhMB8GA1UdIwQYMBaAFA1EXBZTRMGCfh0gqyX0
AWPYvnmlMCsGA1UdHwQkMCIwIKAeoByGGmh0dHA6Ly9zZC5zeW1jYi5jb20vc2Qu
Y3JsMFcGCCsGAQUFBwEBBEswSTAfBggrBgEFBQcwAYYTaHR0cDovL3NkLnN5bWNk
LmNvbTAmBggrBgEFBQcwAoYaaHR0cDovL3NkLnN5bWNiLmNvbS9zZC5jcnQwDQYJ
KoZIhvcNAQEFBQADggEBAEhlHPB7R1QwmpQPG1JzplryKqJ5fYZoPEiF3qfSoB+6
cKJ8Xkhg7uga+/ZH55lErVIbWZjxIxtImAOYnQjm8uWiW8ElrbS42plUkNCk/tuU
X7JcomwCaeE4dUrHF/pe9ao6SOh5KXCZGr0HyY64MOvdxOy6RXTsjDeabwI8CeNn
LauoOg9Ey9hl2Dmv+iAY9ANtb+4nMLQoeH3HPfUpaeIUaCm500RKiEw3pjvWRpwV
5HC7RoWa9tyK/7AvMNbXLfURmhO5/MJ9iUjrSdwJOfryscSSPPrGdrgeKTufn4qu
n7W3DwNqap9Cog4hn7KZJXNaNA0qjB54UPEERcd0bTE=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF7DCCBNSgAwIBAgIQbsx6pacDIAm4zrz06VLUkTANBgkqhkiG9w0BAQUFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
aG9yaXR5IC0gRzUwHhcNMTAwMjA4MDAwMDAwWhcNMjAwMjA3MjM1OTU5WjCBtTEL
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQg
aHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMmVmVy
aVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCxh4QfwgxF9byrJZenraI+nLr2wTm4i8rCrFbG
5btljkRPTc5v7QlK1K9OEJxoiy6Ve4mbE8riNDTB81vzSXtig0iBdNGIeGwCU/m8
f0MmV1gzgzszChew0E6RJK2GfWQS3HRKNKEdCuqWHQsV/KNLO85jiND4LQyUhhDK
tpo9yus3nABINYYpUHjoRWPNGUFP9ZXse5jUxHGzUL4os4+guVOc9cosI6n9FAbo
GLSa6Dxugf3kzTU2s1HTaewSulZub5tXxYsU5w7HnO1KVGrJTcW/EbGuHGeBy0RV
M5l/JJs/U0V/hhrzPPptf4H1uErT9YU3HLWm0AnkGHs4TvoPAgMBAAGjggHfMIIB
2zA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnZlcmlz
aWduLmNvbTASBgNVHRMBAf8ECDAGAQH/AgEAMHAGA1UdIARpMGcwZQYLYIZIAYb4
RQEHFwMwVjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL2Nw
czAqBggrBgEFBQcCAjAeGhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhMDQG
A1UdHwQtMCswKaAnoCWGI2h0dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMtZzUu
Y3JsMA4GA1UdDwEB/wQEAwIBBjBtBggrBgEFBQcBDARhMF+hXaBbMFkwVzBVFglp
bWFnZS9naWYwITAfMAcGBSsOAwIaBBSP5dMahqyNjmvDz4Bq1EgYLHsZLjAlFiNo
dHRwOi8vbG9nby52ZXJpc2lnbi5jb20vdnNsb2dvLmdpZjAoBgNVHREEITAfpB0w
GzEZMBcGA1UEAxMQVmVyaVNpZ25NUEtJLTItNjAdBgNVHQ4EFgQUDURcFlNEwYJ+
HSCrJfQBY9i+eaUwHwYDVR0jBBgwFoAUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwDQYJ
KoZIhvcNAQEFBQADggEBAAyDJO/dwwzZWJz+NrbrioBL0aP3nfPMU++CnqOh5pfB
WJ11bOAdG0z60cEtBcDqbrIicFXZIDNAMwfCZYP6j0M3m+oOmmxw7vacgDvZN/R6
bezQGH1JSsqZxxkoor7YdyT3hSaGbYcFQEFn0Sc67dxIHSLNCwuLvPSxe/20majp
dirhGi2HbnTTiN0eIsbfFrYrghQKlFzyUOyvzv9iNw2tZdMGQVPtAhTItVgooazg
W+yzf5VK+wPIrSbb5mZ4EkrZn0L74ZjmQoObj49nJOhhGbXdzbULJgWOw27EyHW4
Rs/iGAZeqa6ogZpHFt4MKGwlJ7net4RYxh84HqTEy2Y=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIE0DCCBDmgAwIBAgIQJQzo4DBhLp8rifcFTXz4/TANBgkqhkiG9w0BAQUFADBf
MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
LkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkw
HhcNMDYxMTA4MDAwMDAwWhcNMjExMTA3MjM1OTU5WjCByjELMAkGA1UEBhMCVVMx
FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVz
dCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2lnbiwgSW5jLiAtIEZv
ciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2lnbiBDbGFzcyAz
IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzUwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1nmAMqudLO07cfLw8
RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbext0uz/o9+B1fs70Pb
ZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIzSdhDY2pSS9KP6HBR
TdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQGBO+QueQA5N06tRn/
Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+rCpSx4/VBEnkjWNH
iDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/NIeWiu5T6CUVAgMB
AAGjggGbMIIBlzAPBgNVHRMBAf8EBTADAQH/MDEGA1UdHwQqMCgwJqAkoCKGIGh0
dHA6Ly9jcmwudmVyaXNpZ24uY29tL3BjYTMuY3JsMA4GA1UdDwEB/wQEAwIBBjA9
BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVy
aXNpZ24uY29tL2NwczAdBgNVHQ4EFgQUf9Nlp8Ld7LvwMAnzQzn6Aq8zMTMwbQYI
KwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQU
j+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVyaXNpZ24uY29t
L3ZzbG9nby5naWYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8v
b2NzcC52ZXJpc2lnbi5jb20wPgYDVR0lBDcwNQYIKwYBBQUHAwEGCCsGAQUFBwMC
BggrBgEFBQcDAwYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEBBQUA
A4GBABMC3fjohgDyWvj4IAxZiGIHzs73Tvm7WaGY5eE43U68ZhjTresY8g3JbT5K
lCDDPLq9ZVTGr0SzEK0saz6r1we2uIFjxfleLuUqZ87NMwwq14lWAyMfs77oOghZ
tOxFNfeKW/9mz1Cvxm1XjRl4t7mi0VfqH5pLr7rJjhJ+xr3/
-----END CERTIFICATE-----

View File

@ -0,0 +1,55 @@
-----BEGIN CERTIFICATE-----
MIIFBDCCA+ygAwIBAgISESFHHMk7S+UhnJWGZxPSC9ovMA0GCSqGSIb3DQEBBQUA
MFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMS0wKwYD
VQQDEyRHbG9iYWxTaWduIERvbWFpbiBWYWxpZGF0aW9uIENBIC0gRzIwHhcNMTQw
MjA2MjAzNjIwWhcNMTUwMjA3MjAzNjIwWjBZMQswCQYDVQQGEwJVUzEwMC4GA1UE
CxMnRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkIGJ5IE9uZUNsaWNrU1NMMRgwFgYD
VQQDEw9kaW5uZXJqZXJrcy5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDprf7CgVDQMo/6RRYHNR5CLwzhvtehaNFTMR6jg6o2x0mb5uSBs6KTQZCe
ruQE54ob84WkrFesw3+GKWXr0JagznYeOb7E5Kscshmg3sKbR109LRNfknfmyC/3
tNRUJiBQ8kghUAXvo/ITeyPS9oO/jUXJ9moiSn7qYBWiqiFS8ofF5PeD+IqtAyjH
rkSUCWGR23wtsfebCX5zutj3agJEKZUx4yoNEh7OhOmolXKh2aaLf752vcHA+3Gk
UO56IXLaEgh5Iz+hKR/OOFWL1pPyxXGp24QnQ41llxTvr8UT3cIKUdcTAKDhkc1l
Cn9W0mpsUaKaD2wrWZXafXmEeAwLAgMBAAGjggHGMIIBwjAOBgNVHQ8BAf8EBAMC
BaAwSQYDVR0gBEIwQDA+BgZngQwBAgEwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93
d3cuZ2xvYmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wLwYDVR0RBCgwJoIPZGlubmVy
amVya3MuY29tghN3d3cuZGlubmVyamVya3MuY29tMAkGA1UdEwQCMAAwHQYDVR0l
BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMD8GA1UdHwQ4MDYwNKAyoDCGLmh0dHA6
Ly9jcmwuZ2xvYmFsc2lnbi5jb20vZ3MvZ3Nkb21haW52YWxnMi5jcmwwgYgGCCsG
AQUFBwEBBHwwejBBBggrBgEFBQcwAoY1aHR0cDovL3NlY3VyZS5nbG9iYWxzaWdu
LmNvbS9jYWNlcnQvZ3Nkb21haW52YWxnMi5jcnQwNQYIKwYBBQUHMAGGKWh0dHA6
Ly9vY3NwMi5nbG9iYWxzaWduLmNvbS9nc2RvbWFpbnZhbGcyMB0GA1UdDgQWBBRA
UKYM4Lxj64fguNZ0V4St1byujDAfBgNVHSMEGDAWgBSWrfqwW7mDZCp2whyKadpC
3P79KDANBgkqhkiG9w0BAQUFAAOCAQEATqaqPSIkvMGPukFvfXGcwjHMh/AmbRXD
LBm1S23k4+4cvMowLV0glX9iGSGigfB54RrbsMdaSNK/CN4ok2GBspNYO7n+mpwy
OopXHzOZPDZVFko1iqCjMvRLmCi8/iOWNvR+A2Tt4GKU42oEI2n7fTsqaHi+G+AG
vti2kdiPj1oOvtwEgGhgY8tg38Q42LeCnNxb9/734PBvZ+Geg9nG9BjpVTRsyc46
tldfHV1I5G4Z6KZ7CZg1Z1IQlCRmVHiTLjWo4gess2Qh5bRZnbrE/Yxwj2u8oL4j
kmEGPypraF2LFXmPntiiGnG9tqsp0h7fZA0KtFg4wmHd9YFGaG9wog==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEWjCCA0KgAwIBAgILBAAAAAABL07hQUMwDQYJKoZIhvcNAQEFBQAwVzELMAkG
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw
MDBaFw0yMjA0MTMxMDAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
YWxTaWduIG52LXNhMS0wKwYDVQQDEyRHbG9iYWxTaWduIERvbWFpbiBWYWxpZGF0
aW9uIENBIC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxo83A
3zNAJuveWteUZtQBY8wzRIng4rjCRw2PrWmGHKhzQgvxcvstrLURcoMi9lbnLsVn
cZ0AHDK84+0uCEWp5vrdyIyDBcFvS9AmSgv2G0XATX6TvA0nhO0wo+nGJibdLR/Y
i8POGdBb/Aif5NjiNeSgaKb2DaN0YEKyl4IkjkGk8i5eto6nbtlsfw07JDVq0Ktb
aveXAgA/UaanbnPKdw12fJu2MBoanPcfKHsOi0cf538FjMbJyLvP6dx6QS6hhtrU
ObLiE0CmqDr6D1MeT+xumAkbypp3s1WFhekuFrWdXlTxSnpsObpuFwY0s7JC4ffz
nJoLEUTeaniOsRNPAgMBAAGjggElMIIBITAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0T
AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUlq36sFu5g2QqdsIcimnaQtz+/SgwRwYD
VR0gBEAwPjA8BgRVHSAAMDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2Jh
bHNpZ24uY29tL3JlcG9zaXRvcnkvMDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9j
cmwuZ2xvYmFsc2lnbi5uZXQvcm9vdC5jcmwwPQYIKwYBBQUHAQEEMTAvMC0GCCsG
AQUFBzABhiFodHRwOi8vb2NzcC5nbG9iYWxzaWduLmNvbS9yb290cjEwHwYDVR0j
BBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZIhvcNAQEFBQADggEBADrn
/K6vBUOAJ3VBX6jwKI8fj4N+sri6rnUxJ4il5blOBEPSregTAKPbGQEwnmw8Un9c
3qtnw4QEVFGZnmMvvdW3wNXaAw5J0+Gzkk/fkk59riJqzti8/Hyua7aK6kVikBHT
C3GnXgYi/0046rk6bs1nGgJ/S/O/DnlvvtUpMllZHZYIm3CP9x5cRntO0J20U8gS
AhsNuzLrWVO5PhtWjRXI8UI/d/4f5W2eZh+r2rKDV7QMItKGvNoy18DtcIV8k6rw
l9w5EdLYieuNkKO2UCXLbNmmw2/7iFS45JJwh855O/DeNr8DBAA9+e+eqWek9IY+
I5e4KnHi7f5piGe/Jlw=
-----END CERTIFICATE-----

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEizCCA/agAwIBAgIISfg49he9h+AwCwYJKoZIhvcNAQELMH0xCzAJBgNVBAYT
AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv
MRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQG
A1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDA0MTEyMjU2MzdaFw0xNDA0MTEyMzAx
MzdaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UE
CxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5j
b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDlCnV+vj0sVPy8SqHL
AlI+xwnPhWgzj2VevD6Nz1Zu1BeQ5m5y4CWCf+GmRGTP7+a/C510Fw6rpmInB0Ng
xxwQ2rC08fJtCnijlGH/VjEPIHY5lRaAomcM8Rgx6JOuv9BpZJKpr9pyUMV53JeW
RbWuLH5nEMdyk9NpetS2gWxt4/D20QlhK/tHkROrcLmEUddwIGdwE8JzI88c77Fu
u6pgMtHKvl4GGH0yvb4T7PvCdH8V2tCH7bt8roXd9MSyFVy7uORkfouip7EsVREU
mlcY5EvpR141KXbZqiOQiusJ+u76mEUQNk8wCR1/CW/ii9v1BKOVjXwCfEtIXjg0
APJx1VNSSH6XoDpUETL+eQ4J0FL9XNbsDuYar7+zD0N1/5vSo3HLNRQR9f0lbsys
sWBEN+CxK19xyPumr21Z0bU0f1B5H52VSF0q3I1Ju9wRo994a7YipdGcmZ2lChmT
7r3mzlBTYl3poU26q34v8wG9U7Jv4fsZJ+RGebDI+TR3QG6Yod06l9oEYZxWXBY7
STOs8wuTu3huSnan/IpWnV017Vsc61D5G+QrqcxZdXckt3anZKCF75JpUnJ7vuow
TmmHlb8KIMa9mOvcuGX4P6mz8gTi2arl/aL27kj9Q0Jgv/y1ebe2Bx2P9TF6+VND
DL3J/vSVlFeqLt2reAIBKnytLwIDAQABo4GDMIGAMA4GA1UdDwEB/wQEAwIApDAS
BgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTXXUgpaSwO9HOrQBxGqOOSFHsH
EDAfBgNVHSMEGDAWgBS4Xu+uZ1C31vMH5Wq+VbNnOg2SPjAaBgNVHREEEzARgg9j
ZnNzbC1pbnRlci5jb20wCwYJKoZIhvcNAQELA4GBAJg3FejhZNUWht3AFoFz9Pmn
2B4+Rhcz3Vy2AkGTI6tNR3TkaDIejyBkeEtf4pmR480tq3xFZkCZ6BZY2f7mvRto
DWo3AdXcLeYDtbDmNGJFL6mAlyG1A87n7EgUnP8hEjtiYP8dyCGJD0JOKZAy/kMq
XFzYgAa1t27VSc/XkiG7
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEhDCCA++gAwIBAgIIQsTa4VjjFPswCwYJKoZIhvcNAQEFMH0xCzAJBgNVBAYT
AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv
MRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQG
A1UEAwwNQ0ZTU0xfVEVTVF9DQTAgFw0xNDA5MjMxODQ5NThaGA8yMTE0MDkyNDE4
NTQ1OFowgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJlMRwwGgYD
VQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2Nv
MRMwEQYDVQQIEwpDYWxpZm9ybmlhMR0wGwYDVQQDExRjbG91ZGZsYXJlLWludGVy
LmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOUKdX6+PSxU/LxK
ocsCUj7HCc+FaDOPZV68Po3PVm7UF5DmbnLgJYJ/4aZEZM/v5r8LnXQXDqumYicH
Q2DHHBDasLTx8m0KeKOUYf9WMQ8gdjmVFoCiZwzxGDHok66/0Glkkqmv2nJQxXnc
l5ZFta4sfmcQx3KT02l61LaBbG3j8PbRCWEr+0eRE6twuYRR13AgZ3ATwnMjzxzv
sW67qmAy0cq+XgYYfTK9vhPs+8J0fxXa0Iftu3yuhd30xLIVXLu45GR+i6KnsSxV
ERSaVxjkS+lHXjUpdtmqI5CK6wn67vqYRRA2TzAJHX8Jb+KL2/UEo5WNfAJ8S0he
ODQA8nHVU1JIfpegOlQRMv55DgnQUv1c1uwO5hqvv7MPQ3X/m9Kjccs1FBH1/SVu
zKyxYEQ34LErX3HI+6avbVnRtTR/UHkfnZVIXSrcjUm73BGj33hrtiKl0ZyZnaUK
GZPuvebOUFNiXemhTbqrfi/zAb1Tsm/h+xkn5EZ5sMj5NHdAbpih3TqX2gRhnFZc
FjtJM6zzC5O7eG5Kdqf8iladXTXtWxzrUPkb5CupzFl1dyS3dqdkoIXvkmlScnu+
6jBOaYeVvwogxr2Y69y4Zfg/qbPyBOLZquX9ovbuSP1DQmC//LV5t7YHHY/1MXr5
U0MMvcn+9JWUV6ou3at4AgEqfK0vAgMBAAGjezB5MA4GA1UdDwEB/wQEAwIApDAT
BgNVHSUEDDAKBggrBgEFBQcDATASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQW
BBSIYLoYpHe4QQQb1e93UcJbFLogPzAfBgNVHSMEGDAWgBS4Xu+uZ1C31vMH5Wq+
VbNnOg2SPjALBgkqhkiG9w0BAQUDgYEAXSegwl0vRG7N9FBO+9u1Neh9oeQNm5Ld
U5FK1qs4BhI/F4MRW4hxN8D25B6tPMtKR93Rkeg/wGz3DPwAhvjVFCOQlzFfW0S9
dEduUgl2j8ICcgLawFDp7eYsUJfcBwffGOS/RAtUG59Q52tt8FNXU9QtaKaSn/Vq
mrb08gYFNzg=
-----END CERTIFICATE-----

View File

@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIBezCCASECAQAwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMR0wGwYDVQQDExRjbG91ZGZsYXJl
LWludGVyLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLgOKlWwIAIeURde
yvDMhgfn6xPp1gn8oUeLmsniBm7I+j84IsVzUso8/MpjMZ9nB8lQUanhv3Kmqcyj
HNj+iFegMjAwBgkqhkiG9w0BCQ4xIzAhMB8GA1UdEQQYMBaCFGNsb3VkZmxhcmUt
aW50ZXIuY29tMAoGCCqGSM49BAMCA0gAMEUCIEJcy2mn2YyK8lVE+HHmr2OsmdbH
4CLDVXFBwxke8ObqAiEAx/il1cDKvQ/I36b4XjBnOX2jcQ5oaCNPFFBE74WQ/ps=
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEILbwI4u4bw+HtafMqFnrL7LOrqNEZH5rW5ygSrigfrVLoAoGCCqGSM49
AwEHoUQDQgAEuA4qVbAgAh5RF17K8MyGB+frE+nWCfyhR4uayeIGbsj6PzgixXNS
yjz8ymMxn2cHyVBRqeG/cqapzKMc2P6IVw==
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICwzCCAi6gAwIBAgIIOdnmLRHpa9owCwYJKoZIhvcNAQEFMH0xCzAJBgNVBAYT
AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv
MRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQG
A1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDA1MjcxNzUzMjhaFw0xNTA1MjcxNzU4
MjhaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UE
CxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5j
b20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4DipVsCACHlEXXsrwzIYH5+sT
6dYJ/KFHi5rJ4gZuyPo/OCLFc1LKPPzKYzGfZwfJUFGp4b9ypqnMoxzY/ohXo4GG
MIGDMA4GA1UdDwEB/wQEAwIApjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH
AwIwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUwsdbMieQ99qPYmlsL5L8
2HWQyakwHwYDVR0jBBgwFoAUuF7vrmdQt9bzB+VqvlWzZzoNkj4wCwYJKoZIhvcN
AQEFA4GBAIyZZ7dPQsDKLR+ZwDXjSdsTws+9mZvqzfTb89LJ7H1pEoVI0x+MQncG
zbsN713Vu2GCBHZilmfINsBbZYdaqqVTpqLtAFMce7aBiLrpcLHyegzTJIn+zzMa
v5498Mo7S8NUOeGtTAPepExf9lOxiexynEkk5U+AF04nzspNOwTY
-----END CERTIFICATE-----

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIFGzCCAwUCAQAwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMR0wGwYDVQQDExRjbG91ZGZsYXJl
LWludGVyLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOUKdX6+
PSxU/LxKocsCUj7HCc+FaDOPZV68Po3PVm7UF5DmbnLgJYJ/4aZEZM/v5r8LnXQX
DqumYicHQ2DHHBDasLTx8m0KeKOUYf9WMQ8gdjmVFoCiZwzxGDHok66/0Glkkqmv
2nJQxXncl5ZFta4sfmcQx3KT02l61LaBbG3j8PbRCWEr+0eRE6twuYRR13AgZ3AT
wnMjzxzvsW67qmAy0cq+XgYYfTK9vhPs+8J0fxXa0Iftu3yuhd30xLIVXLu45GR+
i6KnsSxVERSaVxjkS+lHXjUpdtmqI5CK6wn67vqYRRA2TzAJHX8Jb+KL2/UEo5WN
fAJ8S0heODQA8nHVU1JIfpegOlQRMv55DgnQUv1c1uwO5hqvv7MPQ3X/m9Kjccs1
FBH1/SVuzKyxYEQ34LErX3HI+6avbVnRtTR/UHkfnZVIXSrcjUm73BGj33hrtiKl
0ZyZnaUKGZPuvebOUFNiXemhTbqrfi/zAb1Tsm/h+xkn5EZ5sMj5NHdAbpih3TqX
2gRhnFZcFjtJM6zzC5O7eG5Kdqf8iladXTXtWxzrUPkb5CupzFl1dyS3dqdkoIXv
kmlScnu+6jBOaYeVvwogxr2Y69y4Zfg/qbPyBOLZquX9ovbuSP1DQmC//LV5t7YH
HY/1MXr5U0MMvcn+9JWUV6ou3at4AgEqfK0vAgMBAAGgSzBJBgkqhkiG9w0BCQ4x
PDA6MDgGA1UdEQQxMC+CFGNsb3VkZmxhcmUtaW50ZXIuY29tghd3d3djbG91ZGZs
YXJlLWludGVyLmNvbTALBgkqhkiG9w0BAQ0DggIBAHtSt/v+IHQmSK5UiQWwjRWA
ZezIWVlJuselW8DEPNHzDtnraVhjPSFP995Cqh9fc89kx2Bt9hDhjNteTB+pJW6B
aCRRZygJ6/m3Ii1XqTFgfEJBWwuIX1Req0PCW/ayegdLzzYbSZ31wRICCveBQyGw
vRtzIBUeMvz9MgLJ8zx7eN7fDhrvy+Y1SkC4g0sAQTYYfM9P/He4k5hx79hmd2YC
mUDAlNZV0g0dY0qR4cITmhniIFW5iZBplY7DmqooUXrj5yEga2QMj/RA16lPzHbz
7ceUlcH2L6/V6zMR/rfCiGRoWInxWSuuJhLIVLmoEo0590w6KVEZifHxsRpl4l09
imvzwTSQGIrY8jF9AxOD0rRA9wXCT9h8XtBWyJZ1/DmzJG8+7oZ/HdE9XhzwNujD
Q6lBOj+dznju7k/snYCZVq501JLPeql8vQrq0O/xSqSK4yN1IG4NisZeDK2BZEOy
QhnKXodIKf+zXnFw86lZ/ZwHQFr6jOSxmbrZ2OiY34m7Yd9oeIaMPviysRih2x4Q
O6DFz72f97+xFZuXIbmn8DPQV8U9bk/gbrfUCPnx/icS8UoPsBKc9Gio0FZO4+8A
4/ac3oeN0zy/WjsBP+J50CRUXMrRI9KO+/bI4pcT14B31YbuSo6ygIkIkj7YDh36
+4ZG6HnUPQI8HteF9hzp
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEA5Qp1fr49LFT8vEqhywJSPscJz4VoM49lXrw+jc9WbtQXkOZu
cuAlgn/hpkRkz+/mvwuddBcOq6ZiJwdDYMccENqwtPHybQp4o5Rh/1YxDyB2OZUW
gKJnDPEYMeiTrr/QaWSSqa/aclDFedyXlkW1rix+ZxDHcpPTaXrUtoFsbePw9tEJ
YSv7R5ETq3C5hFHXcCBncBPCcyPPHO+xbruqYDLRyr5eBhh9Mr2+E+z7wnR/FdrQ
h+27fK6F3fTEshVcu7jkZH6LoqexLFURFJpXGORL6UdeNSl22aojkIrrCfru+phF
EDZPMAkdfwlv4ovb9QSjlY18AnxLSF44NADycdVTUkh+l6A6VBEy/nkOCdBS/VzW
7A7mGq+/sw9Ddf+b0qNxyzUUEfX9JW7MrLFgRDfgsStfccj7pq9tWdG1NH9QeR+d
lUhdKtyNSbvcEaPfeGu2IqXRnJmdpQoZk+695s5QU2Jd6aFNuqt+L/MBvVOyb+H7
GSfkRnmwyPk0d0BumKHdOpfaBGGcVlwWO0kzrPMLk7t4bkp2p/yKVp1dNe1bHOtQ
+RvkK6nMWXV3JLd2p2Sghe+SaVJye77qME5ph5W/CiDGvZjr3Lhl+D+ps/IE4tmq
5f2i9u5I/UNCYL/8tXm3tgcdj/UxevlTQwy9yf70lZRXqi7dq3gCASp8rS8CAwEA
AQKCAgBPyl/6Qm3vNsBBHELXBTz/r7lEOTZ+19K5uRyVrIhw3aRED3KkxF9s4f4L
PUJdija5kWNN4QZ0V+dTr10SpuqpGHZ84tjQkdhLLFMjb7Rxj56AGucW8vyxboA+
SsbAFwSU4ruRL7kLIAZbmLSaXjiXr9ptL1Q8HzGESo0180qB0enNIi+BUaAdY3YV
wJRwe05xOmiui8Ou9uedLgeDCw+kqa+aUM1SlE9xUNaZ/HIMYScwxuTkpbYuDmKG
W1H2tCh1IUk3lTox9Pds+UmVAtuayVWEtB8mqAZGd9Yh8bNF68w7MrbEmhbmJhbH
fdMjehOrfO08GWj9OK3FTUWJIFdVFdjE6M5F/9kKjZhTH6Q6N0GU2M65v+R5CRWW
3vJu7Nhfek4gRa9zWVjHNQbpBoDDnB0MC5s7MQ6HnVQ3u4dZMDYo/XQrc/uIeaT+
cuN8IW1u9nEGdNT0+xmV40oRgXGMcWH9kkzctSHyt7d+BiWZLL2PFGV/RprNUklp
1kFl2p7piof4mgueiU/iJvosl9E1eVdWQgrrz33NMuO+Ox+PHgIyWHC6A9FDdscd
9FQ3AFmGIVNgRzJi3V0/v5ESqkdJ/QpmHwS0uoP7zdK4/zeCz6Nav/QdmkEOV6Si
GLn7k0xxdRUNW5dHjLXacmy279fB3rM8PxkAUbLh4VKHVlGNyQKCAQEA54YN0wRv
AI/lY/Z6wpolMpA7Gt2305yfFP+9oTORWxR8MKtO/aCmuxqF9qX/PMRa1HeXxCCM
O9zD3BqChNS3kwKSVlt79KiY4Q6HflTTeG55kXsmRxxljoi46zgcuwJPPlexciO4
nMoaac3/pQXnuwu65iLyg0/8Nw609C9xXyjQM1szeow9dL46/i2g5kh43Qu560Um
Mjcv9TOV4G+efatOv0sQGUgHHoKQVw2kVvImYRkiXHg/aaRqGSk3zlvoVTQVN+hJ
rn0Sh+AlBY7NiG0r/wT7p+PGASq/vG3JX+cJ0V6KXaI0s9f7NLNkWMx6cITO77Je
pbKmIYaoadeASwKCAQEA/UE19ErMQyX1o2pScaWZ4JpKY2R4i13VvPbhL0ztDJdv
iPhO/HaaWTyn29Tve7KpRlbr5PMWy3Szk4NnL6Tn/PCywHRPniW/UzgScm5fYhBe
x5KJZS8q9ImgJNiwM8yow31CJNr+l/d3pcKl3SoeBv/lUqAI0M1AhsyBkpcRm76W
jVOD+BYCV8E3q2hx/3aWOcJdPAsS9rb3qBBsuMgdl2NI+pidGr9+Xpwc+Xmk7KwI
9bKhS3ecVs0ujmNlpzKcNbIWbmpnJFEMwEPoLFdb+i4eZqzlDLAaag07wEL6akEl
OJ421YHqFZe3oJLNjV+6BzswOweA5qiH3TAoRR1gLQKCAQEAnvnMuk38Do3APLC9
wKxpyFuDSkJefJ66GYg15N/s+naJhD3NQpiyhB2FSUTYixhlKiloe9LBmEVR8+v8
HUuXNgn5A/VTmz69oyP/475JaxOoxD2kngWgsoutNk7UY5EFatB6Vt6yYG7iTi6W
UPFKGoTGdEog7gvZKtEdbeK53VbAB9Oi+I4dkPEivvAD4Lx4yYfIxQU5YhfFBYDD
dFYQpUghDXd0eXec89VBWZVTeCRUOC4zCv3CxT6RX++Ok1NGqGLYAwist3TIaaZ+
pV9WQEx+fmEkkDb1+k0pVTCpqwGRG0PojLzZpXgz1Q8tY1Ac7vAyzCJVnT+blb/K
GstQGwKCAQA14kIQkDmVr+XrtxuDgrCS0UEylJXxUS3A3uZaogttumrIwcxMew+s
HPO6Gjw6HXFWvffC5tXaxCHRKQwzXurdLnlZ6WVnSLDEjBGgt0skGkeQPuVs2fRR
w1aHgHM9EjZ2IZiJLu8sdkLGyftwax2ob5njUpmNk54/EBQhlHLyqEJwH2zcxBIL
idjGZ5qZuCmOcIRV2iVWyOc4owX+6tUg+Mb2SrJilovUpXKkwfUNRi1B2Zfn7rMc
5NsbAJsIUARciF+tboYze+synUAw7wVq3ZUqU28InA+CsP4dkiKlqOa6fS89jj64
CWfQimuhwNRb5YQFizsp2IHP1gc7bVyhAoIBADuUUe4J/SH6GqweX6IcuAmOgcio
o5kMr6EM5g8Ly3P1FgeshlK4Jyt+ufKD6yr91Td7TBIa8xnPHlnU3Rji0O4F/jyn
DMIcdFVQkbXtWum/NrprWiDAaWKl1JtmKs+l8fpeL/V8pbjlFx5A7H5qjlTswfq+
vsbrNUkA1DB4yyq8DJzgkVT3LpD14dvROSI2mAJRd8aaGEBmvNX/pJF2cjevmwnz
HB1AU2Pex0yCBi17FYe+72SG9XHR3u4CZ1gT+X62v0TsuKa6WMneVkMraB3M2ltO
f2WuVcay35mPGAnKjvSuj4j3NvgeELmSo97CC5A/bc2d4pDjmb1pkYbrOiQ=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,27 @@
-----BEGIN CERTIFICATE-----
MIIEizCCA/agAwIBAgIIeM7v534l+W0wCwYJKoZIhvcNAQELMH0xCzAJBgNVBAYT
AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv
MRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQG
A1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDA0MTEyMTIyMzdaFw0xOTA0MTEyMTI3
MzdaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UE
CxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5j
b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDlCnV+vj0sVPy8SqHL
AlI+xwnPhWgzj2VevD6Nz1Zu1BeQ5m5y4CWCf+GmRGTP7+a/C510Fw6rpmInB0Ng
xxwQ2rC08fJtCnijlGH/VjEPIHY5lRaAomcM8Rgx6JOuv9BpZJKpr9pyUMV53JeW
RbWuLH5nEMdyk9NpetS2gWxt4/D20QlhK/tHkROrcLmEUddwIGdwE8JzI88c77Fu
u6pgMtHKvl4GGH0yvb4T7PvCdH8V2tCH7bt8roXd9MSyFVy7uORkfouip7EsVREU
mlcY5EvpR141KXbZqiOQiusJ+u76mEUQNk8wCR1/CW/ii9v1BKOVjXwCfEtIXjg0
APJx1VNSSH6XoDpUETL+eQ4J0FL9XNbsDuYar7+zD0N1/5vSo3HLNRQR9f0lbsys
sWBEN+CxK19xyPumr21Z0bU0f1B5H52VSF0q3I1Ju9wRo994a7YipdGcmZ2lChmT
7r3mzlBTYl3poU26q34v8wG9U7Jv4fsZJ+RGebDI+TR3QG6Yod06l9oEYZxWXBY7
STOs8wuTu3huSnan/IpWnV017Vsc61D5G+QrqcxZdXckt3anZKCF75JpUnJ7vuow
TmmHlb8KIMa9mOvcuGX4P6mz8gTi2arl/aL27kj9Q0Jgv/y1ebe2Bx2P9TF6+VND
DL3J/vSVlFeqLt2reAIBKnytLwIDAQABo4GDMIGAMA4GA1UdDwEB/wQEAwIApDAS
BgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTXXUgpaSwO9HOrQBxGqOOSFHsH
EDAfBgNVHSMEGDAWgBS4Xu+uZ1C31vMH5Wq+VbNnOg2SPjAaBgNVHREEEzARgg9j
ZnNzbC1pbnRlci5jb20wCwYJKoZIhvcNAQELA4GBABqJOYgV+qEgkG/BIgsGaJ/Z
Neey0x0MwxPvA87e24GiYxYXX8ypR2DfLtuSjYfT0PVOWI5+3o9b3wnHhOu0aVe8
YK/7XUWOakt8Jv/fE0fGs4Ps5IeMynWBgwrf/6IQWEfnf/1siCrTf0yUEn0PMGu6
q2sLytoPYeibTYLuP1ED
-----END CERTIFICATE-----

View File

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEZTCCAk+gAwIBAgIIQTLl8XrmJF0wCwYJKoZIhvcNAQENMIGMMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwOTIzMTc1
NTI4WhcNMTUwNTMxMTgwMDI4WjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs
b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT
DVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNs
b3VkZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGw
f3F0XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73P
IXGyfNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo3sweTAO
BgNVHQ8BAf8EBAMCAKAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwEgYDVR0TAQH/BAgw
BgEB/wIBATAdBgNVHQ4EFgQUPd/cWIaY6MopIO6ldKx79dLpF1cwHwYDVR0jBBgw
FoAU111IKWksDvRzq0AcRqjjkhR7BxAwCwYJKoZIhvcNAQENA4ICAQAz96PKFCed
WSwOchAQszXDzaIcKSyyA1DQYEn5LvDTAcMgHg0KSb9bArv40ctwwvWQKwb9M4+M
ax1+xmZi7Vg9wjad4Pz+UHDxf+rnEAFBuN6dneNpv1J9qxK1yf9GkMsl10CGeh0x
AkHOut5rbb2hL0OrLOxhUK5M7pJf8zCMIirz9MHBDC+Xy4IKfEh8s4sKc2XRjnKv
/H2tjcUL9AP8c7AA3GPoMuMVZSygI3SmH0gu+wQcR3KQCnetvObqNoI/DEv+45pI
2B7CAf5FVhErqM9Gq2IuQQt9/7YosxL+wTvVNobKYmwP9NWFVtkJYpF1I63LtnaJ
+MN9b/fVYDjI9IBKXFD10eec2CZkUEj13bWOgIN1gwyL6IzXPOaT/oax/TVlMIpI
q3YnjMCEVS09sp2zAuLMYd97G8CMaRtGrtJM5V4rZ5l5u0w0So8FJac3pYugRm3V
udpPNUBkR4FPMCMUGExJWNADooDXs31UxPQXMdcRai8Gb1mZQGyOhQQJgH1MsoYY
f4OFvwvWsWlKL063Jc+a31fLs6LaH+4AVDyYgfBT5SyK9jQEr1q3HSGyzs7K48iP
MEBWnfd2VOyoUdbENab5vZo5ewnj79xp7Qbcugduu2phs0oxp1737fS13BgdpDdQ
9eueSTt1QsRE0SShMG5VqewfxQNk5BYrYg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEZTCCAk+gAwIBAgIIQkDSA6gKNdAwCwYJKoZIhvcNAQENMIGMMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwOTIzMTc1
NjEwWhcNMTYwMTAyMTgwMTEwWjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs
b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT
DVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNs
b3VkZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGw
f3F0XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73P
IXGyfNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo3sweTAO
BgNVHQ8BAf8EBAMCAKAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwEgYDVR0TAQH/BAgw
BgEB/wIBATAdBgNVHQ4EFgQUPd/cWIaY6MopIO6ldKx79dLpF1cwHwYDVR0jBBgw
FoAU111IKWksDvRzq0AcRqjjkhR7BxAwCwYJKoZIhvcNAQENA4ICAQCi53nUiAaE
aHSpCkebgcktFfDTTsY/7YCPFv0RxE08f+OMzndWpoS6dFYCxP1HetlYOh5a/3wm
9zj2IL6rY7CXyI6x2RH97Sdc4ibjcIqHdUuaFmG+qwjtI8TGeOFvwrdwx6i610Mc
cLqgc3gvtcLeqbMFkA0/js+viMg+bMTR+cEEIT3MHMq7byOzQa+koia9ZLAdiZsj
IkWGTg5tkySnwyQztLRjWknZ1syujarcoJ9VY8uWipQNMzrs91ZaAwHmeSdODsbn
Wvg7HZInbxCIzQz1/65FhdHzPbOv8MmQ1wlHqNNPYehXOgptsv9GN+DJlhiM62dp
tTRJnc3URyP4xfkVWtYXxuGBdGXPaktsNFBb163QJtrajAe9FtBqsIEUDDuUjNB1
rbw7xYUW5byiMPA1auHU3CHXg86LwU3TpE0d5hZriNCKzfLSpWJZa3WmQ0bc1ZLC
1m3EhK0fDqGz+NbyP7eyJl1nUGwElbhbHaK1HpopxtGKKi1xbMTjpU2Ery8bUXcK
EsUJWsxb8yU6BC25+ZWcHeuTaeSK+zlk/kvyMXgueslAoWUznmNhrLrTX3Ansjrf
MT1eo/cy+OU5FJslmhV0lhMy2W/lsAUqTGugMP2x1lhUnKy3/17XULh8v6BfmddR
XpQy1L6IceNMPfsKCvt5pg3urtpunV8ImQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEZTCCAk+gAwIBAgIIbHgGsniC/IUwCwYJKoZIhvcNAQENMIGMMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwOTIzMTc1
NzE3WhcNMTYwNjAyMTgwMjE3WjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs
b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT
DVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNs
b3VkZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGw
f3F0XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73P
IXGyfNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo3sweTAO
BgNVHQ8BAf8EBAMCAKAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwEgYDVR0TAQH/BAgw
BgEB/wIBATAdBgNVHQ4EFgQUPd/cWIaY6MopIO6ldKx79dLpF1cwHwYDVR0jBBgw
FoAU111IKWksDvRzq0AcRqjjkhR7BxAwCwYJKoZIhvcNAQENA4ICAQDkbiOxZ4Pq
y6TrubrmTwcRLLSDcXi8WeWv2AG6nAqZWAJXi/eUFggzNNgvAt4B/VH81YCkRN7R
YAefVztVSRxffOFTEk8PDQ55pthTbCdnaeJL1dP7PGZ7Dg1djxSC9G2jJ38xFQY3
5aM7BwbFcs5Iqd9MhbJIhrJfL22O5XpTTWzHGLOsS4H42If9qkTtajjMPwMbPUrZ
jJj6bWyOqfuGKef/Orn72EV9fPwyrLoTg9TPlKqX9ghm7fvuc9UVcEbFOAVSV9lS
3mEct1uwLBAEi/xhOlGN7jOn6q/w/0h2CI04FUe3/HB5rR0ffbqc2AdjO2R5uMgl
9XQ+u4/kOE5hHha8cSNSyScRiWr3yrG+3TaX4qdKnVUdP0LcoKZBm7+JYqTqsW5U
jr+sQmJrvWcEMx9Q5+5krDRhTWmyd4V27zgZ5T5Ilv3TC3zZ+VjJASKEHPFrClae
PdwkBdhxJRaXhkku7slW9Dgw4E0/0el2iytODoikzwcdlQQwQhvAnbtrJOhsXX1b
OpSQGfSZerAsuYSpiaNygF39HKxzgeZAEI5H+0R048Fcpysq8KVIET2B78zn0+mx
nSNOBP3SDLMcvlXDALpD6svSzQKuoQ1gyZUZzrJanbNJgA1gg2dQyEs13xhS8Vuc
Jtq40kpRnc9Pr3LNTL4Z95xMMqxYRBc3Xw==
-----END CERTIFICATE-----

View File

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEZTCCAk+gAwIBAgIIfwUOgDbzUY4wCwYJKoZIhvcNAQENMIGMMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwOTIzMTc1
NzM1WhcNMTcwMTAyMTgwMjM1WjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs
b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT
DVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNs
b3VkZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGw
f3F0XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73P
IXGyfNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo3sweTAO
BgNVHQ8BAf8EBAMCAKAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwEgYDVR0TAQH/BAgw
BgEB/wIBATAdBgNVHQ4EFgQUPd/cWIaY6MopIO6ldKx79dLpF1cwHwYDVR0jBBgw
FoAU111IKWksDvRzq0AcRqjjkhR7BxAwCwYJKoZIhvcNAQENA4ICAQAyTpWzbiOa
cB6FIsfRCGpqKZiaNMeuaJr8RPDYgXMAU4pK0ymZfZPWcUDDsVLEt5Zz4fJBd4E1
UzJ0GuuFswhGoICmYF397akwfOnXcbV+TBjdPPtsnsq71jRFlswdf6HkGqdQfMls
x6JnkM9XXDlWqKoVV8sHhnfvDOD5xZv2nLhIwBzt3dnrkhAKAI9jncHuJWO0KaxW
n8KhrAuoerQAz3+VM0y30siSAs4tBgmrKIc8AFh/S72VHUzBAnlc00LYE4M26/fQ
28EJzy01F3uuHHyRn56uQ/miWT5ttPxdUneN4SzQV7oB3ij6W9+QIrK0/uDMhw7l
NLk3nyj9V+XYbgynpgB2N92BK0MNYI9Jp0ULFgrAQaGadoo4XfDquQulWx8c7Zo+
/qpcjnAVy317LjvWZbdGu/rBEU6j7rMOKqRcmjkm+7ESuLv6OOj4JVMtvSEdtpPL
MK4mNAYa3O8qJMphjwokseF+9AfpBEJJzFFYiV7LyjFZlexSFOE1XZ1i0KaR7k+0
RxbBxz9ciovhYgDr9db3jQqcU8UR8mIgxcpqmWFinNmZ0Sr0/pkXyZzZPUmH99bD
vZEsmuFsEv1r+/7BAaPtWFnXwaacFkdEpVOKq5VJtNXMvAgYt7RJQobTDFFIMTaN
j4NKTmOqq1CTnqNmxPZqAyDotTucUJAbRA==
-----END CERTIFICATE-----

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC3TCCAkigAwIBAgIIPcD+KefD8UcwCwYJKoZIhvcNAQELMH0xCzAJBgNVBAYT
AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv
MRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQG
A1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDA0MTIwMDA3MzhaFw0xOTA0MTIwMDE3
MzhaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UE
CxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5j
b20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQhWSM0kbB/cXRdYkfvJBJW3G6gGJ2L
kk2xDsWHERBxkSXdv7/WIXrRMFjZiLorAm4DqcgTvc8hcbJ82FOHDgPwKJltpyRD
+5+DPYPK/HJYUM/MuvtNd4z/TBKn+KajFZ6jgYMwgYAwDgYDVR0PAQH/BAQDAgCk
MBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFEH5iiJSMibfj/AHAd0kYTha
5+z8MB8GA1UdIwQYMBaAFLhe765nULfW8wflar5Vs2c6DZI+MBoGA1UdEQQTMBGC
D2Nmc3NsLWludGVyLmNvbTALBgkqhkiG9w0BAQsDgYEAeTbzK6PFmAIWS1UQLw9L
CT6xKEUUhBtaYQNOczbxQ/iUdA8HLV8l4ou0ehewX3J+hmqylfv1f1rYIkDcAMHp
Lo2GfdT889wDJx+LuooBJDgLtXRvCxT7RFyKssQAsa32AJriYwxFbWNI0rkq4Ahs
/gOxML7hEGLskaFRGamcFRM=
-----END CERTIFICATE-----

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC+zCCAmagAwIBAgIISplHJE830hMwCwYJKoZIhvcNAQEFMH0xCzAJBgNVBAYT
AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv
MRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQG
A1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDA5MjkyMTM5NTZaFw0xNTA5MjkyMTQ0
NTZaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UE
CxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5j
b20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQhWSM0kbB/cXRdYkfvJBJW3G6gGJ2L
kk2xDsWHERBxkSXdv7/WIXrRMFjZiLorAm4DqcgTvc8hcbJ82FOHDgPwKJltpyRD
+5+DPYPK/HJYUM/MuvtNd4z/TBKn+KajFZ6jgaEwgZ4wDgYDVR0PAQH/BAQDAgCg
MB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0G
A1UdDgQWBBQ939xYhpjoyikg7qV0rHv10ukXVzAfBgNVHSMEGDAWgBS4Xu+uZ1C3
1vMH5Wq+VbNnOg2SPjAfBgNVHREEGDAWghRjbG91ZGZsYXJlLWludGVyLmNvbTAL
BgkqhkiG9w0BAQUDgYEAjVnk7Q/SY61E4epnel+3+NDW8dSWFl4J5lNnIs81NqXX
+cuXhY4gCiCmCD9u89BchhdyydqwsqCnSQHPm6Y3NZnDNnERpZw2qkPv5T0VuHJi
YZ7RaZYgG+f7xWS/KbvL7bZ5IVEFBjnUgnoT2V+bFDRmIkhLxc2jCIFXWt5RPA0=
-----END CERTIFICATE-----

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIDCjCCAfQCAQAwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRcwFQYDVQQDEw5jbG91ZGZsYXJl
LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOLFLykOd2j31AQn
kaToYtstGvw5wLb4YnlzipQ6aULlD0H0GHM9IwhdSmcTWUWPb/U83g/ma1uD3Pp2
IdWd6xfjyOJF5XhgkyfRY65wS6vPZRm2MNSFXem+0AKHdhxIhb/QPMASqC/yaiPi
nvtOpBiCNl1Q2N4y9pkV0oD/T4rrn3RXP6iL1k4CNRS54JPCd+aI5Om+axVPU8Id
ZeUXQwXISaFrcC/bFXAHGX5hBMVu34lhCxvR4smweZkVmW++bIv26az8TSb5nVn4
TstLJIaOoOqot0sis04+0oX/GXfTPfkWyzfTVFN7cb9H+gz0FZJKtXQZv6qdntji
9FdR+pkCAwEAAaBAMD4GCSqGSIb3DQEJDjExMC8wLQYDVR0RBCYwJIIOY2xvdWRm
bGFyZS5jb22CEnd3dy5jbG91ZGZsYXJlLmNvbTALBgkqhkiG9w0BAQsDggEBABfM
9XTMqMqmfAAymWC4/W+vbh301KBoydcTnDQ/7B+ftHRE0O3FUsdL3wobj3qBieJo
MiQwiL7+GksszHvN9+YOUi70wpFuKghLhadb7p5GzL0+JgK2eQnLYb37/lQSiWwn
hht1YMOzErR/KHlxNUafk71bDEeytUcOvvtujf86nZiEnBpvp47zDjMkDersczM0
wj7S50IY8/vRsc2Q8vy+Q7D2FPEwjs4wCGVSqzwX2NPn3fZb/2pWRCie9kxHUfUP
L5xO4WoFGuirT6E2GnUWDdH661Pj5yEKvmr+qPl+eVoLjrtx0g5rAmA7rGlGrkqp
r4idH/BbJUaDlRHM/Hk=
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA4sUvKQ53aPfUBCeRpOhi2y0a/DnAtvhieXOKlDppQuUPQfQY
cz0jCF1KZxNZRY9v9TzeD+ZrW4Pc+nYh1Z3rF+PI4kXleGCTJ9FjrnBLq89lGbYw
1IVd6b7QAod2HEiFv9A8wBKoL/JqI+Ke+06kGII2XVDY3jL2mRXSgP9PiuufdFc/
qIvWTgI1FLngk8J35ojk6b5rFU9Twh1l5RdDBchJoWtwL9sVcAcZfmEExW7fiWEL
G9HiybB5mRWZb75si/bprPxNJvmdWfhOy0skho6g6qi3SyKzTj7Shf8Zd9M9+RbL
N9NUU3txv0f6DPQVkkq1dBm/qp2e2OL0V1H6mQIDAQABAoIBAQCzT3HcCAlZoeUu
p88dU3efkUnuOQhuZXcQS9E/JfTHpXHsF8Qhky0ZVxMW8BC91Q6VHt0EO5GWWm0o
SrK0Q9t6F25npRcumUaizIoCi9756tMpgouX8CDzTCMUbOJyuNGxe0oeImKFDyzo
VTCazHMqwgOUw/HHuQqOv9ekkrzlva8U+Z5MGZB4B2acHIAJHO9uYGzdeAjF3grm
dQ3QFGXJM0JzPmXfnUiDeOWIoVbo4YROFhf7qNlcnyLdkrYe0/XsSYQM9dRGKRPK
nkOkMv0sC8rOqNuJUn3tf1OOjzVQxlzB8Key6MOQ1c+kqsdCnL88/93CvI5NHazx
hwUmesmBAoGBAPpkDtgeWjxeIjOfuxXDYb04XbVmKquKNOIEk5OADmaacSGzdemh
XLRaNVMEYMcgMJViDDKW8g4k+zuZgzooMxNynlLNU5wfazwX2LLjReJFvZb/SxMM
N9+vQo8fcGz+p5g1tbeE6w86mpsTiAGx9Wa4J4GnY8jF6XUjZHO0X91pAoGBAOfZ
qrDkPMDSiVk62FP6LlPrj09bt1NTkBfv5dWhN/XeHjuus7unDhNiRmphhgF0VZse
XPtT/PUO0YgYlyaYJDDDE0IxgHuoK9wvEb2sqEtkZSw7IUhehheZ/+YfXzSA5fwa
vhXt0ghB0d9oVJuRoxb17MncjpjDAKy0QR5drR2xAoGBAMlNwkVseZ2JDLQ2WgHQ
N/cZpvUc83dAQO3pQgBW9rz0s7mlf0naqh5xW+enYGsW7RhcYHQXuPk4MCelbsRF
53JeNv1ZCDw/YkZI4bZIVDnrWdZY3zGsJAuY6skIPKnUPkd3/uVRXm267ut4U2MR
gLsZmOF7AxU6UEwVrT/8pwnpAoGAKxbVFlMUx3FZfW/mTJUujwI0fDc7dw0MtqYr
POzdjaBeVhE97h46C3g0Rgkh8ptAXbfi6ALP/GtonbaUQOP9teJLbf3tNw4mOKG2
1l2EWZ6q/vFuWhjXKwO//3DNLODX3WbK9SBh7I7vBmpJbzA980J5Y3rONa3oLjDB
+XbHecECgYEArOEv2D3fE3Hd6rEbxXinqekxMa+V1OCDO1IPz4wwr9RDMVUMxwqF
f0es1PQ2eMJGrAMbySxPfSZG05ou/tA+zR0qPwc/+dX0BbaXCiNT3gbhvL1L2fBc
7wr+MIUe2fi54JUWrUNMDHngRhXRKt2rZZRTfqVaFmZX02Y3fMZ2dWg=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDXDCCAwKgAwIBAgIIfTkIXBckiQ4wCgYIKoZIzj0EAwIwgYwxCzAJBgNVBAYT
AlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJlMRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2lu
ZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMR0wGwYDVQQDExRjbG91ZGZsYXJlLWludGVyLmNvbTAeFw0xNDA1MjcxNzU1
MjZaFw0xNTA1MjcxODAwMjZaMIGGMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xv
dWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEXMBUGA1UEAxMOY2xv
dWRmbGFyZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDixS8p
Dndo99QEJ5Gk6GLbLRr8OcC2+GJ5c4qUOmlC5Q9B9BhzPSMIXUpnE1lFj2/1PN4P
5mtbg9z6diHVnesX48jiReV4YJMn0WOucEurz2UZtjDUhV3pvtACh3YcSIW/0DzA
Eqgv8moj4p77TqQYgjZdUNjeMvaZFdKA/0+K6590Vz+oi9ZOAjUUueCTwnfmiOTp
vmsVT1PCHWXlF0MFyEmha3Av2xVwBxl+YQTFbt+JYQsb0eLJsHmZFZlvvmyL9ums
/E0m+Z1Z+E7LSySGjqDqqLdLIrNOPtKF/xl30z35Fss301RTe3G/R/oM9BWSSrV0
Gb+qnZ7Y4vRXUfqZAgMBAAGjgYYwgYMwDgYDVR0PAQH/BAQDAgCmMB0GA1UdJQQW
MBQGCCsGAQUFBwMBBggrBgEFBQcDAjASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1Ud
DgQWBBR9vuLqsiu6H1S6qG9JmUSh687mqDAfBgNVHSMEGDAWgBTCx1syJ5D32o9i
aWwvkvzYdZDJqTAKBggqhkjOPQQDAgNIADBFAiA1sNmHjK4MF0USUIYiCBINpG3b
G8zRSWc5mEwCVCCGGQIhAPuTm12Hnmc6PUMmS12uQoz+UpQ536lYpHqLoe/yZHDk
-----END CERTIFICATE-----

View File

@ -0,0 +1,12 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIB0jCCAVcCAQAwgYwxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl
MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh
bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMR0wGwYDVQQDExRjbG91ZGZsYXJl
LWludGVyLmNvbTB2MBAGByqGSM49AgEGBSuBBAAiA2IABCFZIzSRsH9xdF1iR+8k
ElbcbqAYnYuSTbEOxYcREHGRJd2/v9YhetEwWNmIuisCbgOpyBO9zyFxsnzYU4cO
A/AomW2nJEP7n4M9g8r8clhQz8y6+013jP9MEqf4pqMVnqBLMEkGCSqGSIb3DQEJ
DjE8MDowOAYDVR0RBDEwL4IUY2xvdWRmbGFyZS1pbnRlci5jb22CF3d3d2Nsb3Vk
ZmxhcmUtaW50ZXIuY29tMAoGCCqGSM49BAMDA2kAMGYCMQD6kSGGc3/DeFAWrPUX
qSlnTTm57DpzUoHQE306DfbFB6DFfoORNM5Z98chnZ+Ell4CMQCzYhOvIh3+GPGF
MuYYIAfQV2JG+n7pjfpJ+X1Ee2bOtA4ZO39P9/FTEtJUXt+Ivqw=
-----END CERTIFICATE REQUEST-----

View File

@ -0,0 +1,6 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDAVVKPnV+KoCmQRq1zGg6n5PjjBFZdVPcKi9fNe78ZqMAMfLSfycPcS
e6HJVt8ylCegBwYFK4EEACKhZANiAAQhWSM0kbB/cXRdYkfvJBJW3G6gGJ2Lkk2x
DsWHERBxkSXdv7/WIXrRMFjZiLorAm4DqcgTvc8hcbJ82FOHDgPwKJltpyRD+5+D
PYPK/HJYUM/MuvtNd4z/TBKn+KajFZ4=
-----END EC PRIVATE KEY-----

View File

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEbjCCAligAwIBAgIIeHSbZwALpoAwCwYJKoZIhvcNAQELMIGMMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwNDExMjEy
MjM4WhcNMTkwNDExMjEyNzM4WjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs
b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT
DVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNs
b3VkZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGw
f3F0XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73P
IXGyfNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo4GDMIGA
MA4GA1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBRB
+YoiUjIm34/wBwHdJGE4Wufs/DAfBgNVHSMEGDAWgBTXXUgpaSwO9HOrQBxGqOOS
FHsHEDAaBgNVHREEEzARgg9jZnNzbC1pbnRlci5jb20wCwYJKoZIhvcNAQELA4IC
AQCaj2i8wr9r3FS8Tw5QHD+tPmryrHsiLlERVanTif9kt/fRc1/hm/pv2lTLK8kK
U5Eti1jCB2T/DQGj4Z/amRndasXpUb5wTtMb9V6jN4pRfgw+C5ska9o5zFrIGJF0
GbSe1VVUedJ1LH3US3a79eVGmyAwcfTRMNhn+e+uYky2VYCQIEGGQ8rZAM3TveoT
N8J7Lqwtuo3DWz0IYx60DUvabpqJ+9Dl6rhTvTfyYvQK4vl2xApGf4Uo87JbNQfq
q40UXfBtMaAvIPEKCyTdOVVDrfgW0DQTl7wS+Z3p6kNm0NMI53TFTbgIuU9QiPPB
I5NdqISEPFW/HS5q0+zR1KdG4EmEjmpCX78s+uviHpHQloWQT9ov4KbXbf8y5Xso
lv+2gcd5TVjYxPRbo3SMtGRQho5uq2BNy6Q0K0//3OE+X+v+ZDi8n4MU3uA7dGGA
7uAUZOYPzNKS7ryW3h4PZIfiI5Fv9tBNnu9O3I2UH6fHNFQQLzJPCXertPmrORjP
EyCNCOhfsNwLd5Qq53cDbG1mkZro/xKDvAOx2LQcGFtmx4v1NXI204V50aSzy8vY
vQnM0gEY/YxoCq3wSjc9yeUftyv2LIgJvuXjkeHkV7gQQ+jx/HY6J7fnJGSzKMKp
/GPaPCNKvCY/72ik2gbmdvLbaRGeVJ07JO46YWEUrGb/1A==
-----END CERTIFICATE-----

View File

@ -0,0 +1,53 @@
-----BEGIN CERTIFICATE-----
MIIEizCCA/agAwIBAgIIeM7v534l+W0wCwYJKoZIhvcNAQELMH0xCzAJBgNVBAYT
AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv
MRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQG
A1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDA0MTEyMTIyMzdaFw0xOTA0MTEyMTI3
MzdaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UE
CxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET
MBEGA1UECBMKQ2FsaWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5j
b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDlCnV+vj0sVPy8SqHL
AlI+xwnPhWgzj2VevD6Nz1Zu1BeQ5m5y4CWCf+GmRGTP7+a/C510Fw6rpmInB0Ng
xxwQ2rC08fJtCnijlGH/VjEPIHY5lRaAomcM8Rgx6JOuv9BpZJKpr9pyUMV53JeW
RbWuLH5nEMdyk9NpetS2gWxt4/D20QlhK/tHkROrcLmEUddwIGdwE8JzI88c77Fu
u6pgMtHKvl4GGH0yvb4T7PvCdH8V2tCH7bt8roXd9MSyFVy7uORkfouip7EsVREU
mlcY5EvpR141KXbZqiOQiusJ+u76mEUQNk8wCR1/CW/ii9v1BKOVjXwCfEtIXjg0
APJx1VNSSH6XoDpUETL+eQ4J0FL9XNbsDuYar7+zD0N1/5vSo3HLNRQR9f0lbsys
sWBEN+CxK19xyPumr21Z0bU0f1B5H52VSF0q3I1Ju9wRo994a7YipdGcmZ2lChmT
7r3mzlBTYl3poU26q34v8wG9U7Jv4fsZJ+RGebDI+TR3QG6Yod06l9oEYZxWXBY7
STOs8wuTu3huSnan/IpWnV017Vsc61D5G+QrqcxZdXckt3anZKCF75JpUnJ7vuow
TmmHlb8KIMa9mOvcuGX4P6mz8gTi2arl/aL27kj9Q0Jgv/y1ebe2Bx2P9TF6+VND
DL3J/vSVlFeqLt2reAIBKnytLwIDAQABo4GDMIGAMA4GA1UdDwEB/wQEAwIApDAS
BgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTXXUgpaSwO9HOrQBxGqOOSFHsH
EDAfBgNVHSMEGDAWgBS4Xu+uZ1C31vMH5Wq+VbNnOg2SPjAaBgNVHREEEzARgg9j
ZnNzbC1pbnRlci5jb20wCwYJKoZIhvcNAQELA4GBABqJOYgV+qEgkG/BIgsGaJ/Z
Neey0x0MwxPvA87e24GiYxYXX8ypR2DfLtuSjYfT0PVOWI5+3o9b3wnHhOu0aVe8
YK/7XUWOakt8Jv/fE0fGs4Ps5IeMynWBgwrf/6IQWEfnf/1siCrTf0yUEn0PMGu6
q2sLytoPYeibTYLuP1ED
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEbjCCAligAwIBAgIIeHSbZwALpoAwCwYJKoZIhvcNAQELMIGMMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwNDExMjEy
MjM4WhcNMTkwNDExMjEyNzM4WjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs
b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT
DVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNs
b3VkZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGw
f3F0XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73P
IXGyfNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo4GDMIGA
MA4GA1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBRB
+YoiUjIm34/wBwHdJGE4Wufs/DAfBgNVHSMEGDAWgBTXXUgpaSwO9HOrQBxGqOOS
FHsHEDAaBgNVHREEEzARgg9jZnNzbC1pbnRlci5jb20wCwYJKoZIhvcNAQELA4IC
AQCaj2i8wr9r3FS8Tw5QHD+tPmryrHsiLlERVanTif9kt/fRc1/hm/pv2lTLK8kK
U5Eti1jCB2T/DQGj4Z/amRndasXpUb5wTtMb9V6jN4pRfgw+C5ska9o5zFrIGJF0
GbSe1VVUedJ1LH3US3a79eVGmyAwcfTRMNhn+e+uYky2VYCQIEGGQ8rZAM3TveoT
N8J7Lqwtuo3DWz0IYx60DUvabpqJ+9Dl6rhTvTfyYvQK4vl2xApGf4Uo87JbNQfq
q40UXfBtMaAvIPEKCyTdOVVDrfgW0DQTl7wS+Z3p6kNm0NMI53TFTbgIuU9QiPPB
I5NdqISEPFW/HS5q0+zR1KdG4EmEjmpCX78s+uviHpHQloWQT9ov4KbXbf8y5Xso
lv+2gcd5TVjYxPRbo3SMtGRQho5uq2BNy6Q0K0//3OE+X+v+ZDi8n4MU3uA7dGGA
7uAUZOYPzNKS7ryW3h4PZIfiI5Fv9tBNnu9O3I2UH6fHNFQQLzJPCXertPmrORjP
EyCNCOhfsNwLd5Qq53cDbG1mkZro/xKDvAOx2LQcGFtmx4v1NXI204V50aSzy8vY
vQnM0gEY/YxoCq3wSjc9yeUftyv2LIgJvuXjkeHkV7gQQ+jx/HY6J7fnJGSzKMKp
/GPaPCNKvCY/72ik2gbmdvLbaRGeVJ07JO46YWEUrGb/1A==
-----END CERTIFICATE-----

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFMzCCBBugAwIBAgIDESgIMA0GCSqGSIb3DQEBCwUAMDwxCzAJBgNVBAYTAlVT
MRcwFQYDVQQKEw5HZW9UcnVzdCwgSW5jLjEUMBIGA1UEAxMLUmFwaWRTU0wgQ0Ew
HhcNMTQwMzA5MDcwODA2WhcNMTUwNDEwMTkwODU3WjCBvjEpMCcGA1UEBRMgOWRp
eGpNZTVGQS9Fd0pjMWxhM01IQ1JBOTNnTFZobkoxEzARBgNVBAsTCkdUODM1NTg5
ODAxMTAvBgNVBAsTKFNlZSB3d3cucmFwaWRzc2wuY29tL3Jlc291cmNlcy9jcHMg
KGMpMTQxLzAtBgNVBAsTJkRvbWFpbiBDb250cm9sIFZhbGlkYXRlZCAtIFJhcGlk
U1NMKFIpMRgwFgYDVQQDDA8qLmxhemFkYS5jb20ucGgwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDKlGxmI33lQbcQHI5Fhf6EQGoqVVmHYRU5whipFg5g
BButLAPj8TTDgD6a1iKPDsnQvNZnVjKLnjAu8c/lwwF7GevfuAS/SXasmKiONJ1b
w+/MPWia/j++AoGIwKJe6py6r9INy3rTFrwuzSemzTnAja/VYtMPiAR/tAIK8FmU
JZpop8JvGMr0KpmCBrJ4/HxFQwlmJjjSuvronNtN3h10vX1vNXCyobwaL2UplY8c
0QZv6PdS8gzdki4H5AwOSnzJiY6RJPH6qR9dotQFYvSEHwUACHp7AOKkhdtO/+fG
rd0+0vXLznyhIGZjT+T8at1H3/1udBhUkoAc76Xlqu3FAgMBAAGjggG5MIIBtTAf
BgNVHSMEGDAWgBRraT1qGEJK3Y8CZTn9NSSGeJEWMDAOBgNVHQ8BAf8EBAMCBaAw
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMCkGA1UdEQQiMCCCDyoubGF6
YWRhLmNvbS5waIINbGF6YWRhLmNvbS5waDBDBgNVHR8EPDA6MDigNqA0hjJodHRw
Oi8vcmFwaWRzc2wtY3JsLmdlb3RydXN0LmNvbS9jcmxzL3JhcGlkc3NsLmNybDAd
BgNVHQ4EFgQUWh9QqoLYcrJ8u85o7CXAemGUtKQwDAYDVR0TAQH/BAIwADB4Bggr
BgEFBQcBAQRsMGowLQYIKwYBBQUHMAGGIWh0dHA6Ly9yYXBpZHNzbC1vY3NwLmdl
b3RydXN0LmNvbTA5BggrBgEFBQcwAoYtaHR0cDovL3JhcGlkc3NsLWFpYS5nZW90
cnVzdC5jb20vcmFwaWRzc2wuY3J0MEwGA1UdIARFMEMwQQYKYIZIAYb4RQEHNjAz
MDEGCCsGAQUFBwIBFiVodHRwOi8vd3d3Lmdlb3RydXN0LmNvbS9yZXNvdXJjZXMv
Y3BzMA0GCSqGSIb3DQEBCwUAA4IBAQA8NsDVERjLyQ8CjsF4IcE9jNjr0ryXMV2E
qLu27SAAp+walxOlf8RbZbwO7NxjiJi6Lubu0GOEVIrzqTzGta131dkKwmxVpSVq
LcOpHHqmQTv0WDYKjhNc5PhjhfpjLnK5xjhJsd6us/FT/VYaeeq9XEnjCTc9JME8
DUWnE0DK+14pOgH/KAzIpQCUGRaWOZgXbFkptIhgKikqOd/otKBHy/JNvqOk9ugJ
zz+O5ZMBUZq3h7rDIjt/o+l4mv+f2DG1eiugOXLIukpMeK+ixM5b8uuprXqEK7oG
qTal86QTe9+E/2KbOO+jUmh5JNsQmkYZKm4foM7pk4bHXPWxUco5
-----END CERTIFICATE-----

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
-----BEGIN CERTIFICATE-----
MIICsDCCAjegAwIBAgIIDmHBNS+T0F8wCgYIKoZIzj0EAwMwgYwxCzAJBgNVBAYT
AlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJlMRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2lu
ZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMR0wGwYDVQQDExRjbG91ZGZsYXJlLWludGVyLmNvbTAeFw0xNDA0MTEyMTIy
MzhaFw0xOTA0MTEyMTI3MzhaMIGLMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xv
dWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEcMBoGA1UEAxMTY2xv
dWRmbGFyZS1sZWFmLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIxG/fG9
y/gjlAXvB77beERLbBooN98FGFAxVUA5IglylvgmfNxUmI8mM2Uw9tzOLm9vORAr
aSSM4/6iSpCJreCjgYEwfzAOBgNVHQ8BAf8EBAMCAKQwEgYDVR0TAQH/BAgwBgEB
/wIBATAdBgNVHQ4EFgQU4t+cr91ma5IxOPeiezgN8W9FBNowHwYDVR0jBBgwFoAU
QfmKIlIyJt+P8AcB3SRhOFrn7PwwGQYDVR0RBBIwEIIOY2Zzc2wtbGVhZi5jb20w
CgYIKoZIzj0EAwMDZwAwZAIwYQWcWr79DPrIBnphpHZPuxnGust6NtD0aSffB1cF
NlYtggjJZDbLijAgD0Bwi3THAjA639xrNxVgc/LkJcHfSRhs8Jhv9cxQxIVf3g8w
6tBymEgJ6L8aIPGgXNRJGs7FmPs=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEbjCCAligAwIBAgIIeHSbZwALpoAwCwYJKoZIhvcNAQELMIGMMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwNDExMjEy
MjM4WhcNMTkwNDExMjEyNzM4WjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs
b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT
DVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNs
b3VkZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGw
f3F0XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73P
IXGyfNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo4GDMIGA
MA4GA1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBRB
+YoiUjIm34/wBwHdJGE4Wufs/DAfBgNVHSMEGDAWgBTXXUgpaSwO9HOrQBxGqOOS
FHsHEDAaBgNVHREEEzARgg9jZnNzbC1pbnRlci5jb20wCwYJKoZIhvcNAQELA4IC
AQCaj2i8wr9r3FS8Tw5QHD+tPmryrHsiLlERVanTif9kt/fRc1/hm/pv2lTLK8kK
U5Eti1jCB2T/DQGj4Z/amRndasXpUb5wTtMb9V6jN4pRfgw+C5ska9o5zFrIGJF0
GbSe1VVUedJ1LH3US3a79eVGmyAwcfTRMNhn+e+uYky2VYCQIEGGQ8rZAM3TveoT
N8J7Lqwtuo3DWz0IYx60DUvabpqJ+9Dl6rhTvTfyYvQK4vl2xApGf4Uo87JbNQfq
q40UXfBtMaAvIPEKCyTdOVVDrfgW0DQTl7wS+Z3p6kNm0NMI53TFTbgIuU9QiPPB
I5NdqISEPFW/HS5q0+zR1KdG4EmEjmpCX78s+uviHpHQloWQT9ov4KbXbf8y5Xso
lv+2gcd5TVjYxPRbo3SMtGRQho5uq2BNy6Q0K0//3OE+X+v+ZDi8n4MU3uA7dGGA
7uAUZOYPzNKS7ryW3h4PZIfiI5Fv9tBNnu9O3I2UH6fHNFQQLzJPCXertPmrORjP
EyCNCOhfsNwLd5Qq53cDbG1mkZro/xKDvAOx2LQcGFtmx4v1NXI204V50aSzy8vY
vQnM0gEY/YxoCq3wSjc9yeUftyv2LIgJvuXjkeHkV7gQQ+jx/HY6J7fnJGSzKMKp
/GPaPCNKvCY/72ik2gbmdvLbaRGeVJ07JO46YWEUrGb/1A==
-----END CERTIFICATE-----

View File

@ -0,0 +1,43 @@
-----BEGIN CERTIFICATE-----
MIIEbjCCAligAwIBAgIIeHSbZwALpoAwCwYJKoZIhvcNAQELMIGMMQswCQYDVQQG
EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp
bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv
cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwNDExMjEy
MjM4WhcNMTkwNDExMjEyNzM4WjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs
b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT
DVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNs
b3VkZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGw
f3F0XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73P
IXGyfNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo4GDMIGA
MA4GA1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBRB
+YoiUjIm34/wBwHdJGE4Wufs/DAfBgNVHSMEGDAWgBTXXUgpaSwO9HOrQBxGqOOS
FHsHEDAaBgNVHREEEzARgg9jZnNzbC1pbnRlci5jb20wCwYJKoZIhvcNAQELA4IC
AQCaj2i8wr9r3FS8Tw5QHD+tPmryrHsiLlERVanTif9kt/fRc1/hm/pv2lTLK8kK
U5Eti1jCB2T/DQGj4Z/amRndasXpUb5wTtMb9V6jN4pRfgw+C5ska9o5zFrIGJF0
GbSe1VVUedJ1LH3US3a79eVGmyAwcfTRMNhn+e+uYky2VYCQIEGGQ8rZAM3TveoT
N8J7Lqwtuo3DWz0IYx60DUvabpqJ+9Dl6rhTvTfyYvQK4vl2xApGf4Uo87JbNQfq
q40UXfBtMaAvIPEKCyTdOVVDrfgW0DQTl7wS+Z3p6kNm0NMI53TFTbgIuU9QiPPB
I5NdqISEPFW/HS5q0+zR1KdG4EmEjmpCX78s+uviHpHQloWQT9ov4KbXbf8y5Xso
lv+2gcd5TVjYxPRbo3SMtGRQho5uq2BNy6Q0K0//3OE+X+v+ZDi8n4MU3uA7dGGA
7uAUZOYPzNKS7ryW3h4PZIfiI5Fv9tBNnu9O3I2UH6fHNFQQLzJPCXertPmrORjP
EyCNCOhfsNwLd5Qq53cDbG1mkZro/xKDvAOx2LQcGFtmx4v1NXI204V50aSzy8vY
vQnM0gEY/YxoCq3wSjc9yeUftyv2LIgJvuXjkeHkV7gQQ+jx/HY6J7fnJGSzKMKp
/GPaPCNKvCY/72ik2gbmdvLbaRGeVJ07JO46YWEUrGb/1A==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICsDCCAjegAwIBAgIIDmHBNS+T0F8wCgYIKoZIzj0EAwMwgYwxCzAJBgNVBAYT
AlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJlMRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2lu
ZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMR0wGwYDVQQDExRjbG91ZGZsYXJlLWludGVyLmNvbTAeFw0xNDA0MTEyMTIy
MzhaFw0xOTA0MTEyMTI3MzhaMIGLMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xv
dWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMN
U2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEcMBoGA1UEAxMTY2xv
dWRmbGFyZS1sZWFmLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIxG/fG9
y/gjlAXvB77beERLbBooN98FGFAxVUA5IglylvgmfNxUmI8mM2Uw9tzOLm9vORAr
aSSM4/6iSpCJreCjgYEwfzAOBgNVHQ8BAf8EBAMCAKQwEgYDVR0TAQH/BAgwBgEB
/wIBATAdBgNVHQ4EFgQU4t+cr91ma5IxOPeiezgN8W9FBNowHwYDVR0jBBgwFoAU
QfmKIlIyJt+P8AcB3SRhOFrn7PwwGQYDVR0RBBIwEIIOY2Zzc2wtbGVhZi5jb20w
CgYIKoZIzj0EAwMDZwAwZAIwYQWcWr79DPrIBnphpHZPuxnGust6NtD0aSffB1cF
NlYtggjJZDbLijAgD0Bwi3THAjA639xrNxVgc/LkJcHfSRhs8Jhv9cxQxIVf3g8w
6tBymEgJ6L8aIPGgXNRJGs7FmPs=
-----END CERTIFICATE-----

View File

@ -0,0 +1,45 @@
-----BEGIN CERTIFICATE-----
MIIH5TCCBs2gAwIBAgIQBVtqmc8x2tl8Fe1v9s7UoDANBgkqhkiG9w0BAQsFADBw
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBTSEEyIEhpZ2ggQXNz
dXJhbmNlIFNlcnZlciBDQTAeFw0xMzEyMzEwMDAwMDBaFw0xNzAxMDQxMjAwMDBa
MHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRUwEwYDVQQHEwxT
YW50YSBNb25pY2ExGTAXBgNVBAoTEFJpb3QgR2FtZXMsIEluYy4xITAfBgNVBAMT
GGxxLm5hMS5sb2wucmlvdGdhbWVzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAKpicFUVqYz1ArARQ5LoncPu14OhxYq9dYUJvm9tdKCdig7KQYDa
4SDo8cMiWPuxr3DB48ZeIOpZtF9VplDhdDGMnbifO8ohCiAuCITnP+ft+dLdcTeB
AW5M5l2MaLxj/vdCRzENPa7KbzS0jnd5WBIkwsgkh8E4OSrTkIfNK+kQiBMz+9Td
EHJagUvZ9vfTu0aSXx7+9bw/rDxCylWDbevYNBiBFMfgclhrW6NLlXBhbmRzxeY+
lOScZdJkKoMGFXVaXsHh6BILdIypedCNrURBKWzFoke3y9dU8IPqgfdkvhm1aQXv
EdLo/gh+GMbaYK5ZFBYhneX/MQIqMNpoBZcCAwEAAaOCBHIwggRuMB8GA1UdIwQY
MBaAFFFo/5CvAgd1PMzZZWRiohK4WXI7MB0GA1UdDgQWBBQXUcvgDtxEapXyIy30
2IYF6jYhvjCCASgGA1UdEQSCAR8wggEbghhscS5uYTEubG9sLnJpb3RnYW1lcy5j
b22CF2xxLmV1LmxvbC5yaW90Z2FtZXMuY29tghlscS5ldW4xLmxvbC5yaW90Z2Ft
ZXMuY29tghdscS5rci5sb2wucmlvdGdhbWVzLmNvbYIXbHEuYnIubG9sLnJpb3Rn
YW1lcy5jb22CF2xxLnRyLmxvbC5yaW90Z2FtZXMuY29tghdscS5ydS5sb2wucmlv
dGdhbWVzLmNvbYIYbHEub2MxLmxvbC5yaW90Z2FtZXMuY29tghhscS5sYTEubG9s
LnJpb3RnYW1lcy5jb22CGGxxLmxhMi5sb2wucmlvdGdhbWVzLmNvbYIZbHEucGJl
MS5sb2wucmlvdGdhbWVzLmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYI
KwYBBQUHAwEGCCsGAQUFBwMCMHUGA1UdHwRuMGwwNKAyoDCGLmh0dHA6Ly9jcmwz
LmRpZ2ljZXJ0LmNvbS9zaGEyLWhhLXNlcnZlci1nMS5jcmwwNKAyoDCGLmh0dHA6
Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWhhLXNlcnZlci1nMS5jcmwwggHEBgNV
HSAEggG7MIIBtzCCAbMGCWCGSAGG/WwBATCCAaQwOgYIKwYBBQUHAgEWLmh0dHA6
Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0wggFkBggr
BgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAA
QwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAA
YQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUA
cgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4A
ZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAA
bABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBlACAA
aQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIA
ZQBmAGUAcgBlAG4AYwBlAC4wgYMGCCsGAQUFBwEBBHcwdTAkBggrBgEFBQcwAYYY
aHR0cDovL29jc3AuZGlnaWNlcnQuY29tME0GCCsGAQUFBzAChkFodHRwOi8vY2Fj
ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRTSEEySGlnaEFzc3VyYW5jZVNlcnZl
ckNBLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQBzj+qBxGES
JBeOuJo2rc2xT8ezNT2l+Kg/NjSr65t/U+u6xMELufGF/fqWPzsfHT3CXHMiAW2P
lVHejbaePfkeNxHbkaLa15X3+dGOXR04a2QTI4zdexTZ9Xsx8chQZA4bEradIVl+
o2y+MPN+6Hr1g2M2aw2d+1xxvVQcNe2iQDfUWSRd70wt8DXnpqFCumlCwZfYojpk
NQ/ahCg3AKu/3gQPgjyXB7HUf14M8x6PwNw4IK0LCC99HaqQID/V4jVw47beJ6FC
7+dW9nkrZBRgIHnqah90El+75oqwAmW8rewmTOKAXxRafGK6WreAZ0x9qfbBXFiH
VRL8HkH3gqBT
-----END CERTIFICATE-----

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFLzCCBBegAwIBAgIHBJgUJzNPaDANBgkqhkiG9w0BAQsFADCBtDELMAkGA1UE
BhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAY
BgNVBAoTEUdvRGFkZHkuY29tLCBJbmMuMS0wKwYDVQQLEyRodHRwOi8vY2VydHMu
Z29kYWRkeS5jb20vcmVwb3NpdG9yeS8xMzAxBgNVBAMTKkdvIERhZGR5IFNlY3Vy
ZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjAeFw0xNDA0MDkxNjIyMDlaFw0x
NDEyMDQxNjU4NTVaMD8xITAfBgNVBAsTGERvbWFpbiBDb250cm9sIFZhbGlkYXRl
ZDEaMBgGA1UEAwwRKi5zdXJ2ZXlnaXptby5jb20wggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQC+U1kaSViTxGfD5U5krfafN9+4jSq51CNnTZJY1CTJjihb
ym8n1ZNVrrCvivcGHT0BMHzDLVnQ2VLcac1F7TkjT5OCL5neSGLS6fnjoR5lPIvU
KZfG94hPAMXXjqc3/cpDEenQv2JaNDL6r/f4fNzGalqSf5IkPvg0GxX2knWhjwTG
f8cO+z08fKKKGt0tDt5/Dx6rxuqLxSg/eQhTU/QUUObuMeQCXtGfbC4n/sQJz0O5
It5+EofG+KYb4gxJf/aScJv1PRSlSy9wYw6TJoUEn905VsFMH/mJuThzw5H976Vn
Vchh5fUJItlU5T2kVRAnuLF0dI3unl+fx5h+cEXPAgMBAAGjggG4MIIBtDAPBgNV
HRMBAf8EBTADAQEAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAOBgNV
HQ8BAf8EBAMCBaAwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nb2RhZGR5
LmNvbS9nZGlnMnMxLTM4LmNybDBTBgNVHSAETDBKMEgGC2CGSAGG/W0BBxcBMDkw
NwYIKwYBBQUHAgEWK2h0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVw
b3NpdG9yeS8wdgYIKwYBBQUHAQEEajBoMCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz
cC5nb2RhZGR5LmNvbS8wQAYIKwYBBQUHMAKGNGh0dHA6Ly9jZXJ0aWZpY2F0ZXMu
Z29kYWRkeS5jb20vcmVwb3NpdG9yeS9nZGlnMi5jcnQwHwYDVR0jBBgwFoAUQMK9
J47MNIMwojPX+2yz8LQsgM4wLQYDVR0RBCYwJIIRKi5zdXJ2ZXlnaXptby5jb22C
D3N1cnZleWdpem1vLmNvbTAdBgNVHQ4EFgQUogJIgFe3mwaqFPDUiPyEd+JcDi0w
DQYJKoZIhvcNAQELBQADggEBAF+Lu4aZ7JJmxHUY7w+fAxH8iBdMH5zwyjDWEFmP
quUuoy7G+1V5cFecKbXk9iYUgazygDETa2UP8wBWTS6V2RYL/PsF9+HdwOX0e9QA
JxZQ0ry9MZU72H1ZeIDRYGHqKOLMqkLIsbpSyDD6GyRNzLvZx7MiwDJ8Ou5wAT/6
is5Sga9DZ/ffXbg79Ku0yalDwQfimHDAHRQhoYdCf6QhDog+RiNfSdJsU2NofiFu
f920or/2t9t9egCzXQHi3iPC979+eII0YAxnnHEQkndUvSoihVn625wqHetXpcz3
zkQ5F7NVrR/giUHmVE6rpy4uuQ16cwBgMqoTzDBQ/0rs6lo=
-----END CERTIFICATE-----

View File

@ -0,0 +1,92 @@
package bundle
import (
"fmt"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/bundler"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ubiquity"
)
var bundlerUsageText = // Usage text of 'cfssl bundle'
`cfssl bundle -- create a certificate bundle that contains the client cert
Usage of bundle:
- Bundle local certificate files
cfssl bundle [-ca-bundle file] [-int-bundle file] [-key keyfile] [-flavor int] [-metadata file] CERT
- Bundle certificate from remote server.
cfssl bundle -domain domain_name [-ip ip_address] [-ca-bundle file] [-int-bundle file] [-metadata file]
Arguments:
CERT: Client certificate, possible followed by intermediates to form a (partial) chain, use '-' to read from stdin.
Note:
CERT can be specified as flag value. But flag value will take precedence, overwriting the argument.
Flags:
`
// flags used by 'cfssl bundle'
var bundlerFlags = []string{"cert", "key", "ca-bundle", "int-bundle", "flavor", "metadata", "domain", "ip", "config"}
// bundlerMain is the main CLI of bundler functionality.
// TODO(zi): Decide whether to drop the argument list and only use flags to specify all the inputs.
// There are debates on whether flag or arg is more appropriate for required parameters.
func bundlerMain(args []string, c cli.Config) (err error) {
// Grab cert file through args only if flag values for cert and domain are absent
if c.CertFile == "" && c.Domain == "" {
c.CertFile, args, err = cli.PopFirstArgument(args)
if err != nil {
return
}
}
ubiquity.LoadPlatforms(c.Metadata)
flavor := bundler.BundleFlavor(c.Flavor)
// Initialize a bundler with CA bundle and intermediate bundle.
b, err := bundler.NewBundler(c.CABundleFile, c.IntBundleFile)
if err != nil {
return
}
var bundle *bundler.Bundle
if c.CertFile != "" {
if c.CertFile == "-" {
var certPEM, keyPEM []byte
certPEM, err = cli.ReadStdin(c.CertFile)
if err != nil {
return
}
if c.KeyFile != "" {
keyPEM, err = cli.ReadStdin(c.KeyFile)
if err != nil {
return
}
}
bundle, err = b.BundleFromPEM(certPEM, keyPEM, flavor)
if err != nil {
return
}
} else {
// Bundle the client cert
bundle, err = b.BundleFromFile(c.CertFile, c.KeyFile, flavor)
if err != nil {
return
}
}
} else if c.Domain != "" {
bundle, err = b.BundleFromRemote(c.Domain, c.IP, flavor)
if err != nil {
return
}
}
marshaled, err := bundle.MarshalJSON()
if err != nil {
return
}
fmt.Printf("%s", marshaled)
return
}
// Command assembles the definition of Command 'bundle'
var Command = &cli.Command{UsageText: bundlerUsageText, Flags: bundlerFlags, Main: bundlerMain}

View File

@ -0,0 +1 @@
package bundle

View File

@ -0,0 +1,181 @@
package cli
/*
cfssl is the command line tool to issue/sign/bundle client certificate. It's
also a tool to start a HTTP server to handle web requests for signing, bundling
and verification.
Usage:
cfssl command [-flags] arguments
The commands are defined in the cli subpackages and include
bundle create a certificate bundle
sign signs a certificate signing request (CSR)
serve starts a HTTP server handling sign and bundle requests
version prints the current cfssl version
genkey generates a key and an associated CSR
gencert generates a key and a signed certificate
selfsign generates a self-signed certificate
ocspsign signs an OCSP response
Use "cfssl [command] -help" to find out more about a command.
*/
import (
"encoding/json"
"encoding/pem"
"errors"
"flag"
"fmt"
"io/ioutil"
"os"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
)
// Command holds the implementation details of a cfssl command.
type Command struct {
// The Usage Text
UsageText string
// Flags to look up in the global table
Flags []string
// Main runs the command, args are the arguments after flags
Main func(args []string, c Config) error
}
// Parsed command name
var cmdName string
// usage is the cfssl usage heading. It will be appended with names of defined commands in cmds
// to form the final usage message of cfssl.
const usage = `Usage:
Available commands:
`
// printDefaultValue is a helper function to print out a user friendly
// usage message of a flag. It's useful since we want to write customized
// usage message on selected subsets of the global flag set. It is
// borrowed from standard library source code. Since flag value type is
// not exported, default string flag values are printed without
// quotes. The only exception is the empty string, which is printed as "".
func printDefaultValue(f *flag.Flag) {
format := " -%s=%s: %s\n"
if f.DefValue == "" {
format = " -%s=%q: %s\n"
}
fmt.Fprintf(os.Stderr, format, f.Name, f.DefValue, f.Usage)
}
// PopFirstArgument returns the first element and the rest of a string
// slice and return error if failed to do so. It is a helper function
// to parse non-flag arguments previously used in cfssl commands.
func PopFirstArgument(args []string) (string, []string, error) {
if len(args) < 1 {
return "", nil, errors.New("not enough arguments are supplied --- please refer to the usage")
}
return args[0], args[1:], nil
}
// Start is the entrance point of cfssl command line tools.
func Start(cmds map[string]*Command) {
// cfsslFlagSet is the flag sets for cfssl.
var cfsslFlagSet = flag.NewFlagSet("cfssl", flag.ExitOnError)
var c Config
registerFlags(&c, cfsslFlagSet)
// Initial parse of command line arguments. By convention, only -h/-help is supported.
flag.Parse()
if flag.Usage == nil {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, usage)
for name := range cmds {
fmt.Fprintf(os.Stderr, "%s\n", name)
}
}
}
if flag.NArg() < 1 {
fmt.Fprintf(os.Stderr, "No command is given.\n")
flag.Usage()
return
}
// Clip out the command name and args for the command
cmdName = flag.Arg(0)
args := flag.Args()[1:]
cmd, found := cmds[cmdName]
if !found {
fmt.Fprintf(os.Stderr, "Command %s is not defined.\n", cmdName)
flag.Usage()
return
}
// The usage of each individual command is re-written to mention
// flags defined and referenced only in that command.
cfsslFlagSet.Usage = func() {
fmt.Fprintf(os.Stderr, "%s", cmd.UsageText)
for _, name := range cmd.Flags {
if f := cfsslFlagSet.Lookup(name); f != nil {
printDefaultValue(f)
}
}
}
// Parse all flags and take the rest as argument lists for the command
cfsslFlagSet.Parse(args)
args = cfsslFlagSet.Args()
var err error
c.CFG, err = config.LoadFile(c.ConfigFile)
if c.ConfigFile != "" && err != nil {
fmt.Fprintf(os.Stderr, "Failed to load config file\n")
os.Exit(1)
}
if err := cmd.Main(args, c); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
// ReadStdin reads from stdin if the file is "-"
func ReadStdin(filename string) ([]byte, error) {
if filename == "-" {
return ioutil.ReadAll(os.Stdin)
}
return ioutil.ReadFile(filename)
}
// PrintCert outputs a cert, key and csr to stdout
func PrintCert(key, csrBytes, cert []byte) {
out := map[string]string{}
if cert != nil {
out["cert"] = string(cert)
}
if key != nil {
out["key"] = string(key)
}
if csrBytes != nil {
out["csr"] = string(csrBytes)
}
jsonOut, err := json.Marshal(out)
if err != nil {
return
}
fmt.Printf("%s\n", jsonOut)
}
// PrintOCSPResponse outputs an OCSP response to stdout
func PrintOCSPResponse(resp []byte) {
pemResponse := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE REQUEST", Bytes: resp})
out := map[string]string{}
out["ocspResponse"] = string(pemResponse)
jsonOut, err := json.Marshal(out)
if err != nil {
return
}
fmt.Printf("%s\n", jsonOut)
}

View File

@ -0,0 +1,83 @@
package cli
import (
"flag"
"os"
"testing"
)
var cfsslFlagSet = flag.NewFlagSet("cfssl", flag.ExitOnError)
// 'cfssl -help' should be supported.
func TestHelp(t *testing.T) {
called := false
ResetForTesting(func() { called = true })
os.Args = []string{"cfssl", "-help"}
Start(nil)
if !called {
t.Fatal("flag -help is not recognized correctly.")
}
}
// 'cfssl -badflag' should trigger parse error and usage invocation.
func TestUnknownFlag(t *testing.T) {
called := false
os.Args = []string{"cfssl", "-badflag"}
ResetForTesting(func() { called = true })
Start(nil)
if !called {
t.Fatal("Bad flag is not caught.")
}
}
// 'cfssl badcommand' should trigger parse error and usage invocation.
func TestBadCommand(t *testing.T) {
called := false
ResetForTesting(func() { called = true })
os.Args = []string{"cfssl", "badcommand"}
Start(nil)
if !called {
t.Fatal("Bad command is not caught.")
}
}
func TestCommandHelp(t *testing.T) {
called := false
ResetCFSSLFlagSetForTesting(func() { called = true })
args := []string{"-help"}
cfsslFlagSet.Parse(args)
if !called {
t.Fatal("sub-command -help is not recognized.")
}
}
func TestCommandBadFlag(t *testing.T) {
called := false
ResetCFSSLFlagSetForTesting(func() { called = true })
args := []string{"-help", "-badflag"}
cfsslFlagSet.Parse(args)
if !called {
t.Fatal("bad flag for sub-command is not caught.")
}
}
// Additional routines derived from flag unit testing
// ResetForTesting clears all flag state and sets the usage function as directed.
// After calling ResetForTesting, parse errors in flag handling will not
// exit the program.
func ResetForTesting(usage func()) {
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
flag.Usage = usage
}
// ResetCFSSLFlagSetForTesting reset cfsslFlagSet with flag.ContinueOnError so parse
// errors in flag will not exit the program
func ResetCFSSLFlagSetForTesting(usage func()) {
var c Config
cfsslFlagSet = flag.NewFlagSet("cfssl", flag.ContinueOnError)
registerFlags(&c, cfsslFlagSet)
cfsslFlagSet.Usage = usage
}

View File

@ -0,0 +1,98 @@
package cli
import (
"flag"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/pkcs11"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/universal"
)
// Config is a type to hold flag values used by cfssl commands.
type Config struct {
Hostname string
CertFile string
CSRFile string
CAFile string
CAKeyFile string
KeyFile string
IntermediatesFile string
CABundleFile string
IntBundleFile string
Address string
Port int
ConfigFile string
CFG *config.Config
Profile string
IsCA bool
IntDir string
Flavor string
Metadata string
Domain string
IP string
Remote string
Label string
Module string
Token string
PIN string
PKCS11Label string
ResponderFile string
Status string
Reason int
RevokedAt string
Interval int64
}
// registerFlags defines all cfssl command flags and associates their values with variables.
func registerFlags(c *Config, f *flag.FlagSet) {
f.StringVar(&c.Hostname, "hostname", "", "Hostname for the cert")
f.StringVar(&c.CertFile, "cert", "", "Client certificate that contains the public key")
f.StringVar(&c.CSRFile, "csr", "", "Certificate signature request file for new public key")
f.StringVar(&c.CAFile, "ca", "ca.pem", "CA used to sign the new certificate")
f.StringVar(&c.CAKeyFile, "ca-key", "ca-key.pem", "CA private key")
f.StringVar(&c.KeyFile, "key", "", "private key for the certificate")
f.StringVar(&c.IntermediatesFile, "intermediates", "", "intermediate certs")
f.StringVar(&c.CABundleFile, "ca-bundle", "/etc/cfssl/ca-bundle.crt", "Bundle to be used for root certificates pool")
f.StringVar(&c.IntBundleFile, "int-bundle", "/etc/cfssl/int-bundle.crt", "Bundle to be used for intermediate certificates pool")
f.StringVar(&c.Address, "address", "127.0.0.1", "Address to bind")
f.IntVar(&c.Port, "port", 8888, "Port to bind")
f.StringVar(&c.ConfigFile, "config", "", "path to configuration file")
f.StringVar(&c.Profile, "profile", "", "signing profile to use")
f.BoolVar(&c.IsCA, "initca", false, "initialise new CA")
f.StringVar(&c.IntDir, "int-dir", "/etc/cfssl/intermediates", "specify intermediates directory")
f.StringVar(&c.Flavor, "flavor", "ubiquitous", "Bundle Flavor: ubiquitous, optimal and force.")
f.StringVar(&c.Metadata, "metadata", "/etc/cfssl/ca-bundle.crt.metadata", "Metadata file for root certificate presence. The content of the file is a json dictionary (k,v): each key k is SHA-1 digest of a root certificate while value v is a list of key store filenames.")
f.StringVar(&c.Domain, "domain", "", "remote server domain name")
f.StringVar(&c.IP, "ip", "", "remote server ip")
f.StringVar(&c.Remote, "remote", "", "remote CFSSL server")
f.StringVar(&c.Label, "label", "", "key label to use in remote CFSSL server")
f.StringVar(&c.ResponderFile, "responder", "", "Certificate for OCSP responder")
f.StringVar(&c.Status, "status", "good", "Status of the certificate: good, revoked, unknown")
f.IntVar(&c.Reason, "reason", 0, "Reason code for revocation")
f.StringVar(&c.RevokedAt, "revoked-at", "now", "Date of revocation (YYYY-MM-DD)")
f.Int64Var(&c.Interval, "interval", int64(4*helpers.OneDay), "Interval between OCSP updates, in seconds (default: 4 days)")
if pkcs11.Enabled {
f.StringVar(&c.Module, "pkcs11-module", "", "PKCS #11 module")
f.StringVar(&c.Token, "pkcs11-token", "", "PKCS #11 token")
f.StringVar(&c.PIN, "pkcs11-pin", "", "PKCS #11 user PIN")
f.StringVar(&c.PKCS11Label, "pkcs11-label", "", "PKCS #11 label")
}
}
// RootFromConfig returns a universal signer Root structure that can
// be used to produce a signer.
func RootFromConfig(c *Config) universal.Root {
return universal.Root{
Config: map[string]string{
"pkcs11-module": c.Module,
"pkcs11-token": c.Token,
"pkcs11-label": c.PKCS11Label,
"pkcs11-user-pin": c.PIN,
"cert-file": c.CAFile,
"key-file": c.CAKeyFile,
},
ForceRemote: c.Remote != "",
}
}

View File

@ -0,0 +1,123 @@
package gencert
import (
"encoding/json"
"errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli/genkey"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli/sign"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/csr"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/initca"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
)
var gencertUsageText = `cfssl gencert -- generate a new key and signed certificate
Usage of gencert:
cfssl gencert -initca CSRJSON
cfssl gencert -ca cert -ca-key key [-config config] [-profile -profile] HOSTNAME CSRJSON
cfssl gencert -remote remote_host [-config config] [-profile profile] [-label label] HOSTNAME CSRJSON
Arguments:
HOSTNAME: Hostname for the cert
CSRJSON: JSON file containing the request, use '-' for reading JSON from stdin
HOSTNAME should not be included when initalising a new CA.
Flags:
`
var gencertFlags = []string{"initca", "remote", "ca", "ca-key", "config", "profile", "label"}
func gencertMain(args []string, c cli.Config) (err error) {
if c.Hostname == "" && !c.IsCA {
c.Hostname, args, err = cli.PopFirstArgument(args)
if err != nil {
return
}
}
csrJSONFile, args, err := cli.PopFirstArgument(args)
if err != nil {
return
}
csrJSONFileBytes, err := cli.ReadStdin(csrJSONFile)
if err != nil {
return
}
var req csr.CertificateRequest
err = json.Unmarshal(csrJSONFileBytes, &req)
if err != nil {
return
}
if c.IsCA {
var key, cert []byte
cert, err = initca.NewFromPEM(&req, c.CAKeyFile)
if err != nil {
log.Errorf("%v\n", err)
log.Infof("generating a new CA key and certificate from CSR")
cert, key, err = initca.New(&req)
if err != nil {
return
}
}
cli.PrintCert(key, nil, cert)
} else {
if req.CA != nil {
err = errors.New("ca section only permitted in initca")
return
}
// Remote can be forced on the command line or in the config
if c.Remote == "" && c.CFG == nil {
if c.CAFile == "" {
log.Error("need a CA certificate (provide one with -ca)")
return
}
if c.CAKeyFile == "" {
log.Error("need a CA key (provide one with -ca-key)")
return
}
}
var key, csrBytes []byte
g := &csr.Generator{Validator: genkey.Validator}
csrBytes, key, err = g.ProcessRequest(&req)
if err != nil {
key = nil
return
}
s, err := sign.SignerFromConfig(c)
if err != nil {
return err
}
var cert []byte
req := signer.SignRequest{
Request: string(csrBytes),
Hosts: signer.SplitHosts(c.Hostname),
Profile: c.Profile,
Label: c.Label,
}
cert, err = s.Sign(req)
if err != nil {
return err
}
cli.PrintCert(key, csrBytes, cert)
}
return nil
}
// CLIGenCert is a subcommand that generates a new certificate from a
// JSON CSR request file.
var Command = &cli.Command{UsageText: gencertUsageText, Flags: gencertFlags, Main: gencertMain}

View File

@ -0,0 +1 @@
package gencert

View File

@ -0,0 +1,80 @@
package genkey
import (
"encoding/json"
"errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/csr"
cferr "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/initca"
)
var genkeyUsageText = `cfssl genkey -- generate a new key and CSR
Usage of genkey:
cfssl genkey CSRJSON
Arguments:
CSRJSON: JSON file containing the request, use '-' for reading JSON from stdin
Flags:
`
var genkeyFlags = []string{"initca", "config"}
func genkeyMain(args []string, c cli.Config) (err error) {
csrFile, args, err := cli.PopFirstArgument(args)
if err != nil {
return
}
csrFileBytes, err := cli.ReadStdin(csrFile)
if err != nil {
return
}
var req csr.CertificateRequest
err = json.Unmarshal(csrFileBytes, &req)
if err != nil {
return
}
if c.IsCA {
var key, cert []byte
cert, key, err = initca.New(&req)
if err != nil {
return
}
cli.PrintCert(key, nil, cert)
} else {
if req.CA != nil {
err = errors.New("ca section only permitted in initca")
return
}
var key, csrPEM []byte
g := &csr.Generator{Validator: Validator}
csrPEM, key, err = g.ProcessRequest(&req)
if err != nil {
key = nil
return
}
cli.PrintCert(key, csrPEM, nil)
}
return nil
}
// Validator returns true if the csr has at least one host
func Validator(req *csr.CertificateRequest) error {
if len(req.Hosts) == 0 {
return cferr.Wrap(cferr.PolicyError, cferr.InvalidRequest, errors.New("missing hosts field"))
}
return nil
}
// CLIGenKey is a subcommand for generating a new key and CSR from a
// JSON CSR request file.
var Command = &cli.Command{UsageText: genkeyUsageText, Flags: genkeyFlags, Main: genkeyMain}

View File

@ -0,0 +1 @@
package genkey

View File

@ -0,0 +1,74 @@
package ocspsign
import (
"io/ioutil"
"time"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ocsp"
)
var ocspSignerUsageText = // Usage text of 'cfssl ocspsign'
`cfssl ocspsign -- signs an OCSP response for a given CA, cert, and status"
Usage of ocspsign:
cfssl ocspsign -ca cert -responder cert -key key -cert cert [-reason code]
Flags:
`
// Flags of 'cfssl ocspsign'
var ocspSignerFlags = []string{"ca", "responder", "key", "reason", "status", "revoked-at", "interval"}
// ocspSignerMain is the main CLI of OCSP signer functionality.
func ocspSignerMain(args []string, c cli.Config) (err error) {
// Read the cert to be revoked from file
certBytes, err := ioutil.ReadFile(c.CertFile)
if err != nil {
log.Critical("Unable to read certificate: ", err)
return
}
cert, err := helpers.ParseCertificatePEM(certBytes)
if err != nil {
log.Critical("Unable to parse certificate: ", err)
return
}
req := ocsp.SignRequest{
Certificate: cert,
Status: c.Status,
}
if c.Status == "revoked" {
req.Reason = c.Reason
req.RevokedAt = time.Now()
if c.RevokedAt != "now" {
req.RevokedAt, err = time.Parse("2006-01-02", c.RevokedAt)
if err != nil {
log.Critical("Malformed revocation time: ", c.RevokedAt)
return
}
}
}
s, err := ocsp.NewSignerFromFile(c.CAFile, c.ResponderFile, c.KeyFile, time.Duration(c.Interval))
if err != nil {
log.Critical("Unable to create OCSP signer: ", err)
return
}
resp, err := s.Sign(req)
if err != nil {
log.Critical("Unable to sign OCSP response: ", err)
return
}
cli.PrintOCSPResponse(resp)
return
}
// CLISigner assembles the definition of Command 'sign'
var Command = &cli.Command{UsageText: ocspSignerUsageText, Flags: ocspSignerFlags, Main: ocspSignerMain}

View File

@ -0,0 +1,114 @@
package selfsign
import (
"encoding/json"
"fmt"
"os"
"time"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli/genkey"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/csr"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/selfsign"
)
var selfSignUsageText = `cfssl selfsign -- generate a new self-signed key and signed certificate
Usage of gencert:
cfssl selfsign HOSTNAME CSRJSON
WARNING: this should ONLY be used for testing. This should never be
used in production.
WARNING: self-signed certificates are insecure; they do not provide
the authentication required for secure systems. Use these at your own
risk.
Arguments:
HOSTNAME: Hostname for the cert
CSRJSON: JSON file containing the request, use '-' for reading JSON from stdin
Flags:
`
var selfSignFlags = []string{"config"}
func selfSignMain(args []string, c cli.Config) (err error) {
if c.Hostname == "" && !c.IsCA {
c.Hostname, args, err = cli.PopFirstArgument(args)
if err != nil {
return
}
}
csrFile, args, err := cli.PopFirstArgument(args)
if err != nil {
return
}
csrFileBytes, err := cli.ReadStdin(csrFile)
if err != nil {
return
}
var req csr.CertificateRequest
err = json.Unmarshal(csrFileBytes, &req)
if err != nil {
return
}
var key, csrPEM []byte
g := &csr.Generator{Validator: genkey.Validator}
csrPEM, key, err = g.ProcessRequest(&req)
if err != nil {
key = nil
return
}
priv, err := helpers.ParsePrivateKeyPEM(key)
if err != nil {
key = nil
return
}
var profile *config.SigningProfile
// If there is a config, use its signing policy. Otherwise, leave policy == nil
// and NewSigner will use DefaultConfig().
if c.CFG != nil {
if c.Profile != "" && c.CFG.Signing.Profiles != nil {
profile = c.CFG.Signing.Profiles[c.Profile]
}
}
if profile == nil {
profile = config.DefaultConfig()
profile.Expiry = 2190 * time.Hour
}
cert, err := selfsign.Sign(priv, csrPEM, profile)
if err != nil {
key = nil
priv = nil
return
}
fmt.Fprintf(os.Stderr, `*** WARNING ***
Self-signed certificates are dangerous. Use this self-signed
certificate at your own risk.
It is strongly recommended that these certificates NOT be used
in production.
*** WARNING ***
`)
cli.PrintCert(key, csrPEM, cert)
return
}
// CLISelfSign command creates self-signed certificates
var Command = &cli.Command{UsageText: selfSignUsageText, Flags: selfSignFlags, Main: selfSignMain}

View File

@ -0,0 +1 @@
package selfsign

View File

@ -0,0 +1,120 @@
package serve
import (
"errors"
"fmt"
"net/http"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api/bundle"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api/generator"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api/info"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api/initca"
apisign "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/api/sign"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/bundler"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli/sign"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/ubiquity"
)
var serverUsageText = // Usage text of 'cfssl serve'
`cfssl serve -- set up a HTTP server handles CF SSL requests
Usage of serve:
cfssl serve [-address address] [-ca cert] [-ca-bundle bundle] \
[-ca-key key] [-int-bundle bundle] [-port port] [-metadata file] \
[-remote remote_host] [-config config]
Flags:
`
// Flags used by 'cfssl serve'
var serverFlags = []string{"address", "port", "ca", "ca-key", "ca-bundle", "int-bundle", "int-dir", "metadata", "remote", "config"}
// registerHandlers instantiates various handlers and associate them to corresponding endpoints.
func registerHandlers(c cli.Config) error {
log.Info("Setting up signer endpoint")
s, err := sign.SignerFromConfig(c)
if err != nil {
log.Warningf("sign and authsign endpoints are disabled: %v", err)
} else {
if signHandler, err := apisign.NewHandlerFromSigner(s); err == nil {
log.Info("Assigning handler to /sign")
http.Handle("/api/v1/cfssl/sign", signHandler)
} else {
log.Warningf("endpoint '/api/v1/cfssl/sign' is disabled: %v", err)
}
if signHandler, err := apisign.NewAuthHandlerFromSigner(s); err == nil {
log.Info("Assigning handler to /authsign")
http.Handle("/api/v1/cfssl/authsign", signHandler)
} else {
log.Warningf("endpoint '/api/v1/cfssl/authsign' is disabled: %v", err)
}
}
log.Info("Setting up info endpoint")
infoHandler, err := info.NewHandler(s)
if err != nil {
log.Warningf("endpoint '/api/v1/cfssl/info' is disabled: %v", err)
} else {
http.Handle("/api/v1/cfssl/info", infoHandler)
}
log.Info("Setting up new cert endpoint")
if err != nil {
log.Errorf("endpoint '/api/v1/cfssl/newcert' is disabled")
} else {
newCertGenerator := generator.NewCertGeneratorHandlerFromSigner(generator.CSRValidate, s)
http.Handle("/api/v1/cfssl/newcert", newCertGenerator)
}
log.Info("Setting up bundler endpoint")
bundleHandler, err := bundle.NewHandler(c.CABundleFile, c.IntBundleFile)
if err != nil {
log.Warningf("endpoint '/api/v1/cfssl/bundle' is disabled: %v", err)
} else {
http.Handle("/api/v1/cfssl/bundle", bundleHandler)
}
log.Info("Setting up CSR endpoint")
generatorHandler, err := generator.NewHandler(generator.CSRValidate)
if err != nil {
log.Errorf("Failed to set up CSR endpoint: %v", err)
return err
}
http.Handle("/api/v1/cfssl/newkey", generatorHandler)
log.Info("Setting up initial CA endpoint")
http.Handle("/api/v1/cfssl/init_ca", initca.NewHandler())
log.Info("Handler set up complete.")
return nil
}
// serverMain is the command line entry point to the API server. It sets up a
// new HTTP server to handle sign, bundle, and validate requests.
func serverMain(args []string, c cli.Config) error {
// serve doesn't support arguments.
if len(args) > 0 {
return errors.New("argument is provided but not defined; please refer to the usage by flag -h")
}
bundler.IntermediateStash = c.IntDir
err := ubiquity.LoadPlatforms(c.Metadata)
if err != nil {
log.Error(err)
}
err = registerHandlers(c)
if err != nil {
return err
}
addr := fmt.Sprintf("%s:%d", c.Address, c.Port)
log.Info("Now listening on ", addr)
return http.ListenAndServe(addr, nil)
}
// CLIServer assembles the definition of Command 'serve'
var Command = &cli.Command{UsageText: serverUsageText, Flags: serverFlags, Main: serverMain}

View File

@ -0,0 +1,48 @@
package serve
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli"
)
func TestServe(t *testing.T) {
registerHandlers(cli.Config{})
ts := httptest.NewServer(http.DefaultServeMux)
// Soft-enable endpoints should be all disabled due to empty config files.
urlSign := ts.URL + "/api/v1/cfssl/sign"
urlGencert := ts.URL + "/api/v1/cfssl/gencert"
urlBundle := ts.URL + "/api/v1/cfssl/bundle"
urlInitCA := ts.URL + "/api/v1/cfssl/init_ca"
urlCSR := ts.URL + "/api/v1/cfssl/newkey"
// Disabled endpoint should return "404: Not Found"
resp, _ := http.Get(urlSign)
if resp.StatusCode != http.StatusNotFound {
t.Fatal(resp.Status)
}
resp, _ = http.Get(urlGencert)
if resp.StatusCode != http.StatusNotFound {
t.Fatal(resp.Status)
}
resp, _ = http.Get(urlBundle)
if resp.StatusCode != http.StatusNotFound {
t.Fatal(resp.Status)
}
// Enabled endpoint should return "405 Method Not Allowed"
resp, _ = http.Get(urlInitCA)
if resp.StatusCode != http.StatusMethodNotAllowed {
t.Fatal(resp.Status)
}
resp, _ = http.Get(urlCSR)
if resp.StatusCode != http.StatusMethodNotAllowed {
t.Fatal(resp.Status)
}
}

View File

@ -0,0 +1,143 @@
package sign
import (
"encoding/json"
"io/ioutil"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/universal"
)
var signerUsageText = // Usage text of 'cfssl sign'
`cfssl sign -- signs a client cert with a host name by a given CA and CA key
Usage of sign:
cfssl sign -ca cert -ca-key key [-config config] [-profile profile] HOSTNAME CSR [SUBJECT]
cfssl sign -remote remote_host [-config config] [-profile profile] [-label label] HOSTNAME CSR [SUBJECT]
Arguments:
HOSTNAME: Hostname for the cert
CSR: PEM file for certificate request, use '-' for reading PEM from stdin.
Note: HOSTNAME and CSR can also be supplied via flag values; flag values will take precedence over the argument.
SUBJECT is an optional file containing subject information to use for the certificate instead of the subject information in the CSR.
Flags:
`
// Flags of 'cfssl sign'
var signerFlags = []string{"hostname", "csr", "ca", "ca-key", "config", "profile", "label", "remote"}
// SignerFromConfig takes the Config and creates the appropriate
// signer.Signer object
func SignerFromConfig(c cli.Config) (signer.Signer, error) {
// If there is a config, use its signing policy. Otherwise create a default policy.
var policy *config.Signing
if c.CFG != nil {
policy = c.CFG.Signing
} else {
policy = &config.Signing{
Profiles: map[string]*config.SigningProfile{},
Default: config.DefaultConfig(),
}
}
// Make sure the policy reflects the new remote
if c.Remote != "" {
err := policy.OverrideRemotes(c.Remote)
if err != nil {
log.Infof("Invalid remote %v, reverting to configuration default", c.Remote)
return nil, err
}
}
s, err := universal.NewSigner(cli.RootFromConfig(&c), policy)
if err != nil {
return nil, err
}
return s, nil
}
// signerMain is the main CLI of signer functionality.
// [TODO: zi] Decide whether to drop the argument list and only use flags to specify all the inputs.
func signerMain(args []string, c cli.Config) (err error) {
// Grab values through args only if corresponding flags are absent
if c.Hostname == "" {
c.Hostname, args, err = cli.PopFirstArgument(args)
if err != nil {
return
}
}
if c.CSRFile == "" {
c.CSRFile, args, err = cli.PopFirstArgument(args)
if err != nil {
return
}
}
var subjectData *signer.Subject
if len(args) > 0 {
var subjectFile string
subjectFile, args, err = cli.PopFirstArgument(args)
if err != nil {
return
}
var subjectJSON []byte
subjectJSON, err = ioutil.ReadFile(subjectFile)
if err != nil {
return
}
subjectData = new(signer.Subject)
err = json.Unmarshal(subjectJSON, subjectData)
if err != nil {
return
}
}
csr, err := cli.ReadStdin(c.CSRFile)
if err != nil {
return
}
// Remote can be forced on the command line or in the config
if c.Remote == "" && c.CFG == nil {
if c.CAFile == "" {
log.Error("need CA certificate (provide one with -ca)")
return
}
if c.CAKeyFile == "" {
log.Error("need CA key (provide one with -ca-key)")
return
}
}
s, err := SignerFromConfig(c)
if err != nil {
return
}
req := signer.SignRequest{
Hosts: signer.SplitHosts(c.Hostname),
Request: string(csr),
Subject: subjectData,
Profile: c.Profile,
Label: c.Label,
}
cert, err := s.Sign(req)
if err != nil {
return
}
cli.PrintCert(nil, csr, cert)
return
}
// CLISigner assembles the definition of Command 'sign'
var Command = &cli.Command{UsageText: signerUsageText, Flags: signerFlags, Main: signerMain}

View File

@ -0,0 +1 @@
package sign

View File

@ -0,0 +1,36 @@
package version
import (
"fmt"
"runtime"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli"
)
// Version stores the semantic versioning information for CFSSL.
var version = struct {
Major int
Minor int
Patch int
Revision string
}{1, 1, 0, "release"}
func versionString() string {
return fmt.Sprintf("%d.%d.%d", version.Major, version.Minor, version.Patch)
}
// Usage text for 'cfssl version'
var versionUsageText = `cfssl version -- print out the version of CF SSL
Usage of version:
cfssl version
`
// The main functionality of 'cfssl version' is to print out the version info.
func versionMain(args []string, c cli.Config) (err error) {
fmt.Printf("Version: %s\nRevision: %s\nRuntime: %s\n", versionString(), version.Revision, runtime.Version())
return nil
}
// CLIVersioner defines Command 'version'
var Command = &cli.Command{UsageText: versionUsageText, Flags: nil, Main: versionMain}

View File

@ -0,0 +1,7 @@
// +build !release
package version
func init() {
version.Revision = "dev"
}

View File

@ -0,0 +1 @@
package version

View File

@ -0,0 +1,56 @@
/*
cfssl is the command line tool to issue/sign/bundle client certificate. It's
also a tool to start a HTTP server to handle web requests for signing, bundling
and verification.
Usage:
cfssl command [-flags] arguments
The commands are
bundle create a certificate bundle
sign signs a certificate signing request (CSR)
serve starts a HTTP server handling sign and bundle requests
version prints the current cfssl version
genkey generates a key and an associated CSR
gencert generates a key and a signed certificate
selfsign generates a self-signed certificate
Use "cfssl [command] -help" to find out more about a command.
*/
package main
import (
"flag"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli/bundle"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli/gencert"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli/genkey"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli/ocspsign"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli/selfsign"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli/serve"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli/sign"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/cli/version"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
)
// main defines the cfssl usage and registers all defined commands and flags.
func main() {
flag.Usage = nil
// Add command names to cfssl usage
flag.IntVar(&log.Level, "loglevel", log.LevelInfo, "Log level")
// Register commands.
cmds := map[string]*cli.Command{
"bundle": bundle.Command,
"sign": sign.Command,
"serve": serve.Command,
"version": version.Command,
"genkey": genkey.Command,
"gencert": gencert.Command,
"ocspsign": ocspsign.Command,
"selfsign": selfsign.Command,
}
// Register all command flags.
cli.Start(cmds)
}

View File

@ -0,0 +1 @@
package main

View File

@ -0,0 +1,179 @@
// Package initca contains code to initialise a certificate authority,
// generating a new root key and certificate.
package initca
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"io/ioutil"
"time"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/csr"
cferr "github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/errors"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/log"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local"
)
// validator contains the default validation logic for certificate
// requests to the API server. This follows the Baseline Requirements
// for the Issuance and Management of Publicly-Trusted Certificates,
// v.1.1.6, from the CA/Browser Forum
// (https://cabforum.org). Specifically, section 10.2.3 ("Information
// Requirements"), states:
//
// "Applicant information MUST include, but not be limited to, at least one
// Fully-Qualified Domain Name or IP address to be included in the Certificates
// SubjectAltName extension."
func validator(req *csr.CertificateRequest) error {
if len(req.Hosts) == 0 {
return cferr.Wrap(cferr.PolicyError, cferr.InvalidRequest, errors.New("missing hosts field"))
}
return nil
}
// New creates a new root certificate from the certificate request.
func New(req *csr.CertificateRequest) (cert, key []byte, err error) {
if req.CA != nil {
if req.CA.Expiry != "" {
CAPolicy.Default.ExpiryString = req.CA.Expiry
CAPolicy.Default.Expiry, err = time.ParseDuration(req.CA.Expiry)
}
if req.CA.PathLength != 0 {
signer.MaxPathLen = req.CA.PathLength
}
}
g := &csr.Generator{Validator: validator}
csr, 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), nil)
if err != nil {
log.Errorf("failed to create signer: %v", err)
return
}
s.SetPolicy(CAPolicy)
signReq := signer.SignRequest{Hosts: req.Hosts, Request: string(csr)}
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 []byte, err error) {
if req.CA != nil {
if req.CA.Expiry != "" {
CAPolicy.Default.ExpiryString = req.CA.Expiry
CAPolicy.Default.Expiry, err = time.ParseDuration(req.CA.Expiry)
}
if req.CA.PathLength != 0 {
signer.MaxPathLen = req.CA.PathLength
}
}
privData, err := ioutil.ReadFile(keyFile)
if err != nil {
return nil, err
}
priv, err := helpers.ParsePrivateKeyPEM(privData)
if err != nil {
return nil, err
}
var sigAlgo x509.SignatureAlgorithm
switch priv := priv.(type) {
case *rsa.PrivateKey:
bitLength := priv.PublicKey.N.BitLen()
switch {
case bitLength >= 4096:
sigAlgo = x509.SHA512WithRSA
case bitLength >= 3072:
sigAlgo = x509.SHA384WithRSA
case bitLength >= 2048:
sigAlgo = x509.SHA256WithRSA
default:
sigAlgo = x509.SHA1WithRSA
}
case *ecdsa.PrivateKey:
switch priv.Curve {
case elliptic.P521():
sigAlgo = x509.ECDSAWithSHA512
case elliptic.P384():
sigAlgo = x509.ECDSAWithSHA384
case elliptic.P256():
sigAlgo = x509.ECDSAWithSHA256
default:
sigAlgo = x509.ECDSAWithSHA1
}
default:
sigAlgo = x509.UnknownSignatureAlgorithm
}
var tpl = x509.CertificateRequest{
Subject: req.Name(),
SignatureAlgorithm: sigAlgo,
DNSNames: req.Hosts,
}
certReq, err := x509.CreateCertificateRequest(rand.Reader, &tpl, priv)
if err != nil {
log.Errorf("failed to generate a CSR: %v", err)
// The use of CertificateError was a matter of some
// debate; it is the one edge case in which a new
// error category specifically for CSRs might be
// useful, but it was deemed that one edge case did
// not a new category justify.
err = cferr.Wrap(cferr.CertificateError, cferr.BadRequest, err)
return
}
p := &pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: certReq,
}
certReq = pem.EncodeToMemory(p)
s, err := local.NewSigner(priv, nil, signer.DefaultSigAlgo(priv), nil)
if err != nil {
log.Errorf("failed to create signer: %v", err)
return
}
s.SetPolicy(CAPolicy)
signReq := signer.SignRequest{Request: string(certReq)}
cert, err = s.Sign(signReq)
return
}
// CAPolicy contains the CA issuing policy as default policy.
var CAPolicy = &config.Signing{
Default: &config.SigningProfile{
Usage: []string{"cert sign", "crl sign"},
ExpiryString: "43800h",
Expiry: 5 * helpers.OneYear,
CA: true,
},
}

View File

@ -0,0 +1,231 @@
package initca
import (
"crypto/ecdsa"
"crypto/rsa"
"io/ioutil"
"strings"
"testing"
"time"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/config"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/csr"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/helpers"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer"
"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cloudflare/cfssl/signer/local"
)
type KeyRequest struct {
keyAlgo string
keyLen int
}
var validKeyParams = []KeyRequest{
{
keyAlgo: "rsa",
keyLen: 2048,
},
{
keyAlgo: "rsa",
keyLen: 3072,
},
{
keyAlgo: "rsa",
keyLen: 4096,
},
{
keyAlgo: "ecdsa",
keyLen: 256,
},
{
keyAlgo: "ecdsa",
keyLen: 384,
},
{
keyAlgo: "ecdsa",
keyLen: 521,
},
}
var csrFiles = []string{
"testdata/rsa2048.csr",
"testdata/rsa3072.csr",
"testdata/rsa4096.csr",
"testdata/ecdsa256.csr",
"testdata/ecdsa384.csr",
"testdata/ecdsa521.csr",
}
var invalidCryptoParams = []KeyRequest{
// Weak Key
{
keyAlgo: "rsa",
keyLen: 1024,
},
// Bad param
{
keyAlgo: "rsaCrypto",
keyLen: 2048,
},
{
keyAlgo: "ecdsa",
keyLen: 2000,
},
}
func TestInitCA(t *testing.T) {
var req *csr.CertificateRequest
hostname := "cloudflare.com"
for _, param := range validKeyParams {
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: &csr.KeyRequest{
Algo: param.keyAlgo,
Size: param.keyLen,
},
}
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.keyLen {
t.Fatal("Cert key length mismatch.")
}
if key.(*rsa.PrivateKey).N.BitLen() != param.keyLen {
t.Fatal("Private key length mismatch.")
}
case "ecdsa":
if cert.PublicKey.(*ecdsa.PublicKey).Curve.Params().BitSize != param.keyLen {
t.Fatal("Cert key length mismatch.")
}
if key.(*ecdsa.PrivateKey).Curve.Params().BitSize != param.keyLen {
t.Fatal("Private key length mismatch.")
}
}
// Start a signer
var CAPolicy = &config.Signing{
Default: &config.SigningProfile{
Usage: []string{"cert sign", "crl sign"},
ExpiryString: "300s",
Expiry: 300 * time.Second,
CA: true,
},
}
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 TestNoHostname(t *testing.T) {
req := &csr.CertificateRequest{
Names: []csr.Name{
{
C: "US",
ST: "California",
L: "San Francisco",
O: "CloudFlare",
OU: "Systems Engineering",
},
},
CN: "cloudflare.com",
// Empty hosts
Hosts: []string{},
KeyRequest: &csr.KeyRequest{
Algo: "rsa",
Size: 2048,
},
}
_, _, err := New(req)
if err == nil {
t.Fatal("InitCA should failed.")
}
if !strings.Contains(err.Error(), `"code":5300`) {
t.Fatal(err)
}
}
func TestInvalidCryptoParams(t *testing.T) {
var req *csr.CertificateRequest
hostname := "cloudflare.com"
for _, test := 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: &csr.KeyRequest{
Algo: test.keyAlgo,
Size: test.keyLen,
},
}
_, _, 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)
}
}
}

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