From b92bd87bf65cf605a1abc2c6263974d85b4bb5d1 Mon Sep 17 00:00:00 2001 From: Henry Avetisyan Date: Sat, 7 Dec 2019 11:27:04 -0800 Subject: [PATCH] support fetching cert bundles from ZTS (#843) --- clients/go/zts/client.go | 32 ++++ clients/go/zts/model.go | 68 +++++++ clients/go/zts/zts_schema.go | 13 ++ .../java/com/yahoo/athenz/zts/ZTSClient.java | 19 ++ .../athenz/zts/ZTSRDLGeneratedClient.java | 19 ++ .../com/yahoo/athenz/zts/ZTSClientTest.java | 32 ++++ .../yahoo/athenz/zts/ZTSRDLClientMock.java | 15 +- .../zts/CertificateAuthorityBundle.java | 46 +++++ .../java/com/yahoo/athenz/zts/ZTSSchema.java | 15 ++ core/zts/src/main/rdl/Instance.rdli | 16 ++ .../zts/CertificateAuthorityBundleTest.java | 54 ++++++ .../com/yahoo/athenz/auth/util/Crypto.java | 62 ++++--- .../yahoo/athenz/auth/util/CryptoTest.java | 51 +++++- .../test/resources/x509_certs_comments.pem | 154 ++++++++++++++++ .../test/resources/x509_certs_no_comments.pem | 48 +++++ servers/zms/conf/zms.properties | 7 + servers/zts/conf/zts.properties | 9 + .../java/com/yahoo/athenz/zts/ZTSConsts.java | 1 + .../java/com/yahoo/athenz/zts/ZTSHandler.java | 1 + .../java/com/yahoo/athenz/zts/ZTSImpl.java | 39 +++- .../com/yahoo/athenz/zts/ZTSResources.java | 24 +++ .../athenz/zts/cert/InstanceCertManager.java | 166 ++++++++++++++++-- .../yahoo/athenz/zts/utils/CertBundle.java | 47 +++++ .../yahoo/athenz/zts/utils/CertBundles.java | 31 ++++ .../com/yahoo/athenz/zts/ZTSImplTest.java | 53 ++++-- .../zts/cert/InstanceCertManagerTest.java | 117 +++++++++++- .../athenz/zts/utils/CertBundleTest.java | 36 ++++ .../athenz/zts/utils/CertBundlesTest.java | 40 +++++ .../yahoo/athenz/zts/utils/IPBlockTest.java | 15 ++ .../athenz/zts/utils/IPPrefixesTest.java | 15 ++ .../test/resources/ca-bundle-file-empty.json | 4 + .../resources/ca-bundle-file-invalid-ssh.json | 19 ++ .../ca-bundle-file-invalid-x509.json | 19 ++ .../resources/ca-bundle-file-invalid.json | 13 ++ .../ca-bundle-file-missing-filename.json | 18 ++ .../src/test/resources/ca-bundle-file.json | 19 ++ servers/zts/src/test/resources/ssh-ca-certs | 1 + .../test/resources/x509_certs_comments.pem | 154 ++++++++++++++++ .../test/resources/x509_certs_no_comments.pem | 48 +++++ 39 files changed, 1475 insertions(+), 65 deletions(-) create mode 100644 core/zts/src/main/java/com/yahoo/athenz/zts/CertificateAuthorityBundle.java create mode 100644 core/zts/src/test/java/com/yahoo/athenz/zts/CertificateAuthorityBundleTest.java create mode 100644 libs/java/auth_core/src/test/resources/x509_certs_comments.pem create mode 100644 libs/java/auth_core/src/test/resources/x509_certs_no_comments.pem create mode 100644 servers/zts/src/main/java/com/yahoo/athenz/zts/utils/CertBundle.java create mode 100644 servers/zts/src/main/java/com/yahoo/athenz/zts/utils/CertBundles.java create mode 100644 servers/zts/src/test/java/com/yahoo/athenz/zts/utils/CertBundleTest.java create mode 100644 servers/zts/src/test/java/com/yahoo/athenz/zts/utils/CertBundlesTest.java create mode 100644 servers/zts/src/test/resources/ca-bundle-file-empty.json create mode 100644 servers/zts/src/test/resources/ca-bundle-file-invalid-ssh.json create mode 100644 servers/zts/src/test/resources/ca-bundle-file-invalid-x509.json create mode 100644 servers/zts/src/test/resources/ca-bundle-file-invalid.json create mode 100644 servers/zts/src/test/resources/ca-bundle-file-missing-filename.json create mode 100644 servers/zts/src/test/resources/ca-bundle-file.json create mode 100644 servers/zts/src/test/resources/ssh-ca-certs create mode 100644 servers/zts/src/test/resources/x509_certs_comments.pem create mode 100644 servers/zts/src/test/resources/x509_certs_no_comments.pem diff --git a/clients/go/zts/client.go b/clients/go/zts/client.go index d2327466030..a2e051bffbe 100644 --- a/clients/go/zts/client.go +++ b/clients/go/zts/client.go @@ -919,6 +919,38 @@ func (client ZTSClient) DeleteInstanceIdentity(provider ServiceName, domain Doma } } +func (client ZTSClient) GetCertificateAuthorityBundle(name SimpleName) (*CertificateAuthorityBundle, error) { + var data *CertificateAuthorityBundle + url := client.URL + "/cacerts/" + fmt.Sprint(name) + resp, err := client.httpGet(url, nil) + if err != nil { + return data, err + } + defer resp.Body.Close() + switch resp.StatusCode { + case 200: + err = json.NewDecoder(resp.Body).Decode(&data) + if err != nil { + return data, err + } + return data, nil + default: + var errobj rdl.ResourceError + contentBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return data, err + } + json.Unmarshal(contentBytes, &errobj) + if errobj.Code == 0 { + errobj.Code = resp.StatusCode + } + if errobj.Message == "" { + errobj.Message = string(contentBytes) + } + return data, errobj + } +} + func (client ZTSClient) PostDomainMetrics(domainName DomainName, req *DomainMetrics) (*DomainMetrics, error) { var data *DomainMetrics url := client.URL + "/metrics/" + fmt.Sprint(domainName) diff --git a/clients/go/zts/model.go b/clients/go/zts/model.go index a69c844a1d1..edc0098f771 100644 --- a/clients/go/zts/model.go +++ b/clients/go/zts/model.go @@ -2101,6 +2101,74 @@ func (self *InstanceIdentity) Validate() error { return nil } +// +// CertificateAuthorityBundle - +// +type CertificateAuthorityBundle struct { + + // + // name of the bundle + // + Name SimpleName `json:"name"` + + // + // set of certificates included in the bundle + // + Certs string `json:"certs"` +} + +// +// NewCertificateAuthorityBundle - creates an initialized CertificateAuthorityBundle instance, returns a pointer to it +// +func NewCertificateAuthorityBundle(init ...*CertificateAuthorityBundle) *CertificateAuthorityBundle { + var o *CertificateAuthorityBundle + if len(init) == 1 { + o = init[0] + } else { + o = new(CertificateAuthorityBundle) + } + return o +} + +type rawCertificateAuthorityBundle CertificateAuthorityBundle + +// +// UnmarshalJSON is defined for proper JSON decoding of a CertificateAuthorityBundle +// +func (self *CertificateAuthorityBundle) UnmarshalJSON(b []byte) error { + var m rawCertificateAuthorityBundle + err := json.Unmarshal(b, &m) + if err == nil { + o := CertificateAuthorityBundle(m) + *self = o + err = self.Validate() + } + return err +} + +// +// Validate - checks for missing required fields, etc +// +func (self *CertificateAuthorityBundle) Validate() error { + if self.Name == "" { + return fmt.Errorf("CertificateAuthorityBundle.name is missing but is a required field") + } else { + val := rdl.Validate(ZTSSchema(), "SimpleName", self.Name) + if !val.Valid { + return fmt.Errorf("CertificateAuthorityBundle.name does not contain a valid SimpleName (%v)", val.Error) + } + } + if self.Certs == "" { + return fmt.Errorf("CertificateAuthorityBundle.certs is missing but is a required field") + } else { + val := rdl.Validate(ZTSSchema(), "String", self.Certs) + if !val.Valid { + return fmt.Errorf("CertificateAuthorityBundle.certs does not contain a valid String (%v)", val.Error) + } + } + return nil +} + // // DomainMetricType - zpe metric attributes // diff --git a/clients/go/zts/zts_schema.go b/clients/go/zts/zts_schema.go index 62ce1945cc0..5bc693494cb 100644 --- a/clients/go/zts/zts_schema.go +++ b/clients/go/zts/zts_schema.go @@ -281,6 +281,11 @@ func init() { tInstanceIdentity.MapField("attributes", "String", "String", true, "other config-like attributes determined at boot time") sb.AddType(tInstanceIdentity.Build()) + tCertificateAuthorityBundle := rdl.NewStructTypeBuilder("Struct", "CertificateAuthorityBundle") + tCertificateAuthorityBundle.Field("name", "SimpleName", false, nil, "name of the bundle") + tCertificateAuthorityBundle.Field("certs", "String", false, nil, "set of certificates included in the bundle") + sb.AddType(tCertificateAuthorityBundle.Build()) + tDomainMetricType := rdl.NewEnumTypeBuilder("Enum", "DomainMetricType") tDomainMetricType.Comment("zpe metric attributes") tDomainMetricType.Element("ACCESS_ALLOWED", "") @@ -607,6 +612,14 @@ func init() { mDeleteInstanceIdentity.Exception("UNAUTHORIZED", "ResourceError", "") sb.AddResource(mDeleteInstanceIdentity.Build()) + mGetCertificateAuthorityBundle := rdl.NewResourceBuilder("CertificateAuthorityBundle", "GET", "/cacerts/{name}") + mGetCertificateAuthorityBundle.Input("name", "SimpleName", true, "", "", false, nil, "name of the CA cert bundle") + mGetCertificateAuthorityBundle.Auth("", "", true, "") + mGetCertificateAuthorityBundle.Exception("BAD_REQUEST", "ResourceError", "") + mGetCertificateAuthorityBundle.Exception("NOT_FOUND", "ResourceError", "") + mGetCertificateAuthorityBundle.Exception("UNAUTHORIZED", "ResourceError", "") + sb.AddResource(mGetCertificateAuthorityBundle.Build()) + mPostDomainMetrics := rdl.NewResourceBuilder("DomainMetrics", "POST", "/metrics/{domainName}") mPostDomainMetrics.Comment("called to post multiple zpe related metric attributes") mPostDomainMetrics.Input("domainName", "DomainName", true, "", "", false, nil, "name of the domain the metrics pertain to") diff --git a/clients/java/zts/core/src/main/java/com/yahoo/athenz/zts/ZTSClient.java b/clients/java/zts/core/src/main/java/com/yahoo/athenz/zts/ZTSClient.java index a5dd4e6649c..8857f67282e 100644 --- a/clients/java/zts/core/src/main/java/com/yahoo/athenz/zts/ZTSClient.java +++ b/clients/java/zts/core/src/main/java/com/yahoo/athenz/zts/ZTSClient.java @@ -536,6 +536,9 @@ public void setZTSRDLGeneratedClient(ZTSRDLGeneratedClient client) { * @param privateKeyFile path to the private key file * @param monitorKeyCertUpdates boolean flag whether or not monitor file updates * @return SSLContext object + * @throws InterruptedException + * @throws KeyRefresherException + * @throws IOException */ public SSLContext createSSLContext(final String trustStorePath, final char[] trustStorePassword, final String publicCertFile, final String privateKeyFile, boolean monitorKeyCertUpdates) @@ -2551,6 +2554,22 @@ public void deleteInstanceIdentity(String provider, String domain, } } + /** + * Retrieve list of CA Certificates in PEM format for the given bundle name + * @param bundleName name of the CA Certificate bundle name + * @return CA Certificate bundle including list of CA certificates on success. ZTSClientException will be thrown in case of failure + */ + public CertificateAuthorityBundle getCertificateAuthorityBundle(String bundleName) { + updateServicePrincipal(); + try { + return ztsClient.getCertificateAuthorityBundle(bundleName); + } catch (ResourceException ex) { + throw new ZTSClientException(ex.getCode(), ex.getData()); + } catch (Exception ex) { + throw new ZTSClientException(ZTSClientException.BAD_REQUEST, ex.getMessage()); + } + } + static class ClientKeyRefresherListener implements KeyRefresherListener { long lastCertRefreshTime = 0; diff --git a/clients/java/zts/core/src/main/java/com/yahoo/athenz/zts/ZTSRDLGeneratedClient.java b/clients/java/zts/core/src/main/java/com/yahoo/athenz/zts/ZTSRDLGeneratedClient.java index f0799117874..cf6c6fef22c 100644 --- a/clients/java/zts/core/src/main/java/com/yahoo/athenz/zts/ZTSRDLGeneratedClient.java +++ b/clients/java/zts/core/src/main/java/com/yahoo/athenz/zts/ZTSRDLGeneratedClient.java @@ -473,6 +473,25 @@ public InstanceIdentity deleteInstanceIdentity(String provider, String domain, S } + public CertificateAuthorityBundle getCertificateAuthorityBundle(String name) { + WebTarget target = base.path("/cacerts/{name}") + .resolveTemplate("name", name); + Invocation.Builder invocationBuilder = target.request("application/json"); + if (credsHeader != null) { + invocationBuilder = credsHeader.startsWith("Cookie.") ? invocationBuilder.cookie(credsHeader.substring(7), + credsToken) : invocationBuilder.header(credsHeader, credsToken); + } + Response response = invocationBuilder.get(); + int code = response.getStatus(); + switch (code) { + case 200: + return response.readEntity(CertificateAuthorityBundle.class); + default: + throw new ResourceException(code, response.readEntity(ResourceError.class)); + } + + } + public DomainMetrics postDomainMetrics(String domainName, DomainMetrics req) { WebTarget target = base.path("/metrics/{domainName}") .resolveTemplate("domainName", domainName); diff --git a/clients/java/zts/core/src/test/java/com/yahoo/athenz/zts/ZTSClientTest.java b/clients/java/zts/core/src/test/java/com/yahoo/athenz/zts/ZTSClientTest.java index 6e0dcf29834..2f7a890d8e3 100644 --- a/clients/java/zts/core/src/test/java/com/yahoo/athenz/zts/ZTSClientTest.java +++ b/clients/java/zts/core/src/test/java/com/yahoo/athenz/zts/ZTSClientTest.java @@ -3185,4 +3185,36 @@ public void testPrefetchAccessTokenShouldNotCallServer() throws Exception { ZTSClient.cancelPrefetch(); client.close(); } + + @Test + public void testGetCertificateAuthorityBundle() { + + Principal principal = SimplePrincipal.create("user_domain", "user", + "auth_creds", PRINCIPAL_AUTHORITY); + + ZTSRDLClientMock ztsClientMock = new ZTSRDLClientMock(); + ZTSClient client = new ZTSClient("http://localhost:4080", principal); + client.setZTSRDLGeneratedClient(ztsClientMock); + + CertificateAuthorityBundle bundle = client.getCertificateAuthorityBundle("athenz"); + assertNotNull(bundle); + assertEquals(bundle.getName(), "athenz"); + assertEquals(bundle.getCerts(), "certs"); + + try { + client.getCertificateAuthorityBundle("exc"); + fail(); + } catch (ZTSClientException ex) { + assertEquals(ex.getCode(), 400); + } + + try { + client.getCertificateAuthorityBundle("system"); + fail(); + } catch (ZTSClientException ex) { + assertEquals(ex.getCode(), 404); + } + + client.close(); + } } diff --git a/clients/java/zts/core/src/test/java/com/yahoo/athenz/zts/ZTSRDLClientMock.java b/clients/java/zts/core/src/test/java/com/yahoo/athenz/zts/ZTSRDLClientMock.java index 2efe43da303..c39c5be5018 100644 --- a/clients/java/zts/core/src/test/java/com/yahoo/athenz/zts/ZTSRDLClientMock.java +++ b/clients/java/zts/core/src/test/java/com/yahoo/athenz/zts/ZTSRDLClientMock.java @@ -16,7 +16,6 @@ package com.yahoo.athenz.zts; import java.util.*; - import com.yahoo.rdl.Timestamp; public class ZTSRDLClientMock extends ZTSRDLGeneratedClient implements java.io.Closeable { @@ -449,4 +448,18 @@ public DomainMetrics postDomainMetrics(String domainName, DomainMetrics req) { } return null; } + + @Override + public CertificateAuthorityBundle getCertificateAuthorityBundle(String bundleName) { + if (bundleName.equals("exc")) { + throw new NullPointerException("Invalid request"); + } + if (bundleName.equals("system")) { + throw new ResourceException(404, "Unknown bundle name"); + } + CertificateAuthorityBundle bundle = new CertificateAuthorityBundle(); + bundle.setName(bundleName); + bundle.setCerts("certs"); + return bundle; + } } diff --git a/core/zts/src/main/java/com/yahoo/athenz/zts/CertificateAuthorityBundle.java b/core/zts/src/main/java/com/yahoo/athenz/zts/CertificateAuthorityBundle.java new file mode 100644 index 00000000000..3c82763448c --- /dev/null +++ b/core/zts/src/main/java/com/yahoo/athenz/zts/CertificateAuthorityBundle.java @@ -0,0 +1,46 @@ +// +// This file generated by rdl 1.5.2. Do not modify! +// + +package com.yahoo.athenz.zts; +import com.yahoo.rdl.*; + +// +// CertificateAuthorityBundle - +// +public class CertificateAuthorityBundle { + public String name; + public String certs; + + public CertificateAuthorityBundle setName(String name) { + this.name = name; + return this; + } + public String getName() { + return name; + } + public CertificateAuthorityBundle setCerts(String certs) { + this.certs = certs; + return this; + } + public String getCerts() { + return certs; + } + + @Override + public boolean equals(Object another) { + if (this != another) { + if (another == null || another.getClass() != CertificateAuthorityBundle.class) { + return false; + } + CertificateAuthorityBundle a = (CertificateAuthorityBundle) another; + if (name == null ? a.name != null : !name.equals(a.name)) { + return false; + } + if (certs == null ? a.certs != null : !certs.equals(a.certs)) { + return false; + } + } + return true; + } +} diff --git a/core/zts/src/main/java/com/yahoo/athenz/zts/ZTSSchema.java b/core/zts/src/main/java/com/yahoo/athenz/zts/ZTSSchema.java index 17c36691150..45604174b14 100644 --- a/core/zts/src/main/java/com/yahoo/athenz/zts/ZTSSchema.java +++ b/core/zts/src/main/java/com/yahoo/athenz/zts/ZTSSchema.java @@ -240,6 +240,10 @@ private static Schema build() { .field("serviceToken", "SignedToken", true, "service token instead of TLS certificate") .mapField("attributes", "String", "String", true, "other config-like attributes determined at boot time"); + sb.structType("CertificateAuthorityBundle") + .field("name", "SimpleName", false, "name of the bundle") + .field("certs", "String", false, "set of certificates included in the bundle"); + sb.enumType("DomainMetricType") .comment("zpe metric attributes") .element("ACCESS_ALLOWED") @@ -621,6 +625,17 @@ private static Schema build() { .exception("UNAUTHORIZED", "ResourceError", "") ; + sb.resource("CertificateAuthorityBundle", "GET", "/cacerts/{name}") + .pathParam("name", "SimpleName", "name of the CA cert bundle") + .auth("", "", true) + .expected("OK") + .exception("BAD_REQUEST", "ResourceError", "") + + .exception("NOT_FOUND", "ResourceError", "") + + .exception("UNAUTHORIZED", "ResourceError", "") +; + sb.resource("DomainMetrics", "POST", "/metrics/{domainName}") .comment("called to post multiple zpe related metric attributes") .pathParam("domainName", "DomainName", "name of the domain the metrics pertain to") diff --git a/core/zts/src/main/rdl/Instance.rdli b/core/zts/src/main/rdl/Instance.rdli index fd37e21f166..a703defef66 100644 --- a/core/zts/src/main/rdl/Instance.rdli +++ b/core/zts/src/main/rdl/Instance.rdli @@ -90,3 +90,19 @@ resource InstanceIdentity DELETE "/instance/{provider}/{domain}/{service}/{insta ResourceError INTERNAL_SERVER_ERROR; } } + +type CertificateAuthorityBundle Struct { + SimpleName name; //name of the bundle + String certs; //set of certificates included in the bundle +} + +resource CertificateAuthorityBundle GET "/cacerts/{name}" { + SimpleName name; //name of the CA cert bundle + authenticate; + expected OK; + exceptions { + ResourceError BAD_REQUEST; + ResourceError UNAUTHORIZED; + ResourceError NOT_FOUND; + } +} diff --git a/core/zts/src/test/java/com/yahoo/athenz/zts/CertificateAuthorityBundleTest.java b/core/zts/src/test/java/com/yahoo/athenz/zts/CertificateAuthorityBundleTest.java new file mode 100644 index 00000000000..b029485b449 --- /dev/null +++ b/core/zts/src/test/java/com/yahoo/athenz/zts/CertificateAuthorityBundleTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2019 Oath Holdings, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.yahoo.athenz.zts; + +import org.testng.annotations.Test; +import static org.testng.Assert.*; + +public class CertificateAuthorityBundleTest { + + @Test + public void testCertificateAuthorityBundle() { + + CertificateAuthorityBundle bundle1 = new CertificateAuthorityBundle() + .setCerts("certs").setName("athenz"); + + assertEquals(bundle1.getCerts(), "certs"); + assertEquals(bundle1.getName(), "athenz"); + + CertificateAuthorityBundle bundle2 = new CertificateAuthorityBundle() + .setCerts("certs").setName("athenz"); + + assertEquals(bundle2, bundle1); + assertEquals(bundle1, bundle1); + assertFalse(bundle2.equals("text")); + + bundle1.setCerts("certs2"); + assertNotEquals(bundle2, bundle1); + bundle1.setCerts(null); + assertNotEquals(bundle2, bundle1); + bundle1.setCerts("certs"); + assertEquals(bundle2, bundle1); + + bundle1.setName("athenz2"); + assertNotEquals(bundle2, bundle1); + bundle1.setName(null); + assertNotEquals(bundle2, bundle1); + bundle1.setName("athenz"); + assertEquals(bundle2, bundle1); + } +} diff --git a/libs/java/auth_core/src/main/java/com/yahoo/athenz/auth/util/Crypto.java b/libs/java/auth_core/src/main/java/com/yahoo/athenz/auth/util/Crypto.java index a946cb22d08..ad38bd11029 100644 --- a/libs/java/auth_core/src/main/java/com/yahoo/athenz/auth/util/Crypto.java +++ b/libs/java/auth_core/src/main/java/com/yahoo/athenz/auth/util/Crypto.java @@ -15,34 +15,14 @@ */ package com.yahoo.athenz.auth.util; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.io.StringReader; -import java.io.StringWriter; +import java.io.*; import java.math.BigInteger; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; -import java.security.InvalidKeyException; -import java.security.KeyFactory; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SecureRandom; -import java.security.Security; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.MessageDigest; -import java.security.SignatureException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateParsingException; -import java.security.cert.X509Certificate; +import java.security.*; +import java.security.cert.*; +import java.security.cert.Certificate; import java.security.interfaces.RSAPrivateCrtKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPublicKeySpec; @@ -381,6 +361,40 @@ public static String ybase64EncodeString(String str) { return utf8String(YBase64.encode(utf8Bytes(str))); } + public static String x509CertificatesToPEM(X509Certificate[] x509Certs) throws CryptoException { + StringWriter sw = new StringWriter(); + try (JcaPEMWriter pw = new JcaPEMWriter(sw)) { + for (X509Certificate x509Cert : x509Certs) { + pw.writeObject(x509Cert); + } + } catch (IOException ex) { + LOG.error("Unable to generate PEM output", ex); + throw new CryptoException(ex); + } + return sw.toString(); + } + + public static X509Certificate[] loadX509Certificates(final String certsFile) throws CryptoException { + + File certFile = new File(certsFile); + + try (InputStream certStream = new FileInputStream(certFile)) { + + final CertificateFactory cf = CertificateFactory.getInstance("X.509"); + List certs = (List) cf.generateCertificates(certStream); + if (certs.isEmpty()) { + throw new CryptoException("Certificate file contains empty certificate or an invalid certificate."); + } + return certs.toArray(new X509Certificate[certs.size()]); + } catch (IOException ex) { + LOG.error("loadX509Certificates: unable to process file: " + certFile.getAbsolutePath()); + throw new CryptoException(ex); + } catch (CertificateException ex) { + LOG.error("Unable to load certificates", ex); + throw new CryptoException(ex); + } + } + public static X509Certificate loadX509Certificate(File certFile) throws CryptoException { try (FileReader fileReader = new FileReader(certFile)) { return loadX509Certificate(fileReader); diff --git a/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/util/CryptoTest.java b/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/util/CryptoTest.java index 904332f9378..4d5b6bd3282 100644 --- a/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/util/CryptoTest.java +++ b/libs/java/auth_core/src/test/java/com/yahoo/athenz/auth/util/CryptoTest.java @@ -980,7 +980,6 @@ public void testExtractX509CSRSubjectFieldNull() { }); } - @Test public void testHmacSign() { assertNotNull(Crypto.hmac("testMessage", "testSharedSecret")); @@ -991,5 +990,55 @@ public void testYBase64EncodeString() { assertNotNull(Crypto.ybase64EncodeString("testString")); } + @Test + public void testLoadX509Certificates() { + + X509Certificate[] certs = Crypto.loadX509Certificates("src/test/resources/x509_certs_comments.pem"); + assertTrue(certs.length == 3); + + certs = Crypto.loadX509Certificates("src/test/resources/x509_certs_no_comments.pem"); + assertTrue(certs.length == 3); + // invalid file + + try { + Crypto.loadX509Certificates("src/test/resources/not_present_certs"); + fail(); + } catch (CryptoException ignored) { + } + + // invalid cert + + try { + Crypto.loadX509Certificates("src/test/resources/invalid_x509.cert"); + fail(); + } catch (CryptoException ignored) { + } + + // no cert + + try { + Crypto.loadX509Certificates("src/test/resources/ec_public.key"); + fail(); + } catch (CryptoException ignored) { + } + } + + @Test + public void testX509CertificatesToPEM() throws IOException { + + X509Certificate[] certs1 = Crypto.loadX509Certificates("src/test/resources/x509_certs_comments.pem"); + final String certs1PEM = Crypto.x509CertificatesToPEM(certs1); + assertNotNull(certs1PEM); + + X509Certificate[] certs2 = Crypto.loadX509Certificates("src/test/resources/x509_certs_no_comments.pem"); + final String certs2PEM = Crypto.x509CertificatesToPEM(certs2); + assertNotNull(certs2PEM); + + assertEquals(certs1PEM, certs2PEM); + + File caFile = new File("src/test/resources/x509_certs_no_comments.pem"); + byte[] data = Files.readAllBytes(Paths.get(caFile.toURI())); + assertEquals(certs1PEM, new String(data)); + } } diff --git a/libs/java/auth_core/src/test/resources/x509_certs_comments.pem b/libs/java/auth_core/src/test/resources/x509_certs_comments.pem new file mode 100644 index 00000000000..9933e4826cf --- /dev/null +++ b/libs/java/auth_core/src/test/resources/x509_certs_comments.pem @@ -0,0 +1,154 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 13984162866592987568 (0xc211ba09307bb9b0) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, ST=CA, L=Sunnyvale, O=My Test Company, CN=athenz.syncer + Validity + Not Before: Dec 9 21:54:46 2016 GMT + Not After : Dec 9 21:54:46 2017 GMT + Subject: C=US, ST=CA, L=Sunnyvale, O=My Test Company, CN=athenz.syncer + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (512 bit) + Modulus: + 00:d2:26:a0:47:83:ec:b5:3e:c7:ac:79:74:09:cd: + 44:5b:6b:6d:a6:f6:cb:1a:c3:5c:55:dd:c0:a4:31: + 94:8b:36:3d:67:cb:7a:8d:d7:ab:47:b6:6c:e5:3a: + 91:00:e0:17:3f:c8:90:c3:77:b4:4c:83:81:0f:cd: + bf:47:80:6d:c9 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 06:2D:26:B0:C3:57:88:2D:97:2E:92:A3:5C:FC:F5:0C:C7:FD:3E:81 + X509v3 Authority Key Identifier: + keyid:06:2D:26:B0:C3:57:88:2D:97:2E:92:A3:5C:FC:F5:0C:C7:FD:3E:81 + DirName:/C=US/ST=CA/L=Sunnyvale/O=My Test Company/CN=athenz.syncer + serial:C2:11:BA:09:30:7B:B9:B0 + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 3e:75:64:51:d8:9c:43:39:35:f3:4f:65:1c:dc:a1:4a:a6:fa: + a7:e9:31:8c:2e:76:45:f2:3c:6d:9f:a4:00:23:a9:a4:b2:c2: + a4:db:b7:9e:eb:a1:fb:dc:93:27:55:36:60:b8:c4:ef:be:27: + e3:cb:bb:e2:4e:c4:12:6b:c9:dc +-----BEGIN CERTIFICATE----- +MIICfzCCAimgAwIBAgIJAMIRugkwe7mwMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRgwFgYDVQQK +Ew9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVuei5zeW5jZXIwHhcNMTYx +MjA5MjE1NDQ2WhcNMTcxMjA5MjE1NDQ2WjBgMQswCQYDVQQGEwJVUzELMAkGA1UE +CBMCQ0ExEjAQBgNVBAcTCVN1bm55dmFsZTEYMBYGA1UEChMPTXkgVGVzdCBDb21w +YW55MRYwFAYDVQQDEw1hdGhlbnouc3luY2VyMFwwDQYJKoZIhvcNAQEBBQADSwAw +SAJBANImoEeD7LU+x6x5dAnNRFtrbab2yxrDXFXdwKQxlIs2PWfLeo3Xq0e2bOU6 +kQDgFz/IkMN3tEyDgQ/Nv0eAbckCAwEAAaOBxTCBwjAdBgNVHQ4EFgQUBi0msMNX +iC2XLpKjXPz1DMf9PoEwgZIGA1UdIwSBijCBh4AUBi0msMNXiC2XLpKjXPz1DMf9 +PoGhZKRiMGAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vu +bnl2YWxlMRgwFgYDVQQKEw9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVu +ei5zeW5jZXKCCQDCEboJMHu5sDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA +A0EAPnVkUdicQzk1809lHNyhSqb6p+kxjC52RfI8bZ+kACOppLLCpNu3nuuh+9yT +J1U2YLjE774n48u74k7EEmvJ3A== +-----END CERTIFICATE----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 12421309962382880561 (0xac615b9990da2f31) + Signature Algorithm: ecdsa-with-SHA1 + Issuer: C=US, ST=CA, L=Sunnyvale, O=My Test Company, CN=athenz.syncer + Validity + Not Before: Dec 9 22:04:57 2016 GMT + Not After : Dec 9 22:04:57 2017 GMT + Subject: C=US, ST=CA, L=Sunnyvale, O=My Test Company, CN=athenz.syncer + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:8d:31:eb:01:14:93:94:f4:86:c9:5a:4f:cd:c3: + 35:5c:b9:af:df:17:9c:6e:c7:02:34:32:9e:30:ab: + 71:d0:9e:01:37:55:d9:e6:e9:79:fa:81:96:2f:d2: + 5d:9c:2f:2f:7f:bb:3a:20:f7:31:3a:f2:19:b4:7e: + cd:14:85:67:fb + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Key Identifier: + 73:50:6E:8F:65:EE:96:E7:31:F4:85:82:AE:38:01:42:35:BA:54:E9 + X509v3 Authority Key Identifier: + keyid:73:50:6E:8F:65:EE:96:E7:31:F4:85:82:AE:38:01:42:35:BA:54:E9 + DirName:/C=US/ST=CA/L=Sunnyvale/O=My Test Company/CN=athenz.syncer + serial:AC:61:5B:99:90:DA:2F:31 + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: ecdsa-with-SHA1 + 30:44:02:20:58:f8:a0:a2:34:c8:e4:3e:87:7c:0b:1f:48:b9: + fc:bd:b1:f3:e3:a2:11:2c:5a:d8:e1:02:12:e9:0c:38:31:c6: + 02:20:69:a2:8d:0a:7e:d2:21:66:32:7a:5a:b2:9f:8d:55:31: + ac:ef:e2:80:19:aa:b4:7c:6c:b2:ff:aa:59:45:d1:b8 +-----BEGIN CERTIFICATE----- +MIICejCCAiKgAwIBAgIJAKxhW5mQ2i8xMAkGByqGSM49BAEwYDELMAkGA1UEBhMC +VVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUxGDAWBgNVBAoTD015 +IFRlc3QgQ29tcGFueTEWMBQGA1UEAxMNYXRoZW56LnN5bmNlcjAeFw0xNjEyMDky +MjA0NTdaFw0xNzEyMDkyMjA0NTdaMGAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD +QTESMBAGA1UEBxMJU3Vubnl2YWxlMRgwFgYDVQQKEw9NeSBUZXN0IENvbXBhbnkx +FjAUBgNVBAMTDWF0aGVuei5zeW5jZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AASNMesBFJOU9IbJWk/NwzVcua/fF5xuxwI0Mp4wq3HQngE3Vdnm6Xn6gZYv0l2c +Ly9/uzog9zE68hm0fs0UhWf7o4HFMIHCMB0GA1UdDgQWBBRzUG6PZe6W5zH0hYKu +OAFCNbpU6TCBkgYDVR0jBIGKMIGHgBRzUG6PZe6W5zH0hYKuOAFCNbpU6aFkpGIw +YDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUx +GDAWBgNVBAoTD015IFRlc3QgQ29tcGFueTEWMBQGA1UEAxMNYXRoZW56LnN5bmNl +coIJAKxhW5mQ2i8xMAwGA1UdEwQFMAMBAf8wCQYHKoZIzj0EAQNHADBEAiBY+KCi +NMjkPod8Cx9Iufy9sfPjohEsWtjhAhLpDDgxxgIgaaKNCn7SIWYyelqyn41VMazv +4oAZqrR8bLL/qllF0bg= +-----END CERTIFICATE----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 13984162866592987568 (0xc211ba09307bb9b0) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, ST=CA, L=Sunnyvale, O=My Test Company, CN=athenz.syncer + Validity + Not Before: Dec 9 21:54:46 2016 GMT + Not After : Dec 9 21:54:46 2017 GMT + Subject: C=US, ST=CA, L=Sunnyvale, O=My Test Company, CN=athenz.syncer + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (512 bit) + Modulus: + 00:d2:26:a0:47:83:ec:b5:3e:c7:ac:79:74:09:cd: + 44:5b:6b:6d:a6:f6:cb:1a:c3:5c:55:dd:c0:a4:31: + 94:8b:36:3d:67:cb:7a:8d:d7:ab:47:b6:6c:e5:3a: + 91:00:e0:17:3f:c8:90:c3:77:b4:4c:83:81:0f:cd: + bf:47:80:6d:c9 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 06:2D:26:B0:C3:57:88:2D:97:2E:92:A3:5C:FC:F5:0C:C7:FD:3E:81 + X509v3 Authority Key Identifier: + keyid:06:2D:26:B0:C3:57:88:2D:97:2E:92:A3:5C:FC:F5:0C:C7:FD:3E:81 + DirName:/C=US/ST=CA/L=Sunnyvale/O=My Test Company/CN=athenz.syncer + serial:C2:11:BA:09:30:7B:B9:B0 + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 3e:75:64:51:d8:9c:43:39:35:f3:4f:65:1c:dc:a1:4a:a6:fa: + a7:e9:31:8c:2e:76:45:f2:3c:6d:9f:a4:00:23:a9:a4:b2:c2: + a4:db:b7:9e:eb:a1:fb:dc:93:27:55:36:60:b8:c4:ef:be:27: + e3:cb:bb:e2:4e:c4:12:6b:c9:dc +-----BEGIN CERTIFICATE----- +MIICfzCCAimgAwIBAgIJAMIRugkwe7mwMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRgwFgYDVQQK +Ew9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVuei5zeW5jZXIwHhcNMTYx +MjA5MjE1NDQ2WhcNMTcxMjA5MjE1NDQ2WjBgMQswCQYDVQQGEwJVUzELMAkGA1UE +CBMCQ0ExEjAQBgNVBAcTCVN1bm55dmFsZTEYMBYGA1UEChMPTXkgVGVzdCBDb21w +YW55MRYwFAYDVQQDEw1hdGhlbnouc3luY2VyMFwwDQYJKoZIhvcNAQEBBQADSwAw +SAJBANImoEeD7LU+x6x5dAnNRFtrbab2yxrDXFXdwKQxlIs2PWfLeo3Xq0e2bOU6 +kQDgFz/IkMN3tEyDgQ/Nv0eAbckCAwEAAaOBxTCBwjAdBgNVHQ4EFgQUBi0msMNX +iC2XLpKjXPz1DMf9PoEwgZIGA1UdIwSBijCBh4AUBi0msMNXiC2XLpKjXPz1DMf9 +PoGhZKRiMGAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vu +bnl2YWxlMRgwFgYDVQQKEw9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVu +ei5zeW5jZXKCCQDCEboJMHu5sDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA +A0EAPnVkUdicQzk1809lHNyhSqb6p+kxjC52RfI8bZ+kACOppLLCpNu3nuuh+9yT +J1U2YLjE774n48u74k7EEmvJ3A== +-----END CERTIFICATE----- diff --git a/libs/java/auth_core/src/test/resources/x509_certs_no_comments.pem b/libs/java/auth_core/src/test/resources/x509_certs_no_comments.pem new file mode 100644 index 00000000000..5b1353979ed --- /dev/null +++ b/libs/java/auth_core/src/test/resources/x509_certs_no_comments.pem @@ -0,0 +1,48 @@ +-----BEGIN CERTIFICATE----- +MIICfzCCAimgAwIBAgIJAMIRugkwe7mwMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRgwFgYDVQQK +Ew9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVuei5zeW5jZXIwHhcNMTYx +MjA5MjE1NDQ2WhcNMTcxMjA5MjE1NDQ2WjBgMQswCQYDVQQGEwJVUzELMAkGA1UE +CBMCQ0ExEjAQBgNVBAcTCVN1bm55dmFsZTEYMBYGA1UEChMPTXkgVGVzdCBDb21w +YW55MRYwFAYDVQQDEw1hdGhlbnouc3luY2VyMFwwDQYJKoZIhvcNAQEBBQADSwAw +SAJBANImoEeD7LU+x6x5dAnNRFtrbab2yxrDXFXdwKQxlIs2PWfLeo3Xq0e2bOU6 +kQDgFz/IkMN3tEyDgQ/Nv0eAbckCAwEAAaOBxTCBwjAdBgNVHQ4EFgQUBi0msMNX +iC2XLpKjXPz1DMf9PoEwgZIGA1UdIwSBijCBh4AUBi0msMNXiC2XLpKjXPz1DMf9 +PoGhZKRiMGAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vu +bnl2YWxlMRgwFgYDVQQKEw9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVu +ei5zeW5jZXKCCQDCEboJMHu5sDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA +A0EAPnVkUdicQzk1809lHNyhSqb6p+kxjC52RfI8bZ+kACOppLLCpNu3nuuh+9yT +J1U2YLjE774n48u74k7EEmvJ3A== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICejCCAiKgAwIBAgIJAKxhW5mQ2i8xMAkGByqGSM49BAEwYDELMAkGA1UEBhMC +VVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUxGDAWBgNVBAoTD015 +IFRlc3QgQ29tcGFueTEWMBQGA1UEAxMNYXRoZW56LnN5bmNlcjAeFw0xNjEyMDky +MjA0NTdaFw0xNzEyMDkyMjA0NTdaMGAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD +QTESMBAGA1UEBxMJU3Vubnl2YWxlMRgwFgYDVQQKEw9NeSBUZXN0IENvbXBhbnkx +FjAUBgNVBAMTDWF0aGVuei5zeW5jZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AASNMesBFJOU9IbJWk/NwzVcua/fF5xuxwI0Mp4wq3HQngE3Vdnm6Xn6gZYv0l2c +Ly9/uzog9zE68hm0fs0UhWf7o4HFMIHCMB0GA1UdDgQWBBRzUG6PZe6W5zH0hYKu +OAFCNbpU6TCBkgYDVR0jBIGKMIGHgBRzUG6PZe6W5zH0hYKuOAFCNbpU6aFkpGIw +YDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUx +GDAWBgNVBAoTD015IFRlc3QgQ29tcGFueTEWMBQGA1UEAxMNYXRoZW56LnN5bmNl +coIJAKxhW5mQ2i8xMAwGA1UdEwQFMAMBAf8wCQYHKoZIzj0EAQNHADBEAiBY+KCi +NMjkPod8Cx9Iufy9sfPjohEsWtjhAhLpDDgxxgIgaaKNCn7SIWYyelqyn41VMazv +4oAZqrR8bLL/qllF0bg= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICfzCCAimgAwIBAgIJAMIRugkwe7mwMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRgwFgYDVQQK +Ew9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVuei5zeW5jZXIwHhcNMTYx +MjA5MjE1NDQ2WhcNMTcxMjA5MjE1NDQ2WjBgMQswCQYDVQQGEwJVUzELMAkGA1UE +CBMCQ0ExEjAQBgNVBAcTCVN1bm55dmFsZTEYMBYGA1UEChMPTXkgVGVzdCBDb21w +YW55MRYwFAYDVQQDEw1hdGhlbnouc3luY2VyMFwwDQYJKoZIhvcNAQEBBQADSwAw +SAJBANImoEeD7LU+x6x5dAnNRFtrbab2yxrDXFXdwKQxlIs2PWfLeo3Xq0e2bOU6 +kQDgFz/IkMN3tEyDgQ/Nv0eAbckCAwEAAaOBxTCBwjAdBgNVHQ4EFgQUBi0msMNX +iC2XLpKjXPz1DMf9PoEwgZIGA1UdIwSBijCBh4AUBi0msMNXiC2XLpKjXPz1DMf9 +PoGhZKRiMGAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vu +bnl2YWxlMRgwFgYDVQQKEw9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVu +ei5zeW5jZXKCCQDCEboJMHu5sDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA +A0EAPnVkUdicQzk1809lHNyhSqb6p+kxjC52RfI8bZ+kACOppLLCpNu3nuuh+9yT +J1U2YLjE774n48u74k7EEmvJ3A== +-----END CERTIFICATE----- diff --git a/servers/zms/conf/zms.properties b/servers/zms/conf/zms.properties index 267770c1fd2..4bc3c0b0d97 100644 --- a/servers/zms/conf/zms.properties +++ b/servers/zms/conf/zms.properties @@ -17,6 +17,13 @@ athenz.zms.authority_classes=com.yahoo.athenz.auth.impl.PrincipalAuthority,com.y # in the athenz.zms.authority_classes setting #athenz.zms.principal_authority_class= +# User Authority class. If defined and the server is configured to validate +# all user principals when adding them as members in a role (this is configured +# with athenz.zms.validate_user_members property), the ZMS Server will call +# the authority isValidUser api method for validation. This class must be one of +# the classes listed in the athenz.zms.authority_classes setting +#athenz.zms.user_authority_class= + # Specifies the user domain name for the current installation #athenz.user_domain=user diff --git a/servers/zts/conf/zts.properties b/servers/zts/conf/zts.properties index c46d4bc40ac..6c3025cea96 100644 --- a/servers/zts/conf/zts.properties +++ b/servers/zts/conf/zts.properties @@ -492,3 +492,12 @@ athenz.zts.cert_signer_factory_class=com.yahoo.athenz.zts.cert.impl.SelfCertSign # successful, it will create that file so that the server can # now report that the server is ready to accept production traffic #athenz.zts.health_check_path= + +# Path to the json configuration file that specifies the certificate +# bundles that can be requested from the ZTS server. An example +# of the config file available in src/test/resources/ca-bundle-file.json +# If the bundle type is x509 then the server will parse all the +# certificates and generate their PEM representation again in order +# to remove any comments present in the file thus reducing the +# content size when requested +#athenz.zts.cert_authority_bundles_fname diff --git a/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSConsts.java b/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSConsts.java index 35a91f0b1f8..dd65627ff50 100644 --- a/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSConsts.java +++ b/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSConsts.java @@ -58,6 +58,7 @@ public final class ZTSConsts { public static final String ZTS_PROP_CERT_ALLOWED_O_VALUES = "athenz.zts.cert_allowed_o_values"; public static final String ZTS_PROP_CERT_ALLOWED_OU_VALUES = "athenz.zts.cert_allowed_ou_values"; public static final String ZTS_PROP_INSTANCE_CERT_IP_FNAME = "athenz.zts.instance_cert_ip_fname"; + public static final String ZTS_PROP_CERT_BUNDLES_FNAME = "athenz.zts.cert_authority_bundles_fname"; public static final String ZTS_PROP_OAUTH_ISSUER = "athenz.zts.oauth_issuer"; public static final String ZTS_PROP_OAUTH_OPENID_SCOPE = "athenz.zts.oauth_openid_scope"; diff --git a/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSHandler.java b/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSHandler.java index 97083698191..521264188be 100644 --- a/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSHandler.java +++ b/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSHandler.java @@ -31,6 +31,7 @@ public interface ZTSHandler { Response postInstanceRegisterInformation(ResourceContext context, InstanceRegisterInformation info); InstanceIdentity postInstanceRefreshInformation(ResourceContext context, String provider, String domain, String service, String instanceId, InstanceRefreshInformation info); void deleteInstanceIdentity(ResourceContext context, String provider, String domain, String service, String instanceId); + CertificateAuthorityBundle getCertificateAuthorityBundle(ResourceContext context, String name); DomainMetrics postDomainMetrics(ResourceContext context, String domainName, DomainMetrics req); Status getStatus(ResourceContext context); Response postSSHCertRequest(ResourceContext context, SSHCertRequest certRequest); diff --git a/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSImpl.java b/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSImpl.java index 211026806ee..c7c9fcc245c 100644 --- a/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSImpl.java +++ b/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSImpl.java @@ -1300,6 +1300,7 @@ void checkRoleTokenAuthorizedServiceRequest(final Principal principal, } // Token interface + @Override public RoleToken getRoleToken(ResourceContext ctx, String domainName, String roleNames, Integer minExpiryTime, Integer maxExpiryTime, String proxyForPrincipal) { @@ -3877,7 +3878,43 @@ public Access getAccess(ResourceContext ctx, String domainName, String roleName, metric.stopTiming(timerMetric, domainName, principalDomain); return access; } - + + public CertificateAuthorityBundle getCertificateAuthorityBundle(ResourceContext ctx, String name) { + + final String caller = "getcertificateauthoritybundle"; + final String callerTiming = "getcertificateauthoritybundle_timing"; + final String principalDomain = logPrincipalAndGetDomain(ctx); + + metric.increment(HTTP_GET); + metric.increment(HTTP_REQUEST); + metric.increment(caller); + + validateRequest(ctx.request(), name, caller); + validate(name, TYPE_SIMPLE_NAME, principalDomain, caller); + + // for consistent handling of all requests, we're going to convert + // all incoming object values into lower case since ZMS Server + // saves all of its object names in lower case + + name = name.toLowerCase(); + Object timerMetric = metric.startTiming(callerTiming, name, principalDomain); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("getCertificateAuthorityBundle(name: {})", name); + } + + // fetch the requested bundle from the cert manager + + CertificateAuthorityBundle bundle = instanceCertManager.getCertificateAuthorityBundle(name); + if (bundle == null) { + throw notFoundError("getCertificateAuthorityBundle: No such bundle: " + name, + caller, ZTSConsts.ZTS_UNKNOWN_DOMAIN, principalDomain); + } + + metric.stopTiming(timerMetric, name, principalDomain); + return bundle; + } + /* * /metrics/{domainName} */ diff --git a/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSResources.java b/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSResources.java index bd2ffd0149c..6f6d91b2007 100644 --- a/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSResources.java +++ b/servers/zts/src/main/java/com/yahoo/athenz/zts/ZTSResources.java @@ -500,6 +500,30 @@ public void deleteInstanceIdentity(@PathParam("provider") String provider, @Path } } + @GET + @Path("/cacerts/{name}") + @Produces(MediaType.APPLICATION_JSON) + public CertificateAuthorityBundle getCertificateAuthorityBundle(@PathParam("name") String name) { + try { + ResourceContext context = this.delegate.newResourceContext(this.request, this.response); + context.authenticate(); + return this.delegate.getCertificateAuthorityBundle(context, name); + } catch (ResourceException e) { + int code = e.getCode(); + switch (code) { + case ResourceException.BAD_REQUEST: + throw typedException(code, e, ResourceError.class); + case ResourceException.NOT_FOUND: + throw typedException(code, e, ResourceError.class); + case ResourceException.UNAUTHORIZED: + throw typedException(code, e, ResourceError.class); + default: + System.err.println("*** Warning: undeclared exception (" + code + ") for resource getCertificateAuthorityBundle"); + throw typedException(code, e, ResourceError.class); + } + } + } + @POST @Path("/metrics/{domainName}") @Consumes(MediaType.APPLICATION_JSON) diff --git a/servers/zts/src/main/java/com/yahoo/athenz/zts/cert/InstanceCertManager.java b/servers/zts/src/main/java/com/yahoo/athenz/zts/cert/InstanceCertManager.java index 42a0bed56bb..c573944b841 100644 --- a/servers/zts/src/main/java/com/yahoo/athenz/zts/cert/InstanceCertManager.java +++ b/servers/zts/src/main/java/com/yahoo/athenz/zts/cert/InstanceCertManager.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019 Oath Holdings Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.yahoo.athenz.zts.cert; import java.io.File; @@ -13,6 +28,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.yahoo.athenz.auth.util.Crypto; +import com.yahoo.athenz.auth.util.CryptoException; import com.yahoo.athenz.zts.utils.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,6 +37,7 @@ import com.yahoo.athenz.zts.SSHCertRequest; import com.yahoo.athenz.zts.ZTSConsts; import com.yahoo.athenz.zts.InstanceIdentity; +import com.yahoo.athenz.zts.CertificateAuthorityBundle; import com.yahoo.athenz.zts.ResourceException; import com.yahoo.athenz.auth.Authorizer; @@ -35,6 +52,8 @@ public class InstanceCertManager { private static final Logger LOGGER = LoggerFactory.getLogger(InstanceCertManager.class); + private static final String CA_TYPE_X509 = "x509"; + private Authorizer authorizer; private CertSigner certSigner; private SSHSigner sshSigner; @@ -48,6 +67,7 @@ public class InstanceCertManager { private boolean responseSendSSHSignerCerts; private boolean responseSendX509SignerCerts; private ObjectMapper jsonMapper; + private Map certAuthorityBundles = null; public InstanceCertManager(final PrivateKeyStore keyStore, Authorizer authorizer, boolean readOnlyMode) { @@ -75,24 +95,12 @@ public InstanceCertManager(final PrivateKeyStore keyStore, Authorizer authorizer loadCertificateObjectStore(keyStore); - // check to see if we have been provided with a x.509/ssh certificate - // bundle or we need to fetch one from the certsigner - - responseSendSSHSignerCerts = Boolean.parseBoolean( - System.getProperty(ZTSConsts.ZTS_PROP_RESP_SSH_SIGNER_CERTS, "true")); - responseSendX509SignerCerts = Boolean.parseBoolean( - System.getProperty(ZTSConsts.ZTS_PROP_RESP_X509_SIGNER_CERTS, "true")); - - // if we're not asked to skip sending certificate signers then - // check to see if we need to load them from files instead of - // certsigner directly + // load any configuration wrt certificate signers and any + // configured certificate bundles - if (responseSendX509SignerCerts) { - caX509CertificateSigner = loadCertificateBundle(ZTSConsts.ZTS_PROP_X509_CA_CERT_FNAME); - } - if (responseSendSSHSignerCerts) { - sshUserCertificateSigner = loadCertificateBundle(ZTSConsts.ZTS_PROP_SSH_USER_CA_CERT_FNAME); - sshHostCertificateSigner = loadCertificateBundle(ZTSConsts.ZTS_PROP_SSH_HOST_CA_CERT_FNAME); + if (!loadCertificateAuthorityBundles()) { + throw new ResourceException(ResourceException.INTERNAL_SERVER_ERROR, + "Unable to load Certificate Authority Bundles"); } // load our allowed cert refresh and instance register ip blocks @@ -123,6 +131,124 @@ void shutdown() { } } + private boolean loadCertificateAuthorityBundles() { + + // check to see if we have been provided with a x.509/ssh certificate + // bundle or we need to fetch one from the certsigner + + responseSendSSHSignerCerts = Boolean.parseBoolean( + System.getProperty(ZTSConsts.ZTS_PROP_RESP_SSH_SIGNER_CERTS, "true")); + responseSendX509SignerCerts = Boolean.parseBoolean( + System.getProperty(ZTSConsts.ZTS_PROP_RESP_X509_SIGNER_CERTS, "true")); + + // if we're not asked to skip sending certificate signers then + // check to see if we need to load them from files instead of + // certsigner directly + + if (responseSendX509SignerCerts) { + caX509CertificateSigner = loadCertificateBundle(ZTSConsts.ZTS_PROP_X509_CA_CERT_FNAME); + } + if (responseSendSSHSignerCerts) { + sshUserCertificateSigner = loadCertificateBundle(ZTSConsts.ZTS_PROP_SSH_USER_CA_CERT_FNAME); + sshHostCertificateSigner = loadCertificateBundle(ZTSConsts.ZTS_PROP_SSH_HOST_CA_CERT_FNAME); + } + + // now let's fetch our configured certificate authority bundles + + certAuthorityBundles = new HashMap<>(); + + final String caBundleFile = System.getProperty(ZTSConsts.ZTS_PROP_CERT_BUNDLES_FNAME); + if (caBundleFile == null || caBundleFile.isEmpty()) { + return true; + } + + byte[] data = readFileContents(caBundleFile); + if (data == null) { + return false; + } + + CertBundles certBundles = null; + try { + certBundles = jsonMapper.readValue(data, CertBundles.class); + } catch (Exception ex) { + LOGGER.error("Unable to parse CA bundle file: {} - {}", caBundleFile, ex.getMessage()); + } + + if (certBundles == null) { + return false; + } + + List bundleList = certBundles.getCertBundles(); + if (bundleList == null || bundleList.isEmpty()) { + LOGGER.error("No CA bundles available in the file: {}", caBundleFile); + return false; + } + + for (CertBundle bundle : bundleList) { + if (!processCertificateAuthorityBundle(bundle)) { + return false; + } + } + + return true; + } + + private boolean processCertificateAuthorityBundle(CertBundle bundle) { + + final String name = bundle.getName(); + final String fileName = bundle.getFilename(); + if (fileName == null || fileName.isEmpty()) { + LOGGER.error("Bundle {} does not have a file configured", name); + return false; + } + + // if this is x.509 certificate bundle then we're going + // to extract our data, validate all certs and regenerate + // our pem data to remove any other comments from the data + // file to minimize the data size + + String bundleData = null; + if (CA_TYPE_X509.equalsIgnoreCase(bundle.getType())) { + bundleData = extractX509CertificateBundle(fileName); + } else { + byte[] data = readFileContents(fileName); + if (data != null) { + bundleData = new String(data); + } + } + + if (bundleData == null) { + LOGGER.error("Unable to load bundle {} from file {}", name, fileName); + return false; + } + + CertificateAuthorityBundle certAuthorityBundle = new CertificateAuthorityBundle() + .setName(name) + .setCerts(bundleData); + + certAuthorityBundles.put(name, certAuthorityBundle); + return true; + } + + String extractX509CertificateBundle(final String filename) { + + try { + // first load our certificates file to make sure + // all the certs in the file are valid + + X509Certificate[] x509Certs = Crypto.loadX509Certificates(filename); + + // then re-generate the certs in pem format to + // remove any comments/etc to minimize the data size + + return Crypto.x509CertificatesToPEM(x509Certs); + + } catch (CryptoException ex) { + LOGGER.error("Unable to load certificate file", ex); + return null; + } + } + private boolean loadAllowedInstanceCertIPAddresses() { instanceCertIPBlocks = new HashMap<>(); @@ -329,7 +455,11 @@ private void loadCertificateObjectStore(PrivateKeyStore keyStore) { public void setCertStore(CertRecordStore certStore) { this.certStore = certStore; } - + + public CertificateAuthorityBundle getCertificateAuthorityBundle(final String name) { + return certAuthorityBundles.get(name); + } + public X509CertRecord getX509CertRecord(final String provider, X509Certificate cert) { if (certStore == null) { diff --git a/servers/zts/src/main/java/com/yahoo/athenz/zts/utils/CertBundle.java b/servers/zts/src/main/java/com/yahoo/athenz/zts/utils/CertBundle.java new file mode 100644 index 00000000000..8ba79907039 --- /dev/null +++ b/servers/zts/src/main/java/com/yahoo/athenz/zts/utils/CertBundle.java @@ -0,0 +1,47 @@ +/* + * Copyright 2019 Oath Holdings, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yahoo.athenz.zts.utils; + +public class CertBundle { + + private String name; + private String type; + private String filename; + + public String getFilename() { + return filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/servers/zts/src/main/java/com/yahoo/athenz/zts/utils/CertBundles.java b/servers/zts/src/main/java/com/yahoo/athenz/zts/utils/CertBundles.java new file mode 100644 index 00000000000..50072b4e684 --- /dev/null +++ b/servers/zts/src/main/java/com/yahoo/athenz/zts/utils/CertBundles.java @@ -0,0 +1,31 @@ +/* + * Copyright 2019 Oath Holdings, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yahoo.athenz.zts.utils; + +import java.util.List; + +public class CertBundles { + + private List certBundles; + + public List getCertBundles() { + return certBundles; + } + + public void setCertBundles(List certBundles) { + this.certBundles = certBundles; + } +} diff --git a/servers/zts/src/test/java/com/yahoo/athenz/zts/ZTSImplTest.java b/servers/zts/src/test/java/com/yahoo/athenz/zts/ZTSImplTest.java index 7ec370912cd..241b8e9ff6f 100644 --- a/servers/zts/src/test/java/com/yahoo/athenz/zts/ZTSImplTest.java +++ b/servers/zts/src/test/java/com/yahoo/athenz/zts/ZTSImplTest.java @@ -96,7 +96,6 @@ public class ZTSImplTest { private ZTSAuthorizer authorizer = null; private DataStore store = null; private PrivateKey privateKey = null; - PublicKey publicKey = null; private AuditLogger auditLogger = null; private CloudStore cloudStore = null; @Mock private CloudStore mockCloudStore; @@ -275,16 +274,6 @@ private ResourceContext createResourceContext(Principal principal, HttpServletRe Mockito.when(rsrcCtxWrapper.response()).thenReturn(mockServletResponse); return rsrcCtxWrapper; } - - Object getWebAppExcEntity(javax.ws.rs.WebApplicationException wex) { - javax.ws.rs.core.Response resp = wex.getResponse(); - return resp.getEntity(); - } - - Object getWebAppExcMapValue(javax.ws.rs.WebApplicationException wex, String header) { - javax.ws.rs.core.MultivaluedMap mvmap = wex.getResponse().getMetadata(); - return mvmap.getFirst(header); - } private static Role createRoleObject(String domainName, String roleName, String trust) { @@ -10703,4 +10692,46 @@ public void testGetConfiguredRoleListExpiryTimeMinsMultipleDomainSetting() { assertEquals(zts.getConfiguredRoleListExpiryTimeMins(requestedRoleList), 90); } + + @Test + public void testGetCertificateAuthorityBundle() { + + Principal principal = SimplePrincipal.create("user_domain", "user1", + "v=U1;d=user_domain;n=user;s=signature", 0, null); + ResourceContext context = createResourceContext(principal); + + ChangeLogStore structStore = new ZMSFileChangeLogStore("/tmp/zts_server_unit_tests/zts_root", + privateKey, "0"); + + DataStore store = new DataStore(structStore, null); + ZTSImpl ztsImpl = new ZTSImpl(mockCloudStore, store); + + System.setProperty(ZTSConsts.ZTS_PROP_CERT_BUNDLES_FNAME, "src/test/resources/ca-bundle-file.json"); + InstanceCertManager certManager = new InstanceCertManager(null, null, true); + ztsImpl.instanceCertManager = certManager; + + CertificateAuthorityBundle bundle = ztsImpl.getCertificateAuthorityBundle(context, "athenz"); + assertNotNull(bundle); + + bundle = ztsImpl.getCertificateAuthorityBundle(context, "system"); + assertNotNull(bundle); + + bundle = ztsImpl.getCertificateAuthorityBundle(context, "ssh"); + assertNotNull(bundle); + + try { + ztsImpl.getCertificateAuthorityBundle(context, "unknown"); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.NOT_FOUND); + } + + try { + ztsImpl.getCertificateAuthorityBundle(context, "athenz test"); + fail(); + } catch (ResourceException ignored) { + } + + System.clearProperty(ZTSConsts.ZTS_PROP_CERT_BUNDLES_FNAME); + } } diff --git a/servers/zts/src/test/java/com/yahoo/athenz/zts/cert/InstanceCertManagerTest.java b/servers/zts/src/test/java/com/yahoo/athenz/zts/cert/InstanceCertManagerTest.java index 63df922f00d..9346dcb4132 100644 --- a/servers/zts/src/test/java/com/yahoo/athenz/zts/cert/InstanceCertManagerTest.java +++ b/servers/zts/src/test/java/com/yahoo/athenz/zts/cert/InstanceCertManagerTest.java @@ -25,7 +25,6 @@ import com.yahoo.athenz.auth.util.Crypto; import com.yahoo.athenz.common.server.cert.CertSigner; -import com.yahoo.athenz.zts.store.impl.ZMSFileChangeLogStore; import com.yahoo.athenz.zts.utils.IPBlock; import com.yahoo.athenz.auth.Principal; @@ -204,7 +203,7 @@ public void testGetX509CertRecordNoCertStore() { instance.setCertSigner(null); instance.setCertStore(null); - X509CertRecord certRecord = instance.getX509CertRecord("ostk", (X509Certificate) null); + X509CertRecord certRecord = instance.getX509CertRecord("ostk", null); assertNull(certRecord); certRecord = instance.getX509CertRecord("ostk", "instance-id", "athenz.production"); @@ -577,7 +576,7 @@ public void testLoadAllowedCertIPAddressesInvalidFile() { System.setProperty(ZTSConsts.ZTS_PROP_INSTANCE_CERT_IP_FNAME, "invalid-file"); try { - InstanceCertManager instance = new InstanceCertManager(null, null, true); + new InstanceCertManager(null, null, true); fail(); } catch (Exception ex) { assertTrue(ex.getMessage().contains("Unable to load Provider Allowed IP Blocks")); @@ -592,7 +591,7 @@ public void testLoadAllowedCertIPAddressesInvalidJson() { System.setProperty(ZTSConsts.ZTS_PROP_INSTANCE_CERT_IP_FNAME, "src/test/resources/instance_cert_ipblocks_invalid_json.txt"); try { - InstanceCertManager instance = new InstanceCertManager(null, null, true); + new InstanceCertManager(null, null, true); fail(); } catch (Exception ex) { assertTrue(ex.getMessage().contains("Unable to load Provider Allowed IP Blocks")); @@ -607,7 +606,7 @@ public void testLoadAllowedCertIPAddressesInvalidIPFile() { System.setProperty(ZTSConsts.ZTS_PROP_INSTANCE_CERT_IP_FNAME, "src/test/resources/instance_cert_ipblocks_invalid_ip.txt"); try { - InstanceCertManager instance = new InstanceCertManager(null, null, true); + new InstanceCertManager(null, null, true); fail(); } catch (Exception ex) { assertTrue(ex.getMessage().contains("Unable to load Provider Allowed IP Blocks")); @@ -695,7 +694,7 @@ public void testInvalidCertSignerClass() { System.setProperty(ZTSConsts.ZTS_PROP_CERT_SIGNER_FACTORY_CLASS, "invalid"); try { - InstanceCertManager instanceCertManager = new InstanceCertManager(null, null, true); + new InstanceCertManager(null, null, true); fail(); } catch (Exception ex) { assertTrue(ex.getMessage().contains("Invalid certsigner class")); @@ -708,7 +707,7 @@ public void testInvalidSSHSignerClass() { System.setProperty(ZTSConsts.ZTS_PROP_SSH_SIGNER_FACTORY_CLASS, "invalid"); try { - InstanceCertManager instanceCertManager = new InstanceCertManager(null, null, true); + new InstanceCertManager(null, null, true); fail(); } catch (Exception ex) { assertTrue(ex.getMessage().contains("Invalid sshsigner class")); @@ -721,7 +720,7 @@ public void testInvalidCertRecordStoreClass() { System.setProperty(ZTSConsts.ZTS_PROP_CERT_RECORD_STORE_FACTORY_CLASS, "invalid"); try { - InstanceCertManager instanceCertManager = new InstanceCertManager(null, null, true); + new InstanceCertManager(null, null, true); fail(); } catch (Exception ex) { assertTrue(ex.getMessage().contains("Invalid cert record store factory class")); @@ -734,7 +733,7 @@ public void testInitWithIncorrectCABundle() { System.setProperty(ZTSConsts.ZTS_PROP_X509_CA_CERT_FNAME, "invalid-file"); try { - InstanceCertManager instanceCertManager = new InstanceCertManager(null, null, false); + new InstanceCertManager(null, null, false); fail(); } catch (ResourceException ex) { assertEquals(500, ex.getCode()); @@ -838,4 +837,104 @@ public void testLogNoCertStore() { instance.log(null, null, null, null, null); instance.shutdown(); } + + @Test + public void loadCertificateAuthorityBundlesInvalidFile() { + + System.setProperty(ZTSConsts.ZTS_PROP_CERT_BUNDLES_FNAME, "invalid-file"); + + try { + new InstanceCertManager(null, null, true); + fail(); + } catch (Exception ex) { + assertTrue(ex.getMessage().contains("Unable to load Certificate Authority Bundles")); + } + + System.setProperty(ZTSConsts.ZTS_PROP_CERT_BUNDLES_FNAME, "src/test/resources/ca-bundle-file-invalid.json"); + + try { + new InstanceCertManager(null, null, true); + fail(); + } catch (Exception ex) { + assertTrue(ex.getMessage().contains("Unable to load Certificate Authority Bundles")); + } + + System.setProperty(ZTSConsts.ZTS_PROP_CERT_BUNDLES_FNAME, "src/test/resources/ca-bundle-file-missing-filename.json"); + + try { + new InstanceCertManager(null, null, true); + fail(); + } catch (Exception ex) { + assertTrue(ex.getMessage().contains("Unable to load Certificate Authority Bundles")); + } + + System.setProperty(ZTSConsts.ZTS_PROP_CERT_BUNDLES_FNAME, "src/test/resources/ca-bundle-file-empty.json"); + + try { + new InstanceCertManager(null, null, true); + fail(); + } catch (Exception ex) { + assertTrue(ex.getMessage().contains("Unable to load Certificate Authority Bundles")); + } + + System.setProperty(ZTSConsts.ZTS_PROP_CERT_BUNDLES_FNAME, "src/test/resources/ca-bundle-file-invalid-x509.json"); + + try { + new InstanceCertManager(null, null, true); + fail(); + } catch (Exception ex) { + assertTrue(ex.getMessage().contains("Unable to load Certificate Authority Bundles")); + } + + System.setProperty(ZTSConsts.ZTS_PROP_CERT_BUNDLES_FNAME, "src/test/resources/ca-bundle-file-invalid-ssh.json"); + + try { + new InstanceCertManager(null, null, true); + fail(); + } catch (Exception ex) { + assertTrue(ex.getMessage().contains("Unable to load Certificate Authority Bundles")); + } + + System.clearProperty(ZTSConsts.ZTS_PROP_CERT_BUNDLES_FNAME); + } + + @Test + public void loadCertificateAuthorityBundles() throws IOException { + + System.setProperty(ZTSConsts.ZTS_PROP_CERT_BUNDLES_FNAME, "src/test/resources/ca-bundle-file.json"); + + InstanceCertManager certManager = new InstanceCertManager(null, null, true); + + // test our valid bundles. athenz and system should present same data + // since one just includes comments + + CertificateAuthorityBundle bundleAthenz = certManager.getCertificateAuthorityBundle("athenz"); + assertNotNull(bundleAthenz); + + assertEquals(bundleAthenz.getName(), "athenz"); + final String athenzData = bundleAthenz.getCerts(); + + CertificateAuthorityBundle bundleSystem = certManager.getCertificateAuthorityBundle("system"); + assertNotNull(bundleSystem); + + assertEquals(bundleSystem.getName(), "system"); + final String systemData = bundleSystem.getCerts(); + + assertEquals(athenzData, systemData); + + // compare the contents with the actual file contents + + File caFile = new File("src/test/resources/x509_certs_no_comments.pem"); + byte[] data = Files.readAllBytes(Paths.get(caFile.toURI())); + assertEquals(athenzData, new String(data)); + + CertificateAuthorityBundle bundleSsh = certManager.getCertificateAuthorityBundle("ssh"); + assertNotNull(bundleSsh); + + assertEquals(bundleSsh.getName(), "ssh"); + final String sshData = bundleSsh.getCerts(); + assertEquals(sshData, "ssh-certificate-authority-keys"); + + System.clearProperty(ZTSConsts.ZTS_PROP_CERT_BUNDLES_FNAME); + } } diff --git a/servers/zts/src/test/java/com/yahoo/athenz/zts/utils/CertBundleTest.java b/servers/zts/src/test/java/com/yahoo/athenz/zts/utils/CertBundleTest.java new file mode 100644 index 00000000000..f641a6f687a --- /dev/null +++ b/servers/zts/src/test/java/com/yahoo/athenz/zts/utils/CertBundleTest.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019 Oath Holdings Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yahoo.athenz.zts.utils; + +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +public class CertBundleTest { + + @Test + public void testCertBundle() { + + CertBundle bundle = new CertBundle(); + bundle.setFilename("filename"); + bundle.setName("athenz"); + bundle.setType("x.509"); + + assertEquals(bundle.getFilename(), "filename"); + assertEquals(bundle.getName(), "athenz"); + assertEquals(bundle.getType(), "x.509"); + } +} diff --git a/servers/zts/src/test/java/com/yahoo/athenz/zts/utils/CertBundlesTest.java b/servers/zts/src/test/java/com/yahoo/athenz/zts/utils/CertBundlesTest.java new file mode 100644 index 00000000000..e3ff9d5fea1 --- /dev/null +++ b/servers/zts/src/test/java/com/yahoo/athenz/zts/utils/CertBundlesTest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2019 Oath Holdings Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.yahoo.athenz.zts.utils; + +import org.testng.annotations.Test; +import java.util.Collections; + +import static org.testng.Assert.*; + +public class CertBundlesTest { + + @Test + public void testCertBundles() { + CertBundles bundles = new CertBundles(); + + bundles.setCertBundles(null); + assertNull(bundles.getCertBundles()); + + CertBundle bundle = new CertBundle(); + bundle.setFilename("filename"); + bundle.setName("athenz"); + bundle.setType("x.509"); + + bundles.setCertBundles(Collections.singletonList(bundle)); + assertNotNull(bundles.getCertBundles()); + } +} diff --git a/servers/zts/src/test/java/com/yahoo/athenz/zts/utils/IPBlockTest.java b/servers/zts/src/test/java/com/yahoo/athenz/zts/utils/IPBlockTest.java index 15b366089b7..94b45946ccf 100644 --- a/servers/zts/src/test/java/com/yahoo/athenz/zts/utils/IPBlockTest.java +++ b/servers/zts/src/test/java/com/yahoo/athenz/zts/utils/IPBlockTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019 Oath Holdings Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.yahoo.athenz.zts.utils; import org.testng.annotations.Test; diff --git a/servers/zts/src/test/java/com/yahoo/athenz/zts/utils/IPPrefixesTest.java b/servers/zts/src/test/java/com/yahoo/athenz/zts/utils/IPPrefixesTest.java index 7ddd9944ee4..feec6c2110f 100644 --- a/servers/zts/src/test/java/com/yahoo/athenz/zts/utils/IPPrefixesTest.java +++ b/servers/zts/src/test/java/com/yahoo/athenz/zts/utils/IPPrefixesTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2019 Oath Holdings Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.yahoo.athenz.zts.utils; import org.testng.annotations.Test; diff --git a/servers/zts/src/test/resources/ca-bundle-file-empty.json b/servers/zts/src/test/resources/ca-bundle-file-empty.json new file mode 100644 index 00000000000..9854af16e64 --- /dev/null +++ b/servers/zts/src/test/resources/ca-bundle-file-empty.json @@ -0,0 +1,4 @@ +{ + "certBundles": [ + ] +} diff --git a/servers/zts/src/test/resources/ca-bundle-file-invalid-ssh.json b/servers/zts/src/test/resources/ca-bundle-file-invalid-ssh.json new file mode 100644 index 00000000000..40a7552323f --- /dev/null +++ b/servers/zts/src/test/resources/ca-bundle-file-invalid-ssh.json @@ -0,0 +1,19 @@ +{ + "certBundles": [ + { + "name": "athenz", + "filename": "src/test/resources/invalid-file", + "type": "ssh" + }, + { + "name": "system", + "filename": "src/test/resources/x509_certs_no_comments.pem", + "type": "x509" + }, + { + "name": "ssh", + "filename": "src/test/resources/ssh-ca-certs", + "type": "ssh" + } + ] +} diff --git a/servers/zts/src/test/resources/ca-bundle-file-invalid-x509.json b/servers/zts/src/test/resources/ca-bundle-file-invalid-x509.json new file mode 100644 index 00000000000..6c41d138402 --- /dev/null +++ b/servers/zts/src/test/resources/ca-bundle-file-invalid-x509.json @@ -0,0 +1,19 @@ +{ + "certBundles": [ + { + "name": "athenz", + "filename": "src/test/resources/invalid-file", + "type": "x509" + }, + { + "name": "system", + "filename": "src/test/resources/x509_certs_no_comments.pem", + "type": "x509" + }, + { + "name": "ssh", + "filename": "src/test/resources/ssh-ca-certs", + "type": "ssh" + } + ] +} diff --git a/servers/zts/src/test/resources/ca-bundle-file-invalid.json b/servers/zts/src/test/resources/ca-bundle-file-invalid.json new file mode 100644 index 00000000000..92d7e1bed45 --- /dev/null +++ b/servers/zts/src/test/resources/ca-bundle-file-invalid.json @@ -0,0 +1,13 @@ +{ + "certBundles": [ + { + "name": "athenz", + "filename": "src/test/resources/x509_certs_comments.pem", + "type": "x509" + }, + { + "name": "system", + filename + } + ] +} \ No newline at end of file diff --git a/servers/zts/src/test/resources/ca-bundle-file-missing-filename.json b/servers/zts/src/test/resources/ca-bundle-file-missing-filename.json new file mode 100644 index 00000000000..6a3f4d65706 --- /dev/null +++ b/servers/zts/src/test/resources/ca-bundle-file-missing-filename.json @@ -0,0 +1,18 @@ +{ + "certBundles": [ + { + "name": "athenz", + "type": "x509" + }, + { + "name": "system", + "filename": "src/test/resources/x509_certs_no_comments.pem", + "type": "x509" + }, + { + "name": "ssh", + "filename": "src/test/resources/ssh-ca-certs", + "type": "ssh" + } + ] +} diff --git a/servers/zts/src/test/resources/ca-bundle-file.json b/servers/zts/src/test/resources/ca-bundle-file.json new file mode 100644 index 00000000000..efe7c92d9da --- /dev/null +++ b/servers/zts/src/test/resources/ca-bundle-file.json @@ -0,0 +1,19 @@ +{ + "certBundles": [ + { + "name": "athenz", + "filename": "src/test/resources/x509_certs_comments.pem", + "type": "x509" + }, + { + "name": "system", + "filename": "src/test/resources/x509_certs_no_comments.pem", + "type": "x509" + }, + { + "name": "ssh", + "filename": "src/test/resources/ssh-ca-certs", + "type": "ssh" + } + ] +} diff --git a/servers/zts/src/test/resources/ssh-ca-certs b/servers/zts/src/test/resources/ssh-ca-certs new file mode 100644 index 00000000000..b5314332506 --- /dev/null +++ b/servers/zts/src/test/resources/ssh-ca-certs @@ -0,0 +1 @@ +ssh-certificate-authority-keys \ No newline at end of file diff --git a/servers/zts/src/test/resources/x509_certs_comments.pem b/servers/zts/src/test/resources/x509_certs_comments.pem new file mode 100644 index 00000000000..9933e4826cf --- /dev/null +++ b/servers/zts/src/test/resources/x509_certs_comments.pem @@ -0,0 +1,154 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 13984162866592987568 (0xc211ba09307bb9b0) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, ST=CA, L=Sunnyvale, O=My Test Company, CN=athenz.syncer + Validity + Not Before: Dec 9 21:54:46 2016 GMT + Not After : Dec 9 21:54:46 2017 GMT + Subject: C=US, ST=CA, L=Sunnyvale, O=My Test Company, CN=athenz.syncer + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (512 bit) + Modulus: + 00:d2:26:a0:47:83:ec:b5:3e:c7:ac:79:74:09:cd: + 44:5b:6b:6d:a6:f6:cb:1a:c3:5c:55:dd:c0:a4:31: + 94:8b:36:3d:67:cb:7a:8d:d7:ab:47:b6:6c:e5:3a: + 91:00:e0:17:3f:c8:90:c3:77:b4:4c:83:81:0f:cd: + bf:47:80:6d:c9 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 06:2D:26:B0:C3:57:88:2D:97:2E:92:A3:5C:FC:F5:0C:C7:FD:3E:81 + X509v3 Authority Key Identifier: + keyid:06:2D:26:B0:C3:57:88:2D:97:2E:92:A3:5C:FC:F5:0C:C7:FD:3E:81 + DirName:/C=US/ST=CA/L=Sunnyvale/O=My Test Company/CN=athenz.syncer + serial:C2:11:BA:09:30:7B:B9:B0 + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 3e:75:64:51:d8:9c:43:39:35:f3:4f:65:1c:dc:a1:4a:a6:fa: + a7:e9:31:8c:2e:76:45:f2:3c:6d:9f:a4:00:23:a9:a4:b2:c2: + a4:db:b7:9e:eb:a1:fb:dc:93:27:55:36:60:b8:c4:ef:be:27: + e3:cb:bb:e2:4e:c4:12:6b:c9:dc +-----BEGIN CERTIFICATE----- +MIICfzCCAimgAwIBAgIJAMIRugkwe7mwMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRgwFgYDVQQK +Ew9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVuei5zeW5jZXIwHhcNMTYx +MjA5MjE1NDQ2WhcNMTcxMjA5MjE1NDQ2WjBgMQswCQYDVQQGEwJVUzELMAkGA1UE +CBMCQ0ExEjAQBgNVBAcTCVN1bm55dmFsZTEYMBYGA1UEChMPTXkgVGVzdCBDb21w +YW55MRYwFAYDVQQDEw1hdGhlbnouc3luY2VyMFwwDQYJKoZIhvcNAQEBBQADSwAw +SAJBANImoEeD7LU+x6x5dAnNRFtrbab2yxrDXFXdwKQxlIs2PWfLeo3Xq0e2bOU6 +kQDgFz/IkMN3tEyDgQ/Nv0eAbckCAwEAAaOBxTCBwjAdBgNVHQ4EFgQUBi0msMNX +iC2XLpKjXPz1DMf9PoEwgZIGA1UdIwSBijCBh4AUBi0msMNXiC2XLpKjXPz1DMf9 +PoGhZKRiMGAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vu +bnl2YWxlMRgwFgYDVQQKEw9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVu +ei5zeW5jZXKCCQDCEboJMHu5sDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA +A0EAPnVkUdicQzk1809lHNyhSqb6p+kxjC52RfI8bZ+kACOppLLCpNu3nuuh+9yT +J1U2YLjE774n48u74k7EEmvJ3A== +-----END CERTIFICATE----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 12421309962382880561 (0xac615b9990da2f31) + Signature Algorithm: ecdsa-with-SHA1 + Issuer: C=US, ST=CA, L=Sunnyvale, O=My Test Company, CN=athenz.syncer + Validity + Not Before: Dec 9 22:04:57 2016 GMT + Not After : Dec 9 22:04:57 2017 GMT + Subject: C=US, ST=CA, L=Sunnyvale, O=My Test Company, CN=athenz.syncer + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:8d:31:eb:01:14:93:94:f4:86:c9:5a:4f:cd:c3: + 35:5c:b9:af:df:17:9c:6e:c7:02:34:32:9e:30:ab: + 71:d0:9e:01:37:55:d9:e6:e9:79:fa:81:96:2f:d2: + 5d:9c:2f:2f:7f:bb:3a:20:f7:31:3a:f2:19:b4:7e: + cd:14:85:67:fb + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Key Identifier: + 73:50:6E:8F:65:EE:96:E7:31:F4:85:82:AE:38:01:42:35:BA:54:E9 + X509v3 Authority Key Identifier: + keyid:73:50:6E:8F:65:EE:96:E7:31:F4:85:82:AE:38:01:42:35:BA:54:E9 + DirName:/C=US/ST=CA/L=Sunnyvale/O=My Test Company/CN=athenz.syncer + serial:AC:61:5B:99:90:DA:2F:31 + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: ecdsa-with-SHA1 + 30:44:02:20:58:f8:a0:a2:34:c8:e4:3e:87:7c:0b:1f:48:b9: + fc:bd:b1:f3:e3:a2:11:2c:5a:d8:e1:02:12:e9:0c:38:31:c6: + 02:20:69:a2:8d:0a:7e:d2:21:66:32:7a:5a:b2:9f:8d:55:31: + ac:ef:e2:80:19:aa:b4:7c:6c:b2:ff:aa:59:45:d1:b8 +-----BEGIN CERTIFICATE----- +MIICejCCAiKgAwIBAgIJAKxhW5mQ2i8xMAkGByqGSM49BAEwYDELMAkGA1UEBhMC +VVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUxGDAWBgNVBAoTD015 +IFRlc3QgQ29tcGFueTEWMBQGA1UEAxMNYXRoZW56LnN5bmNlcjAeFw0xNjEyMDky +MjA0NTdaFw0xNzEyMDkyMjA0NTdaMGAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD +QTESMBAGA1UEBxMJU3Vubnl2YWxlMRgwFgYDVQQKEw9NeSBUZXN0IENvbXBhbnkx +FjAUBgNVBAMTDWF0aGVuei5zeW5jZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AASNMesBFJOU9IbJWk/NwzVcua/fF5xuxwI0Mp4wq3HQngE3Vdnm6Xn6gZYv0l2c +Ly9/uzog9zE68hm0fs0UhWf7o4HFMIHCMB0GA1UdDgQWBBRzUG6PZe6W5zH0hYKu +OAFCNbpU6TCBkgYDVR0jBIGKMIGHgBRzUG6PZe6W5zH0hYKuOAFCNbpU6aFkpGIw +YDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUx +GDAWBgNVBAoTD015IFRlc3QgQ29tcGFueTEWMBQGA1UEAxMNYXRoZW56LnN5bmNl +coIJAKxhW5mQ2i8xMAwGA1UdEwQFMAMBAf8wCQYHKoZIzj0EAQNHADBEAiBY+KCi +NMjkPod8Cx9Iufy9sfPjohEsWtjhAhLpDDgxxgIgaaKNCn7SIWYyelqyn41VMazv +4oAZqrR8bLL/qllF0bg= +-----END CERTIFICATE----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 13984162866592987568 (0xc211ba09307bb9b0) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=US, ST=CA, L=Sunnyvale, O=My Test Company, CN=athenz.syncer + Validity + Not Before: Dec 9 21:54:46 2016 GMT + Not After : Dec 9 21:54:46 2017 GMT + Subject: C=US, ST=CA, L=Sunnyvale, O=My Test Company, CN=athenz.syncer + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (512 bit) + Modulus: + 00:d2:26:a0:47:83:ec:b5:3e:c7:ac:79:74:09:cd: + 44:5b:6b:6d:a6:f6:cb:1a:c3:5c:55:dd:c0:a4:31: + 94:8b:36:3d:67:cb:7a:8d:d7:ab:47:b6:6c:e5:3a: + 91:00:e0:17:3f:c8:90:c3:77:b4:4c:83:81:0f:cd: + bf:47:80:6d:c9 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 06:2D:26:B0:C3:57:88:2D:97:2E:92:A3:5C:FC:F5:0C:C7:FD:3E:81 + X509v3 Authority Key Identifier: + keyid:06:2D:26:B0:C3:57:88:2D:97:2E:92:A3:5C:FC:F5:0C:C7:FD:3E:81 + DirName:/C=US/ST=CA/L=Sunnyvale/O=My Test Company/CN=athenz.syncer + serial:C2:11:BA:09:30:7B:B9:B0 + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 3e:75:64:51:d8:9c:43:39:35:f3:4f:65:1c:dc:a1:4a:a6:fa: + a7:e9:31:8c:2e:76:45:f2:3c:6d:9f:a4:00:23:a9:a4:b2:c2: + a4:db:b7:9e:eb:a1:fb:dc:93:27:55:36:60:b8:c4:ef:be:27: + e3:cb:bb:e2:4e:c4:12:6b:c9:dc +-----BEGIN CERTIFICATE----- +MIICfzCCAimgAwIBAgIJAMIRugkwe7mwMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRgwFgYDVQQK +Ew9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVuei5zeW5jZXIwHhcNMTYx +MjA5MjE1NDQ2WhcNMTcxMjA5MjE1NDQ2WjBgMQswCQYDVQQGEwJVUzELMAkGA1UE +CBMCQ0ExEjAQBgNVBAcTCVN1bm55dmFsZTEYMBYGA1UEChMPTXkgVGVzdCBDb21w +YW55MRYwFAYDVQQDEw1hdGhlbnouc3luY2VyMFwwDQYJKoZIhvcNAQEBBQADSwAw +SAJBANImoEeD7LU+x6x5dAnNRFtrbab2yxrDXFXdwKQxlIs2PWfLeo3Xq0e2bOU6 +kQDgFz/IkMN3tEyDgQ/Nv0eAbckCAwEAAaOBxTCBwjAdBgNVHQ4EFgQUBi0msMNX +iC2XLpKjXPz1DMf9PoEwgZIGA1UdIwSBijCBh4AUBi0msMNXiC2XLpKjXPz1DMf9 +PoGhZKRiMGAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vu +bnl2YWxlMRgwFgYDVQQKEw9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVu +ei5zeW5jZXKCCQDCEboJMHu5sDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA +A0EAPnVkUdicQzk1809lHNyhSqb6p+kxjC52RfI8bZ+kACOppLLCpNu3nuuh+9yT +J1U2YLjE774n48u74k7EEmvJ3A== +-----END CERTIFICATE----- diff --git a/servers/zts/src/test/resources/x509_certs_no_comments.pem b/servers/zts/src/test/resources/x509_certs_no_comments.pem new file mode 100644 index 00000000000..5b1353979ed --- /dev/null +++ b/servers/zts/src/test/resources/x509_certs_no_comments.pem @@ -0,0 +1,48 @@ +-----BEGIN CERTIFICATE----- +MIICfzCCAimgAwIBAgIJAMIRugkwe7mwMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRgwFgYDVQQK +Ew9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVuei5zeW5jZXIwHhcNMTYx +MjA5MjE1NDQ2WhcNMTcxMjA5MjE1NDQ2WjBgMQswCQYDVQQGEwJVUzELMAkGA1UE +CBMCQ0ExEjAQBgNVBAcTCVN1bm55dmFsZTEYMBYGA1UEChMPTXkgVGVzdCBDb21w +YW55MRYwFAYDVQQDEw1hdGhlbnouc3luY2VyMFwwDQYJKoZIhvcNAQEBBQADSwAw +SAJBANImoEeD7LU+x6x5dAnNRFtrbab2yxrDXFXdwKQxlIs2PWfLeo3Xq0e2bOU6 +kQDgFz/IkMN3tEyDgQ/Nv0eAbckCAwEAAaOBxTCBwjAdBgNVHQ4EFgQUBi0msMNX +iC2XLpKjXPz1DMf9PoEwgZIGA1UdIwSBijCBh4AUBi0msMNXiC2XLpKjXPz1DMf9 +PoGhZKRiMGAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vu +bnl2YWxlMRgwFgYDVQQKEw9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVu +ei5zeW5jZXKCCQDCEboJMHu5sDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA +A0EAPnVkUdicQzk1809lHNyhSqb6p+kxjC52RfI8bZ+kACOppLLCpNu3nuuh+9yT +J1U2YLjE774n48u74k7EEmvJ3A== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICejCCAiKgAwIBAgIJAKxhW5mQ2i8xMAkGByqGSM49BAEwYDELMAkGA1UEBhMC +VVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUxGDAWBgNVBAoTD015 +IFRlc3QgQ29tcGFueTEWMBQGA1UEAxMNYXRoZW56LnN5bmNlcjAeFw0xNjEyMDky +MjA0NTdaFw0xNzEyMDkyMjA0NTdaMGAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJD +QTESMBAGA1UEBxMJU3Vubnl2YWxlMRgwFgYDVQQKEw9NeSBUZXN0IENvbXBhbnkx +FjAUBgNVBAMTDWF0aGVuei5zeW5jZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AASNMesBFJOU9IbJWk/NwzVcua/fF5xuxwI0Mp4wq3HQngE3Vdnm6Xn6gZYv0l2c +Ly9/uzog9zE68hm0fs0UhWf7o4HFMIHCMB0GA1UdDgQWBBRzUG6PZe6W5zH0hYKu +OAFCNbpU6TCBkgYDVR0jBIGKMIGHgBRzUG6PZe6W5zH0hYKuOAFCNbpU6aFkpGIw +YDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTdW5ueXZhbGUx +GDAWBgNVBAoTD015IFRlc3QgQ29tcGFueTEWMBQGA1UEAxMNYXRoZW56LnN5bmNl +coIJAKxhW5mQ2i8xMAwGA1UdEwQFMAMBAf8wCQYHKoZIzj0EAQNHADBEAiBY+KCi +NMjkPod8Cx9Iufy9sfPjohEsWtjhAhLpDDgxxgIgaaKNCn7SIWYyelqyn41VMazv +4oAZqrR8bLL/qllF0bg= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICfzCCAimgAwIBAgIJAMIRugkwe7mwMA0GCSqGSIb3DQEBBQUAMGAxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vubnl2YWxlMRgwFgYDVQQK +Ew9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVuei5zeW5jZXIwHhcNMTYx +MjA5MjE1NDQ2WhcNMTcxMjA5MjE1NDQ2WjBgMQswCQYDVQQGEwJVUzELMAkGA1UE +CBMCQ0ExEjAQBgNVBAcTCVN1bm55dmFsZTEYMBYGA1UEChMPTXkgVGVzdCBDb21w +YW55MRYwFAYDVQQDEw1hdGhlbnouc3luY2VyMFwwDQYJKoZIhvcNAQEBBQADSwAw +SAJBANImoEeD7LU+x6x5dAnNRFtrbab2yxrDXFXdwKQxlIs2PWfLeo3Xq0e2bOU6 +kQDgFz/IkMN3tEyDgQ/Nv0eAbckCAwEAAaOBxTCBwjAdBgNVHQ4EFgQUBi0msMNX +iC2XLpKjXPz1DMf9PoEwgZIGA1UdIwSBijCBh4AUBi0msMNXiC2XLpKjXPz1DMf9 +PoGhZKRiMGAxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAGA1UEBxMJU3Vu +bnl2YWxlMRgwFgYDVQQKEw9NeSBUZXN0IENvbXBhbnkxFjAUBgNVBAMTDWF0aGVu +ei5zeW5jZXKCCQDCEboJMHu5sDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUA +A0EAPnVkUdicQzk1809lHNyhSqb6p+kxjC52RfI8bZ+kACOppLLCpNu3nuuh+9yT +J1U2YLjE774n48u74k7EEmvJ3A== +-----END CERTIFICATE-----