Add a utility for building X.509 chains
This commit is contained in:
parent
401928b5aa
commit
bbc3d5af82
|
@ -0,0 +1,393 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
_ "embed"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Certificate struct {
|
||||||
|
Store *x509.Certificate
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadCertFromDER(der []byte) (Certificate, error) {
|
||||||
|
cert := Certificate { Store: nil }
|
||||||
|
store, err := x509.ParseCertificate(der)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return cert, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cert.Store = store
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadCertFromPEM(pem string) (Certificate, error) {
|
||||||
|
cert := Certificate { Store: nil }
|
||||||
|
|
||||||
|
if ! strings.HasPrefix(pem, "-----BEGIN CERTIFICATE-----") {
|
||||||
|
return cert, errors.New("invalid certificate header")
|
||||||
|
} else if ! strings.HasSuffix(pem, "-----END CERTIFICATE-----") {
|
||||||
|
return cert, errors.New("invalid certificate footer")
|
||||||
|
}
|
||||||
|
|
||||||
|
encoded := strings.Split(pem, "\n")
|
||||||
|
data := strings.Join(encoded[1:len(encoded) - 1], "\n")
|
||||||
|
bytes, err := base64.StdEncoding.DecodeString(data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return cert, err
|
||||||
|
}
|
||||||
|
|
||||||
|
store, err := x509.ParseCertificate(bytes)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return cert, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cert.Store = store
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cert Certificate) ToPEM() string {
|
||||||
|
pem_export := "-----BEGIN CERTIFICATE-----\n"
|
||||||
|
encoded := base64.StdEncoding.EncodeToString(cert.Store.Raw)
|
||||||
|
idx := 0
|
||||||
|
|
||||||
|
for idx < len(encoded) {
|
||||||
|
if idx + 64 < len(encoded) {
|
||||||
|
pem_export += encoded[idx:idx + 64] + "\n"
|
||||||
|
} else {
|
||||||
|
pem_export += encoded[idx:] + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
idx += 64
|
||||||
|
}
|
||||||
|
|
||||||
|
pem_export += "-----END CERTIFICATE-----"
|
||||||
|
return pem_export
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cert Certificate) IsSelfSigned() bool {
|
||||||
|
err := cert.Store.CheckSignatureFrom(cert.Store)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cert Certificate) IsRoot() bool {
|
||||||
|
return cert.IsSelfSigned() && cert.Store.IsCA
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cert Certificate) IsIntermediate() bool {
|
||||||
|
err := cert.Store.CheckSignatureFrom(cert.Store)
|
||||||
|
return err == nil && cert.Store.IsCA
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cert Certificate) IsLeaf() bool {
|
||||||
|
if cert.Store.IsCA {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
allowed_uses := []x509.ExtKeyUsage {
|
||||||
|
x509.ExtKeyUsageAny, x509.ExtKeyUsageServerAuth,
|
||||||
|
x509.ExtKeyUsageClientAuth,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, use := range cert.Store.ExtKeyUsage {
|
||||||
|
good_use := false
|
||||||
|
|
||||||
|
for _, allowed := range allowed_uses {
|
||||||
|
if use == allowed {
|
||||||
|
good_use = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! good_use {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cert Certificate) GetHTTPAuthorityURL() (string, bool) {
|
||||||
|
for _, url := range cert.Store.IssuingCertificateURL {
|
||||||
|
if strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") {
|
||||||
|
return url, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cert Certificate) Parent(parent Certificate) bool {
|
||||||
|
return cert.Store.CheckSignatureFrom(parent.Store) == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CertificateChain struct {
|
||||||
|
Certificates []Certificate
|
||||||
|
Leaf, Root *Certificate
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadCertsFromPEM(pem string) (CertificateChain, error) {
|
||||||
|
chain := CertificateChain {
|
||||||
|
Certificates: []Certificate { }, Leaf: nil, Root: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
parser, err := regexp.Compile("-----BEGIN CERTIFICATE-----\r?\n([A-Za-z0-9/+=\r\n]+)\r?\n-----END CERTIFICATE-----")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return chain, err
|
||||||
|
}
|
||||||
|
|
||||||
|
matches := parser.FindAllStringSubmatch(string(pem), -1)
|
||||||
|
|
||||||
|
for _, match := range matches {
|
||||||
|
cert, err := LoadCertFromPEM(match[0])
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return chain, err
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.Certificates = append(chain.Certificates, cert)
|
||||||
|
chain.Root = &chain.Certificates[len(chain.Certificates) - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(chain.Certificates) == 0 {
|
||||||
|
return chain, errors.New("no certificates found")
|
||||||
|
}
|
||||||
|
|
||||||
|
chain.Leaf = &chain.Certificates[0]
|
||||||
|
return chain, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:embed roots.pem
|
||||||
|
var BAKED_IN_ROOTS string
|
||||||
|
|
||||||
|
func LoadSystemStore() (CertificateChain, error) {
|
||||||
|
cert_path := ""
|
||||||
|
|
||||||
|
if path, has := os.LookupEnv("SSL_CERT_FILE"); has {
|
||||||
|
cert_path = path
|
||||||
|
} else if runtime.GOOS == "linux" {
|
||||||
|
cert_path = "/etc/ssl/certs/ca-certificates.crt"
|
||||||
|
} else if runtime.GOOS == "darwin" {
|
||||||
|
cert_path = "/etc/ssl/cert.pem"
|
||||||
|
}
|
||||||
|
|
||||||
|
if cert_path == "" {
|
||||||
|
return LoadCertsFromPEM(BAKED_IN_ROOTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
pem, err := os.ReadFile(cert_path)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return CertificateChain { }, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return LoadCertsFromPEM(string(pem))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (chain CertificateChain) ToPEM() string {
|
||||||
|
pem_export := ""
|
||||||
|
|
||||||
|
for _, link := range chain.Certificates {
|
||||||
|
pem_export += link.ToPEM() + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
return pem_export
|
||||||
|
}
|
||||||
|
|
||||||
|
func (chain *CertificateChain) Prepend(cert Certificate) {
|
||||||
|
chain.Certificates = append([]Certificate { cert }, chain.Certificates...)
|
||||||
|
chain.Leaf = &chain.Certificates[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (chain *CertificateChain) Append(cert Certificate) {
|
||||||
|
chain.Certificates = append(chain.Certificates, cert)
|
||||||
|
chain.Root = &chain.Certificates[len(chain.Certificates) - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (chain CertificateChain) IsLinked() bool {
|
||||||
|
for idx, link := range chain.Certificates[1:] {
|
||||||
|
err := chain.Certificates[idx].Store.CheckSignatureFrom(link.Store)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (chain CertificateChain) IsComplete(test_links ...bool) bool {
|
||||||
|
if len(test_links) > 0 && test_links[0] {
|
||||||
|
return chain.Root.IsRoot() && chain.Leaf.IsLeaf() && chain.IsLinked()
|
||||||
|
} else {
|
||||||
|
return chain.Root.IsRoot() && chain.Leaf.IsLeaf()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (chain CertificateChain) Links() int {
|
||||||
|
return len(chain.Certificates)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
has_sys_pool := true
|
||||||
|
sys_pool, err := LoadSystemStore()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "error: unable to load system pool: %s\n", err)
|
||||||
|
has_sys_pool = false
|
||||||
|
}
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
args := flag.Args()
|
||||||
|
|
||||||
|
for _, arg := range args {
|
||||||
|
pem, err := os.ReadFile(arg)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: error: %s\n", arg, err)
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: skipping file...\n", arg)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
chain, err := LoadCertsFromPEM(string(pem))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: error: %s\n", arg, err)
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: skipping file...\n", arg)
|
||||||
|
continue
|
||||||
|
} else if chain.Leaf.IsSelfSigned() {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: certificate is self-signed\n", arg)
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: skipping file...\n", arg)
|
||||||
|
continue
|
||||||
|
} else if ! chain.IsLinked() {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: error: certificates do not form a chain\n", arg)
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: skipping file...\n", arg)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for ! chain.Root.IsRoot() {
|
||||||
|
found_link := false
|
||||||
|
aia_url, found_url := chain.Root.GetHTTPAuthorityURL()
|
||||||
|
|
||||||
|
if found_url {
|
||||||
|
res, err := http.Get(aia_url)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: error: %s\n", arg, err)
|
||||||
|
|
||||||
|
if ! has_sys_pool {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: attempting alternate chain discovery methods\n", arg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cert_data, err := io.ReadAll(res.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: error: %s\n", arg, err)
|
||||||
|
|
||||||
|
if ! has_sys_pool {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: attempting alternate chain discovery methods\n", arg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cert := Certificate { Store: nil }
|
||||||
|
mime := "der"
|
||||||
|
|
||||||
|
switch res.Header.Get("content-type") {
|
||||||
|
case "application/pkix-cer": mime = "der"
|
||||||
|
case "application/pkix-cert": mime = "der"
|
||||||
|
case "application/x-x509-ca-cert": mime = "der"
|
||||||
|
case "application/x-pem-file": mime = "pem"
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(aia_url, ".der") || mime == "der" {
|
||||||
|
link, err := LoadCertFromDER(cert_data)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: error: %s\n", arg, err)
|
||||||
|
|
||||||
|
if ! has_sys_pool {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: attempting alternate chain discovery methods\n", arg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cert = link
|
||||||
|
found_link = true
|
||||||
|
}
|
||||||
|
} else if strings.HasSuffix(aia_url, ".pem") || mime == "pem" {
|
||||||
|
link, err := LoadCertFromPEM(string(cert_data))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: error: %s\n", arg, err)
|
||||||
|
|
||||||
|
if ! has_sys_pool {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: attempting alternate chain discovery methods\n", arg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cert = link
|
||||||
|
found_link = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found_link {
|
||||||
|
chain.Append(cert)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: unable to determine link file type\n", arg)
|
||||||
|
|
||||||
|
if ! has_sys_pool {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: attempting alternate chain discovery methods\n", arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Body.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! found_link && has_sys_pool {
|
||||||
|
for _, sys_cert := range sys_pool.Certificates {
|
||||||
|
if chain.Root.Parent(sys_cert) {
|
||||||
|
chain.Append(sys_cert)
|
||||||
|
found_link = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! found_link {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if chain.IsComplete(true) {
|
||||||
|
file, err := ioutil.TempFile("", "*-chain.pem")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: error: %s\n", arg, err)
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: skipping file...\n", arg)
|
||||||
|
} else {
|
||||||
|
if _, err := file.Write([]byte(chain.ToPEM())); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: error: %s\n", arg, err)
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: skipping file...\n", arg)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s: complete chain has been saved to %s\n", arg, file.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
file.Close()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: unable to determine next link in chain\n", arg)
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: skipping file...\n", arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
|
||||||
|
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT
|
||||||
|
EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp
|
||||||
|
ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz
|
||||||
|
NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH
|
||||||
|
EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE
|
||||||
|
AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw
|
||||||
|
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD
|
||||||
|
E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH
|
||||||
|
/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy
|
||||||
|
DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh
|
||||||
|
GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR
|
||||||
|
tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA
|
||||||
|
AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
|
||||||
|
FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX
|
||||||
|
WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu
|
||||||
|
9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr
|
||||||
|
gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo
|
||||||
|
2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
|
||||||
|
LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI
|
||||||
|
4uJEvlz36hz1
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
|
||||||
|
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||||
|
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
||||||
|
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
|
||||||
|
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
||||||
|
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
|
||||||
|
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
|
||||||
|
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
|
||||||
|
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
|
||||||
|
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
|
||||||
|
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
|
||||||
|
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
|
||||||
|
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
|
||||||
|
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
|
||||||
|
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
|
||||||
|
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
|
||||||
|
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
|
||||||
|
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
|
||||||
|
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
||||||
|
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||||
|
+OkuE6N36B9K
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
||||||
|
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||||
|
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
||||||
|
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
||||||
|
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
||||||
|
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
||||||
|
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
||||||
|
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
||||||
|
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
||||||
|
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
||||||
|
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
||||||
|
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
||||||
|
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
||||||
|
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
||||||
|
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
||||||
|
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
||||||
|
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
||||||
|
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
||||||
|
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
||||||
|
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
||||||
|
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
||||||
|
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
||||||
|
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
||||||
|
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
||||||
|
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
||||||
|
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
||||||
|
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
||||||
|
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
||||||
|
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF
|
||||||
|
ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
|
||||||
|
b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL
|
||||||
|
MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv
|
||||||
|
b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj
|
||||||
|
ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM
|
||||||
|
9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw
|
||||||
|
IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6
|
||||||
|
VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L
|
||||||
|
93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm
|
||||||
|
jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
|
||||||
|
AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA
|
||||||
|
A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI
|
||||||
|
U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs
|
||||||
|
N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv
|
||||||
|
o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU
|
||||||
|
5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy
|
||||||
|
rqXRfboQnoZsG4q5WTP468SQvvG5
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
|
||||||
|
RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
|
||||||
|
VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
|
||||||
|
DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
|
||||||
|
ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
|
||||||
|
VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
|
||||||
|
mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
|
||||||
|
IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
|
||||||
|
mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
|
||||||
|
XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
|
||||||
|
dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
|
||||||
|
jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
|
||||||
|
BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
|
||||||
|
DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
|
||||||
|
9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
|
||||||
|
jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
|
||||||
|
Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
|
||||||
|
ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
|
||||||
|
R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC
|
||||||
|
VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T
|
||||||
|
U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp
|
||||||
|
Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx
|
||||||
|
NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv
|
||||||
|
dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv
|
||||||
|
bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49
|
||||||
|
AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA
|
||||||
|
VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku
|
||||||
|
WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP
|
||||||
|
MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX
|
||||||
|
5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ
|
||||||
|
ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg
|
||||||
|
h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg==
|
||||||
|
-----END CERTIFICATE-----
|
Loading…
Reference in New Issue