Skip to content

Commit

Permalink
Produce serial number selector in x509pop NodeAttestor (spiffe#4216)
Browse files Browse the repository at this point in the history
* Produce serial number selector in x509pop NodeAttestor

The serial number of an X.509 certificate is supposed to be unique and
may be a useful way to organize authorized workload entries.

Signed-off-by: Ryan Turner <turner@uber.com>

* Represent serial number as hex

Signed-off-by: Ryan Turner <turner@uber.com>

* Document x509pop serialnumber selector and Agent SPIFFE ID path template parameter

Also remove mention of Subject.CommonName as a path template field, as
it is different from the certificate serial number.

Signed-off-by: Ryan Turner <turner@uber.com>

* Fix agent ID path template field name

Signed-off-by: Ryan Turner <turner@uber.com>

---------

Signed-off-by: Ryan Turner <turner@uber.com>
Co-authored-by: Evan Gilman <evan@spirl.com>
Signed-off-by: Neniel <11655196+Neniel@users.noreply.github.com>
  • Loading branch information
2 people authored and Neniel committed Aug 24, 2023
1 parent ddb1a4e commit 2355c85
Show file tree
Hide file tree
Showing 10 changed files with 95 additions and 74 deletions.
15 changes: 8 additions & 7 deletions doc/plugin_server_nodeattestor_x509pop.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ A sample configuration:
|------------------|-------------------------------------------------------------------|------------------------------------------------------------------------------------------|
| Common Name | `x509pop:subject:cn:example.org` | The Subject's Common Name (see X.500 Distinguished Names) |
| SHA1 Fingerprint | `x509pop:ca:fingerprint:0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33` | The SHA1 fingerprint as a hex string for each cert in the PoP chain, excluding the leaf. |
| SerialNumber | `x509pop:serialnumber:0a1b2c3d4e5f` | The leaf certificate serial number as a lowercase hexadecimal string |

## Agent Path Template

Expand All @@ -49,10 +50,10 @@ The template formatter is using Golang text/template conventions, it can referen

Some useful values are:

| Value | Description |
|-----------------------|---------------------------------------------------------|
| .PluginName | The name of the plugin |
| .Fingerprint | The SHA1 fingerprint of the agent's x509 certificate |
| .TrustDomain | The configured trust domain |
| .Subject.CommonName | The common name field of the agent's x509 certificate |
| .Subject.SerialNumber | The serial number field of the agent's x509 certificate |
| Value | Description |
|-----------------------|----------------------------------------------------------------------------------------------|
| .PluginName | The name of the plugin |
| .Fingerprint | The SHA1 fingerprint of the agent's x509 certificate |
| .TrustDomain | The configured trust domain |
| .Subject.CommonName | The common name field of the agent's x509 certificate |
| .SerialNumberHex | The serial number field of the agent's x509 certificate represented as lowercase hexadecimal |
26 changes: 20 additions & 6 deletions pkg/common/plugin/x509pop/x509pop.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ var DefaultAgentPathTemplate = agentpathtemplate.MustParse("/{{ .PluginName }}/{

type agentPathTemplateData struct {
*x509.Certificate
Fingerprint string
PluginName string
TrustDomain string
SerialNumberHex string
Fingerprint string
PluginName string
TrustDomain string
}

type AttestationData struct {
Expand Down Expand Up @@ -267,9 +268,10 @@ func Fingerprint(cert *x509.Certificate) string {
// MakeAgentID creates an agent ID from X.509 certificate data.
func MakeAgentID(td spiffeid.TrustDomain, agentPathTemplate *agentpathtemplate.Template, cert *x509.Certificate) (spiffeid.ID, error) {
agentPath, err := agentPathTemplate.Execute(agentPathTemplateData{
Certificate: cert,
PluginName: PluginName,
Fingerprint: Fingerprint(cert),
Certificate: cert,
PluginName: PluginName,
SerialNumberHex: SerialNumberHex(cert.SerialNumber),
Fingerprint: Fingerprint(cert),
})
if err != nil {
return spiffeid.ID{}, err
Expand All @@ -278,6 +280,18 @@ func MakeAgentID(td spiffeid.TrustDomain, agentPathTemplate *agentpathtemplate.T
return idutil.AgentID(td, agentPath)
}

// SerialNumberHex returns a certificate serial number represented as lowercase hexadecimal with an even number of characters
func SerialNumberHex(serialNumber *big.Int) string {
serialHex := fmt.Sprintf("%x", serialNumber)
if len(serialHex)%2 == 1 {
// Append leading 0 in cases where hexadecimal representation is odd number of characters
// in order to be more consistent with other tooling that displays certificate serial numbers.
serialHex = "0" + serialHex
}

return serialHex
}

func generateNonce() ([]byte, error) {
b := make([]byte, nonceLen)
if _, err := rand.Read(b); err != nil {
Expand Down
7 changes: 6 additions & 1 deletion pkg/server/plugin/nodeattestor/x509pop/x509pop.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ func (p *Plugin) setConfiguration(config *configuration) {
}

func buildSelectorValues(leaf *x509.Certificate, chains [][]*x509.Certificate) []string {
selectorValues := []string{}
var selectorValues []string

if leaf.Subject.CommonName != "" {
selectorValues = append(selectorValues, "subject:cn:"+leaf.Subject.CommonName)
Expand All @@ -263,5 +263,10 @@ func buildSelectorValues(leaf *x509.Certificate, chains [][]*x509.Certificate) [
}
}

if leaf.SerialNumber != nil {
serialNumberHex := x509pop.SerialNumberHex(leaf.SerialNumber)
selectorValues = append(selectorValues, "serialnumber:"+serialNumberHex)
}

return selectorValues
}
5 changes: 3 additions & 2 deletions pkg/server/plugin/nodeattestor/x509pop/x509pop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ func (s *Suite) TestAttestSuccess() {
},
{
desc: "success with custom agent id (ca_bundle_paths)",
expectAgentID: "spiffe://example.org/spire/agent/cn/COMMONNAME",
giveConfig: s.createConfiguration("ca_bundle_paths", `agent_path_template = "/cn/{{ .Subject.CommonName }}"`),
expectAgentID: "spiffe://example.org/spire/agent/serialnumber/0a1b2c3d4e5f",
giveConfig: s.createConfiguration("ca_bundle_paths", `agent_path_template = "/serialnumber/{{ .SerialNumberHex }}"`),
},
}

Expand Down Expand Up @@ -124,6 +124,7 @@ func (s *Suite) TestAttestSuccess() {
{Type: "x509pop", Value: "subject:cn:COMMONNAME"},
{Type: "x509pop", Value: "ca:fingerprint:" + x509pop.Fingerprint(s.intermediateCert)},
{Type: "x509pop", Value: "ca:fingerprint:" + x509pop.Fingerprint(s.rootCert)},
{Type: "x509pop", Value: "serialnumber:0a1b2c3d4e5f"},
}, result.Selectors)
})
}
Expand Down
10 changes: 5 additions & 5 deletions test/fixture/nodeattestor/x509pop/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func main() {
rootKey := generateRSAKey()

rootCert := createRootCertificate(rootKey, &x509.Certificate{
SerialNumber: big.NewInt(1),
SerialNumber: big.NewInt(0x1a2b3c),
BasicConstraintsValid: true,
IsCA: true,
NotAfter: neverExpires,
Expand All @@ -34,7 +34,7 @@ func main() {
intermediateKey := generateRSAKey()

intermediateCert := createCertificate(intermediateKey, &x509.Certificate{
SerialNumber: big.NewInt(1),
SerialNumber: big.NewInt(0x4d5e6f),
BasicConstraintsValid: true,
IsCA: true,
NotAfter: neverExpires,
Expand All @@ -43,7 +43,7 @@ func main() {
leafKey := generateRSAKey()

leafCert := createCertificate(leafKey, &x509.Certificate{
SerialNumber: big.NewInt(1),
SerialNumber: big.NewInt(0x0a1b2c3d4e5f),
KeyUsage: x509.KeyUsageDigitalSignature,
NotAfter: neverExpires,
Subject: pkix.Name{CommonName: "COMMONNAME"},
Expand Down Expand Up @@ -81,7 +81,7 @@ func writeKey(path string, key interface{}) {
Type: "PRIVATE KEY",
Bytes: keyBytes,
})
err = os.WriteFile(path, pemBytes, 0600)
err = os.WriteFile(path, pemBytes, 0o600)
panice(err)
}

Expand All @@ -94,6 +94,6 @@ func writeCerts(path string, certs ...*x509.Certificate) {
})
panice(err)
}
err := os.WriteFile(path, data.Bytes(), 0600)
err := os.WriteFile(path, data.Bytes(), 0o600)
panice(err)
}
16 changes: 8 additions & 8 deletions test/fixture/nodeattestor/x509pop/intermediate.pem
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBZjCB8aADAgECAgEBMA0GCSqGSIb3DQEBCwUAMAAwIhgPMDAwMTAxMDEwMDAw
MDBaGA85OTk5MTIzMTIzNTk1OVowADB8MA0GCSqGSIb3DQEBAQUAA2sAMGgCYQDW
UpS68MTlVPbSNdjicX80KYFmK5CMA8nQs1EXEXAdwWuKzMy7obvTu7bIqbqLJQvi
PfNwXu986tg/s51PeoJ9UoUSf5bJqPW05tYEDSiqOQsf21Snjp3cxZIC/+pLA28C
AwEAAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUYbZxH0x8WrcY9r++
pEyVwL48W2MwDQYJKoZIhvcNAQELBQADYQCoXQg2nK51+w5bEyg+qa0N+sWkyYYq
S/GR/YnJuPAVsBZ4pug/ggkdHtrb3g4fPlCNseTFyJYbYcXl+DqW6sJp8ZhnrmYP
y7Pj902GdgJRpzg0DrSttqT8AJvMBXDccK8=
MIIBaDCB86ADAgECAgNNXm8wDQYJKoZIhvcNAQELBQAwADAiGA8wMDAxMDEwMTAw
MDAwMFoYDzk5OTkxMjMxMjM1OTU5WjAAMHwwDQYJKoZIhvcNAQEBBQADawAwaAJh
AMq9BNinPfs3XWJzJ1qmxB93CaXA+sMD/6A+cXHfTkxwDcL/CvSsJckwkeDOM1qk
3w+1Jqap/MrGn73o4UIbAGFP+pAExNkZzMSebTEIntHeel7NkOI5G7rGLV8MzTcj
awIDAQABozIwMDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSndp4TSJoEojHy
FmZ0eFSq0PbubDANBgkqhkiG9w0BAQsFAANhAJxFRW6nM2qH0N/NhjzU0ijkDOIW
1VgsBtCV8YZSA2dWJ3mEyyB9PtYdfNL2269pDk5tsFyUr3VZSpmB7DclThhEwcnR
6SEUoqpy7HkeAK8OOQIr3OF/RymPye+Evg/xeQ==
-----END CERTIFICATE-----
34 changes: 17 additions & 17 deletions test/fixture/nodeattestor/x509pop/leaf-crt-bundle.pem
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIBfTCCAQegAwIBAgIBATANBgkqhkiG9w0BAQsFADAAMCIYDzAwMDEwMTAxMDAw
MDAwWhgPOTk5OTEyMzEyMzU5NTlaMBUxEzARBgNVBAMTCkNPTU1PTk5BTUUwfDAN
BgkqhkiG9w0BAQEFAANrADBoAmEArRwxAq+ajkNS7mJ+ia8GSV/+5HzmHHmLQuoD
P6iNw4AMtYZFV01Dw4aphMfstRasoRnfxKCTkfqQAZkUkDZ1jQdS+QDGvscKQ+mR
PKckkDTopeCp1WGTc3WEUB5Q7DJvAgMBAAGjMzAxMA4GA1UdDwEB/wQEAwIHgDAf
BgNVHSMEGDAWgBRhtnEfTHxatxj2v76kTJXAvjxbYzANBgkqhkiG9w0BAQsFAANh
ALDVUEceVCdPUsWjbXOIB64tO4sVQk4eIH974nXG5HPuMW8f2CwnE2Fg0dirEose
y7l11jo5zbpsk7YXBE34B93ds5Ofcte2reUatEhh44yWC+FN2atelYK1hh/FpMBC
GQ==
MIIBgjCCAQygAwIBAgIGChssPU5fMA0GCSqGSIb3DQEBCwUAMAAwIhgPMDAwMTAx
MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowFTETMBEGA1UEAxMKQ09NTU9OTkFN
RTB8MA0GCSqGSIb3DQEBAQUAA2sAMGgCYQDgxdLNdlVhNRNEcR5n9GdrNDXFV7CU
uE/XgXM5na9pOxeNSWTZ/StahYJONcNyLw8ZbiUREuHyx5lkNtFF5JcW3o1RoNBD
HFviyuJf7WUgDTBTctqQp/Y2q4uQPle92N8CAwEAAaMzMDEwDgYDVR0PAQH/BAQD
AgeAMB8GA1UdIwQYMBaAFKd2nhNImgSiMfIWZnR4VKrQ9u5sMA0GCSqGSIb3DQEB
CwUAA2EAr1WNJKb8H1pkPrMzzRLerF0QAK+/wal7OacRQ4x9pxl+rA+0bwgfsA0x
jSR/u6Jx83ASGMmuNLCnizqcNQtcj2hr1yPr5cPHFxjuISBPirNrkCONymnbhDU4
RYoeKuK/
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIBZjCB8aADAgECAgEBMA0GCSqGSIb3DQEBCwUAMAAwIhgPMDAwMTAxMDEwMDAw
MDBaGA85OTk5MTIzMTIzNTk1OVowADB8MA0GCSqGSIb3DQEBAQUAA2sAMGgCYQDW
UpS68MTlVPbSNdjicX80KYFmK5CMA8nQs1EXEXAdwWuKzMy7obvTu7bIqbqLJQvi
PfNwXu986tg/s51PeoJ9UoUSf5bJqPW05tYEDSiqOQsf21Snjp3cxZIC/+pLA28C
AwEAAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUYbZxH0x8WrcY9r++
pEyVwL48W2MwDQYJKoZIhvcNAQELBQADYQCoXQg2nK51+w5bEyg+qa0N+sWkyYYq
S/GR/YnJuPAVsBZ4pug/ggkdHtrb3g4fPlCNseTFyJYbYcXl+DqW6sJp8ZhnrmYP
y7Pj902GdgJRpzg0DrSttqT8AJvMBXDccK8=
MIIBaDCB86ADAgECAgNNXm8wDQYJKoZIhvcNAQELBQAwADAiGA8wMDAxMDEwMTAw
MDAwMFoYDzk5OTkxMjMxMjM1OTU5WjAAMHwwDQYJKoZIhvcNAQEBBQADawAwaAJh
AMq9BNinPfs3XWJzJ1qmxB93CaXA+sMD/6A+cXHfTkxwDcL/CvSsJckwkeDOM1qk
3w+1Jqap/MrGn73o4UIbAGFP+pAExNkZzMSebTEIntHeel7NkOI5G7rGLV8MzTcj
awIDAQABozIwMDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSndp4TSJoEojHy
FmZ0eFSq0PbubDANBgkqhkiG9w0BAQsFAANhAJxFRW6nM2qH0N/NhjzU0ijkDOIW
1VgsBtCV8YZSA2dWJ3mEyyB9PtYdfNL2269pDk5tsFyUr3VZSpmB7DclThhEwcnR
6SEUoqpy7HkeAK8OOQIr3OF/RymPye+Evg/xeQ==
-----END CERTIFICATE-----
22 changes: 11 additions & 11 deletions test/fixture/nodeattestor/x509pop/leaf-key.pem
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
-----BEGIN PRIVATE KEY-----
MIIB5AIBADANBgkqhkiG9w0BAQEFAASCAc4wggHKAgEAAmEArRwxAq+ajkNS7mJ+
ia8GSV/+5HzmHHmLQuoDP6iNw4AMtYZFV01Dw4aphMfstRasoRnfxKCTkfqQAZkU
kDZ1jQdS+QDGvscKQ+mRPKckkDTopeCp1WGTc3WEUB5Q7DJvAgMBAAECYERzwGGa
hNg6gQGoyvaw0iCTqBw701ZxLYNRO+WhRiWHbf8d0C+cau5XQpMx50UYaJX3389u
RQbWO5Z9zNcgSRAuLZuXvDzOeOtV3OPGjfRhhXVpJwYp461XaUbKDs/PEQIxAOMg
O8sicHBmrC9g94pjniApVP2gA4McjwE2NIngSk9W6urSGfmN3kbfZJWfDNokyQIx
AMMeBmkKFKh+t1niz0ezpLv9u9F/9d/g1d4ZxQ7PVLeXblNvkGOWXTbUWiC+4xXR
dwIwV9NZC72veOda4Z8/WVYYCRuyb7h2Yzah3bgWLNJ8KZ3UjSTdQnCnaQRtIqY0
FA5xAjA7JrMSzPeOTTwQh+4G8rMSOs3hqUVE5chwWKeg4cHxFntf2AaR6le/84iP
PM9jIekCMQDJu/Jmcth5PotCT4eE5bPsOYuWMhYyWkYIprP6uTKhURFJf3pxVgGt
8ApA517OF6w=
MIIB5QIBADANBgkqhkiG9w0BAQEFAASCAc8wggHLAgEAAmEA4MXSzXZVYTUTRHEe
Z/RnazQ1xVewlLhP14FzOZ2vaTsXjUlk2f0rWoWCTjXDci8PGW4lERLh8seZZDbR
ReSXFt6NUaDQQxxb4sriX+1lIA0wU3LakKf2NquLkD5XvdjfAgMBAAECYQDQw0ug
Iwj3qPkFN177REeNjUdny6Mo7erSCNxBJhCWVxjv07wErxxGDfxRsaOeeM76uIdK
GDDC24ehhw67EKXG+ZR6yjOtCmBoSSg0irCa02bVolEZKGd/oNuDv2O7u/kCMQDs
rPvXsTSs3wSaow+HxiCg0q79LEj7n56RD5f36cOh+9FFR6lTnq90YNCkenPmep0C
MQDzIArz5XKqa6wngoDz13l4Rh7WELLe8L/H8uFW5a9sh6N2NtbVcTTKtdiMRiLO
GqsCMER6YHut6AHEnT5ow4uTRcmCa+MQtyUmr+hkzKGMX3A0vf3/l/x7w2Q2xudw
Dqy2+QIxAL1M8bo9qS5wb7bDskwqNfYkAtaCYgOcSHcS8cmLRS+n3OqqgFPotUYw
mM9/vJHlyQIwAoq+1Ydsmb3VBwhjVOCVPtNgACcfTSZkv/wQQUUGsLZxMxjTLKeG
EvB1EDwioRiE
-----END PRIVATE KEY-----
18 changes: 9 additions & 9 deletions test/fixture/nodeattestor/x509pop/leaf.pem
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBfTCCAQegAwIBAgIBATANBgkqhkiG9w0BAQsFADAAMCIYDzAwMDEwMTAxMDAw
MDAwWhgPOTk5OTEyMzEyMzU5NTlaMBUxEzARBgNVBAMTCkNPTU1PTk5BTUUwfDAN
BgkqhkiG9w0BAQEFAANrADBoAmEArRwxAq+ajkNS7mJ+ia8GSV/+5HzmHHmLQuoD
P6iNw4AMtYZFV01Dw4aphMfstRasoRnfxKCTkfqQAZkUkDZ1jQdS+QDGvscKQ+mR
PKckkDTopeCp1WGTc3WEUB5Q7DJvAgMBAAGjMzAxMA4GA1UdDwEB/wQEAwIHgDAf
BgNVHSMEGDAWgBRhtnEfTHxatxj2v76kTJXAvjxbYzANBgkqhkiG9w0BAQsFAANh
ALDVUEceVCdPUsWjbXOIB64tO4sVQk4eIH974nXG5HPuMW8f2CwnE2Fg0dirEose
y7l11jo5zbpsk7YXBE34B93ds5Ofcte2reUatEhh44yWC+FN2atelYK1hh/FpMBC
GQ==
MIIBgjCCAQygAwIBAgIGChssPU5fMA0GCSqGSIb3DQEBCwUAMAAwIhgPMDAwMTAx
MDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowFTETMBEGA1UEAxMKQ09NTU9OTkFN
RTB8MA0GCSqGSIb3DQEBAQUAA2sAMGgCYQDgxdLNdlVhNRNEcR5n9GdrNDXFV7CU
uE/XgXM5na9pOxeNSWTZ/StahYJONcNyLw8ZbiUREuHyx5lkNtFF5JcW3o1RoNBD
HFviyuJf7WUgDTBTctqQp/Y2q4uQPle92N8CAwEAAaMzMDEwDgYDVR0PAQH/BAQD
AgeAMB8GA1UdIwQYMBaAFKd2nhNImgSiMfIWZnR4VKrQ9u5sMA0GCSqGSIb3DQEB
CwUAA2EAr1WNJKb8H1pkPrMzzRLerF0QAK+/wal7OacRQ4x9pxl+rA+0bwgfsA0x
jSR/u6Jx83ASGMmuNLCnizqcNQtcj2hr1yPr5cPHFxjuISBPirNrkCONymnbhDU4
RYoeKuK/
-----END CERTIFICATE-----
16 changes: 8 additions & 8 deletions test/fixture/nodeattestor/x509pop/root-crt.pem
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBZjCB8aADAgECAgEBMA0GCSqGSIb3DQEBCwUAMAAwIhgPMDAwMTAxMDEwMDAw
MDBaGA85OTk5MTIzMTIzNTk1OVowADB8MA0GCSqGSIb3DQEBAQUAA2sAMGgCYQDb
gVZ0/8TYYk7DUArD+jJBNZRYCpnWlIcsHZXS+EkANck4PKK0noS+dytNB+EeIvb2
5oudnHoa7kItA2WHpC6H5o3KSacTk5W/YWrU8pDvtSrFmK+tBXjoFTkJVDbmIaMC
AwEAAaMyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUlC1s6l4WVTeJyDd9
zU9e+sCYIwQwDQYJKoZIhvcNAQELBQADYQDZu2CQxpqpu2veGuT9UmCyFzhk1km6
oU+QE4ZBegzTk3wAXaky3TxY1NSi8pzx7ynDG5tRIVbVZFnA6VX6qaXJpFr8jFFr
tatahAJuWMCNHfwVZ7Hx4mPztoPHQaEEIpw=
MIIBaDCB86ADAgECAgMaKzwwDQYJKoZIhvcNAQELBQAwADAiGA8wMDAxMDEwMTAw
MDAwMFoYDzk5OTkxMjMxMjM1OTU5WjAAMHwwDQYJKoZIhvcNAQEBBQADawAwaAJh
AL4/Jxt3mhua02ZqRhineH5BFYCaDaBWNs8p3twsXEr0E6b9onZpUnkgdIVseU0X
qjuZP3c4/i2c1TnwN5iomXfj3hzeTB5e/D+kzX6+UxvFrMayxtGKNbCHr+ZIjU18
5QIDAQABozIwMDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQEvQNv9Uxp79n0
yMXzvMBRSoDX8DANBgkqhkiG9w0BAQsFAANhAAvYkMYqEnl8XLq2luwy6XJyXuLI
Ge+TFgoBIYsLryfLua3l/tMhm2l7497PMXJDVwSwObTRKbkVmwzFGEHcqb/x7kyJ
QJJJHT6TOAWuqv6HbdCJI/jJzRn3zm3ngyDqIg==
-----END CERTIFICATE-----

0 comments on commit 2355c85

Please sign in to comment.