Skip to content

Commit d919fa5

Browse files
authored
Merge pull request #1590 from PereBueno/JENKINS-73460
[JENKINS-73460] add FIPS compliance checks to plugin whem runnign in FIPS mode
2 parents 78bd4a1 + 9a9d310 commit d919fa5

File tree

12 files changed

+527
-5
lines changed

12 files changed

+527
-5
lines changed

pom.xml

+4-5
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@
8181
<groupId>org.jenkins-ci.plugins</groupId>
8282
<artifactId>authentication-tokens</artifactId>
8383
</dependency>
84+
<dependency>
85+
<groupId>org.jenkins-ci.plugins</groupId>
86+
<artifactId>bouncycastle-api</artifactId>
87+
</dependency>
8488

8589
<dependency>
8690
<!-- Requires Permission -->
@@ -173,11 +177,6 @@
173177
<version>4.2.1</version>
174178
<scope>test</scope>
175179
</dependency>
176-
<dependency>
177-
<groupId>org.jenkins-ci.plugins</groupId>
178-
<artifactId>bouncycastle-api</artifactId>
179-
<scope>test</scope>
180-
</dependency>
181180
<dependency>
182181
<groupId>org.jenkins-ci.plugins</groupId>
183182
<artifactId>docker-workflow</artifactId>

src/main/java/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloud.java

+123
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@
4040
import java.net.SocketTimeoutException;
4141
import java.net.URL;
4242
import java.net.UnknownHostException;
43+
import java.security.PublicKey;
44+
import java.security.UnrecoverableKeyException;
45+
import java.security.cert.Certificate;
46+
import java.security.interfaces.DSAPublicKey;
47+
import java.security.interfaces.ECPublicKey;
48+
import java.security.interfaces.RSAPublicKey;
4349
import java.util.ArrayList;
4450
import java.util.Base64;
4551
import java.util.Collection;
@@ -52,9 +58,11 @@
5258
import java.util.logging.Logger;
5359
import javax.servlet.ServletException;
5460
import jenkins.authentication.tokens.api.AuthenticationTokens;
61+
import jenkins.bouncycastle.api.PEMEncodable;
5562
import jenkins.metrics.api.Metrics;
5663
import jenkins.model.Jenkins;
5764
import jenkins.model.JenkinsLocationConfiguration;
65+
import jenkins.security.FIPS140;
5866
import jenkins.util.SystemProperties;
5967
import jenkins.websocket.WebSockets;
6068
import net.sf.json.JSONObject;
@@ -268,6 +276,7 @@ public String getServerUrl() {
268276

269277
@DataBoundSetter
270278
public void setServerUrl(@NonNull String serverUrl) {
279+
ensureKubernetesUrlInFipsMode(serverUrl);
271280
this.serverUrl = Util.fixEmpty(serverUrl);
272281
}
273282

@@ -277,6 +286,7 @@ public String getServerCertificate() {
277286

278287
@DataBoundSetter
279288
public void setServerCertificate(String serverCertificate) {
289+
ensureServerCertificateInFipsMode(serverCertificate);
280290
this.serverCertificate = Util.fixEmpty(serverCertificate);
281291
}
282292

@@ -286,6 +296,7 @@ public boolean isSkipTlsVerify() {
286296

287297
@DataBoundSetter
288298
public void setSkipTlsVerify(boolean skipTlsVerify) {
299+
ensureSkipTlsVerifyInFipsMode(skipTlsVerify);
289300
this.skipTlsVerify = skipTlsVerify;
290301
}
291302

@@ -651,6 +662,75 @@ public Collection<NodeProvisioner.PlannedNode> provision(
651662
return Collections.emptyList();
652663
}
653664

665+
/**
666+
* Checks if URL is using HTTPS, required in FIPS mode
667+
* Continues if URL is secure or not in FIPS mode, throws an {@link IllegalArgumentException} if not.
668+
* @param url Kubernetes server URL
669+
*/
670+
private static void ensureKubernetesUrlInFipsMode(String url) {
671+
if (!FIPS140.useCompliantAlgorithms() || StringUtils.isBlank(url)) {
672+
return;
673+
}
674+
if (!url.startsWith("https:")) {
675+
throw new IllegalArgumentException(Messages.KubernetesCloud_kubernetesServerUrlIsNotSecure());
676+
}
677+
}
678+
679+
/**
680+
* Checks if TLS verification is being skipped, which is not allowed in FIPS mode
681+
* Continues if not being skipped or not in FIPS mode, throws an {@link IllegalArgumentException} if not.
682+
* @param skipTlsVerify value to check
683+
*/
684+
private static void ensureSkipTlsVerifyInFipsMode(boolean skipTlsVerify) {
685+
if (FIPS140.useCompliantAlgorithms() && skipTlsVerify) {
686+
throw new IllegalArgumentException(Messages.KubernetesCloud_skipTlsVerifyNotAllowedInFIPSMode());
687+
}
688+
}
689+
690+
/**
691+
* Checks if server certificate is allowed if FIPS mode.
692+
* Allowed certificates use a public key with the following algorithms and sizes:
693+
* <ul>
694+
* <li>DSA with key size >= 2048</li>
695+
* <li>RSA with key size >= 2048</li>
696+
* <li>Elliptic curve (ED25519) with field size >= 224</li>
697+
* </ul>
698+
* If certificate is valid and allowed or not in FIPS mode method will just exit.
699+
* If not it will throw an {@link IllegalArgumentException}.
700+
* @param serverCertificate String containing the certificate PEM.
701+
*/
702+
private static void ensureServerCertificateInFipsMode(String serverCertificate) {
703+
if (!FIPS140.useCompliantAlgorithms()) {
704+
return;
705+
}
706+
if (StringUtils.isBlank(serverCertificate)) {
707+
throw new IllegalArgumentException(Messages.KubernetesCloud_serverCertificateKeyEmpty());
708+
}
709+
try {
710+
PEMEncodable pem = PEMEncodable.decode(serverCertificate);
711+
Certificate cert = pem.toCertificate();
712+
if (cert == null) {
713+
throw new IllegalArgumentException(Messages.KubernetesCloud_serverCertificateNotACertificate());
714+
}
715+
PublicKey publicKey = cert.getPublicKey();
716+
if (publicKey instanceof RSAPublicKey) {
717+
if (((RSAPublicKey) publicKey).getModulus().bitLength() < 2048) {
718+
throw new IllegalArgumentException(Messages.KubernetesCloud_serverCertificateKeySize());
719+
}
720+
} else if (publicKey instanceof DSAPublicKey) {
721+
if (((DSAPublicKey) publicKey).getParams().getP().bitLength() < 2048) {
722+
throw new IllegalArgumentException(Messages.KubernetesCloud_serverCertificateKeySize());
723+
}
724+
} else if (publicKey instanceof ECPublicKey) {
725+
if (((ECPublicKey) publicKey).getParams().getCurve().getField().getFieldSize() < 224) {
726+
throw new IllegalArgumentException(Messages.KubernetesCloud_serverCertificateKeySizeEC());
727+
}
728+
}
729+
} catch (RuntimeException | UnrecoverableKeyException | IOException e) {
730+
throw new IllegalArgumentException(e.getMessage(), e);
731+
}
732+
}
733+
654734
@Override
655735
public void replaceTemplate(PodTemplate oldTemplate, PodTemplate newTemplate) {
656736
this.removeTemplate(oldTemplate);
@@ -913,6 +993,44 @@ public FormValidation doTestConnection(
913993
}
914994
}
915995

996+
@RequirePOST
997+
@SuppressWarnings({"unused", "lgtm[jenkins/csrf]"
998+
}) // used by jelly and already fixed jenkins security scan warning
999+
public FormValidation doCheckSkipTlsVerify(@QueryParameter boolean skipTlsVerify) {
1000+
Jenkins.get().checkPermission(Jenkins.MANAGE);
1001+
try {
1002+
ensureSkipTlsVerifyInFipsMode(skipTlsVerify);
1003+
} catch (IllegalArgumentException ex) {
1004+
return FormValidation.error(ex, ex.getLocalizedMessage());
1005+
}
1006+
return FormValidation.ok();
1007+
}
1008+
1009+
@RequirePOST
1010+
@SuppressWarnings({"unused", "lgtm[jenkins/csrf]"
1011+
}) // used by jelly and already fixed jenkins security scan warning
1012+
public FormValidation doCheckServerCertificate(@QueryParameter String serverCertificate) {
1013+
Jenkins.get().checkPermission(Jenkins.MANAGE);
1014+
try {
1015+
ensureServerCertificateInFipsMode(serverCertificate);
1016+
} catch (IllegalArgumentException ex) {
1017+
return FormValidation.error(ex, ex.getLocalizedMessage());
1018+
}
1019+
return FormValidation.ok();
1020+
}
1021+
1022+
@RequirePOST
1023+
@SuppressWarnings("unused") // used by jelly
1024+
public FormValidation doCheckServerUrl(@QueryParameter String serverUrl) {
1025+
Jenkins.get().checkPermission(Jenkins.MANAGE);
1026+
try {
1027+
ensureKubernetesUrlInFipsMode(serverUrl);
1028+
} catch (IllegalArgumentException ex) {
1029+
return FormValidation.error(ex.getLocalizedMessage());
1030+
}
1031+
return FormValidation.ok();
1032+
}
1033+
9161034
@RequirePOST
9171035
@SuppressWarnings("unused") // used by jelly
9181036
public ListBoxModel doFillCredentialsIdItems(
@@ -1126,6 +1244,11 @@ private Object readResolve() {
11261244
Level.INFO, "Upgraded Kubernetes server certificate key: {0}", serverCertificate.substring(0, 80));
11271245
}
11281246

1247+
// FIPS checks if in FIPS mode
1248+
ensureServerCertificateInFipsMode(serverCertificate);
1249+
ensureKubernetesUrlInFipsMode(serverUrl);
1250+
ensureSkipTlsVerifyInFipsMode(skipTlsVerify);
1251+
11291252
if (maxRequestsPerHost == 0) {
11301253
maxRequestsPerHost = DEFAULT_MAX_REQUESTS_PER_HOST;
11311254
}

src/main/resources/org/csanchez/jenkins/plugins/kubernetes/Messages.properties

+7
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,10 @@ KubernetesSlave.HomeWarning=[WARNING] HOME is set to / in the agent container. Y
99
entry in /etc/passwd. Please add a user to your Dockerfile or set the HOME environment \
1010
variable to a valid directory in the pod template definition.
1111
KubernetesCloudNotAllowed.Description=Kubernetes cloud {0} is not allowed for folder containing job {1}
12+
KubernetesCloud.skipTlsVerifyNotAllowedInFIPSMode=Skipping TLS verification is not allowed in FIPS mode
13+
KubernetesCloud.serverCertificateIsNotApprovedInFIPSMode=Certificate is not valid: {0}
14+
KubernetesCloud.serverCertificateKeySize=Invalid key size, at least 2048 is needed in FIPS mode.
15+
KubernetesCloud.serverCertificateKeySizeEC=Invalid curve size, at least 224 is needed in FIPS mode.
16+
KubernetesCloud.serverCertificateKeyEmpty=Certificate is mandatory in FIPS mode.
17+
KubernetesCloud.serverCertificateNotACertificate=Provided PEM doesn't contain a certificate.
18+
KubernetesCloud.kubernetesServerUrlIsNotSecure=HTTPS secure URLs are mandatory in FIPS mode.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package org.csanchez.jenkins.plugins.kubernetes;
2+
3+
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.containsString;
5+
import static org.hamcrest.Matchers.notNullValue;
6+
import static org.hamcrest.Matchers.nullValue;
7+
import static org.junit.Assert.assertThrows;
8+
9+
import hudson.ExtensionList;
10+
import io.jenkins.cli.shaded.org.apache.commons.io.FileUtils;
11+
import java.io.IOException;
12+
import java.nio.charset.Charset;
13+
import java.nio.file.Paths;
14+
import jenkins.security.FIPS140;
15+
import org.junit.ClassRule;
16+
import org.junit.Rule;
17+
import org.junit.Test;
18+
import org.jvnet.hudson.test.FlagRule;
19+
import org.jvnet.hudson.test.Issue;
20+
import org.jvnet.hudson.test.JenkinsRule;
21+
import org.jvnet.hudson.test.recipes.LocalData;
22+
23+
public class KubernetesCloudFIPSTest {
24+
25+
@ClassRule
26+
public static FlagRule<String> fipsFlag = FlagRule.systemProperty(FIPS140.class.getName() + ".COMPLIANCE", "true");
27+
28+
@Rule
29+
public JenkinsRule r = new JenkinsRule();
30+
31+
@Test
32+
@Issue("JENKINS-73460")
33+
public void onlyFipsCompliantValuesAreAcceptedTest() throws IOException {
34+
KubernetesCloud cloud = new KubernetesCloud("test-cloud");
35+
assertThrows(IllegalArgumentException.class, () -> cloud.setSkipTlsVerify(true));
36+
cloud.setSkipTlsVerify(false);
37+
assertThrows(IllegalArgumentException.class, () -> cloud.setServerUrl("http://example.org"));
38+
cloud.setServerUrl("https://example.org");
39+
assertThrows(
40+
"Invalid certificates throw exception",
41+
IllegalArgumentException.class,
42+
() -> cloud.setServerCertificate(getCert("not-a-cert")));
43+
Throwable exception = assertThrows(
44+
"Invalid length", IllegalArgumentException.class, () -> cloud.setServerCertificate(getCert("rsa1024")));
45+
assertThat(exception.getLocalizedMessage(), containsString("2048"));
46+
cloud.setServerCertificate(getCert("rsa2048"));
47+
exception = assertThrows(
48+
"invalid length", IllegalArgumentException.class, () -> cloud.setServerCertificate(getCert("dsa1024")));
49+
assertThat(exception.getLocalizedMessage(), containsString("2048"));
50+
cloud.setServerCertificate(getCert("dsa2048"));
51+
exception = assertThrows(
52+
"Invalid field size",
53+
IllegalArgumentException.class,
54+
() -> cloud.setServerCertificate(getCert("ecdsa192")));
55+
assertThat(exception.getLocalizedMessage(), containsString("224"));
56+
cloud.setServerCertificate(getCert("ecdsa224"));
57+
}
58+
59+
@Test
60+
@Issue("JENKINS-73460")
61+
@LocalData
62+
public void nonCompliantCloudsAreCleanedTest() {
63+
assertThat("compliant-cloud is loaded", r.jenkins.getCloud("compliant-cloud"), notNullValue());
64+
assertThat("with-skip-tls is not loaded", r.jenkins.getCloud("with-skip-tls"), nullValue());
65+
assertThat("with-http-endpoint is not loaded", r.jenkins.getCloud("with-http-endpoint"), nullValue());
66+
assertThat("with-invalid-cert is not loaded", r.jenkins.getCloud("with-invalid-cert"), nullValue());
67+
}
68+
69+
@Test
70+
@Issue("JENKINS-73460")
71+
public void formValidationTest() throws IOException {
72+
ExtensionList<KubernetesCloud.DescriptorImpl> descriptors =
73+
ExtensionList.lookup(KubernetesCloud.DescriptorImpl.class);
74+
KubernetesCloud.DescriptorImpl descriptor = descriptors.stream()
75+
.filter(d -> d.getClass().isAssignableFrom(KubernetesCloud.DescriptorImpl.class))
76+
.findFirst()
77+
.orElseGet(KubernetesCloud.DescriptorImpl::new);
78+
assertThat(
79+
"Valid url doesn't raise error",
80+
descriptor.doCheckServerUrl("https://eample.org").getMessage(),
81+
nullValue());
82+
assertThat(
83+
"Invalid url raises error",
84+
descriptor.doCheckServerUrl("http://eample.org").getMessage(),
85+
notNullValue());
86+
assertThat(
87+
"Valid cert doesn't raise error",
88+
descriptor.doCheckServerCertificate(getCert("rsa2048")).getMessage(),
89+
nullValue());
90+
assertThat(
91+
"Invalid cert raises error",
92+
descriptor.doCheckServerCertificate(getCert("rsa1024")).getMessage(),
93+
notNullValue());
94+
assertThat(
95+
"No TLS skip doesn't raise error",
96+
descriptor.doCheckSkipTlsVerify(false).getMessage(),
97+
nullValue());
98+
assertThat(
99+
"TLS skip raises error", descriptor.doCheckSkipTlsVerify(true).getMessage(), notNullValue());
100+
}
101+
102+
private String getCert(String alg) throws IOException {
103+
return FileUtils.readFileToString(
104+
Paths.get("src/test/resources/org/csanchez/jenkins/plugins/kubernetes/KubernetesCloudFIPSTest/certs")
105+
.resolve(alg)
106+
.toFile(),
107+
Charset.defaultCharset());
108+
}
109+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDDzCCArygAwIBAgIUYBblXc5PhKw86bwvpcfBv/PxOmEwCwYJYIZIAWUDBAMC
3+
MEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJ
4+
bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjQwNzE4MTU0MDAxWhcNMjQwODE3
5+
MTU0MDAxWjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8G
6+
A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBvzCCATQGByqGSM44BAEw
7+
ggEnAoGBAN1Se68u8JFTdXrZ2ED2RDG89TRR51j1E7SPwlYItWQGAnJUkTRKyhYO
8+
6oItvkuie/9HqjFVcz0hW1D0EfKKuJjv2VjkN07bQyZ9Kr92tfnkC2IOvbfDs8Ev
9+
g3GBOjKs7Bi9inOcQZCKt1ZeNd4j4jDrLPnisEc8urcBlbxnNCiHAh0A7PRieQst
10+
d6p4yVCVQp/fsjmUp+bL7phoR5jxNQKBgQC4K58rznGE8QmhSsUonv5Uf+gnnpDI
11+
6eDiOUH/DpIDIMftK6AQ4wp6YY4pZP+dxfbBt9uimmfdyuvrO3i1oOD3UqmVSCwd
12+
Qome8YPGfxtTYYB/o05li7KPzHTqVcGXZQont9IK+uQaCwnzv/dsERol2F2aPMnD
13+
6JE2hd/DCUWiqQOBhAACgYBeEMMpp5ROkIDZ5h4HeDDZztn1zWRrnsV89Cs6WcjR
14+
vbeumfVIoo06yws5tZdMfssrBjk+irKFKIU9edhiKcOjB8ssMJi+7tOEWEC9ooHo
15+
F6cOqiYmhLBhLrIyv5dZUe8RtyJRZaP+4bn3PbxZ7Cij8DWHntnwhEjrqlUp6vCq
16+
K6MhMB8wHQYDVR0OBBYEFPJmnQzXspNFvJNwPqxj//pvgzBlMAsGCWCGSAFlAwQD
17+
AgNAADA9AhwNYi4S6Cq9uh9KKBYz9jYdiJYjI2lmDnSFYGtQAh0AvZkHSf6BfSKE
18+
STBsggtQOqDvcao45reTjyaDcw==
19+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEkzCCBECgAwIBAgIUDza9+LN/FgNVUl6tr01fR+wt/U0wCwYJYIZIAWUDBAMC
3+
MEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJ
4+
bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjQwNzE4MTU0MTA1WhcNMjQwODE3
5+
MTU0MTA1WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8G
6+
A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIDQzCCAjUGByqGSM44BAEw
7+
ggIoAoIBAQC3MkKb6PbOGFDYkhMjbIbz9lREVMRGkLCSWCwa3R7KwWjE4yPl0ONq
8+
pLGYWSKtS51y06vR+wcxgjQynKL2s1pTpdX3EcGwJtb3SdR1vLLHWoUyr5d4adtL
9+
UNmu7SRe9pSXsw3Y6j8c1T/UlGkREHXHlJlwON13NY5ZEKkZ5vtaOsJaEL4mrxvz
10+
/dR/joWbXNSfPdi5aeu6U2W2/zUwcOcu+PtvYafLGr5FL+fAG0UniQgF86FB//px
11+
CVeHngm54gwLTJJPtsnKKJwa6pCXbmANzsYCCj7tpGEQH1ZcyYzNNuABm5OZCCco
12+
Si4TlwCkVe/Rrb5Ko04Kdqg5/XWA3sn1Ah0Au8eLbfMm7x7dhJ9/Yv55DLoU1Nq8
13+
waI6VGIdJQKCAQBZTWaquC3p30SXJ314Bh1zFPNEqcdsrNWnmZqzSKXfMuRPQRAQ
14+
oAwMMWABtycYFPzK7/RsUihPdj1kIF46ArC+QXeo0zAQjKIcrNGIOBuvILsBx5On
15+
F+Cw260PYIami45AqwhuACBoq6K6OxI24vSms72TMj+HRt8gtzWt1cP1MNxbyxho
16+
rxKHUq/bLbBKfYbg4hSnvqMAw9mawEsRVYQzR+pAZqWE+otcvxkpR2PYWKR/i0dq
17+
+PmJuqbHghMq6kss9a2d/tWQUGyUJkjr2qkjtn1CVuxpcDWcQ/0b0cpdCN83iSRA
18+
EOJJY/g8B4kerasdPSO/e/gOfp6Pe57FI5iVA4IBBgACggEBAJnwtqFo43cc8en3
19+
hH2lbN7A3VDNkNejkl3kdhohHjgXNS2Cwlfn3y1QtYrw1BeG1Sdh4WTFCLIePc6t
20+
3quwLhRLDGzgD7YlvE4WYoE5JidAM+qcQiwdbXOhSljCeJFCdkg+7SH0LWwwW555
21+
I3K64XeE0/ONn9bjkhYPfDv5HG2fiEDL1bc6sgoraN6Cb0p2MmXQiSz+FMdHTLlg
22+
yNrIdkNQxxKlYHJzNqwmesaSy9SmA/woGyrIrmX/XCExJwpasnaL+c/efU8H4zOC
23+
5eO8JUqUiWISTZn47YPrd/zFEDrk9heGgi5ZabUneAS69DaYa+sC/Xxa/ZCIMt0A
24+
9tN26gujITAfMB0GA1UdDgQWBBTDOjTyqpCiM996xYmndSV3QcGy+TALBglghkgB
25+
ZQMEAwIDQAAwPQIdAJGqVySKZk1O4SQHsWAzTI+706myofPowzs1hvsCHCkZmLGG
26+
ZrWsuaK1nMVltEQER+Eodz9oLRDgqew=
27+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBwDCCAXagAwIBAgIUOwG28vjFm4KoiGieHrBRgzhrzGAwCgYIKoZIzj0EAwIw
3+
RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
4+
dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA3MTgxNTQ4MzBaFw0yNDA4MTcx
5+
NTQ4MzBaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD
6+
VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwSjAUBgcqhkjOPQIBBgkrJAMD
7+
AggBAQQDMgAEpFiVE3YkcIaJVP9DsLIZE620gyX23AxQahhWjywp8hp+DKO4voH3
8+
HlKfdeDEZ5nfo1MwUTAdBgNVHQ4EFgQUCQxoboqlb8uG3RrOqtk4Dxil4xwwHwYD
9+
VR0jBBgwFoAUCQxoboqlb8uG3RrOqtk4Dxil4xwwDwYDVR0TAQH/BAUwAwEB/zAK
10+
BggqhkjOPQQDAgM4ADA1AhkAiI0732BOdYpjG1EgZ2y1Y1W9qLjgKLH7AhgwSQbA
11+
qPWq3wYiP1gZsVMavRL9K1ggspE=
12+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBzzCCAX6gAwIBAgIUMxqDFkKRXOeP325owDz02IZomgUwCgYIKoZIzj0EAwIw
3+
RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
4+
dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA3MTgxNTQ5MDVaFw0yNDA4MTcx
5+
NTQ5MDVaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD
6+
VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwUjAUBgcqhkjOPQIBBgkrJAMD
7+
AggBAQYDOgAEOuIfMAfhqilO6Q1VxiAjuQnTkpFH2MYcyFjyG9O2OG71KFuB4hC8
8+
r6NSSxVCx88TjKzcnm/u/HijUzBRMB0GA1UdDgQWBBQtPSBGTPqBRJQOVhf/c8Xh
9+
5s0aOjAfBgNVHSMEGDAWgBQtPSBGTPqBRJQOVhf/c8Xh5s0aOjAPBgNVHRMBAf8E
10+
BTADAQH/MAoGCCqGSM49BAMCAz8AMDwCHAOWGI94ia/Ck3JgqIPFCGZUqR8uh9vC
11+
ovacsJACHC8VSwu0hEqevytqT7HH9E/DCMYORANJBZz5GyY=
12+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)