--- old/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java Fri May 22 20:10:48 2015 +++ new/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java Fri May 22 20:10:48 2015 @@ -1058,6 +1058,39 @@ } /** + * Determines if the keystore {@code Entry} for the specified + * {@code alias} is an instance or subclass of the specified + * {@code entryClass}. + * + * @param alias the alias name + * @param entryClass the entry class + * + * @return true if the keystore {@code Entry} for the specified + * {@code alias} is an instance or subclass of the + * specified {@code entryClass}, false otherwise + * + * @since 1.5 + */ + @Override + public boolean + engineEntryInstanceOf(String alias, + Class entryClass) + { + if (entryClass == KeyStore.TrustedCertificateEntry.class) { + return engineIsCertificateEntry(alias); + } + + Entry entry = entries.get(alias.toLowerCase(Locale.ENGLISH)); + if (entryClass == KeyStore.PrivateKeyEntry.class) { + return (entry != null && entry instanceof PrivateKeyEntry); + } + if (entryClass == KeyStore.SecretKeyEntry.class) { + return (entry != null && entry instanceof SecretKeyEntry); + } + return false; + } + + /** * Returns the (alias) name of the first keystore entry whose certificate * matches the given certificate. * @@ -1089,7 +1122,7 @@ } else { continue; } - if (certElem.equals(cert)) { + if (certElem != null && certElem.equals(cert)) { return alias; } } @@ -1932,7 +1965,12 @@ safeContentsData = safeContents.getData(); } else if (contentType.equals((Object)ContentInfo.ENCRYPTED_DATA_OID)) { if (password == null) { - continue; + + if (debug != null) { + debug.println("Warning: skipping PKCS#7 encryptedData" + + " content-type - no password was supplied"); + } + continue; } if (debug != null) { @@ -1974,8 +2012,9 @@ password = new char[1]; continue; } - throw new IOException( - "failed to decrypt safe contents entry: " + e, e); + throw new IOException("keystore password was incorrect", + new UnrecoverableKeyException( + "failed to decrypt safe contents entry: " + e)); } } } else { --- old/src/share/classes/sun/security/provider/JavaKeyStore.java Fri May 22 20:10:49 2015 +++ new/src/share/classes/sun/security/provider/JavaKeyStore.java Fri May 22 20:10:49 2015 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,9 +31,10 @@ import java.security.cert.CertificateFactory; import java.security.cert.CertificateException; import java.util.*; -import sun.misc.IOUtils; +import sun.misc.IOUtils; import sun.security.pkcs.EncryptedPrivateKeyInfo; +import sun.security.pkcs12.PKCS12KeyStore; /** * This class provides the keystore implementation referred to as "JKS". @@ -64,6 +65,13 @@ return alias; } } + + // special JKS that supports JKS and PKCS12 file formats + public static final class DualFormatJKS extends KeyStoreDelegator { + public DualFormatJKS() { + super("JKS", JKS.class, "PKCS12", PKCS12KeyStore.class); + } + } private static final int MAGIC = 0xfeedfeed; private static final int VERSION_1 = 0x01; --- old/src/share/classes/sun/security/provider/SunEntries.java Fri May 22 20:10:50 2015 +++ new/src/share/classes/sun/security/provider/SunEntries.java Fri May 22 20:10:49 2015 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -228,7 +228,8 @@ /* * KeyStore */ - map.put("KeyStore.JKS", "sun.security.provider.JavaKeyStore$JKS"); + map.put("KeyStore.JKS", + "sun.security.provider.JavaKeyStore$DualFormatJKS"); map.put("KeyStore.CaseExactJKS", "sun.security.provider.JavaKeyStore$CaseExactJKS"); map.put("KeyStore.DKS", "sun.security.provider.DomainKeyStore$DKS"); --- old/src/share/lib/security/java.security-aix Fri May 22 20:10:50 2015 +++ new/src/share/lib/security/java.security-aix Fri May 22 20:10:50 2015 @@ -171,6 +171,15 @@ keystore.type=jks # +# Controls compatibility mode for the JKS keystore type. +# +# When set to 'true', the JKS keystore type supports loading +# keystore files in either JKS or PKCS12 format. When set to 'false' +# it supports loading only JKS keystore files. +# +keystore.type.compat=true + +# # List of comma-separated packages that start with or equal this string # will cause a security exception to be thrown when # passed to checkPackageAccess unless the --- old/src/share/lib/security/java.security-linux Fri May 22 20:10:51 2015 +++ new/src/share/lib/security/java.security-linux Fri May 22 20:10:51 2015 @@ -171,6 +171,15 @@ keystore.type=jks # +# Controls compatibility mode for the JKS keystore type. +# +# When set to 'true', the JKS keystore type supports loading +# keystore files in either JKS or PKCS12 format. When set to 'false' +# it supports loading only JKS keystore files. +# +keystore.type.compat=true + +# # List of comma-separated packages that start with or equal this string # will cause a security exception to be thrown when # passed to checkPackageAccess unless the --- old/src/share/lib/security/java.security-macosx Fri May 22 20:10:51 2015 +++ new/src/share/lib/security/java.security-macosx Fri May 22 20:10:51 2015 @@ -172,6 +172,15 @@ keystore.type=jks # +# Controls compatibility mode for the JKS keystore type. +# +# When set to 'true', the JKS keystore type supports loading +# keystore files in either JKS or PKCS12 format. When set to 'false' +# it supports loading only JKS keystore files. +# +keystore.type.compat=true + +# # List of comma-separated packages that start with or equal this string # will cause a security exception to be thrown when # passed to checkPackageAccess unless the --- old/src/share/lib/security/java.security-solaris Fri May 22 20:10:52 2015 +++ new/src/share/lib/security/java.security-solaris Fri May 22 20:10:52 2015 @@ -173,6 +173,15 @@ keystore.type=jks # +# Controls compatibility mode for the JKS keystore type. +# +# When set to 'true', the JKS keystore type supports loading +# keystore files in either JKS or PKCS12 format. When set to 'false' +# it supports loading only JKS keystore files. +# +keystore.type.compat=true + +# # List of comma-separated packages that start with or equal this string # will cause a security exception to be thrown when # passed to checkPackageAccess unless the --- old/src/share/lib/security/java.security-windows Fri May 22 20:10:53 2015 +++ new/src/share/lib/security/java.security-windows Fri May 22 20:10:52 2015 @@ -172,6 +172,15 @@ keystore.type=jks # +# Controls compatibility mode for the JKS keystore type. +# +# When set to 'true', the JKS keystore type supports loading +# keystore files in either JKS or PKCS12 format. When set to 'false' +# it supports loading only JKS keystore files. +# +keystore.type.compat=true + +# # List of comma-separated packages that start with or equal this string # will cause a security exception to be thrown when # passed to checkPackageAccess unless the --- /dev/null Fri May 22 20:10:53 2015 +++ new/src/share/classes/sun/security/provider/KeyStoreDelegator.java Fri May 22 20:10:53 2015 @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +import java.io.*; +import java.security.*; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.CertificateException; +import java.util.*; + +import sun.security.util.Debug; + +/** + * This class delegates to a primary or secondary keystore implementation. + * + * @since 1.8 + */ + +class KeyStoreDelegator extends KeyStoreSpi { + + private static final String KEYSTORE_TYPE_COMPAT = "keystore.type.compat"; + private static final Debug debug = Debug.getInstance("keystore"); + + private final String primaryType; // the primary keystore's type + private final String secondaryType; // the secondary keystore's type + private final Class primaryKeyStore; + // the primary keystore's class + private final Class secondaryKeyStore; + // the secondary keystore's class + private String type; // the delegate's type + private KeyStoreSpi keystore; // the delegate + private boolean compatModeEnabled = true; + + public KeyStoreDelegator( + String primaryType, + Class primaryKeyStore, + String secondaryType, + Class secondaryKeyStore) { + + // Check whether compatibility mode has been disabled + // (Use inner-class instead of lambda to avoid init/ClassLoader problem) + compatModeEnabled = "true".equalsIgnoreCase( + AccessController.doPrivileged( + new PrivilegedAction() { + public String run() { + return Security.getProperty(KEYSTORE_TYPE_COMPAT); + } + } + )); + + if (compatModeEnabled) { + this.primaryType = primaryType; + this.secondaryType = secondaryType; + this.primaryKeyStore = primaryKeyStore; + this.secondaryKeyStore = secondaryKeyStore; + } else { + this.primaryType = primaryType; + this.secondaryType = null; + this.primaryKeyStore = primaryKeyStore; + this.secondaryKeyStore = null; + + if (debug != null) { + debug.println("WARNING: compatibility mode disabled for " + + primaryType + " and " + secondaryType + " keystore types"); + } + } + } + + @Override + public Key engineGetKey(String alias, char[] password) + throws NoSuchAlgorithmException, UnrecoverableKeyException { + return keystore.engineGetKey(alias, password); + } + + @Override + public Certificate[] engineGetCertificateChain(String alias) { + return keystore.engineGetCertificateChain(alias); + } + + @Override + public Certificate engineGetCertificate(String alias) { + return keystore.engineGetCertificate(alias); + } + + @Override + public Date engineGetCreationDate(String alias) { + return keystore.engineGetCreationDate(alias); + } + + @Override + public void engineSetKeyEntry(String alias, Key key, char[] password, + Certificate[] chain) throws KeyStoreException { + keystore.engineSetKeyEntry(alias, key, password, chain); + } + + @Override + public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) + throws KeyStoreException { + keystore.engineSetKeyEntry(alias, key, chain); + } + + @Override + public void engineSetCertificateEntry(String alias, Certificate cert) + throws KeyStoreException { + keystore.engineSetCertificateEntry(alias, cert); + } + + @Override + public void engineDeleteEntry(String alias) throws KeyStoreException { + keystore.engineDeleteEntry(alias); + } + + @Override + public Enumeration engineAliases() { + return keystore.engineAliases(); + } + + @Override + public boolean engineContainsAlias(String alias) { + return keystore.engineContainsAlias(alias); + } + + @Override + public int engineSize() { + return keystore.engineSize(); + } + + @Override + public boolean engineIsKeyEntry(String alias) { + return keystore.engineIsKeyEntry(alias); + } + + @Override + public boolean engineIsCertificateEntry(String alias) { + return keystore.engineIsCertificateEntry(alias); + } + + @Override + public String engineGetCertificateAlias(Certificate cert) { + return keystore.engineGetCertificateAlias(cert); + } + + @Override + public KeyStore.Entry engineGetEntry(String alias, + KeyStore.ProtectionParameter protParam) + throws KeyStoreException, NoSuchAlgorithmException, + UnrecoverableEntryException { + return keystore.engineGetEntry(alias, protParam); + } + + @Override + public void engineSetEntry(String alias, KeyStore.Entry entry, + KeyStore.ProtectionParameter protParam) + throws KeyStoreException { + keystore.engineSetEntry(alias, entry, protParam); + } + + @Override + public boolean engineEntryInstanceOf(String alias, + Class entryClass) { + return keystore.engineEntryInstanceOf(alias, entryClass); + } + + @Override + public void engineStore(OutputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException { + + if (debug != null) { + debug.println("Storing keystore in " + type + " format"); + } + keystore.engineStore(stream, password); + } + + @Override + public void engineLoad(InputStream stream, char[] password) + throws IOException, NoSuchAlgorithmException, CertificateException { + + // A new keystore is always created in the primary keystore format + if (stream == null || !compatModeEnabled) { + try { + keystore = primaryKeyStore.newInstance(); + + } catch (InstantiationException | IllegalAccessException e) { + // can safely ignore + } + type = primaryType; + + if (debug != null && stream == null) { + debug.println("Creating a new keystore in " + type + " format"); + } + keystore.engineLoad(stream, password); + + } else { + // First try the primary keystore then try the secondary keystore + try (InputStream bufferedStream = new BufferedInputStream(stream)) { + bufferedStream.mark(Integer.MAX_VALUE); + + try { + keystore = primaryKeyStore.newInstance(); + type = primaryType; + keystore.engineLoad(bufferedStream, password); + + } catch (Exception e) { + + // incorrect password + if (e instanceof IOException && + e.getCause() instanceof UnrecoverableKeyException) { + throw (IOException)e; + } + + try { + keystore = secondaryKeyStore.newInstance(); + type = secondaryType; + bufferedStream.reset(); + keystore.engineLoad(bufferedStream, password); + + if (debug != null) { + debug.println("WARNING: switching from " + + primaryType + " to " + secondaryType + + " keystore file format has altered the " + + "keystore security level"); + } + + } catch (InstantiationException | + IllegalAccessException e2) { + // can safely ignore + + } catch (IOException | + NoSuchAlgorithmException | + CertificateException e3) { + + // incorrect password + if (e3 instanceof IOException && + e3.getCause() instanceof + UnrecoverableKeyException) { + throw (IOException)e3; + } + // rethrow the outer exception + if (e instanceof IOException) { + throw (IOException)e; + } else if (e instanceof CertificateException) { + throw (CertificateException)e; + } else if (e instanceof NoSuchAlgorithmException) { + throw (NoSuchAlgorithmException)e; + } + } + } + } + + if (debug != null) { + debug.println("Loaded a keystore in " + type + " format"); + } + } + } +} --- /dev/null Fri May 22 20:10:54 2015 +++ new/test/java/security/KeyStore/TestKeystoreCompat.java Fri May 22 20:10:54 2015 @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8062552 + * @run main/othervm TestKeystoreCompat + * @summary test compatibility mode for JKS and PKCS12 keystores + */ + +import java.io.*; +import java.security.*; +import java.security.KeyStore.*; +import java.security.cert.*; +import javax.crypto.*; +import javax.security.auth.callback.*; + +public class TestKeystoreCompat { + private static final char[] PASSWORD = "changeit".toCharArray(); + private static final String DIR = System.getProperty("test.src", "."); + // This is an arbitrary X.509 certificate + private static final String CERT_FILE = "trusted.pem"; + + public static final void main(String[] args) throws Exception { + + // Testing empty keystores + + init("empty.jks", "JKS"); + init("empty.jceks", "JCEKS"); + init("empty.p12", "PKCS12"); + + load("empty.jks", "JKS"); + load("empty.jceks", "JCEKS"); + load("empty.p12", "PKCS12"); + load("empty.p12", "JKS"); // test compatibility mode + load("empty.jks", "PKCS12", true); // test without compatibility mode + load("empty.jks", "JKS", false); // test without compatibility mode + load("empty.p12", "JKS", true); // test without compatibility mode + load("empty.p12", "PKCS12", false); // test without compatibility mode + + build("empty.jks", "JKS", true); + build("empty.jks", "JKS", false); + build("empty.jceks", "JCEKS", true); + build("empty.jceks", "JCEKS", false); + build("empty.p12", "PKCS12", true); + build("empty.p12", "PKCS12", false); + + // Testing keystores containing an X.509 certificate + + X509Certificate cert = loadCertificate(CERT_FILE); + init("onecert.jks", "JKS", cert); + init("onecert.jceks", "JCEKS", cert); + init("onecert.p12", "PKCS12", cert); + + load("onecert.jks", "JKS"); + load("onecert.jceks", "JCEKS"); + load("onecert.p12", "PKCS12"); + load("onecert.p12", "JKS"); // test compatibility mode + load("onecert.jks", "PKCS12", true); // test without compatibility mode + load("onecert.jks", "JKS", false); // test without compatibility mode + load("onecert.p12", "JKS", true); // test without compatibility mode + load("onecert.p12", "PKCS12", false); // test without compatibility mode + + build("onecert.jks", "JKS", true); + build("onecert.jks", "JKS", false); + build("onecert.jceks", "JCEKS", true); + build("onecert.jceks", "JCEKS", false); + build("onecert.p12", "PKCS12", true); + build("onecert.p12", "PKCS12", false); + + // Testing keystores containing a secret key + + SecretKey key = generateSecretKey("AES", 128); + init("onekey.jceks", "JCEKS", key); + init("onekey.p12", "PKCS12", key); + + load("onekey.jceks", "JCEKS"); + load("onekey.p12", "PKCS12"); + load("onekey.p12", "JKS"); // test compatibility mode + load("onekey.p12", "JKS", true); // test without compatibility mode + load("onekey.p12", "PKCS12", false); // test without compatibility mode + + build("onekey.jceks", "JCEKS", true); + build("onekey.jceks", "JCEKS", false); + build("onekey.p12", "PKCS12", true); + build("onekey.p12", "PKCS12", false); + + System.out.println("OK."); + } + + // Instantiate an empty keystore using the supplied keystore type + private static void init(String file, String type) throws Exception { + KeyStore ks = KeyStore.getInstance(type); + ks.load(null, null); + try (OutputStream stream = new FileOutputStream(file)) { + ks.store(stream, PASSWORD); + } + System.out.println("Created a " + type + " keystore named '" + file + "'"); + } + + // Instantiate a keystore using the supplied keystore type & create an entry + private static void init(String file, String type, X509Certificate cert) + throws Exception { + KeyStore ks = KeyStore.getInstance(type); + ks.load(null, null); + ks.setEntry("mycert", new KeyStore.TrustedCertificateEntry(cert), null); + try (OutputStream stream = new FileOutputStream(file)) { + ks.store(stream, PASSWORD); + } + System.out.println("Created a " + type + " keystore named '" + file + "'"); + } + + // Instantiate a keystore using the supplied keystore type & create an entry + private static void init(String file, String type, SecretKey key) + throws Exception { + KeyStore ks = KeyStore.getInstance(type); + ks.load(null, null); + ks.setEntry("mykey", new KeyStore.SecretKeyEntry(key), + new PasswordProtection(PASSWORD)); + try (OutputStream stream = new FileOutputStream(file)) { + ks.store(stream, PASSWORD); + } + System.out.println("Created a " + type + " keystore named '" + file + "'"); + } + + // Instantiate a keystore by probing the supplied file for the keystore type + private static void build(String file, String type, boolean usePassword) + throws Exception { + + Builder builder; + if (usePassword) { + builder = Builder.newInstance(type, null, new File(file), + new PasswordProtection(PASSWORD)); + } else { + builder = Builder.newInstance(type, null, new File(file), + new CallbackHandlerProtection(new DummyHandler())); + } + KeyStore ks = builder.getKeyStore(); + if (!type.equalsIgnoreCase(ks.getType())) { + throw new Exception("ERROR: expected a " + type + " keystore, " + + "got a " + ks.getType() + " keystore instead"); + } else { + System.out.println("Built a " + type + " keystore named '" + file + "'"); + } + } + + // Load the keystore entries + private static void load(String file, String type) throws Exception { + KeyStore ks = KeyStore.getInstance(type); + try (InputStream stream = new FileInputStream(file)) { + ks.load(stream, PASSWORD); + } + if (!type.equalsIgnoreCase(ks.getType())) { + throw new Exception("ERROR: expected a " + type + " keystore, " + + "got a " + ks.getType() + " keystore instead"); + } else { + System.out.println("Loaded a " + type + " keystore named '" + file + "'"); + } + } + + // Load the keystore entries (with compatibility mode disabled) + private static void load(String file, String type, boolean expectFailure) + throws Exception { + Security.setProperty("keystore.type.compat", "false"); + try { + load(file, type); + if (expectFailure) { + throw new Exception("ERROR: expected load to fail but it didn't"); + } + } catch (IOException e) { + if (expectFailure) { + System.out.println("Failed to load a " + type + " keystore named '" + file + "' (as expected)"); + } else { + throw e; + } + } finally { + Security.setProperty("keystore.type.compat", "true"); + } + } + + // Read an X.509 certificate from the supplied file + private static X509Certificate loadCertificate(String certFile) + throws Exception { + X509Certificate cert = null; + try (FileInputStream certStream = + new FileInputStream(DIR + "/" + certFile)) { + CertificateFactory factory = + CertificateFactory.getInstance("X.509"); + return (X509Certificate) factory.generateCertificate(certStream); + } + } + + // Generate a secret key using the supplied algorithm name and key size + private static SecretKey generateSecretKey(String algorithm, int size) + throws NoSuchAlgorithmException { + KeyGenerator generator = KeyGenerator.getInstance(algorithm); + generator.init(size); + return generator.generateKey(); + } + + private static class DummyHandler implements CallbackHandler { + public void handle(Callback[] callbacks) + throws IOException, UnsupportedCallbackException { + System.out.println("** Callbackhandler invoked"); + for (int i = 0; i < callbacks.length; i++) { + Callback cb = callbacks[i]; + if (cb instanceof PasswordCallback) { + PasswordCallback pcb = (PasswordCallback)cb; + pcb.setPassword(PASSWORD); + break; + } + } + } + } +} --- /dev/null Fri May 22 20:10:55 2015 +++ new/test/java/security/KeyStore/trusted.pem Fri May 22 20:10:54 2015 @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIF5DCCBMygAwIBAgIQGVCD3zqdD1ZMZZ/zLAPnQzANBgkqhkiG9w0BAQUFADCBvDELMAkGA1UE +BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBO +ZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29t +L3JwYSAoYykxMDE2MDQGA1UEAxMtVmVyaVNpZ24gQ2xhc3MgMyBJbnRlcm5hdGlvbmFsIFNlcnZl +ciBDQSAtIEczMB4XDTEyMDcxMDAwMDAwMFoXDTEzMDczMTIzNTk1OVowgbgxCzAJBgNVBAYTAlVT +MRMwEQYDVQQIEwpDYWxpZm9ybmlhMRcwFQYDVQQHFA5SZWR3b29kIFNob3JlczEbMBkGA1UEChQS +T3JhY2xlIENvcnBvcmF0aW9uMRIwEAYDVQQLFAlHbG9iYWwgSVQxMzAxBgNVBAsUKlRlcm1zIG9m +IHVzZSBhdCB3d3cudmVyaXNpZ24uY29tL3JwYSAoYykwNTEVMBMGA1UEAxQMKi5vcmFjbGUuY29t +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz/dOCGrWzPj62q0ZkF59Oj9Fli4wHAuX +U4/S0yBXF8j6K7TKWFTQkGZt3+08KUhmLm1CE1DbbyRJT292YNXYXunNaKdABob8kaBO/NESUOEJ +0SZh7fd0xCSJAAPiwOMrM5jLeb/dEpU6nP74Afrhu5ffvKdcvTRGguj9H2oVsisTK8Z1HsiiwcJG +JXcrjvdCZoPU4FHvK03XZPAqPHKNSaJOrux6kRIWYjQMlmL+qDOb0nNHa6gBdi+VqqJHJHeAM677 +dcUd0jn2m2OWtUnrM3MJZQof7/z27RTdX5J8np0ChkUgm63biDgRZO7uZP0DARQ0I6lZMlrarT8/ +sct3twIDAQABo4IB4jCCAd4wFwYDVR0RBBAwDoIMKi5vcmFjbGUuY29tMAkGA1UdEwQCMAAwCwYD +VR0PBAQDAgWgMEQGA1UdIAQ9MDswOQYLYIZIAYb4RQEHFwMwKjAoBggrBgEFBQcCARYcaHR0cHM6 +Ly93d3cudmVyaXNpZ24uY29tL3JwYTAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwbgYI +KwYBBQUHAQwEYjBgoV6gXDBaMFgwVhYJaW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUS2u5KJYGDLvQ +UjibKaxLB4shBRgwJhYkaHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nbzEuZ2lmMHIGCCsG +AQUFBwEBBGYwZDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AudmVyaXNpZ24uY29tMDwGCCsGAQUF +BzAChjBodHRwOi8vc3ZyaW50bC1nMy1haWEudmVyaXNpZ24uY29tL1NWUkludGxHMy5jZXIwQQYD +VR0fBDowODA2oDSgMoYwaHR0cDovL3N2cmludGwtZzMtY3JsLnZlcmlzaWduLmNvbS9TVlJJbnRs +RzMuY3JsMB8GA1UdIwQYMBaAFNebfNgioBX33a1fzimbWMO8RgC1MA0GCSqGSIb3DQEBBQUAA4IB +AQAITRBlEo+qXLwCL53Db2BGnhDgnSomjne8aCmU7Yt4Kp91tzJdhNuaC/wwDuzD2dPJqzemae3s +wKiOXrmDQZDj9NNTdkrXHnCvDR4TpOynWe3zBa0bwKnV2cIRKcv482yV53u0kALyFZbagYPwOOz3 +YJA/2SqdcDn9Ztc/ABQ1SkyXyA5j4LJdf2g7BtYrFxjy0RG6We2iM781WSB/9MCNKyHgiwd3KpLf +urdSKLzy1elNAyt1P3UHwBIIvZ6sJIr/eeELc54Lxt6PtQCXx8qwxYTYXWPXbLgKBHdebgrmAbPK +TfD69wysvjk6vwSHjmvaqB4R4WRcgkuT+1gxx+ve +-----END CERTIFICATE-----