wfe2: Check signatures before serving cert chains (#5273)

Add a check to `wfe2.Certificate` to ensure that the chain we select to
serve with the end-entity cert actually validates the end-entity's
signature. Add new test certificates, generated to match our actual
hierarchy. Update wfe2 tests to use the new test certificates, as well
as new mocks, in order to properly test the new check.

The new test certs and overhauled tests are necessary because the prior
wfe2 tests built and served chains that were not valid, and in
fact could not be valid because they were built with self-signed certs.

Fixes #5225
This commit is contained in:
Aaron Gable 2021-02-09 09:09:49 -08:00 committed by GitHub
parent 2efabf57b6
commit 1e11833478
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 875 additions and 180 deletions

View File

@ -7,14 +7,11 @@ import (
"errors"
"fmt"
"io/ioutil"
"math/big"
"net"
"strings"
"testing"
"time"
"github.com/jmhodges/clock"
"github.com/letsencrypt/boulder/test"
jose "gopkg.in/square/go-jose.v2"
"github.com/letsencrypt/boulder/core"
@ -302,16 +299,6 @@ func (sa *StorageAuthority) GetCertificate(_ context.Context, serial string) (co
DER: certBlock.Bytes,
Issued: sa.clk.Now().Add(-1 * time.Hour),
}, nil
} else if serial == "0000000000000000000000000000000000b3" {
certPEM, _ := ioutil.ReadFile("test/178.crt")
block, _ := pem.Decode(certPEM)
issuer, _ := x509.ParseCertificate(block.Bytes)
_, cert := test.ThrowAwayCertWithSerial(&testing.T{}, 1, big.NewInt(0xb3), issuer)
return core.Certificate{
RegistrationID: 1,
DER: cert.Raw,
Issued: sa.clk.Now(),
}, nil
} else if serial == "000000000000000000000000000000626164" {
return core.Certificate{}, errors.New("bad")
} else {

28
test/hierarchy/README.md Normal file
View File

@ -0,0 +1,28 @@
# Boulder Test Hierarchy
This directory contains certificates which are analogues of Let's Encrypt's
active hierarchy. These are useful for ensuring that our tests cover all of
our actual situations, such as cross-signed intermediates, cross-signed roots,
both RSA and ECDSA roots and intermediates, and having issuance chains with
more than one intermediate in them. Also included are a selection of fake
end-entity certificates, issued from each of the intermediates. This directory
does not include private keys for the roots, as Boulder should never perform
any operations which require access to root private keys.
## Usage
These certificates (particularly their subject info and public key info) are
subject to change at any time. Values derived from these certificates, such as
their `Serial`, `IssuerID`, `Fingerprint`, or `IssuerNameID` should never be
hard-coded in tests or mocks. If you need to assert facts about those values
in a test, load the cert from disk and compute those values dynamically.
In general, loading and using one of these certificates for a test might
look like:
```go
ee, _ := CA.IssuePrecertificate(...)
cert, _ := core.LoadCert("test/hierarchy/int-e1.cert.pem")
id := issuance.Certificate{Certificate: cert}.NameID()
test.AssertEqual(t, issuance.GetIssuerNameID(ee), id)
```

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC0TCCAlagAwIBAgIIA65R21EVWjwwCgYIKoZIzj0EAwMwSTELMAkGA1UEBhMC
WFgxFTATBgNVBAoTDEJvdWxkZXIgVGVzdDEjMCEGA1UEAxMaKFRFU1QpIEVsZWdh
bnQgRWxlcGhhbnQgRTEwHhcNMjEwMjA0MDAxMTMyWhcNMjMwMzA2MDAxMTMyWjAh
MR8wHQYDVQQDExZlZS5pbnQtZTEuYm91bGRlci50ZXN0MIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAuwGj8QiyNhEgRRYVxFRi+5HeGQk7+7KUP4Ky3SX4
gyErddykJFpR+wfcOZy5f5QHb/lWopoPhBRmKLCJBWgNKR4WKeGODufALlej2eti
iGAh8rNNjM75xRWCKIQdFITP+062wP2mXYlj58XETbZditm//0rdW5i3Og7gRrSR
25brJkK6LK2OQaxuMI/0Uof1nlIg2LuNLazZBgZxl6ZJXtSMQNGarejAja1GBqG9
9/ZCzRatr75oKph8jyocjrJFod/36rEyBBSIPCsJEKPVDuhS4vYe8P4iyP43+Jtt
3q6rCDQ5TvW6zzjP59eZjgOPnCqobNnqOjXYKmox1uOVowIDAQABo4GEMIGBMA4G
A1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYD
VR0TAQH/BAIwADAfBgNVHSMEGDAWgBQB2rt6yyUgjl551vmWQi8CQSkHvjAhBgNV
HREEGjAYghZlZS5pbnQtZTEuYm91bGRlci50ZXN0MAoGCCqGSM49BAMDA2kAMGYC
MQCwKc9EQTAmi0EerjMg/hxUeVdrWc8m+1bKNGT3lwoG7mPyj11O/+XLsFw0J8ms
J7kCMQDILNmDBkI3/O09h9cy64CXlWFU5VAfNGGCZkq3pzL/wQvfAn4D1irS2lS7
fJp8N4M=
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAuwGj8QiyNhEgRRYVxFRi+5HeGQk7+7KUP4Ky3SX4gyErddyk
JFpR+wfcOZy5f5QHb/lWopoPhBRmKLCJBWgNKR4WKeGODufALlej2etiiGAh8rNN
jM75xRWCKIQdFITP+062wP2mXYlj58XETbZditm//0rdW5i3Og7gRrSR25brJkK6
LK2OQaxuMI/0Uof1nlIg2LuNLazZBgZxl6ZJXtSMQNGarejAja1GBqG99/ZCzRat
r75oKph8jyocjrJFod/36rEyBBSIPCsJEKPVDuhS4vYe8P4iyP43+Jtt3q6rCDQ5
TvW6zzjP59eZjgOPnCqobNnqOjXYKmox1uOVowIDAQABAoIBAEZSvpjUoVetpwnz
3SmgZjyeRPv7OgBTzmX3u1VogwSkw5gl6d/1yyBwe0N7CVLNkuJrzEWHU9Bib2xb
vps23sQYmVMUi/xU8DM9J9O6LaqFJB8FiGMsLkcL6I9d5yWhMCkcF6OJfzdrhBNT
jpd+vbyKWCYjvAxG6Jg/od1U6AjAGjo4gsJ/Z3267yVjrhf1bOk4CIKn5qL8kzIx
+VF7Q0+ilyAg2a992MnnOQIE+Q2I/tD0jCbNLC5qDIV+4pdOcTZ699THpaa3jL8s
HvM5T+1ovjBRDXXLrKOTmeyMhYm1VaNhQV3ElWAIaLVbstjdyuNyM34RwZXx8OOJ
vVNd0hECgYEA6N1AtOsIiqHYsbyCWRaQc3pXA3XafWFEBU8zyRTckHGxrjJiaKZ4
UiFRJ+ur+7SN38jL6ZQM0AzAq69KQ1BJe7kro/84vWColmyJHjWbJ7x61OCmrww3
8IXphpjGBPqzCSH2kjfyM/M5xkq1+PA4sRs7AQY1fhvtnASZaZ0rTkkCgYEAzZYG
B8J9TYeun3VdIMpFLVSNr21oZb7vR+vpYXn2g/N75rOjL9LHhxNOwpwTrJGlW5cY
SBvjQcz7/GHRXhZqxxhEU5cL3DVX3FfNuwnRBOw1LOR0QLHFKdh2BMDWf+AutER3
i310snhXPZMScFGqi7khsO8Rs9OJ4rzNQHWvdIsCgYEA19oHmexnrYHayN4xgX0u
Byz3LWj4T9JyZ+2D1jf1QBtzlUJ1AAaXb6IchUGq2RYDkNWjVu/6dHvtuPcygnUQ
uJPrhQgWQ00u2MjgzVTpbosC3QMk3wwXamfnEPHaVFFC1gtacS1U4JzsCAfG6Gtc
UacpKYjk2vHubfnBbynWM6kCgYEAgf0f5vwkekcWNKDit37tapIR3CATaHHnndQe
hpG1Ow1TBDYFMpHVsySUIhzJm82jflv08HMhqFNR6Ox4k0MdVLGVUj0pNJ1N5nZm
EKNOVAx+OtpgXx+ICMNjK/I6LjSzkyvPYpV6mfXZQ4egmwAoE5yFHviqeseAYar7
JIzE2a0CgYACsJJ8APZWkJIpPCPBpDthaX8oedl5OM6uMn/C26qG7hlQNTt9Pxhh
gteAsVG2LKaTECTqP+XSMH/Gv9FCqjKfSHbg3gkfFM51qZPylwG6EDMmDO4NvMDh
jsv+hRL+/KPyMHphW4OB5kDa+d2Eu6vUGBi2lGq3+MblU0iTo0uFIQ==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICzDCCAlKgAwIBAgIIBgOX92IAEs4wCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC
WFgxFTATBgNVBAoTDEJvdWxkZXIgVGVzdDEfMB0GA1UEAxMWKFRFU1QpIEVzb3Rl
cmljIEVtdSBFMjAeFw0yMTAyMDQwMDExMzJaFw0yMzAzMDYwMDExMzJaMCExHzAd
BgNVBAMTFmVlLmludC1lMi5ib3VsZGVyLnRlc3QwggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQCzvIZgIbnZKusM6YRvqVQwTlO5d/Hu8v+U51FgXrtUuHmF
BvwIlsZGaiKi8UTxd6YvzX+dYcb/UPzSI91xBLj4xt4TWXmYPo9QoTqbJbY4djOR
lrkxIg5hCKAObIte/o+h5v85/QTAWhckT1TLjwb7AS5M1zSJIcRcV+YC7nKR+5Eq
VafLVe0gtPRV2P+zoJeE9VUjz63lMrlv/COgg3oyxoVsbHsWLEqqgTgLoAovlt5T
D6oKuV9pwRTEoGu6Xj9RBBmIA6Mf7N7/2eX6d5gRJJ8BlbOgWDOIv3W/owXeNMkt
MMdtnnKUX534IQaDfp6/5kvdfphNmUN0TW7g6/KlAgMBAAGjgYQwgYEwDgYDVR0P
AQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMB
Af8EAjAAMB8GA1UdIwQYMBaAFJI1guafAH22PqUMp0pfnQLtQGkgMCEGA1UdEQQa
MBiCFmVlLmludC1lMi5ib3VsZGVyLnRlc3QwCgYIKoZIzj0EAwMDaAAwZQIxAP+V
QA21/1IPmMPtcpnDCvYPyQipJLytv+/tqtnsoqVWtsiTzbzQX9zxuwjoLyt2awIw
XDmR/S0uXG3XHez1LdhAUqxftzoZvjm9rINoyLlevG/HSw7UWZGxBdIsdzPkgFJP
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAs7yGYCG52SrrDOmEb6lUME5TuXfx7vL/lOdRYF67VLh5hQb8
CJbGRmoiovFE8XemL81/nWHG/1D80iPdcQS4+MbeE1l5mD6PUKE6myW2OHYzkZa5
MSIOYQigDmyLXv6Poeb/Of0EwFoXJE9Uy48G+wEuTNc0iSHEXFfmAu5ykfuRKlWn
y1XtILT0Vdj/s6CXhPVVI8+t5TK5b/wjoIN6MsaFbGx7FixKqoE4C6AKL5beUw+q
CrlfacEUxKBrul4/UQQZiAOjH+ze/9nl+neYESSfAZWzoFgziL91v6MF3jTJLTDH
bZ5ylF+d+CEGg36ev+ZL3X6YTZlDdE1u4OvypQIDAQABAoIBAHxwZCCeeQuwOTih
XH3qoE0pjbH1J12mg+lWFfNA4zYO2qONaGWR7gjGZuClZnQ/wKGMB3SxQ5N1QPVE
u4YKHP6wwQRoiFUtyw+p8OeFvplszNtZnTI1P/tSe25BHGVSnaMcSUyervF17lvH
SQ/+IHkcIjA1NzxSUp8UhD03Vb9XYaCbB3XwPTgnXgqA3czkyzBRyTGN/QekruvK
P760Rgv11bqcGK7MDcK1QPX3fwQsBN5+xq5XinyO7lfDFKasi1P+75jBYLSKDBwQ
dlwmI3/vnFikA6YutviAgARTnLFvrNVr5f1Gf8SCllXY2rdZmSL0I8ya0pf3rsVj
q4CDj+kCgYEA4Fsx01pHDR1zaQTwrMl/fms5oT/QuDgT8yAR4nRp7dVcqZNAM7Iq
kvtpYbJBQmz3wtdW3NgBg1H6hwOZhm70NlRAsOa2IimuwWpvPDslyNsbnqj/S1HN
jKk/Mja9EGJ1o/8tSPQUS3/9wgyea3N1J+lshRm74aGU2UazusFPwEsCgYEAzRY/
fI5xfZpPwszYhkq9UC7FucMS6A786IAAN0JxJyswaelMfMUrRfiI8EufriiiX7QT
fuRlDZhfDUVBjCzLQgwnN8txZrRZ5KH49pC5oqw3q5z0iXJgpXSn5sxYRXVbm4rs
A+9ruaFldxVvTpE5xKvzTQGVBJVeY6CaubVAos8CgYBUPsox4+dkLFfm6nz5VNxz
+w1z2EOmuR/8nmE42J/iN8kIwAtOnitQb+l9TvMkX0iVuEicutuulPzu79IZYdaA
BBkalDd2EpLVfALy6f7hMi1n4Wuju77kf7UERPuviFlGUI6Po19vjksaL6TZEky+
xO8D98rOCd+byum4SdiJiwKBgDEUlfT1EewBNf1kkJzy3gOGbgNaz/eBPr1VhLe0
yueYymlOT+O8O/Lu27bGIlzHlLRaoB/KAPUT9gty+5DUV4Bi8C/GHEl799dje/Vm
BUcM9/W2Bj+ug7qVBGmTlbxprZa31GvMrHcsTOAG3TBsSOrsS7muGz+Rj5lAIkc3
PVS5AoGBAL0gKWUHDTqzJtO5K+xlceQ8rUMR3mkD0GiU86TYl5gIOlZIPDFk9L5/
BdMjlDcMAFQi+xmlGVwrfMtZt9SVsDNWwl/Ef3pF1nDJP8iMjJu6O/Qoujx7MFMc
kepraEs1eFmtnoHJb3Dr42JU++p3SbNuQdR1ijiLMYO2DLOylNjD
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDbjCCAlagAwIBAgIIHXJEPbUYmCEwDQYJKoZIhvcNAQELBQAwRjELMAkGA1UE
BhMCWFgxFTATBgNVBAoTDEJvdWxkZXIgVGVzdDEgMB4GA1UEAxMXKFRFU1QpIFJh
ZGljYWwgUmhpbm8gUjMwHhcNMjEwMjA0MDAxMTMyWhcNMjMwMzA2MDAxMTMyWjAh
MR8wHQYDVQQDExZlZS5pbnQtcjMuYm91bGRlci50ZXN0MIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAwM0mcX8w4fRiAvPVlLUy1cdnLxOuMcsf7A6Ui+Kj
SyMsDu6x1d67YaYSdghUmFxY7viMeHEItE0i77GyBtOwj9HvNRFRAeP5V8HDJ7LA
THGrpY5pmebLdWq/hiK9fCbxEsu6BlapCfKvEI8QFeFrPb+e7YoRA2F+F5bJh0ns
lMCzvpx13fgtcxc8BEGU3TbaeT9nH7Gnl81sHmk9LnKCS7ZrH51EDU/xcvbczo/9
NIkOLONYgpMLNJRwiIbizTJFf009mlxs8uYhgQF4kMqYUR2vpqm1hZSqgaLds+iQ
ag61Tvp+W3dZC4fDHWijiEellffT9WLR3cMydUczDn9nbQIDAQABo4GEMIGBMA4G
A1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYD
VR0TAQH/BAIwADAfBgNVHSMEGDAWgBSKYLCGNk1dzO93c+p6K6Ku23ZjSTAhBgNV
HREEGjAYghZlZS5pbnQtcjMuYm91bGRlci50ZXN0MA0GCSqGSIb3DQEBCwUAA4IB
AQBsE21bs6SKXK99ReuwvvINFuogdTfCBsB3+zNp5PyAKGlW8BdZEY50euTe8A2x
D9yXMJ46+wkm2m4TkyflaxKh52441XzHf4cfBQr3Lyk9PX7kvUpe8rWlAxvzilD0
IwciW5/Pz2XB0e3P1feDNEA+W3+IINGJJlcKLYnvn/PL6oZRXcVLtZV6iIxtrIBu
gJ7bczkLPgAIedb9a1KZw6uP3q6sQU2UK3+yjAExq1TfHBXbvnDK2bYcbxQFHFkQ
MU48Ji8KFX9Q1EQwYEYE3y3NLZeYdU5ho2Sc4xMYm0DEPHEd9wROqAWIQGyb3ncc
IH5Dwzf8WjDRd8P4GR6dh9Tl
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAwM0mcX8w4fRiAvPVlLUy1cdnLxOuMcsf7A6Ui+KjSyMsDu6x
1d67YaYSdghUmFxY7viMeHEItE0i77GyBtOwj9HvNRFRAeP5V8HDJ7LATHGrpY5p
mebLdWq/hiK9fCbxEsu6BlapCfKvEI8QFeFrPb+e7YoRA2F+F5bJh0nslMCzvpx1
3fgtcxc8BEGU3TbaeT9nH7Gnl81sHmk9LnKCS7ZrH51EDU/xcvbczo/9NIkOLONY
gpMLNJRwiIbizTJFf009mlxs8uYhgQF4kMqYUR2vpqm1hZSqgaLds+iQag61Tvp+
W3dZC4fDHWijiEellffT9WLR3cMydUczDn9nbQIDAQABAoIBACJ/ElfQWCC1pyu8
EQTwfs39RZsIvGwwWd/UpAN7Y5g4DNQHJU6M8Z4BJuBzkR9JtqfbVNbf8pzACHY2
pxHNopO5DkHCfWoHLj/jbVWXCA0lcX7HwmFFCDZUCTyozpp+JTglt30W2FvtTiF6
V3hywstjk74QrAac1QDHe/t24Jukp3LRnQ1XZbCNaseBkSovBUynMwAGHExgA96u
plwbIgYZwRQQ3To0UV8EF+akqsmhJHkV0LkhJB1WaJuipSPY5LJHkwj9PHVZZy4j
eMUuEjBdEPTGVORY+eUH64C5M8PLae8I2C9rae+P/rdRPRNkZBt+Igt8QjJkYERk
2r8IUEkCgYEA1TDSCRlCjJfAM+SxweRvTTCpcCvRUYOhQMaiaOsHQrBNr1XR3C0g
Nr8eLIRwDyhUJMhHlhbfTdM7Cal6e9d5qRLVuygNB50CFIkGXAb2iCG0+NBsv4n0
W+9vDoA3o7Jh2WhdhwL5mQQL03ItGhyHhnBv6d+ORs8hpAw27zK5k5cCgYEA54Q0
h8Z9aSSgiEGGSnDk57fUdHV9TM++p9APBq2p5ylG9K+Y/VtT74zSpJ2CiOqLJSnf
4QIgWQfe+FuMwaN1RRIe7lO9Wlz3pOJww+MO+rF0pjRZTky2ZTt9i68Rt2Hk4qiu
XIIy4YqmuHMJ1sd0ropjgk4bJ31krL1lsKCKrZsCgYAKvPPHW4tbk4Ut1/YQIxZs
F+hg6wQXC/9CSP8DM9tgw4qWK0dvxKIbv9KgQWd3i/t5AtGAQNSskdgma2/s7vSE
zJsRWzoUyRbCvAgi+ILQZoo8AhuIJkW1n8DDRTgIOcLt9XDIjSDPUUHbO6QD7a3x
2pX4fLco3+P85FScBb0NLwKBgDGStHDSRq5J4nnqlefArrMTQNHDCpZ08V0bhuwm
KXhO9VuVcgvmD13+6GfJNlc86ZiGk+KpQuXtcof5inU4G/czPx5HHgeIWpqaxgyb
xOxXLSQdl3XVpUSd7W8IiKGcu5bxCYzTcDOtLa/XKicsREbPaSlQsi3Ngs4eK/Ub
Gza7AoGAPWVMBHsVx+Gey1aoS9KVpbL5BIIRkiQsWXYIQmXIM1DOCBJSvwiOCe3m
zeRa9MqUfMIZsBM4UKu02VqA8dFl6YwqZ9eYFS9Z3QuuM27mOE7iWRA8RugcSXm0
wQoOjbfhKG0YEIXCZLgqFtPBaVaxhPsUI0wdTL/frN35NU5d6Sk=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDcDCCAligAwIBAgIID4VhX15UXkAwDQYJKoZIhvcNAQELBQAwSDELMAkGA1UE
BhMCWFgxFTATBgNVBAoTDEJvdWxkZXIgVGVzdDEiMCAGA1UEAxMZKFRFU1QpIFJl
c2lsaWVudCBSYXZlbiBSNDAeFw0yMTAyMDQwMDExMzJaFw0yMzAzMDYwMDExMzJa
MCExHzAdBgNVBAMTFmVlLmludC1yNC5ib3VsZGVyLnRlc3QwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCn1htXlVxjt3wrSJ2en4U38+hemAgc248607NM
y4mXaDH4KVOBLZX6vAX1BVLXtoLxg3gPM4/Gq6IZ02QiXV0llnozrUq8fACpOZMG
VerYGoM1w7d2k4rIw/l0FIaQZ+ciNcNyunWkohllS3H+aHvM2Qx6pokiy++h1pSy
xANKRaC1QsBGarhZSyJsVQddXarG+cB3F+cjFZGFKjTUHVFyVtxwE+vds/TCMfcS
ppShs1bkHuX6A11MN/owwmHFgtsY8JfnpgqcYISCBxaTzGjc/YNdvkjSS8Lgdzct
6vR1QqKmaboT5x5ego2iDcwkGuyxOe2ZAesgOYHeWeamukBDAgMBAAGjgYQwgYEw
DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAM
BgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFJwEDm+6022jWylljJDC/oidg9gsMCEG
A1UdEQQaMBiCFmVlLmludC1yNC5ib3VsZGVyLnRlc3QwDQYJKoZIhvcNAQELBQAD
ggEBAJvgWoSLu5zY107xD4RFQBplx9sKnF5E0bFZewdXD8LVMAiAm10gbLe3dLzZ
/5ee8pCXexPuBjRkSSXMYfUCijomQgYqjeSO/t+70PZg4mwd+6tfrBX/G5HRvOiT
CaFjoC+6gh1tucvoseNh70SCFvI2kEIHh/0ZD6S+i7oQX1YBvD4i+8R2yX9CU9a2
EfPsZUX2VvFTk5Q6amaX/JXeyj/8ZXknSQNR4icuvSpx1Kp+k2DQvF2wWw/jQp18
NMhmD6KPwYudPc1M1OXtglYS6NokXazdKglR8h04AxinPIcsZsWaUsxSWPwfVqAW
ISTdK/SKiXhXxgJ3tBoWzpOThn8=
-----END CERTIFICATE-----

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAp9YbV5VcY7d8K0idnp+FN/PoXpgIHNuPOtOzTMuJl2gx+ClT
gS2V+rwF9QVS17aC8YN4DzOPxquiGdNkIl1dJZZ6M61KvHwAqTmTBlXq2BqDNcO3
dpOKyMP5dBSGkGfnIjXDcrp1pKIZZUtx/mh7zNkMeqaJIsvvodaUssQDSkWgtULA
Rmq4WUsibFUHXV2qxvnAdxfnIxWRhSo01B1RclbccBPr3bP0wjH3EqaUobNW5B7l
+gNdTDf6MMJhxYLbGPCX56YKnGCEggcWk8xo3P2DXb5I0kvC4Hc3Ler0dUKipmm6
E+ceXoKNog3MJBrssTntmQHrIDmB3lnmprpAQwIDAQABAoIBAQCFIg+aT/5zkw7J
/tYZB4zTL4U50/tLeNaK4XcvCZ1hHuPUaGO26oQ32oIXNFvchQglsBXCaTI5c9go
CEk8ATdsI4tYBrRsAyk7E1KPCgQ52/4M3e1f//VtABeWftmnHuR3fJJHJaVALN1c
PpZ0KklZ2ypM+GF72q2BgQd/LoE1nfCiuUrSmheKOFHERUkNS+AE8qTiiPp9Sn3C
zMA+fAbE6CZfGiGinxXe2j0k+KCkM3m5ObzfWgrMYp/82j7tIlmPPJtC7Km48QIX
O6wcTWhN/VRyYhsyniyS9nifEjcq+dJFZ+/AD7VTHf9f2I/3WzJ0n0ADALXGUThV
UhWhJzihAoGBANcnBx/duR7fTRioPxFVzC/DdENG/pgLU1ikoDUNfMl21uZnaRwN
YuC2UXdcvwEGmxzoQ7xvS0DaOmHrZWmnFaz+S0zXqSNu2TJC6ngyDcAx0sYqko3j
s7JRnNaCqpjL9efAb+AasXsJKhPm8kPOEoeeXUme/Dopy//eaPiGCh1JAoGBAMez
ZI96uO+pcc+YlBQOQsq8XE1Yr7wMfyWJnGlHscAlQ5xz6xJUMEyPJCuG4K8wMfOz
BFl4fArh+/VEFOgWiok1I12FfAm/xRkGFp+9txyXj02VtJTX1iVLQ/Bso2+UYEEN
f4sVpUwFCCz/5torkaEGNSYMb5n69AyUY970Va0rAoGARqKdiCy29hfBq/KwofRV
EOlOZjgMpcYyGswRfNlsuoe1jfctXvRWHgg9Pr7IRoHwstDeTCMNxcDfof4yUTl1
uFHUTuoOsX9W91VYvRVRxmOVG1Imw0aaXFTG9PX5JCjyFp/rGtwooIgltFsB9pjV
JIktf1oe3MmUG/Dc7Zqz/2ECgYAHjDs3xQ6qWEAp9X1bSLKzkOz4K2rw85P2qj3U
KNaKCZ6FkkgHOFFfA2X9kyp41Jx+tnxqmUgu7R2lxn33y6pOx0hf54SppargaD+A
qB38oanT592cZo/8dtzJgIGo3PXKX6U7b4UA24vUj5N9GXp2mJJ3rq6lJjwFIbKo
oZl/YwKBgAZJgJGv8Aqtcq5bfov4vE3DsyzZSB7S9OtX8d7jZuALtOYGeYADMNBK
/8TPvVW4WcIIN1O+VLGp4wAMJwlNDV9PanoXBbEDZSDOt8y6ag7LipeFGxBOx3IH
/qDUZgzwznUc+U+JMHYQcu8cFeBVJSUboDnvcm1JKsGoKOeI4Ejs
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC1zCCAl2gAwIBAgIRAKEKMTHhmcPVLqCw0WNZeaUwCgYIKoZIzj0EAwMwSDEL
MAkGA1UEBhMCWFgxFTATBgNVBAoTDEJvdWxkZXIgVGVzdDEiMCAGA1UEAxMZKFRF
U1QpIElyaWRlc2NlbnQgSXJpcyBYMjAeFw0yMDA5MDQwMDAwMDBaFw0yNTA5MTUx
NjAwMDBaMEkxCzAJBgNVBAYTAlhYMRUwEwYDVQQKEwxCb3VsZGVyIFRlc3QxIzAh
BgNVBAMTGihURVNUKSBFbGVnYW50IEVsZXBoYW50IEUxMHYwEAYHKoZIzj0CAQYF
K4EEACIDYgAExW7wTIngu6HQoRbp2OdTPw3vZY+nDOtazlM3GqNk7BTbpjYqX4ck
gp2unGQoLmQs6np1PDlPFUAGsmW5UMik088vRutd19eUKBDRFRRP3Wu+olMq050Y
0b5zfjvrzgA2o4IBCDCCAQQwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsG
AQUFBwMCBggrBgEFBQcDATASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBQB
2rt6yyUgjl551vmWQi8CQSkHvjAfBgNVHSMEGDAWgBRzP5+/l/ViqS7jourE1Xr5
paFTVjAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAKGFmh0dHA6Ly94Mi5pLmxl
bmNyLm9yZy8wJwYDVR0fBCAwHjAcoBqgGIYWaHR0cDovL3gyLmMubGVuY3Iub3Jn
LzAiBgNVHSAEGzAZMAgGBmeBDAECATANBgsrBgEEAYLfEwEBATAKBggqhkjOPQQD
AwNoADBlAjEAi7Q0STnZ1frkUOD6s7xIZ81S0wDuvJBcb/6Q5DUom1etMcMt0PvI
VsaAN9Pww4TrAjAU72jytj7ULm64MosmKpNBS9TGzpzPEDqPY0tzU38/2aheZmMN
dP+fYeZH872n0zQ=
-----END CERTIFICATE-----

View File

@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDA7b+7NLS4oi3jI5XMy
rSe5LnC1xixOQrij+tMGjHMR8WpIKyHc+aaevr1DxSW1ggmhZANiAATFbvBMieC7
odChFunY51M/De9lj6cM61rOUzcao2TsFNumNipfhySCna6cZCguZCzqenU8OU8V
QAayZblQyKTTzy9G613X15QoENEVFE/da76iUyrTnRjRvnN+O+vOADY=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC0zCCAligAwIBAgIQYWYcHcOHBZprayi5n0huzTAKBggqhkjOPQQDAzBIMQsw
CQYDVQQGEwJYWDEVMBMGA1UEChMMQm91bGRlciBUZXN0MSIwIAYDVQQDExkoVEVT
VCkgSXJpZGVzY2VudCBJcmlzIFgyMB4XDTIwMDkwNDAwMDAwMFoXDTI1MDkxNTE2
MDAwMFowRTELMAkGA1UEBhMCWFgxFTATBgNVBAoTDEJvdWxkZXIgVGVzdDEfMB0G
A1UEAxMWKFRFU1QpIEVzb3RlcmljIEVtdSBFMjB2MBAGByqGSM49AgEGBSuBBAAi
A2IABO6nJy6raRyPH9ZcXYbnkPIS/r/9W134KlnfgDRWw4jqoNU+T5i0xliWu0o5
4VlwasQmKe+LWpKvlIS6ZW0Kbu1eqNBU5hVXXl9LpqYxI+t6/HjQiZuT33CMyCBn
SR81BqOCAQgwggEEMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcD
AgYIKwYBBQUHAwEwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUkjWC5p8A
fbY+pQynSl+dAu1AaSAwHwYDVR0jBBgwFoAUcz+fv5f1Yqku46LqxNV6+aWhU1Yw
MgYIKwYBBQUHAQEEJjAkMCIGCCsGAQUFBzAChhZodHRwOi8veDIuaS5sZW5jci5v
cmcvMCcGA1UdHwQgMB4wHKAaoBiGFmh0dHA6Ly94Mi5jLmxlbmNyLm9yZy8wIgYD
VR0gBBswGTAIBgZngQwBAgEwDQYLKwYBBAGC3xMBAQEwCgYIKoZIzj0EAwMDaQAw
ZgIxAOGjfngXtNcnjperk3xdHRuM72wwjxtUyWhMGc6uwPGE4YFEI0DrhsHvxldA
n8ngCAIxAODGvwRDv6MJnyPxao0XMgdHSahqXWY1Itgn5Ng1O3vMIvgXDhgdazCc
Hvopt14c8Q==
-----END CERTIFICATE-----

View File

@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBWIM3FNps4vLbGRx6U
NZi6loX3QhPDSYBoMdRVFRPL+s77ecnjqIcu5RlNLULZ8P2hZANiAATupycuq2kc
jx/WXF2G55DyEv6//Vtd+CpZ34A0VsOI6qDVPk+YtMZYlrtKOeFZcGrEJinvi1qS
r5SEumVtCm7tXqjQVOYVV15fS6amMSPrevx40Imbk99wjMggZ0kfNQY=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEMTCCAxmgAwIBAgIQWMoTtGAjA/DvOIUjng6FvTANBgkqhkiG9w0BAQsFADBX
MQswCQYDVQQGEwJYWDEZMBcGA1UEChMQKFRFU1QpIElkZW5UcnVzdDEtMCsGA1UE
AxMkKFRFU1QpIERpYXBoYW5vdXMgRGlhbW9uZCBSb290IENBIFgzMB4XDTIwMTAw
NzE5MjE0MFoXDTIxMDkyOTE5MjE0MFowRjELMAkGA1UEBhMCWFgxFTATBgNVBAoT
DEJvdWxkZXIgVGVzdDEgMB4GA1UEAxMXKFRFU1QpIFJhZGljYWwgUmhpbm8gUjMw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIWoAFGWbqRxP0cJJQ3DIo
JQaOSI5kEIWPA3XZ28uXlwiQ8b4Jmr2F/zhQWQ03OlSIWOPeS+2GefQNuDbZclLv
0/ssiUlNimlSvx3H1cvyvUSAPVu/Dfyglfqevxd7SAPL5SKQ/mIaKBo7LpHzn4hi
kC9TG09qQn4wgpkX6fEU6fMPW8PITPELpoiODJw3RMGMacaiHztT4u5FV4wDkEzO
nR92XxDLNZzIzoop/WXpYrGOVM7sx0KeOwosDtOriMWkNpL3rNHnwcbpzaNs6tbB
x3/UDHh2tWoNfc3d3suApbJzgD0ZQDs7CNM38+za0EOlnsI44A7zcB6qWI6hkWP5
AgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUH
AwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFIpgsIY2
TV3M73dz6noroq7bdmNJMB8GA1UdIwQYMBaAFBk7wtJhQcogCFYs8mRLNeZtqM4K
MDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gxLmkubGVuY3Iu
b3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5jci5vcmcvMCIG
A1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQBgt8TAQEBMA0GCSqGSIb3DQEBCwUA
A4IBAQBxOJe0OWwtJgxL6mOjuTTSwx+QEqDGO9As/tkExAFLCg7o5Ou+Nf9BVm/a
FPRS3gYOSnZ9+gOACH5tDLh5uZY1uhzEgkstwZQhCODw9iIyGQjvGVmAxNV+Mhwc
PozAaxZMPriQHu1YlCuq3UEq4xHuzswEWp9YAGptHL5mbIJ3M2FGzfPpR1o7U2Gb
r1FNYqLiNacT+DSITPAykB+rrSR2NQkgb3HuygBh6mao7yB7BEpWmsb0fMdtukVk
JfX7Xx/1pdgCbY+FFidwuwrcztfEF9uZab/rW6xgKr+FuteIrcq9NDFv+xm9EPI2
jRPSWXv4B1Tmuo+Azi9aG9oOXg7L
-----END CERTIFICATE-----

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFIDCCAwigAwIBAgIQOMM6fFS4BsgdmM1bqD3mtTANBgkqhkiG9w0BAQsFADBG
MQswCQYDVQQGEwJYWDEVMBMGA1UEChMMQm91bGRlciBUZXN0MSAwHgYDVQQDExco
VEVTVCkgSW5lZmZhYmxlIEljZSBYMTAeFw0yMDA5MDQwMDAwMDBaFw0yNTA5MTUx
NjAwMDBaMEYxCzAJBgNVBAYTAlhYMRUwEwYDVQQKEwxCb3VsZGVyIFRlc3QxIDAe
BgNVBAMTFyhURVNUKSBSYWRpY2FsIFJoaW5vIFIzMIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAyFqABRlm6kcT9HCSUNwyKCUGjkiOZBCFjwN12dvLl5cI
kPG+CZq9hf84UFkNNzpUiFjj3kvthnn0Dbg22XJS79P7LIlJTYppUr8dx9XL8r1E
gD1bvw38oJX6nr8Xe0gDy+UikP5iGigaOy6R85+IYpAvUxtPakJ+MIKZF+nxFOnz
D1vDyEzxC6aIjgycN0TBjGnGoh87U+LuRVeMA5BMzp0fdl8QyzWcyM6KKf1l6WKx
jlTO7MdCnjsKLA7Tq4jFpDaS96zR58HG6c2jbOrWwcd/1Ax4drVqDX3N3d7LgKWy
c4A9GUA7OwjTN/Ps2tBDpZ7COOAO83AeqliOoZFj+QIDAQABo4IBCDCCAQQwDgYD
VR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATASBgNV
HRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBSKYLCGNk1dzO93c+p6K6Ku23ZjSTAf
BgNVHSMEGDAWgBTsAG5kwCQWsvVti8sNSotsstfBjTAyBggrBgEFBQcBAQQmMCQw
IgYIKwYBBQUHMAKGFmh0dHA6Ly94MS5pLmxlbmNyLm9yZy8wJwYDVR0fBCAwHjAc
oBqgGIYWaHR0cDovL3gxLmMubGVuY3Iub3JnLzAiBgNVHSAEGzAZMAgGBmeBDAEC
ATANBgsrBgEEAYLfEwEBATANBgkqhkiG9w0BAQsFAAOCAgEAtnRyLKD/Zo/JrNzy
8XDpfJ2td0I8KssWQpVM+Szdb92ebXUsQ3uFsSsc00X31D9eJLQ/tHEueUT+pHRA
qRT0Iw2A2tZpZhLj36xULC6ofQkKMUCbP6ZSsucygwGP4UTOfIZ6+dtGApsh63hi
hECa7sllJxltPvRr2Pmz1IlemgihosBGTZWCnsTdA55VYPQa7aYlJ1Y2mwKDct90
Jol2fKuHdSN8EXt1FJUtmZ/iMWkPSE3/r8PLGS9m7rwiYb88oLb0tw3DUnp4FXHc
hQqS3m0bBkiPkPP6Ls7Nz/LkNNUuK1OJaa6qtzuhomzgSXWiXNIigxzCTZjq+Fhb
3H9PD0F719uCpv65E1iUumfU80r/JxIO33KcFnF3RZw3fgWcQVMEp5Ad7tChNSyc
3nJzIJ+my3ZASNv1N0TZfAzzfGXFJlZQ6Nf8PccmcUa9xc/0W1J9blvw6BMAe6CX
E0nhHaefo1nsx43UdimYejgufIRgqPDsPPBsF15G00UvZusBzFttw/ub2N2MM56f
YDCVCQQNqAHuT6ehx4y1bNYTHbM2OIEo2jNno0Sy2dQvxfUlgwlQIICh+7rF5FIy
/vhclA4MF1vo3FLfZeKWayL65yhI8ANuYonsCUqqrEqRJc/GWlL6a6qm8lxyNmDB
cJ0X3oAVQ2f9t6TKvq3QDsFHiPI=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDIWoAFGWbqRxP0
cJJQ3DIoJQaOSI5kEIWPA3XZ28uXlwiQ8b4Jmr2F/zhQWQ03OlSIWOPeS+2GefQN
uDbZclLv0/ssiUlNimlSvx3H1cvyvUSAPVu/Dfyglfqevxd7SAPL5SKQ/mIaKBo7
LpHzn4hikC9TG09qQn4wgpkX6fEU6fMPW8PITPELpoiODJw3RMGMacaiHztT4u5F
V4wDkEzOnR92XxDLNZzIzoop/WXpYrGOVM7sx0KeOwosDtOriMWkNpL3rNHnwcbp
zaNs6tbBx3/UDHh2tWoNfc3d3suApbJzgD0ZQDs7CNM38+za0EOlnsI44A7zcB6q
WI6hkWP5AgMBAAECggEASLzad392Zp2xd/AanrKinwJ6M9PRpjB9XKOD+LkcXAeg
O4cYWEJOhkRXPIxoCOHraKjk6YKlVEoYOZbkiuM/iwRpzwx0iWszu6/Y7wEGjzT5
lpkwItfAHMj2eQWlT8OgZTjl6MAB+78NbukEYe9MQ4RXOhPTLB/B0njHffAX72Av
PmyYI5MQxiL7A63ewzksB+CMGExEHypvt89lZkG/gPhWs0tvvHImwESW5CH+oQOc
bIaB0flJq0+xQCGhZkDR0YlAlWOGQSwpcCMzKG1+zjr6L1nopcQysA2aWI9jkMAy
kx2u3e7kf3TkrOqx/yvEEdVp+qsT+azGzcuwsnUwkQKBgQD1W+Hgp+An2FCbtMh9
H9UPHt/HcxUq76qsu07BsfnJjSjRx88z3iLCOv6HeLUR688TEztN+mC+CcRaji+i
DeRe3j6ooc+a3XxxtcxE2d9z+xtTRLt8HfSosFH76ZR2pJ/soicf/cP96XttM0Z8
cs9CBvpiPBboTCuOSvcr2s2VhQKBgQDRCvEK0QdUx+pvvcQiAaCpHBKp8uwa65xi
hmr9XYeEP6M9Yp7Dt8iA8dFwl7ri/pig1fFKtsf1n4q/EbWlUwigCAfhkf9kEl5E
Rnj1LbywR4qA0w6UDEP70UM//VTHDxbrePeRjOoJBheMdABwRJCWvU0mZCE7WLRy
TfHb0Z9U5QKBgDqBNUQHY5i8qMPoAKJtU7VuTDfXxiVdzpmvdCEVmhUoNqKG/W5F
uo4L2SNecfaa/t5yiIKYgDbwR0S8gLkojNreLZyyMLmhtIm8qr+EIBccujBJxFbd
IbiTiokB8mez63pWU/P546EI6mhogJcuHSOGXG/OGjw75WrhjzyCyOCtAoGAY3rl
gtQ+vOX2dv7D27sSjefCKgZkvdrqLSjyuWhNGW5/bLMGAvXvAQ4TMZXDZkrqr3+g
uIGLXyRxjsQKwYZmUGIB/iLQevsSyUMQRP1jEjC5hNzrzyCXKbtIWadhNOnFaoHC
rw10Qp8XjcuWedbnSBUGJgL4nZl1JgBZ3NZBENECgYAJuTUvD2yE31U1LY69OnYC
fpzX15Vi79rznj8xNZo86U5l/JDZnwD+gvxwDq6sQMbRO2Pav8GO+eutZ7btpAUi
CrC8OCSFVr0+XPPizPx0FS11PS/T1ETYvKU8TC0FA6gRycvq1OqmMQbW3gaZNY3E
Dd2nXB2sx4ZoalvqJylmEQ==
-----END PRIVATE KEY-----

View File

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIENDCCAxygAwIBAgIRAKpe9os9nQK+J6uq1C4bxrcwDQYJKoZIhvcNAQELBQAw
VzELMAkGA1UEBhMCWFgxGTAXBgNVBAoTEChURVNUKSBJZGVuVHJ1c3QxLTArBgNV
BAMTJChURVNUKSBEaWFwaGFub3VzIERpYW1vbmQgUm9vdCBDQSBYMzAeFw0yMDEw
MDcxOTIxNDVaFw0yMTA5MjkxOTIxNDVaMEgxCzAJBgNVBAYTAlhYMRUwEwYDVQQK
EwxCb3VsZGVyIFRlc3QxIjAgBgNVBAMTGShURVNUKSBSZXNpbGllbnQgUmF2ZW4g
UjQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3j6Qtr5/kaJ26ANPl
VGOtKIUH5ICvxoPmOKkZQTDdw/Lk56XaQ8M87wXlNz+bVTh4uDcDE2B3sIaEKnhS
dso1DjqbCKvhLC6Vl/YgqdnlRrueVTCFmt0V2pDs7qAvyyNfsSQEi8n0ZXp7FGuZ
EyOnFBdHYP7Y4OPtevXri7031HhtvKfN0IfA98o5CF6KLZm5c1QqqCLyHK21tC4k
G4PK4k1K2wHfzHrk7josYQvOAWny3uD9896z+ijNh0cr2eJHsJf9aXbQfw5bqVo8
o+5rG4EogVRLcXdQmFA2xQIuLHS+Mz4JgTknTMq/FxaHx6TYLtlkT+mT/tHE48BE
/I/zAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYB
BQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFJwE
Dm+6022jWylljJDC/oidg9gsMB8GA1UdIwQYMBaAFBk7wtJhQcogCFYs8mRLNeZt
qM4KMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0cDovL3gxLmkubGVu
Y3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veDEuYy5sZW5jci5vcmcv
MCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQBgt8TAQEBMA0GCSqGSIb3DQEB
CwUAA4IBAQBe0zod66+cPB06/7sstow5vA6L/8E+IBwWDH9jM/LQyBCV6K28QE5b
Y7v6akxVTxCjN8dyuHA/7GgUWG3eWan/blefn5dSWReTQLERCUCLJCql9ekqzI9J
AZsWzIB3obUusf1l/PX6tENmYOrqsJDomUzUg8h7dGXtk/csJhf55dgwt2GQNxWS
ah8AG8Uhdb5fdGSgKk/0297r3uO5MFcjlu6nax7o1usmA7nZFbfyRUrP74Q2n0h5
04sgAc5ZqByD1ZOyGZfv0vdaRfGYxuzsa3MRN4dO4Ccqti98XDk7wKuAG4td3mhx
BeNAmKEUHoIMPTrI5bakvHDokO9wvh0o
-----END CERTIFICATE-----

View File

@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFIjCCAwqgAwIBAgIQRxLfKYKxwUMlVEJNV0W9yjANBgkqhkiG9w0BAQsFADBG
MQswCQYDVQQGEwJYWDEVMBMGA1UEChMMQm91bGRlciBUZXN0MSAwHgYDVQQDExco
VEVTVCkgSW5lZmZhYmxlIEljZSBYMTAeFw0yMDA5MDQwMDAwMDBaFw0yNTA5MTUx
NjAwMDBaMEgxCzAJBgNVBAYTAlhYMRUwEwYDVQQKEwxCb3VsZGVyIFRlc3QxIjAg
BgNVBAMTGShURVNUKSBSZXNpbGllbnQgUmF2ZW4gUjQwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQC3j6Qtr5/kaJ26ANPlVGOtKIUH5ICvxoPmOKkZQTDd
w/Lk56XaQ8M87wXlNz+bVTh4uDcDE2B3sIaEKnhSdso1DjqbCKvhLC6Vl/Ygqdnl
RrueVTCFmt0V2pDs7qAvyyNfsSQEi8n0ZXp7FGuZEyOnFBdHYP7Y4OPtevXri703
1HhtvKfN0IfA98o5CF6KLZm5c1QqqCLyHK21tC4kG4PK4k1K2wHfzHrk7josYQvO
AWny3uD9896z+ijNh0cr2eJHsJf9aXbQfw5bqVo8o+5rG4EogVRLcXdQmFA2xQIu
LHS+Mz4JgTknTMq/FxaHx6TYLtlkT+mT/tHE48BE/I/zAgMBAAGjggEIMIIBBDAO
BgNVHQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIG
A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFJwEDm+6022jWylljJDC/oidg9gs
MB8GA1UdIwQYMBaAFOwAbmTAJBay9W2Lyw1Ki2yy18GNMDIGCCsGAQUFBwEBBCYw
JDAiBggrBgEFBQcwAoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAe
MBygGqAYhhZodHRwOi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EM
AQIBMA0GCysGAQQBgt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQBwUN+8DSF/tA+F
gwxsx8vr7fVuCA9cM2CuN/iIlTKcGoL5VJYdM4eYyoBhF+TfStnJLEZ3LxdqEzlo
zFuV4GY5RJBSamJEzH3wrtd9whnQgGLl+L44utUegbIvj8pSxz1ONxj8Sf9U/i/Y
FSmw7jtHP4oQvqpTJUquD1hmS9FbVjQNuHdYMaiKdIJCP4i3SQN+2EczBac8JQxK
ZmyrW71n99MFfhGbHBPQR35bAYnTSpu7WFda91gb3LEnYedCHyLrslLd9VP+44Qt
Z2NCUysRo9Wu2i7GJvW8YlecxOPjoFNjX5jr0E/3SHvcn77pUTspEi1StWYJj9el
WcsiVRoEkniHi8qAbk/j5cv/uYPpmWHDzIsXc8tvrlPXBRudokBSo+8LzFHj/0uL
IP7PajlnwSoEDIT4yppbq7JuPXZ/0ukpMZqDv/fatZVUYOSDvGU8fOzkz+5tw4Xk
9QbjRhf4A9hxRQAEWRiSRZnZAV/w/ExFD/JG9uagmHJIqr+m27RMs3CsERdIOWTx
Q2NKVxPG2/OtMZD0klKaUF3aVpQm7njG8PR4A86S0k8s6u0CLZj4DhExWeHu14xS
/8q0JO/T6OvbA0dOkvLJ8o7Pj+KXyHWENcbWhTdETezVAQYtVNvYEzZxyKd79c+A
csfZyG9nwwW9sDh3o1XinwqDDmVECQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC3j6Qtr5/kaJ26
ANPlVGOtKIUH5ICvxoPmOKkZQTDdw/Lk56XaQ8M87wXlNz+bVTh4uDcDE2B3sIaE
KnhSdso1DjqbCKvhLC6Vl/YgqdnlRrueVTCFmt0V2pDs7qAvyyNfsSQEi8n0ZXp7
FGuZEyOnFBdHYP7Y4OPtevXri7031HhtvKfN0IfA98o5CF6KLZm5c1QqqCLyHK21
tC4kG4PK4k1K2wHfzHrk7josYQvOAWny3uD9896z+ijNh0cr2eJHsJf9aXbQfw5b
qVo8o+5rG4EogVRLcXdQmFA2xQIuLHS+Mz4JgTknTMq/FxaHx6TYLtlkT+mT/tHE
48BE/I/zAgMBAAECggEBAKwPic6FPDRG1+n9UqI5a0FppOEUEIgzZXnMjL0ufVay
kSB9/tnMANtCFd2Y3xeEV23ZBz/rztYCcuS6RpTN4pa+4rJl+28TEguJKN3POH8Q
wVcV9WiXFDui54wf1alXGa5eBiv4uHJNGPT73CvdY+L+EyAGTHwQubXmN1P2ZYRJ
Hbgl1SitBn7O6PNkbw3cozJFgcaJKKHVFpLHn6ZGl5x5qaXR56XHHo71rzFd1X+A
VQt3XwQyHHbK6FkO94dcILc6YoQWKq0z/fFS+Zeez94fWtHzaTVOHTxwd2yTr9Tg
KSTMxCHiNwf3OrzF35W89GtEPdSYb8Ud8VscirYcT1ECgYEA75Foqm4ekLzyUQOg
ZwHN6SY/N0adop5PjpXB9jQJEQBaMuunkjyC69KmzXHMExreDwjNTxrobv1knuOc
vfeevkiHMVRwkdwTEbwMCeTqGIF1iGIDAUy8YUaPXcpdcfyCI+LvqTOJXmBzPKyl
I8xms6A7lwGKTXI+TRN4eeqNmLkCgYEAxCbNqRN1Tzmj2hJQ6FM4sS8Oil1vH6em
9txxnEHO2wsrTSkfwIjK8n+F7dfnG+yifghmh8IPZY7W2hRs3GXvgRRn9QMkFLGN
CF+3zjmtnGZ+rtR2EXLzxJzKgDo1kARCOKpmHJpdyEo7APnzlALoYd0BRxMfI12z
Ep4mZQafAAsCgYEAmgt5LuXiN5WXhup7EOFDI2FZktSQdkmvxHKdpw+sqMb+OPH4
7XqFgNgSM9axr7M+CJLTWcNmpD/BnL2lQy3fYGHItLqkK9ZEWMn/P7l3ocxU5B6J
6iMKms5BT8DZN3tzv1mkW7ts4EfKscAd7Bf6DhTBXIc8BDKqxur3NAXTiNkCgYBv
Huhtezd+3VGErdGl+9dnERh0rD/SuABvYyz9b46HKsmqGb0CLryCKlouBpzHhgP7
0Dh9eiOMziHLQ7z0Es9e2beW5uOe0YLrFoajTquaqbnkwznr4qpUXNqfT9qeLrtx
LJ9SXuT4HY1VnUQvOoJ5RmF96Ug/mcpjprJrkxeqRwJ/GfgCxltOkdJCZWIhAs2X
ylyHigDh/0gCoOwivSVdFm695G2w78jUVgfC6DD/KnyCuMu++vvtjkVxTmKOIZGZ
vQlfOjkF+IsF0oOL/yRBqgCtm18jstBnAe0M+yKWyZt1PlDSbg+ronhVIdzI6+Ds
xaAN6/bWHmWEwVGB3MrhSQ==
-----END PRIVATE KEY-----

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDejCCAmKgAwIBAgIQUn3q9pSKHAHwO+HOtDNkLjANBgkqhkiG9w0BAQsFADBX
MQswCQYDVQQGEwJYWDEZMBcGA1UEChMQKFRFU1QpIElkZW5UcnVzdDEtMCsGA1UE
AxMkKFRFU1QpIERpYXBoYW5vdXMgRGlhbW9uZCBSb290IENBIFgzMB4XDTAwMDkz
MDIxMTIxOVoXDTIxMDEzMDE0MDExNVowVzELMAkGA1UEBhMCWFgxGTAXBgNVBAoT
EChURVNUKSBJZGVuVHJ1c3QxLTArBgNVBAMTJChURVNUKSBEaWFwaGFub3VzIERp
YW1vbmQgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
ANMSIROypaPK+0us0pDb8NPtVOLtCgjcJRiYh6xp0xz5C0qq3+Vt709a70mF1w5+
4MlcE/6YPtDn0wPFuvKV7toHY0YIEMlo1xXvOT/pLkefTEgWm7aIz/32JpbYXimX
DjTRef4YopM+zMEbj8RACekZw6NiU/cS2Sm5k+v7PDc/MxLoENRMrvTJZ9E8i4Qg
4vafYMjMMX0fFsz1HWQ4HsXAMMHKCWVDIVJ77kz5j/rfTr+HiWyG7/wJzYIoecek
bi7pDX1PolP1tHdEs2aRzUhhelDCOsE5gZLJcLDXjieglZ3W4Vq5wCoAApRDKCMO
hYVZpIixiOqnSk/aMK20hFMCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
EwEB/wQFMAMBAf8wHQYDVR0OBBYEFBk7wtJhQcogCFYs8mRLNeZtqM4KMA0GCSqG
SIb3DQEBCwUAA4IBAQA2BTzE5rGBb3RoU1+bc7eMOon66mRQSYoFUnP/LqEeSNYG
gLE2Wdr70b5I8vvGs9fJcSAQe6Hoqdvd9eSv+nhnOD/Nfu5dftkXQyEfDm61yTX0
A1eLQ1cNtDTMFpbfemXBMoDgWKkY140U4daqN+yf9QpSoyqR2Cr1HmzEGeUahHaM
/0I+RP2oEyvDnp8HqI5lQOsN/U2z5NBKhb2kCrjfrxQs4EMnqihqX6hlkRO14Fg+
2/LL17d0ZF83I/QGmZ3KVGjIp1I/x8DK5BJexpst9un9NewEwfJZZ8yHtexqU03u
iaMhgikV56g1BVlsQ5FjgTDrcGU+HlrEAFxEQoS8
-----END CERTIFICATE-----

View File

@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFPjCCBCagAwIBAgIRAJa/oQus/hdayzfOeeepyXkwDQYJKoZIhvcNAQELBQAw
VzELMAkGA1UEBhMCWFgxGTAXBgNVBAoTEChURVNUKSBJZGVuVHJ1c3QxLTArBgNV
BAMTJChURVNUKSBEaWFwaGFub3VzIERpYW1vbmQgUm9vdCBDQSBYMzAeFw0yMTAx
MjAxOTE0MDNaFw0yNDA5MzAxODE0MDNaMEYxCzAJBgNVBAYTAlhYMRUwEwYDVQQK
EwxCb3VsZGVyIFRlc3QxIDAeBgNVBAMTFyhURVNUKSBJbmVmZmFibGUgSWNlIFgx
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxH5IxkmHnnk+a4AFNGaS
1jRu9Vry8itwgkd39wgeqNGuBzQhSf+QdfCX/dVv254ak/ULkemyoHotDhEmYQgC
9f2jR6sT9HIA8requNtx7ATpxhiZRpkszMMJq1MEvbdyQUasJQZa9IrQeLnyMJfo
wqq4ecBevkw+aNN7Sw2ISqa0KpF91M9a6f1H+9zbcYLIJyG28+SxUe9qLYG8yMy5
mBh9J5CflGX4jASWjwoaQSpOApIXxnA2taA7txi1cNyixqpqTs48v+fvPLilQ1vk
rMuFfTUv3BjHis4vk8QzbBvr939qbol4ZP5mVGhfyNtU1AFnM8yEq9RsC38x5aLc
HOVRYiLBmteLDdwlag9f8KuO/CfeoWRo1LthoG7KJlEY+ohxwRVNf4/P+C7VZXD/
CWl3C6PeuXvXldmNRCLzn3PjuSMQcLTsA+XcIaKAJAEkGy2DXBJfUd/u+4qFg5tP
VFzMh4bm9ZsadsXaW0VFpoLwSUUsdqt5VpEFXGr6b6pNs3anKZGgAYP7jrsJ/5VG
SPvrRw5MxXcxFwzdlcRk5L76ZBlsTiXuGT5txeHOCFIG2SweKzlFMqMjTWTwV8QO
QBIuhjHYybYwG0FCFeKNFmwCKBpbddurJMGv4WVVnE7dBmvZZm7zealpxr3VvbDC
N3O6J+7RlqtNpEiWgjoTgocCAwEAAaOCARQwggEQMA4GA1UdDwEB/wQEAwIBBjAP
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTsAG5kwCQWsvVti8sNSotsstfBjTAf
BgNVHSMEGDAWgBQZO8LSYUHKIAhWLPJkSzXmbajOCjBLBggrBgEFBQcBAQQ/MD0w
OwYIKwYBBQUHMAKGL2h0dHA6Ly9hcHBzLmlkZW50cnVzdC5jb20vcm9vdHMvZHN0
cm9vdGNheDMucDdjMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRy
dXN0LmNvbS9EU1RST09UQ0FYM0NSTC5jcmwwIgYDVR0gBBswGTAIBgZngQwBAgEw
DQYLKwYBBAGC3xMBAQEwDQYJKoZIhvcNAQELBQADggEBAC4sk0zDpMGG+kXCN7O7
RundAdmgLwJKg3BsWYCqhQtgnKYnj5RA8Zwl5M8IxZFiopxtB+toE3AI2tO8J99u
QSD5FaB9Gh3bcuApkOHoz9cndDdjFSrqaWGFIxLTKTifjpdzvamRKB2KUsCDCanH
Mj0SuHHQNK9pGR6hh7TO9vTlYcay5eCsXMon/zi6c2Tb8/QtGvTG/ryszTtZRnGK
Md8jM/A7B4kFiY4Rah63lZOO4jRu6NjOqBHzbGLy7OHHrVaO8zfHIKtR1vjAeKV9
im4bSnm0qmysw3KDon26x1RL7BSas+WBdYsXCUwbrRkDIstmNOmf3K786U09nszM
MNY=
-----END CERTIFICATE-----

View File

@ -0,0 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFWDCCA0CgAwIBAgIQIscgxTJeigIe7M/ES3JpNTANBgkqhkiG9w0BAQsFADBG
MQswCQYDVQQGEwJYWDEVMBMGA1UEChMMQm91bGRlciBUZXN0MSAwHgYDVQQDExco
VEVTVCkgSW5lZmZhYmxlIEljZSBYMTAeFw0xNTA2MDQxMTA0MzhaFw0zNTA2MDQx
MTA0MzhaMEYxCzAJBgNVBAYTAlhYMRUwEwYDVQQKEwxCb3VsZGVyIFRlc3QxIDAe
BgNVBAMTFyhURVNUKSBJbmVmZmFibGUgSWNlIFgxMIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEAxH5IxkmHnnk+a4AFNGaS1jRu9Vry8itwgkd39wgeqNGu
BzQhSf+QdfCX/dVv254ak/ULkemyoHotDhEmYQgC9f2jR6sT9HIA8requNtx7ATp
xhiZRpkszMMJq1MEvbdyQUasJQZa9IrQeLnyMJfowqq4ecBevkw+aNN7Sw2ISqa0
KpF91M9a6f1H+9zbcYLIJyG28+SxUe9qLYG8yMy5mBh9J5CflGX4jASWjwoaQSpO
ApIXxnA2taA7txi1cNyixqpqTs48v+fvPLilQ1vkrMuFfTUv3BjHis4vk8QzbBvr
939qbol4ZP5mVGhfyNtU1AFnM8yEq9RsC38x5aLcHOVRYiLBmteLDdwlag9f8KuO
/CfeoWRo1LthoG7KJlEY+ohxwRVNf4/P+C7VZXD/CWl3C6PeuXvXldmNRCLzn3Pj
uSMQcLTsA+XcIaKAJAEkGy2DXBJfUd/u+4qFg5tPVFzMh4bm9ZsadsXaW0VFpoLw
SUUsdqt5VpEFXGr6b6pNs3anKZGgAYP7jrsJ/5VGSPvrRw5MxXcxFwzdlcRk5L76
ZBlsTiXuGT5txeHOCFIG2SweKzlFMqMjTWTwV8QOQBIuhjHYybYwG0FCFeKNFmwC
KBpbddurJMGv4WVVnE7dBmvZZm7zealpxr3VvbDCN3O6J+7RlqtNpEiWgjoTgocC
AwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
BBYEFOwAbmTAJBay9W2Lyw1Ki2yy18GNMA0GCSqGSIb3DQEBCwUAA4ICAQBf9Ub2
QuJfmonVLmEhy5sA6zBIMavO0HpVE0DpwtnLoLRW3UdgCzvZw1o/FOK4pv7BNJX5
3PImqEIg4UMPCiC7X9lhj823srxw3zfL9YxrXNX/ROQ7NHgrM+CvyycSDo23J1dR
5mUsqP5JLGOPmjQWjOreKBGttO6U/IwxAOVaohVmAPktBSx0/XX8TS3765h38eLS
snHHFU/gerZXlfmnADhSwIaoMGT5ucZB5y4Mkb3i82w1y0mCnhbrGoXrASPCu++C
9dBN/fs9rHd8NW4RE8PR2C6lJIllPA98Q0GRSUrDiUKnXArHSx2ZlGp0Mtatqc0/
lU81rtr3serKdcqbMO/aD+ampX335d5HEx2cXL2f6bBn9EjWQbWBM2YFPWdUHd8Q
unSsVy+MXSDh+8w+q7Y7EQlXpNd0ADOpOXb3zf+ekYsSIHI/pUlwUJWF/CM8Ysm3
hmbt5Qow05FJTUSTKeNGh4t8WI6rHDGtHery2V5zZsAZ0EGGB1sQQL+IMKbVzl0U
3ek7RVPJKuSyurGOAEhjqo/1gfDmnrevPS7GRU/7dTzB6X4dJIia+WKBcq43QvfG
qUqQtmtTylJUIWLueeGgWMr+JoRgio5UkYRbpJnVBlBIq2sRkfZ1kP/1WciW/HIM
jgCRFTBWwxK9NuJEXsPmentvELy5A/D4uP6gfQ==
-----END CERTIFICATE-----

View File

@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEUDCCAjigAwIBAgIQMoNIizVFHRPRHf3+bcA7LTANBgkqhkiG9w0BAQsFADBG
MQswCQYDVQQGEwJYWDEVMBMGA1UEChMMQm91bGRlciBUZXN0MSAwHgYDVQQDExco
VEVTVCkgSW5lZmZhYmxlIEljZSBYMTAeFw0yMDA5MDQwMDAwMDBaFw0yNTA5MTUx
NjAwMDBaMEgxCzAJBgNVBAYTAlhYMRUwEwYDVQQKEwxCb3VsZGVyIFRlc3QxIjAg
BgNVBAMTGShURVNUKSBJcmlkZXNjZW50IElyaXMgWDIwdjAQBgcqhkjOPQIBBgUr
gQQAIgNiAATVXC/BnBdkaS7EhZPa3177GOn6jdMhoA99KwDk1WYQ1P891U6F2ZSJ
qFVDSbBJPz/LXjrXKTIvLTyFKpFzXesr0TFawRibJJkUPgMY6ohuMwGNJ8U0PWAU
oM6Wq//s/RajgeUwgeIwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
HQYDVR0OBBYEFHM/n7+X9WKpLuOi6sTVevmloVNWMB8GA1UdIwQYMBaAFOwAbmTA
JBay9W2Lyw1Ki2yy18GNMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcwAoYWaHR0
cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRwOi8veDEu
Yy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQBgt8TAQEB
MA0GCSqGSIb3DQEBCwUAA4ICAQDDy1vIFa+mxymBHQvI99OoEQNy8OFQ6MRfnVHB
tHUJ8gV395gv7ukMUoM7DoBJGRFemlQp+RPeDOr17qXOQlbpgURzOhiKA0dtfLWE
hOm6ENXbCSzHphlFOlqBVgnWa8fD97mf6lKt6TOBrj1PGPgyq+anzbeEC3YhelB1
UIPQ72OtYWHi69pJfsUkscDjl4QnozxSWHoxgsVe4nKWnW1Xws+lwhBDZTbgT4zI
jtZ2Z9vhJiqsQvaxaTg+LRQvuktJ8GSA99FCBZfmRkLcvkm/dieo+bLJLncoKlX5
3gwtl35kQj0E0UquChdWdcKcDmaAT+VdYRPSX4HLaENqsgckwkaAKODiz7a9uNQK
qIDBdCj16WbTlwYo9J+yqcYxM2fv4YBIvQ/SkGZoQJ2BMlCKR3pHANZNa7622n/2
RE14wj80CNt10a1hX1qEV8iJOHjiy4hZSYkvb9FVgLbGPLTdYGGSdFtIoEAlt25f
EVhCAr20xx4kdD6Z8avrXe11c945XsE3TJ1veYwPQiWMTjWv/TTb+bFo/6AxZbQ/
Pbe2inH/AyaSr2C36UjSRK/4brI97lu9GSUvEOOePT3QyuUzdi6Ke4V5E/Qzp3Yk
TeVBcj3FK+bSazKjB9ndFa7c31ggmCj1IVXHkmwV+KS7uosmuT1JJU7+fImFtEKB
K1yBhg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICDzCCAZSgAwIBAgIRAJZSYAs5uRSI65L/L/drYDIwCgYIKoZIzj0EAwMwSDEL
MAkGA1UEBhMCWFgxFTATBgNVBAoTDEJvdWxkZXIgVGVzdDEiMCAGA1UEAxMZKFRF
U1QpIElyaWRlc2NlbnQgSXJpcyBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcx
NjAwMDBaMEgxCzAJBgNVBAYTAlhYMRUwEwYDVQQKEwxCb3VsZGVyIFRlc3QxIjAg
BgNVBAMTGShURVNUKSBJcmlkZXNjZW50IElyaXMgWDIwdjAQBgcqhkjOPQIBBgUr
gQQAIgNiAATVXC/BnBdkaS7EhZPa3177GOn6jdMhoA99KwDk1WYQ1P891U6F2ZSJ
qFVDSbBJPz/LXjrXKTIvLTyFKpFzXesr0TFawRibJJkUPgMY6ohuMwGNJ8U0PWAU
oM6Wq//s/RajQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0G
A1UdDgQWBBRzP5+/l/ViqS7jourE1Xr5paFTVjAKBggqhkjOPQQDAwNpADBmAjEA
2Y4+7QDv6mN7Bg28fK/hlzAzz1Bi+zcr2v5aOTXXPrQZxUGu9X3ojuVTO8mfoZgU
AjEAzFZmf002M+ltm3JwSJjShu8aIoD47ymSiNdMiXcf6lTJ6ytKgyImV+frOpjx
0/Ev
-----END CERTIFICATE-----

View File

@ -736,7 +736,6 @@ func (wfe *WebFrontEndImpl) processRevocation(
return probs.ServerInternal(
"unable to verify provided certificate, empty issuerCertificates")
}
// Try to validate the signature on the provided cert using its corresponding
// issuer certificate.
issuerNameID := issuance.GetIssuerNameID(providedCert)
@ -1602,8 +1601,7 @@ func (wfe *WebFrontEndImpl) Certificate(ctx context.Context, logEvent *web.Reque
// server error.
wfe.sendError(response, logEvent, probs.ServerInternal(
fmt.Sprintf(
"Certificate serial %#v has an unknown IssuerNameID %q"+
"- no PEM certificate chain associated.",
"Certificate serial %#v has an unknown IssuerNameID %d - no PEM certificate chain associated.",
serial,
issuerNameID),
), nil)
@ -1617,8 +1615,17 @@ func (wfe *WebFrontEndImpl) Certificate(ctx context.Context, logEvent *web.Reque
return
}
// TODO(#5225): Check that the signature on parsedCert validates from the
// issuer cert in the chain.
// Double check that the signature validates.
err = parsedCert.CheckSignatureFrom(wfe.issuerCertificates[issuerNameID].Certificate)
if err != nil {
wfe.sendError(response, logEvent, probs.ServerInternal(
fmt.Sprintf(
"Certificate serial %#v has a signature which cannot be verified from issuer %d.",
serial,
issuerNameID),
), nil)
return
}
// Prepend the chain with the leaf certificate
responsePEM = append(leafPEM, availableChains[requestedChain]...)

View File

@ -327,30 +327,38 @@ func setupWFE(t *testing.T) (WebFrontEndImpl, clock.FakeClock) {
fc := clock.NewFake()
stats := metrics.NoopRegisterer
chainPEM, err := ioutil.ReadFile("../test/test-ca2.pem")
test.AssertNotError(t, err, "Unable to read ../test/test-ca2.pem")
chainDER, _ := pem.Decode(chainPEM)
chainCrossPEM, err := ioutil.ReadFile("../test/test-ca2-cross.pem")
test.AssertNotError(t, err, "Unable to read ../test/test-ca2-cross.pem")
certChains := map[issuance.IssuerNameID][][]byte{
// The real IssuerNameID of test-ca2
issuance.IssuerNameID(18337263084599622): {
append([]byte{'\n'}, chainPEM...),
append([]byte{'\n'}, chainCrossPEM...),
certChains := map[issuance.IssuerNameID][][]byte{}
issuerCertificates := map[issuance.IssuerNameID]*issuance.Certificate{}
for _, files := range [][]string{
{
"../test/hierarchy/int-r3.cert.pem",
"../test/hierarchy/root-x1.cert.pem",
},
// The IssuerNameID of wfe2/test/178.pem, pretending to be part of a real chain.
issuance.IssuerNameID(66191037641995744): {
append([]byte{'\n'}, chainPEM...),
append([]byte{'\n'}, chainCrossPEM...),
{
"../test/hierarchy/int-r3-cross.cert.pem",
"../test/hierarchy/root-dst.cert.pem",
},
}
issuerCert, err := x509.ParseCertificate(chainDER.Bytes)
test.AssertNotError(t, err, "Unable to parse issuer cert")
issuerCertificates := map[issuance.IssuerNameID]*issuance.Certificate{
issuance.IssuerNameID(18337263084599622): {Certificate: issuerCert},
issuance.IssuerNameID(66191037641995744): {Certificate: issuerCert},
{
"../test/hierarchy/int-e1.cert.pem",
"../test/hierarchy/root-x2.cert.pem",
},
{
"../test/hierarchy/int-e1.cert.pem",
"../test/hierarchy/root-x2-cross.cert.pem",
"../test/hierarchy/root-x1-cross.cert.pem",
"../test/hierarchy/root-dst.cert.pem",
},
} {
certs, err := issuance.LoadChain(files)
test.AssertNotError(t, err, "Unable to load chain")
var buf bytes.Buffer
for _, cert := range certs {
buf.Write([]byte("\n"))
buf.Write(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}))
}
id := certs[0].NameID()
certChains[id] = append(certChains[id], buf.Bytes())
issuerCertificates[id] = certs[0]
}
wfe, err := NewWebFrontEndImpl(stats, fc, testKeyPolicy, certChains, issuerCertificates, nil, nil, blog.NewMock(), 10*time.Second, 30*24*time.Hour, 7*24*time.Hour)
@ -1746,8 +1754,48 @@ func TestAccount(t *testing.T) {
}`)
}
type mockSAWithCert struct {
core.StorageGetter
cert *x509.Certificate
status core.OCSPStatus
}
func newMockSAWithCert(t *testing.T, sa core.StorageGetter, status core.OCSPStatus) *mockSAWithCert {
cert, err := core.LoadCert("../test/hierarchy/ee-r3.cert.pem")
test.AssertNotError(t, err, "Failed to load test cert")
return &mockSAWithCert{sa, cert, status}
}
// GetCertificate returns the mock SA's hard-coded certificate, issued by the
// account with regID 1, if the given serial matches. Otherwise, returns not found.
func (sa *mockSAWithCert) GetCertificate(_ context.Context, serial string) (core.Certificate, error) {
if serial != core.SerialToString(sa.cert.SerialNumber) {
return core.Certificate{}, berrors.NotFoundError("Certificate with serial %q not found", serial)
}
return core.Certificate{
RegistrationID: 1,
Serial: core.SerialToString(sa.cert.SerialNumber),
DER: sa.cert.Raw,
}, nil
}
// GetCertificateStatus returns the mock SA's status, if the given serial matches.
// Otherwise, returns not found.
func (sa *mockSAWithCert) GetCertificateStatus(_ context.Context, serial string) (core.CertificateStatus, error) {
if serial != core.SerialToString(sa.cert.SerialNumber) {
return core.CertificateStatus{}, berrors.NotFoundError("Status for certificate with serial %q not found", serial)
}
return core.CertificateStatus{
Serial: core.SerialToString(sa.cert.SerialNumber),
Status: sa.status,
}, nil
}
func TestGetCertificate(t *testing.T) {
wfe, _ := setupWFE(t)
wfe.SA = newMockSAWithCert(t, wfe.SA, core.OCSPStatusGood)
mux := wfe.Handler(metrics.NoopRegisterer)
makeGet := func(path string) *http.Request {
@ -1763,19 +1811,19 @@ func TestGetCertificate(t *testing.T) {
_, ok := altKey.(*rsa.PrivateKey)
test.Assert(t, ok, "Couldn't load RSA key")
certPemBytes, _ := ioutil.ReadFile("test/178.crt")
certPemBytes, _ := ioutil.ReadFile("../test/hierarchy/ee-r3.cert.pem")
cert, err := core.LoadCert("../test/hierarchy/ee-r3.cert.pem")
test.AssertNotError(t, err, "failed to load test certificate")
chainPemBytes, err := ioutil.ReadFile("../test/hierarchy/int-r3.cert.pem")
test.AssertNotError(t, err, "Error reading ../test/hierarchy/int-r3.cert.pem")
chainCrossPemBytes, err := ioutil.ReadFile("../test/hierarchy/int-r3-cross.cert.pem")
test.AssertNotError(t, err, "Error reading ../test/hierarchy/int-r3-cross.cert.pem")
reqPath := fmt.Sprintf("/acme/cert/%s", core.SerialToString(cert.SerialNumber))
pkixContent := "application/pem-certificate-chain"
chainPemBytes, err := ioutil.ReadFile("../test/test-ca2.pem")
test.AssertNotError(t, err, "Error reading ../test/test-ca2.pem")
chainCrossPemBytes, err := ioutil.ReadFile("../test/test-ca2-cross.pem")
test.AssertNotError(t, err, "Error reading ../test/test-ca2-cross.pem")
noCache := "public, max-age=0, no-cache"
newSerial := "/acme/cert/0000000000000000000000000000000000b3"
newGetSerial := "/get/cert/0000000000000000000000000000000000b3"
goodSerial := "/acme/cert/0000000000000000000000000000000000b2"
notFound := `{"type":"` + probs.V2ErrorNS + `malformed","detail":"Certificate not found","status":404}`
testCases := []struct {
@ -1790,17 +1838,17 @@ func TestGetCertificate(t *testing.T) {
}{
{
Name: "Valid serial",
Request: makeGet(goodSerial),
Request: makeGet(reqPath),
ExpectedStatus: http.StatusOK,
ExpectedHeaders: map[string]string{
"Content-Type": pkixContent,
},
ExpectedCert: append(certPemBytes, append([]byte("\n"), chainPemBytes...)...),
ExpectedLink: fmt.Sprintf(`<http://localhost%s/1>;rel="alternate"`, goodSerial),
ExpectedLink: fmt.Sprintf(`<http://localhost%s/1>;rel="alternate"`, reqPath),
},
{
Name: "Valid serial, POST-as-GET",
Request: makePost(1, nil, goodSerial, ""),
Request: makePost(1, nil, reqPath, ""),
ExpectedStatus: http.StatusOK,
ExpectedHeaders: map[string]string{
"Content-Type": pkixContent,
@ -1809,7 +1857,7 @@ func TestGetCertificate(t *testing.T) {
},
{
Name: "Valid serial, bad POST-as-GET",
Request: makePost(1, nil, goodSerial, "{}"),
Request: makePost(1, nil, reqPath, "{}"),
ExpectedStatus: http.StatusBadRequest,
ExpectedBody: `{
"type": "urn:ietf:params:acme:error:malformed",
@ -1819,7 +1867,7 @@ func TestGetCertificate(t *testing.T) {
},
{
Name: "Valid serial, POST-as-GET from wrong account",
Request: makePost(2, altKey, goodSerial, ""),
Request: makePost(2, altKey, reqPath, ""),
ExpectedStatus: http.StatusForbidden,
ExpectedBody: `{
"type": "urn:ietf:params:acme:error:unauthorized",
@ -1829,20 +1877,10 @@ func TestGetCertificate(t *testing.T) {
},
{
Name: "Unused serial, no cache",
Request: makeGet("/acme/cert/0000000000000000000000000000000000ff"),
Request: makeGet("/acme/cert/000000000000000000000000000000000001"),
ExpectedStatus: http.StatusNotFound,
ExpectedBody: notFound,
},
{
Name: "Internal server error, no cache",
Request: makeGet("/acme/cert/000000000000000000000000000000626164"),
ExpectedStatus: http.StatusInternalServerError,
ExpectedBody: `{
"type": "urn:ietf:params:acme:error:serverInternal",
"status": 500,
"detail": "Failed to retrieve certificate"
}`,
},
{
Name: "Invalid serial, no cache",
Request: makeGet("/acme/cert/nothex"),
@ -1855,63 +1893,35 @@ func TestGetCertificate(t *testing.T) {
ExpectedStatus: http.StatusNotFound,
ExpectedBody: notFound,
},
{
Name: "New cert",
Request: makeGet(newGetSerial),
ExpectedStatus: http.StatusForbidden,
ExpectedBody: `{
"type": "` + probs.V2ErrorNS + `unauthorized",
"detail": "Certificate is too new for GET API. You should only use this non-standard API to access resources created more than 10s ago",
"status": 403
}`,
},
{
Name: "New cert, old endpoint",
Request: makeGet(newSerial),
ExpectedStatus: http.StatusOK,
ExpectedHeaders: map[string]string{
"Content-Type": pkixContent,
},
AnyCert: true,
},
{
Name: "New cert, POST-as-GET",
Request: makePost(1, nil, newSerial, ""),
ExpectedStatus: http.StatusOK,
ExpectedHeaders: map[string]string{
"Content-Type": pkixContent,
},
AnyCert: true,
},
{
Name: "Valid serial (explicit default chain)",
Request: makeGet(goodSerial + "/0"),
Request: makeGet(reqPath + "/0"),
ExpectedStatus: http.StatusOK,
ExpectedHeaders: map[string]string{
"Content-Type": pkixContent,
},
ExpectedLink: fmt.Sprintf(`<http://localhost%s/1>;rel="alternate"`, goodSerial),
ExpectedLink: fmt.Sprintf(`<http://localhost%s/1>;rel="alternate"`, reqPath),
ExpectedCert: append(certPemBytes, append([]byte("\n"), chainPemBytes...)...),
},
{
Name: "Valid serial (explicit alternate chain)",
Request: makeGet(goodSerial + "/1"),
Request: makeGet(reqPath + "/1"),
ExpectedStatus: http.StatusOK,
ExpectedHeaders: map[string]string{
"Content-Type": pkixContent,
},
ExpectedLink: fmt.Sprintf(`<http://localhost%s/0>;rel="alternate"`, goodSerial),
ExpectedLink: fmt.Sprintf(`<http://localhost%s/0>;rel="alternate"`, reqPath),
ExpectedCert: append(certPemBytes, append([]byte("\n"), chainCrossPemBytes...)...),
},
{
Name: "Valid serial (explicit non-existent alternate chain)",
Request: makeGet(goodSerial + "/2"),
Request: makeGet(reqPath + "/2"),
ExpectedStatus: http.StatusNotFound,
ExpectedBody: `{"type":"` + probs.V2ErrorNS + `malformed","detail":"Unknown issuance chain","status":404}`,
},
{
Name: "Valid serial (explicit negative alternate chain)",
Request: makeGet(goodSerial + "/-1"),
Request: makeGet(reqPath + "/-1"),
ExpectedStatus: http.StatusBadRequest,
ExpectedBody: `{"type":"` + probs.V2ErrorNS + `malformed","detail":"Chain ID must be a non-negative integer","status":400}`,
},
@ -1984,14 +1994,165 @@ func TestGetCertificate(t *testing.T) {
}
}
type mockSAWithNewCert struct {
core.StorageGetter
clk clock.Clock
}
func (sa *mockSAWithNewCert) GetCertificate(_ context.Context, serial string) (core.Certificate, error) {
issuer, err := core.LoadCert("../test/hierarchy/int-e1.cert.pem")
if err != nil {
return core.Certificate{}, fmt.Errorf("failed to load test issuer cert: %w", err)
}
issuerKeyPem, err := ioutil.ReadFile("../test/hierarchy/int-e1.key.pem")
if err != nil {
return core.Certificate{}, fmt.Errorf("failed to load test issuer key: %w", err)
}
issuerKey := loadKey(&testing.T{}, issuerKeyPem)
newKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return core.Certificate{}, fmt.Errorf("failed to create test key: %w", err)
}
sn, err := core.StringToSerial(serial)
if err != nil {
return core.Certificate{}, fmt.Errorf("failed to parse test serial: %w", err)
}
template := &x509.Certificate{
SerialNumber: sn,
DNSNames: []string{"new.ee.boulder.test"},
}
certDER, err := x509.CreateCertificate(rand.Reader, template, issuer, &newKey.PublicKey, issuerKey)
if err != nil {
return core.Certificate{}, fmt.Errorf("failed to issue test cert: %w", err)
}
cert, err := x509.ParseCertificate(certDER)
if err != nil {
return core.Certificate{}, fmt.Errorf("failed to parse test cert: %w", err)
}
return core.Certificate{
RegistrationID: 1,
Serial: core.SerialToString(cert.SerialNumber),
Issued: sa.clk.Now().Add(-1 * time.Second),
DER: cert.Raw,
}, nil
}
// TestGetCertificateNew tests for the case when the certificate is new (by
// dynamically generating it at test time), and therefore isn't served by the
// GET api.
func TestGetCertificateNew(t *testing.T) {
wfe, fc := setupWFE(t)
wfe.SA = &mockSAWithNewCert{wfe.SA, fc}
mux := wfe.Handler(metrics.NoopRegisterer)
makeGet := func(path string) *http.Request {
return &http.Request{URL: &url.URL{Path: path}, Method: "GET"}
}
makePost := func(keyID int64, key interface{}, path, body string) *http.Request {
_, _, jwsBody := signRequestKeyID(t, keyID, key, fmt.Sprintf("http://localhost%s", path), body, wfe.nonceService)
return makePostRequestWithPath(path, jwsBody)
}
altKey := loadKey(t, []byte(test2KeyPrivatePEM))
_, ok := altKey.(*rsa.PrivateKey)
test.Assert(t, ok, "Couldn't load RSA key")
pkixContent := "application/pem-certificate-chain"
noCache := "public, max-age=0, no-cache"
testCases := []struct {
Name string
Request *http.Request
ExpectedStatus int
ExpectedHeaders map[string]string
ExpectedBody string
}{
{
Name: "Get",
Request: makeGet("/get/cert/000000000000000000000000000000000001"),
ExpectedStatus: http.StatusForbidden,
ExpectedBody: `{
"type": "` + probs.V2ErrorNS + `unauthorized",
"detail": "Certificate is too new for GET API. You should only use this non-standard API to access resources created more than 10s ago",
"status": 403
}`,
},
{
Name: "ACME Get",
Request: makeGet("/acme/cert/000000000000000000000000000000000002"),
ExpectedStatus: http.StatusOK,
ExpectedHeaders: map[string]string{
"Content-Type": pkixContent,
},
},
{
Name: "ACME POST-as-GET",
Request: makePost(1, nil, "/acme/cert/000000000000000000000000000000000003", ""),
ExpectedStatus: http.StatusOK,
ExpectedHeaders: map[string]string{
"Content-Type": pkixContent,
},
},
}
for _, tc := range testCases {
t.Run(tc.Name, func(t *testing.T) {
responseWriter := httptest.NewRecorder()
mockLog := wfe.log.(*blog.Mock)
mockLog.Clear()
// Mux a request for a certificate
mux.ServeHTTP(responseWriter, tc.Request)
headers := responseWriter.Header()
// Assert that the status code written is as expected
test.AssertEquals(t, responseWriter.Code, tc.ExpectedStatus)
// All of the responses should have the correct cache control header
test.AssertEquals(t, headers.Get("Cache-Control"), noCache)
// If the test cases expects additional headers, check those too
for h, v := range tc.ExpectedHeaders {
test.AssertEquals(t, headers.Get(h), v)
}
// If we're expecting a particular body (because of an error), check that.
if tc.ExpectedBody != "" {
body := responseWriter.Body.String()
test.AssertUnmarshaledEquals(t, body, tc.ExpectedBody)
// Unsuccessful requests should be logged as such
reqlogs := mockLog.GetAllMatching(fmt.Sprintf(`INFO: [^ ]+ [^ ]+ [^ ]+ %d .*`, tc.ExpectedStatus))
if len(reqlogs) != 1 {
t.Errorf("Didn't find info logs with code %d. Instead got:\n%s\n",
tc.ExpectedStatus, strings.Join(mockLog.GetAllMatching(`.*`), "\n"))
}
}
})
}
}
// This uses httptest.NewServer because ServeMux.ServeHTTP won't prevent the
// body from being sent like the net/http Server's actually do.
func TestGetCertificateHEADHasCorrectBodyLength(t *testing.T) {
wfe, _ := setupWFE(t)
wfe.SA = newMockSAWithCert(t, wfe.SA, core.OCSPStatusGood)
certPemBytes, _ := ioutil.ReadFile("test/178.crt")
chainPemBytes, _ := ioutil.ReadFile("../test/test-ca2.pem")
chain := fmt.Sprintf("%s\n%s", string(chainPemBytes), string(certPemBytes))
certPemBytes, _ := ioutil.ReadFile("../test/hierarchy/ee-r3.cert.pem")
cert, err := core.LoadCert("../test/hierarchy/ee-r3.cert.pem")
test.AssertNotError(t, err, "failed to load test certificate")
chainPemBytes, err := ioutil.ReadFile("../test/hierarchy/int-r3.cert.pem")
test.AssertNotError(t, err, "Error reading ../test/hierarchy/int-r3.cert.pem")
chain := fmt.Sprintf("%s\n%s", string(certPemBytes), string(chainPemBytes))
chainLen := strconv.Itoa(len(chain))
mockLog := wfe.log.(*blog.Mock)
@ -2000,7 +2161,8 @@ func TestGetCertificateHEADHasCorrectBodyLength(t *testing.T) {
mux := wfe.Handler(metrics.NoopRegisterer)
s := httptest.NewServer(mux)
defer s.Close()
req, _ := http.NewRequest("HEAD", s.URL+"/acme/cert/0000000000000000000000000000000000b2", nil)
req, _ := http.NewRequest(
"HEAD", fmt.Sprintf("%s/acme/cert/%s", s.URL, core.SerialToString(cert.SerialNumber)), nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
test.AssertNotError(t, err, "do error")
@ -2018,6 +2180,44 @@ func TestGetCertificateHEADHasCorrectBodyLength(t *testing.T) {
test.AssertEquals(t, 0, len(body))
}
type mockSAWithError struct {
core.StorageGetter
}
func (sa *mockSAWithError) GetCertificate(_ context.Context, serial string) (core.Certificate, error) {
return core.Certificate{}, errors.New("Oops")
}
func TestGetCertificateServerError(t *testing.T) {
// TODO: add tests for failure to parse the retrieved cert, a cert whose
// IssuerNameID is unknown, and a cert whose signature can't be verified.
wfe, _ := setupWFE(t)
wfe.SA = &mockSAWithError{wfe.SA}
mux := wfe.Handler(metrics.NoopRegisterer)
cert, err := core.LoadCert("../test/hierarchy/ee-r3.cert.pem")
test.AssertNotError(t, err, "failed to load test certificate")
reqPath := fmt.Sprintf("/acme/cert/%s", core.SerialToString(cert.SerialNumber))
req := &http.Request{URL: &url.URL{Path: reqPath}, Method: "GET"}
// Mux a request for a certificate
responseWriter := httptest.NewRecorder()
mux.ServeHTTP(responseWriter, req)
test.AssertEquals(t, responseWriter.Code, http.StatusInternalServerError)
noCache := "public, max-age=0, no-cache"
test.AssertEquals(t, responseWriter.Header().Get("Cache-Control"), noCache)
body := `{
"type": "urn:ietf:params:acme:error:serverInternal",
"status": 500,
"detail": "Failed to retrieve certificate"
}`
test.AssertUnmarshaledEquals(t, responseWriter.Body.String(), body)
}
func newRequestEvent() *web.RequestEvent {
return &web.RequestEvent{Extra: make(map[string]interface{})}
}
@ -2639,7 +2839,7 @@ func TestGetOrder(t *testing.T) {
}
func makeRevokeRequestJSON(reason *revocation.Reason) ([]byte, error) {
certPemBytes, err := ioutil.ReadFile("../test/test-ee.pem")
certPemBytes, err := ioutil.ReadFile("../test/hierarchy/ee-r3.cert.pem")
if err != nil {
return nil, err
}
@ -2665,49 +2865,13 @@ func makeRevokeRequestJSONForCert(der []byte, reason *revocation.Reason) ([]byte
return revokeRequestJSON, nil
}
const testEESerial string = "000000000000000000004f6fc1b8ffe37a23"
type mockSAWithValidCert struct {
core.StorageGetter
}
// GetCertificate returns a hard-coded cert (test-ee.pem) which was issued by
// account 1 if the requested serial matches; otherwise returns not found.
func (sa *mockSAWithValidCert) GetCertificate(_ context.Context, serial string) (core.Certificate, error) {
if serial != testEESerial {
return core.Certificate{}, berrors.NotFoundError("Certificate with serial %q not found", serial)
}
cert, err := core.LoadCert("../test/test-ee.pem")
if err != nil {
return core.Certificate{}, fmt.Errorf("Failed to load test cert: %w", err)
}
return core.Certificate{
RegistrationID: 1,
Serial: core.SerialToString(cert.SerialNumber),
DER: cert.Raw,
}, nil
}
func (sa *mockSAWithValidCert) GetCertificateStatus(_ context.Context, serial string) (core.CertificateStatus, error) {
if serial != testEESerial {
return core.CertificateStatus{}, berrors.NotFoundError("Status for certificate with serial %q not found", serial)
}
return core.CertificateStatus{
Serial: testEESerial,
Status: core.OCSPStatusGood,
}, nil
}
// Valid revocation request for existing, non-revoked cert, signed with cert
// key.
func TestRevokeCertificateValid(t *testing.T) {
wfe, _ := setupWFE(t)
wfe.SA = &mockSAWithValidCert{wfe.SA}
wfe.SA = newMockSAWithCert(t, wfe.SA, core.OCSPStatusGood)
keyPemBytes, err := ioutil.ReadFile("../test/test-ee.key")
keyPemBytes, err := ioutil.ReadFile("../test/hierarchy/ee-r3.key.pem")
test.AssertNotError(t, err, "Failed to load key")
key := loadKey(t, keyPemBytes)
@ -2727,7 +2891,7 @@ func TestRevokeCertificateValid(t *testing.T) {
// wasn't issued by any issuer the Boulder is aware of.
func TestRevokeCertificateNotIssued(t *testing.T) {
wfe, _ := setupWFE(t)
wfe.SA = &mockSAWithValidCert{wfe.SA}
wfe.SA = newMockSAWithCert(t, wfe.SA, core.OCSPStatusGood)
// Make a self-signed junk certificate
k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
@ -2736,15 +2900,15 @@ func TestRevokeCertificateNotIssued(t *testing.T) {
// This ensures that any failures here are due to the certificate's issuer
// not matching up with issuers known by the mock, rather than due to the
// certificate's serial not matching up with serials known by the mock.
knownSerial, err := core.StringToSerial(testEESerial)
test.AssertNotError(t, err, "Unexpected error converting known serial to bigint")
knownCert, err := core.LoadCert("../test/hierarchy/ee-r3.cert.pem")
test.AssertNotError(t, err, "Unexpected error loading test cert")
template := &x509.Certificate{
SerialNumber: knownSerial,
SerialNumber: knownCert.SerialNumber,
}
certDER, err := x509.CreateCertificate(rand.Reader, template, template, k.Public(), k)
test.AssertNotError(t, err, "Unexpected error creating self-signed junk cert")
keyPemBytes, err := ioutil.ReadFile("../test/test-ee.key")
keyPemBytes, err := ioutil.ReadFile("../test/hierarchy/ee-r3.key.pem")
test.AssertNotError(t, err, "Failed to load key")
key := loadKey(t, keyPemBytes)
@ -2763,10 +2927,10 @@ func TestRevokeCertificateNotIssued(t *testing.T) {
func TestRevokeCertificateReasons(t *testing.T) {
wfe, _ := setupWFE(t)
wfe.SA = &mockSAWithValidCert{wfe.SA}
wfe.SA = newMockSAWithCert(t, wfe.SA, core.OCSPStatusGood)
ra := wfe.RA.(*MockRegistrationAuthority)
keyPemBytes, err := ioutil.ReadFile("../test/test-ee.key")
keyPemBytes, err := ioutil.ReadFile("../test/hierarchy/ee-r3.key.pem")
test.AssertNotError(t, err, "Failed to load key")
key := loadKey(t, keyPemBytes)
@ -2835,7 +2999,7 @@ func TestRevokeCertificateReasons(t *testing.T) {
// that issued the cert.
func TestRevokeCertificateIssuingAccount(t *testing.T) {
wfe, _ := setupWFE(t)
wfe.SA = &mockSAWithValidCert{wfe.SA}
wfe.SA = newMockSAWithCert(t, wfe.SA, core.OCSPStatusGood)
revokeRequestJSON, err := makeRevokeRequestJSON(nil)
test.AssertNotError(t, err, "Failed to make revokeRequestJSON")
@ -2853,18 +3017,16 @@ func TestRevokeCertificateIssuingAccount(t *testing.T) {
test.AssertEquals(t, responseWriter.Body.String(), "")
}
const testEEName string = "example.ee"
type mockSAWithValidAuthz struct {
core.StorageGetter
}
// GetValidAuthorizations says that all accounts have a valid authorization to
// issue for the DNS name contained in test-ee.pem
// issue for the DNS name contained in ee-r3.cert.pem
func (sa mockSAWithValidAuthz) GetValidAuthorizations2(_ context.Context, _ *sapb.GetValidAuthorizationsRequest) (*sapb.Authorizations, error) {
res := sapb.Authorizations{}
res.Authz = append(res.Authz, &sapb.Authorizations_MapElement{
Domain: testEEName,
Domain: "ee.int-r3.boulder.test",
Authz: &corepb.Authorization{},
})
return &res, nil
@ -2874,7 +3036,7 @@ func (sa mockSAWithValidAuthz) GetValidAuthorizations2(_ context.Context, _ *sap
// that has authorizations for names in cert
func TestRevokeCertificateWithAuthorizations(t *testing.T) {
wfe, _ := setupWFE(t)
wfe.SA = mockSAWithValidAuthz{&mockSAWithValidCert{wfe.SA}}
wfe.SA = mockSAWithValidAuthz{newMockSAWithCert(t, wfe.SA, core.OCSPStatusGood)}
revokeRequestJSON, err := makeRevokeRequestJSON(nil)
test.AssertNotError(t, err, "Failed to make revokeRequestJSON")
@ -2895,7 +3057,7 @@ func TestRevokeCertificateWithAuthorizations(t *testing.T) {
// A revocation request signed by an unauthorized key.
func TestRevokeCertificateWrongKey(t *testing.T) {
wfe, _ := setupWFE(t)
wfe.SA = &mockSAWithValidCert{wfe.SA}
wfe.SA = newMockSAWithCert(t, wfe.SA, core.OCSPStatusGood)
test2JWK := loadKey(t, []byte(test2KeyPrivatePEM))
@ -2914,9 +3076,9 @@ func TestRevokeCertificateWrongKey(t *testing.T) {
func TestRevokeCertificateExpired(t *testing.T) {
wfe, fc := setupWFE(t)
wfe.SA = &mockSAWithValidCert{wfe.SA}
wfe.SA = newMockSAWithCert(t, wfe.SA, core.OCSPStatusGood)
keyPemBytes, err := ioutil.ReadFile("../test/test-ee.key")
keyPemBytes, err := ioutil.ReadFile("../test/hierarchy/ee-r3.key.pem")
test.AssertNotError(t, err, "Failed to load key")
key := loadKey(t, keyPemBytes)
@ -2926,7 +3088,7 @@ func TestRevokeCertificateExpired(t *testing.T) {
_, _, jwsBody := signRequestEmbed(
t, key, "http://localhost/revoke-cert", string(revokeRequestJSON), wfe.nonceService)
cert, err := core.LoadCert("../test/test-ee.pem")
cert, err := core.LoadCert("../test/hierarchy/ee-r3.cert.pem")
test.AssertNotError(t, err, "Failed to load test certificate")
fc.Set(cert.NotAfter.Add(time.Hour))
@ -2938,29 +3100,14 @@ func TestRevokeCertificateExpired(t *testing.T) {
test.AssertEquals(t, responseWriter.Body.String(), "{\n \"type\": \"urn:ietf:params:acme:error:unauthorized\",\n \"detail\": \"Certificate is expired\",\n \"status\": 403\n}")
}
type mockSAWithRevokedCert struct {
core.StorageGetter
}
func (sa *mockSAWithRevokedCert) GetCertificateStatus(_ context.Context, serial string) (core.CertificateStatus, error) {
if serial != testEESerial {
return core.CertificateStatus{}, berrors.NotFoundError("Status for certificate with serial %q not found", serial)
}
return core.CertificateStatus{
Serial: testEESerial,
Status: core.OCSPStatusRevoked,
}, nil
}
// Valid revocation request for already-revoked cert
func TestRevokeCertificateAlreadyRevoked(t *testing.T) {
wfe, _ := setupWFE(t)
wfe.SA = &mockSAWithRevokedCert{&mockSAWithValidCert{wfe.SA}}
wfe.SA = newMockSAWithCert(t, wfe.SA, core.OCSPStatusRevoked)
responseWriter := httptest.NewRecorder()
keyPemBytes, err := ioutil.ReadFile("../test/test-ee.key")
keyPemBytes, err := ioutil.ReadFile("../test/hierarchy/ee-r3.key.pem")
test.AssertNotError(t, err, "Failed to load key")
key := loadKey(t, keyPemBytes)
@ -3320,6 +3467,8 @@ func TestGETAPIChallenge(t *testing.T) {
func TestGetAPIAndMandatoryPOSTAsGET(t *testing.T) {
wfe, _ := setupWFE(t)
wfe.SA = newMockSAWithCert(t, wfe.SA, core.OCSPStatusGood)
makeGet := func(path, endpoint string) (*http.Request, *web.RequestEvent) {
return &http.Request{URL: &url.URL{Path: path}, Method: "GET"},
&web.RequestEvent{Endpoint: endpoint, Extra: map[string]interface{}{}}
@ -3327,8 +3476,10 @@ func TestGetAPIAndMandatoryPOSTAsGET(t *testing.T) {
_ = features.Set(map[string]bool{"MandatoryPOSTAsGET": true})
defer features.Reset()
oldSerial := "0000000000000000000000000000000000b2"
req, event := makeGet(oldSerial, getCertPath)
cert, err := core.LoadCert("../test/hierarchy/ee-r3.cert.pem")
test.AssertNotError(t, err, "failed to load test certificate")
req, event := makeGet(core.SerialToString(cert.SerialNumber), getCertPath)
resp := httptest.NewRecorder()
wfe.Certificate(context.Background(), event, resp, req)
test.AssertEquals(t, resp.Code, 200)