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-----