You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2016/02/23 16:40:06 UTC
cxf-fediz git commit: [FEDIZ-153] - Got an initial test-case working
with a lot of hacks
Repository: cxf-fediz
Updated Branches:
refs/heads/master 49b028e3e -> a700f7ae2
[FEDIZ-153] - Got an initial test-case working with a lot of hacks
Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/a700f7ae
Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/a700f7ae
Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/a700f7ae
Branch: refs/heads/master
Commit: a700f7ae2dd0eba786af620f5c671eb5cf62fc4f
Parents: 49b028e
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Tue Feb 23 15:39:41 2016 +0000
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Tue Feb 23 15:39:41 2016 +0000
----------------------------------------------------------------------
.../TrustedIdpOIDCProtocolHandler.java | 382 ++++++++++++-------
.../fediz/service/oidc/FedizSubjectCreator.java | 2 +-
systests/federation/oidc/pom.xml | 1 +
.../oidc/src/test/resources/realmb.cert | 3 +
4 files changed, 257 insertions(+), 131 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/a700f7ae/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java
index f4ffe40..c6ebeba 100644
--- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java
@@ -30,34 +30,23 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Date;
import java.util.List;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.Response;
+import org.w3c.dom.Document;
import org.w3c.dom.Element;
-import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
-
import org.apache.cxf.fediz.core.FederationConstants;
-import org.apache.cxf.fediz.core.config.FedizContext;
-import org.apache.cxf.fediz.core.config.TrustManager;
-import org.apache.cxf.fediz.core.config.jaxb.AudienceUris;
-import org.apache.cxf.fediz.core.config.jaxb.CertificateStores;
-import org.apache.cxf.fediz.core.config.jaxb.ContextConfig;
-import org.apache.cxf.fediz.core.config.jaxb.FederationProtocolType;
-import org.apache.cxf.fediz.core.config.jaxb.KeyStoreType;
-import org.apache.cxf.fediz.core.config.jaxb.TrustManagersType;
-import org.apache.cxf.fediz.core.config.jaxb.TrustedIssuerType;
-import org.apache.cxf.fediz.core.config.jaxb.TrustedIssuers;
-import org.apache.cxf.fediz.core.config.jaxb.ValidationType;
import org.apache.cxf.fediz.core.exception.ProcessingException;
-import org.apache.cxf.fediz.core.processor.FederationProcessorImpl;
-import org.apache.cxf.fediz.core.processor.FedizProcessor;
-import org.apache.cxf.fediz.core.processor.FedizRequest;
-import org.apache.cxf.fediz.core.processor.FedizResponse;
+import org.apache.cxf.fediz.core.util.CertsUtils;
+import org.apache.cxf.fediz.core.util.DOMUtils;
import org.apache.cxf.fediz.service.idp.domain.Idp;
import org.apache.cxf.fediz.service.idp.domain.TrustedIdp;
import org.apache.cxf.fediz.service.idp.spi.TrustedIdpProtocolHandler;
@@ -66,13 +55,29 @@ import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxrs.client.ClientConfiguration;
import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
+import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
+import org.apache.cxf.rs.security.jose.jwt.JwtConstants;
+import org.apache.cxf.rs.security.jose.jwt.JwtToken;
import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
+import org.apache.cxf.rs.security.oauth2.provider.OAuthJSONProvider;
import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
import org.apache.cxf.ws.security.tokenstore.SecurityToken;
import org.apache.wss4j.common.crypto.CertificateStore;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.crypto.Merlin;
+import org.apache.wss4j.common.ext.WSSecurityException;
+import org.apache.wss4j.common.saml.SAMLCallback;
+import org.apache.wss4j.common.saml.SAMLUtil;
+import org.apache.wss4j.common.saml.SamlAssertionWrapper;
+import org.apache.wss4j.common.saml.bean.ConditionsBean;
+import org.apache.wss4j.common.saml.bean.SubjectBean;
+import org.apache.wss4j.common.saml.bean.Version;
+import org.apache.wss4j.common.saml.builder.SAML2Constants;
+import org.apache.wss4j.common.util.DOM2Writer;
import org.apache.xml.security.exceptions.Base64DecodingException;
-import org.apache.xml.security.stax.impl.util.IDGenerator;
import org.apache.xml.security.utils.Base64;
+import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@@ -192,10 +197,10 @@ public class TrustedIdpOIDCProtocolHandler implements TrustedIdpProtocolHandler
String address = "http://localhost:8080/auth/realms/realmb/protocol/openid-connect/token";
List<Object> providers = new ArrayList<Object>();
- providers.add(new JacksonJsonProvider());
+ providers.add(new OAuthJSONProvider());
WebClient client =
- WebClient.create(address, providers, "consumer-id", "90d5da25-e900-443f-a5d5-feb3bb060800", null);
+ WebClient.create(address, providers, "consumer-id", "7c220ee6-77e2-43d3-b531-6ede8a581698", null);
ClientConfiguration config = WebClient.getConfig(client);
@@ -212,123 +217,121 @@ public class TrustedIdpOIDCProtocolHandler implements TrustedIdpProtocolHandler
Response response = client.post(form);
ClientAccessToken accessToken = response.readEntity(ClientAccessToken.class);
- System.out.println("AT: " + accessToken.getTokenKey());
-
- }
-
- try {
- String whr = (String) WebUtils.getAttributeFromFlowScope(context,
- FederationConstants.PARAM_HOME_REALM);
-
- if (whr == null) {
- LOG.warn("Home realm is null");
- throw new IllegalStateException("Home realm is null");
- }
-
- String wresult = (String) WebUtils.getAttributeFromFlowScope(context,
- FederationConstants.PARAM_RESULT);
-
- if (wresult == null) {
- LOG.warn("Parameter wresult not found");
- throw new IllegalStateException("No security token issued");
+ String idToken = accessToken.getParameters().get("id_token");
+ if (idToken == null) {
+ LOG.warn("No IdToken received from the OIDC IdP");
+ return null;
}
-
- FedizContext fedContext = getFedizContext(idp, trustedIdp);
-
- FedizRequest wfReq = new FedizRequest();
- wfReq.setAction(FederationConstants.ACTION_SIGNIN);
- wfReq.setResponseToken(wresult);
-
- FedizProcessor wfProc = new FederationProcessorImpl();
- FedizResponse wfResp = wfProc.processRequest(wfReq, fedContext);
-
- fedContext.close();
-
- Element e = wfResp.getToken();
-
- // Create new Security token with new id.
- // Parameters for freshness computation are copied from original IDP_TOKEN
- String id = IDGenerator.generateID("_");
- SecurityToken idpToken = new SecurityToken(id,
- wfResp.getTokenCreated(), wfResp.getTokenExpires());
-
- idpToken.setToken(e);
- LOG.info("[IDP_TOKEN={}] for user '{}' created from [RP_TOKEN={}] issued by home realm [{}/{}]",
- id, wfResp.getUsername(), wfResp.getUniqueTokenId(), whr, wfResp.getIssuer());
- LOG.debug("Created date={}", wfResp.getTokenCreated());
- LOG.debug("Expired date={}", wfResp.getTokenExpires());
- if (LOG.isDebugEnabled()) {
- LOG.debug("Validated 'wresult' : "
- + System.getProperty("line.separator") + wresult);
+
+ try {
+ X509Certificate validatingCert = getCertificate(trustedIdp);
+ if (validatingCert == null) {
+ LOG.warn("No X.509 Certificate configured for signature validation");
+ return null;
+ }
+
+ /*String whr = (String) WebUtils.getAttributeFromFlowScope(context,
+ FederationConstants.PARAM_HOME_REALM);
+ if (whr == null) {
+ LOG.warn("Home realm is null");
+ throw new IllegalStateException("Home realm is null");
+ }
+
+ String wresult = (String) WebUtils.getAttributeFromFlowScope(context,
+ FederationConstants.PARAM_RESULT);
+ if (wresult == null) {
+ LOG.warn("Parameter wresult not found");
+ throw new IllegalStateException("No security token issued");
+ }*/
+
+ // Parse the received Token
+ JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(idToken);
+ JwtToken jwt = jwtConsumer.getJwtToken();
+
+ for (String claim : jwt.getClaims().asMap().keySet()) {
+ System.out.println("CLAIM: " + claim + " " + jwt.getClaim(claim));
+ }
+
+ if (!jwtConsumer.verifySignatureWith(validatingCert, SignatureAlgorithm.RS256)) {
+ LOG.warn("Signature does not validate");
+ return null;
+ }
+
+ Date created = new Date();
+ if (jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT) != null) {
+ created = new Date((long)jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT) * 1000L);
+ }
+ Date expires = new Date((long)jwt.getClaim(JwtConstants.CLAIM_EXPIRY) * 1000L);
+ System.out.println("IAT: " + created);
+ System.out.println("EXP: " + expires);
+
+ // Convert into a SAML Token
+ SamlAssertionWrapper assertion = createSamlAssertion(idp, jwt, created, expires);
+ Document doc = DOMUtils.createDocument();
+ Element token = assertion.toDOM(doc);
+ System.out.println("TOK: " + DOM2Writer.nodeToString(token));
+
+ // Create new Security token with new id.
+ // Parameters for freshness computation are copied from original IDP_TOKEN
+ SecurityToken idpToken = new SecurityToken(assertion.getId(), created, expires);
+ idpToken.setToken(token);
+
+ // idpToken.setToken(e);
+ // LOG.info("[IDP_TOKEN={}] for user '{}' created from [RP_TOKEN={}] issued by home realm [{}/{}]",
+ // id, wfResp.getUsername(), wfResp.getUniqueTokenId(), whr, wfResp.getIssuer());
+ LOG.debug("Created date={}", created);
+ LOG.debug("Expired date={}", expires);
+ if (LOG.isDebugEnabled()) {
+ //LOG.debug("Validated 'wresult' : "
+ // + System.getProperty("line.separator") + wresult);
+ }
+ return idpToken;
+ } catch (IllegalStateException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ LOG.warn("Unexpected exception occured", ex);
+ throw new IllegalStateException("Unexpected exception occured: " + ex.getMessage());
}
- return idpToken;
- } catch (IllegalStateException ex) {
- throw ex;
- } catch (Exception ex) {
- LOG.warn("Unexpected exception occured", ex);
- throw new IllegalStateException("Unexpected exception occured: " + ex.getMessage());
}
+ return null;
}
- private FedizContext getFedizContext(Idp idpConfig,
- TrustedIdp trustedIdpConfig) throws ProcessingException {
-
- ContextConfig config = new ContextConfig();
-
- config.setName("whatever");
-
- // Configure certificate store
- String certificate = trustedIdpConfig.getCertificate();
- boolean isCertificateLocation = !certificate.startsWith("-----BEGIN CERTIFICATE");
- if (isCertificateLocation) {
- CertificateStores certStores = new CertificateStores();
- TrustManagersType tm0 = new TrustManagersType();
- KeyStoreType ks0 = new KeyStoreType();
- ks0.setType("PEM");
- // ks0.setType("JKS");
- // ks0.setPassword("changeit");
- ks0.setFile(trustedIdpConfig.getCertificate());
- tm0.setKeyStore(ks0);
- certStores.getTrustManager().add(tm0);
- config.setCertificateStores(certStores);
- }
-
- // Configure trusted IDP
- TrustedIssuers trustedIssuers = new TrustedIssuers();
- TrustedIssuerType ti0 = new TrustedIssuerType();
- ti0.setCertificateValidation(ValidationType.PEER_TRUST);
- ti0.setName(trustedIdpConfig.getName());
- // ti0.setSubject(".*CN=www.sts.com.*");
- trustedIssuers.getIssuer().add(ti0);
- config.setTrustedIssuers(trustedIssuers);
-
- FederationProtocolType protocol = new FederationProtocolType();
- config.setProtocol(protocol);
-
- AudienceUris audienceUris = new AudienceUris();
- audienceUris.getAudienceItem().add(idpConfig.getRealm());
- config.setAudienceUris(audienceUris);
-
- FedizContext fedContext = new FedizContext(config);
- if (!isCertificateLocation) {
- CertificateStore cs = null;
-
- X509Certificate cert;
- try {
- cert = parseCertificate(trustedIdpConfig.getCertificate());
- } catch (Exception ex) {
- LOG.error("Failed to parse trusted certificate", ex);
- throw new ProcessingException("Failed to parse trusted certificate");
+ private X509Certificate getCertificate(TrustedIdp trustedIdp)
+ throws CertificateException, Base64DecodingException, IOException {
+ String certificate = trustedIdp.getCertificate();
+ if (certificate != null) {
+ boolean isCertificateLocation = !certificate.startsWith("-----BEGIN CERTIFICATE");
+ if (isCertificateLocation) {
+ InputStream is = null;
+ try {
+ is = Merlin.loadInputStream(Thread.currentThread().getContextClassLoader(), certificate);
+
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) certFactory.generateCertificate(is);
+ } catch (WSSecurityException ex) {
+ LOG.error("Failed to load keystore " + certificate, ex);
+ throw new RuntimeException("Failed to load keystore " + certificate);
+ } catch (IOException ex) {
+ LOG.error("Failed to read keystore", ex);
+ throw new RuntimeException("Failed to read keystore");
+ } catch (CertificateException ex) {
+ // This is ok as it could be a WSS4J properties file
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // Do nothing
+ }
+ }
+ }
+ } else {
+ return parseCertificate(certificate);
}
- cs = new CertificateStore(Collections.singletonList(cert).toArray(new X509Certificate[0]));
-
- TrustManager tm = new TrustManager(cs);
- fedContext.getCertificateStores().add(tm);
- }
+ }
- fedContext.init();
- return fedContext;
+ return null;
}
private X509Certificate parseCertificate(String certificate)
@@ -343,5 +346,124 @@ public class TrustedIdpOIDCProtocolHandler implements TrustedIdpProtocolHandler
}
}
+ private SamlAssertionWrapper createSamlAssertion(Idp idp, JwtToken token,
+ Date created,
+ Date expires) throws Exception {
+ SamlCallbackHandler callbackHandler = new SamlCallbackHandler();
+ callbackHandler.setIssuer(idp.getServiceDisplayName());
+
+ // Subject
+ // TODO
+ SubjectBean subjectBean =
+ new SubjectBean((String)token.getClaim("preferred_username"),
+ SAML2Constants.NAMEID_FORMAT_UNSPECIFIED,
+ SAML2Constants.CONF_BEARER);
+ callbackHandler.setSubjectBean(subjectBean);
+
+ // Conditions
+ ConditionsBean conditionsBean = new ConditionsBean();
+ conditionsBean.setNotAfter(new DateTime(expires));
+ if (token.getClaim(JwtConstants.CLAIM_NOT_BEFORE) != null) {
+ DateTime notBefore = new DateTime((long)token.getClaim(JwtConstants.CLAIM_NOT_BEFORE) * 1000L);
+ conditionsBean.setNotBefore(notBefore);
+ } else {
+ conditionsBean.setNotBefore(new DateTime());
+ }
+ callbackHandler.setConditionsBean(conditionsBean);
+
+ SAMLCallback samlCallback = new SAMLCallback();
+ SAMLUtil.doSAMLCallback(callbackHandler, samlCallback);
+
+ SamlAssertionWrapper assertion = new SamlAssertionWrapper(samlCallback);
+
+ Crypto crypto = getCrypto(idp.getCertificate());
+ assertion.signAssertion(crypto.getDefaultX509Identifier(), idp.getCertificatePassword(),
+ crypto, false);
+
+ return assertion;
+ }
+
+ private Crypto getCrypto(String certificate) throws ProcessingException {
+ if (certificate == null) {
+ return null;
+ }
+
+ // First see if it's a certificate file
+ InputStream is = null;
+ try {
+ is = Merlin.loadInputStream(Thread.currentThread().getContextClassLoader(), certificate);
+
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ X509Certificate cert = (X509Certificate) certFactory.generateCertificate(is);
+ return new CertificateStore(new X509Certificate[]{cert});
+ } catch (WSSecurityException ex) {
+ LOG.error("Failed to load keystore " + certificate, ex);
+ throw new RuntimeException("Failed to load keystore " + certificate);
+ } catch (IOException ex) {
+ LOG.error("Failed to read keystore", ex);
+ throw new RuntimeException("Failed to read keystore");
+ } catch (CertificateException ex) {
+ // This is ok as it could be a WSS4J properties file
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // Do nothing
+ }
+ }
+ }
+
+ // Maybe it's a WSS4J properties file...
+ return CertsUtils.createCrypto(certificate);
+ }
+
+ private static class SamlCallbackHandler implements CallbackHandler {
+ private ConditionsBean conditionsBean;
+ private SubjectBean subjectBean;
+ private String issuer;
+
+ /**
+ * Set the SubjectBean
+ */
+ public void setSubjectBean(SubjectBean subjectBean) {
+ this.subjectBean = subjectBean;
+ }
+
+ /**
+ * Set the ConditionsBean
+ */
+ public void setConditionsBean(ConditionsBean conditionsBean) {
+ this.conditionsBean = conditionsBean;
+ }
+
+ /**
+ * Set the issuer name
+ */
+ public void setIssuer(String issuerName) {
+ this.issuer = issuerName;
+ }
+
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+ for (Callback callback : callbacks) {
+ if (callback instanceof SAMLCallback) {
+ SAMLCallback samlCallback = (SAMLCallback) callback;
+
+ // Set the Subject
+ if (subjectBean != null) {
+ samlCallback.setSubject(subjectBean);
+ }
+ samlCallback.setSamlVersion(Version.SAML_20);
+
+ // Set the issuer
+ samlCallback.setIssuer(issuer);
+
+ // Set the conditions
+ samlCallback.setConditions(conditionsBean);
+ }
+ }
+ }
+
+ }
}
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/a700f7ae/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/FedizSubjectCreator.java
----------------------------------------------------------------------
diff --git a/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/FedizSubjectCreator.java b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/FedizSubjectCreator.java
index f134039..ac1aff6 100644
--- a/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/FedizSubjectCreator.java
+++ b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/FedizSubjectCreator.java
@@ -75,7 +75,7 @@ public class FedizSubjectCreator implements SubjectCreator {
public IdToken convertToIdToken(Element samlToken,
String subjectName,
ClaimCollection claims) {
- // The current SAML Assertion represents anauthentication record.
+ // The current SAML Assertion represents an authentication record.
// It has to be translated into IdToken (JWT) so that it can be returned
// to client applications participating in various OIDC flows.
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/a700f7ae/systests/federation/oidc/pom.xml
----------------------------------------------------------------------
diff --git a/systests/federation/oidc/pom.xml b/systests/federation/oidc/pom.xml
index 4df592a..7d11584 100644
--- a/systests/federation/oidc/pom.xml
+++ b/systests/federation/oidc/pom.xml
@@ -229,6 +229,7 @@
<directory>${basedir}/src/test/resources</directory>
<includes>
<include>entities-realma.xml</include>
+ <include>realmb.cert</include>
</includes>
<filtering>true</filtering>
</resource>
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/a700f7ae/systests/federation/oidc/src/test/resources/realmb.cert
----------------------------------------------------------------------
diff --git a/systests/federation/oidc/src/test/resources/realmb.cert b/systests/federation/oidc/src/test/resources/realmb.cert
new file mode 100644
index 0000000..de19105
--- /dev/null
+++ b/systests/federation/oidc/src/test/resources/realmb.cert
@@ -0,0 +1,3 @@
+-----BEGIN CERTIFICATE-----
+MIICmzCCAYMCBgFTDfCUaDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZyZWFsbWIwHhcNMTYwMjIzMTE0MTIzWhcNMjYwMjIzMTE0MzAzWjARMQ8wDQYDVQQDDAZyZWFsbWIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqFm0GlXs+y+Xhc6xcSPPUwRHXF5WxYIJWcBGS2ooMKHOTnq+js2/t3GBsKuUZiEudflu3q/O5GGHoFQXrib4JGw+y+QMOJhQ8OBg+Hj7iQPNI0xOy8lxUnEblt7ksPaZE1c4dOhqWZMDMt7TSsa90g2j2B+dOtdm1EJKMT9VVF/NpFQSYu6c15VDvvVm1tz0R+/BCkfWAtvU6MDrxWbDEcVTHqpCsrzZ2+n1+IgVuULtKyLgJS5fIRiquu0B51QehMVfko15zLY6UaNvlrq5FcVBHWJrM4pZdgXujfgXOxLSbIw1bzjM0yRnlMvJ05qTTdIq/8uLcUgjZ+VGoiKLLAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEB0UAoWnUOwGFtjyWYs00WjMpfCf00jbZRzBakqfVmJwqFzCuP1D5hArL2L3floKsp50fspzJpWytoARcT+FYr/Xju/0CDTtbMoul6p6Lc+y+YFghd7tOD0rw26lRwsuZZwycc7gqRoqycy6NzUEnHee514UgSQM8nILyZT58CUYKYOhnNdKHcthjwE7IRWGvEMWJ09Pe8OvQMKUWnMU9jpxAk6jbmInapEGoiOT8HQtnsVGruWnIpHwwl1kYJXDRfrVtSi64DtbGEOTfC/+jbWsmH4LwX1LQRsD4HrDoAZUmSn7gI3jmUxckYAuX6gv9Ve4fZbGe9s09tirGQZ5lg=
+-----END CERTIFICATE-----