--- old/make/jdk/src/classes/build/tools/generatecacerts/GenerateCacerts.java	2019-08-14 00:18:04.000000000 +0800
+++ new/make/jdk/src/classes/build/tools/generatecacerts/GenerateCacerts.java	2019-08-14 00:18:03.000000000 +0800
@@ -25,22 +25,15 @@
 
 package build.tools.generatecacerts;
 
-import java.io.DataOutputStream;
-import java.io.FileOutputStream;
+import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.UnsupportedEncodingException;
+import java.io.PrintStream;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.security.DigestOutputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
 import java.util.List;
+import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 /**
@@ -50,99 +43,22 @@
  */
 public class GenerateCacerts {
     public static void main(String[] args) throws Exception {
-        try (FileOutputStream fos = new FileOutputStream(args[1])) {
-            store(args[0], fos, "changeit".toCharArray());
+        try (PrintStream ps = new PrintStream(
+                new File(args[1]), StandardCharsets.UTF_8)) {
+            Files.list(Path.of(args[0]))
+                    .filter(Predicate.not(Files::isDirectory))
+                    .map(p -> p.getFileName().toString())
+                    .filter(s -> !s.equals("README") && !s.startsWith("."))
+                    .sorted()
+                    .forEach(s -> {
+                        try {
+                            ps.println("@alias: " + s + " [jdk]");
+                            Files.lines(Path.of(args[0], s))
+                                    .forEach(ps::println);
+                        } catch (IOException e) {
+                            throw new UncheckedIOException(e);
+                        }
+                    });
         }
     }
-
-    // The following code are copied from JavaKeyStore.java.
-
-    private static final int MAGIC = 0xfeedfeed;
-    private static final int VERSION_2 = 0x02;
-
-    // This method is a simplified version of JavaKeyStore::engineStore.
-    // A new "dir" argument is added. All cert names in "dir" is collected into
-    // a sorted array. Each cert is stored with a creation date set to its
-    // notBefore value. Thus the output is determined as long as the certs
-    // are the same.
-    public static void store(String dir, OutputStream stream, char[] password)
-            throws IOException, NoSuchAlgorithmException, CertificateException
-    {
-        byte[] encoded; // the certificate encoding
-        CertificateFactory cf = CertificateFactory.getInstance("X509");
-
-        MessageDigest md = getPreKeyedHash(password);
-        DataOutputStream dos
-                = new DataOutputStream(new DigestOutputStream(stream, md));
-
-        dos.writeInt(MAGIC);
-        // always write the latest version
-        dos.writeInt(VERSION_2);
-
-        // All file names in dir sorted.
-        // README is excluded. Name starting with "." excluded.
-        List<String> entries = Files.list(Path.of(dir))
-                .map(p -> p.getFileName().toString())
-                .filter(s -> !s.equals("README") && !s.startsWith("."))
-                .collect(Collectors.toList());
-
-        entries.sort(String::compareTo);
-
-        dos.writeInt(entries.size());
-
-        for (String entry : entries) {
-
-            String alias = entry + " [jdk]";
-            X509Certificate cert;
-            try (InputStream fis = Files.newInputStream(Path.of(dir, entry))) {
-                cert = (X509Certificate) cf.generateCertificate(fis);
-            }
-
-            dos.writeInt(2);
-
-            // Write the alias
-            dos.writeUTF(alias);
-
-            // Write the (entry creation) date, which is notBefore of the cert
-            dos.writeLong(cert.getNotBefore().getTime());
-
-            // Write the trusted certificate
-            encoded = cert.getEncoded();
-            dos.writeUTF(cert.getType());
-            dos.writeInt(encoded.length);
-            dos.write(encoded);
-        }
-
-        /*
-         * Write the keyed hash which is used to detect tampering with
-         * the keystore (such as deleting or modifying key or
-         * certificate entries).
-         */
-        byte[] digest = md.digest();
-
-        dos.write(digest);
-        dos.flush();
-    }
-
-    private static MessageDigest getPreKeyedHash(char[] password)
-            throws NoSuchAlgorithmException, UnsupportedEncodingException
-    {
-
-        MessageDigest md = MessageDigest.getInstance("SHA");
-        byte[] passwdBytes = convertToBytes(password);
-        md.update(passwdBytes);
-        Arrays.fill(passwdBytes, (byte) 0x00);
-        md.update("Mighty Aphrodite".getBytes("UTF8"));
-        return md;
-    }
-
-    private static byte[] convertToBytes(char[] password) {
-        int i, j;
-        byte[] passwdBytes = new byte[password.length * 2];
-        for (i=0, j=0; i<password.length; i++) {
-            passwdBytes[j++] = (byte)(password[i] >> 8);
-            passwdBytes[j++] = (byte)password[i];
-        }
-        return passwdBytes;
-    }
 }
--- old/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java	2019-08-14 00:18:05.000000000 +0800
+++ new/src/java.base/share/classes/sun/security/pkcs12/PKCS12KeyStore.java	2019-08-14 00:18:05.000000000 +0800
@@ -63,6 +63,7 @@
 import javax.security.auth.DestroyFailedException;
 import javax.security.auth.x500.X500Principal;
 
+import sun.security.provider.PemKeyStore;
 import sun.security.tools.KeyStoreUtil;
 import sun.security.util.Debug;
 import sun.security.util.DerInputStream;
@@ -108,10 +109,12 @@
     private static final int DEFAULT_PBE_ITERATION_COUNT = 50000;
     private static final int DEFAULT_MAC_ITERATION_COUNT = 100000;
 
-    // special PKCS12 keystore that supports PKCS12 and JKS file formats
-    public static final class DualFormatPKCS12 extends KeyStoreDelegator {
-        public DualFormatPKCS12() {
-            super("PKCS12", PKCS12KeyStore.class, "JKS", JKS.class);
+    // special PKCS12 keystore that supports JKS/PKCS12/PEM file formats
+    public static final class MultipleFormatPKCS12 extends KeyStoreDelegator {
+        public MultipleFormatPKCS12() {
+            super("PKCS12", PKCS12KeyStore.class,
+                    List.of("JKS", "PEM"),
+                    List.of(JKS.class, PemKeyStore.class));
         }
     }
 
@@ -2309,6 +2312,8 @@
                 // The MacData exists.
                 return false;
             }
+        } catch (IOException e) {
+            return false;
         }
         return true;
     }
--- old/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java	2019-08-14 00:18:07.000000000 +0800
+++ new/src/java.base/share/classes/sun/security/provider/JavaKeyStore.java	2019-08-14 00:18:06.000000000 +0800
@@ -68,10 +68,12 @@
         }
     }
 
-    // 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);
+    // special JKS that supports JKS/PKCS12/PEM file formats
+    public static final class MultipleFormatJKS extends KeyStoreDelegator {
+        public MultipleFormatJKS() {
+            super("JKS", JKS.class,
+                    List.of("PKCS12", "PEM"),
+                    List.of(PKCS12KeyStore.class, PemKeyStore.class));
         }
     }
 
--- old/src/java.base/share/classes/sun/security/provider/SunEntries.java	2019-08-14 00:18:10.000000000 +0800
+++ new/src/java.base/share/classes/sun/security/provider/SunEntries.java	2019-08-14 00:18:09.000000000 +0800
@@ -253,16 +253,18 @@
          * KeyStore
          */
         add(p, "KeyStore", "PKCS12",
-                "sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12",
+                "sun.security.pkcs12.PKCS12KeyStore$MultipleFormatPKCS12",
                 null, null);
         add(p, "KeyStore", "JKS",
-                "sun.security.provider.JavaKeyStore$DualFormatJKS",
+                "sun.security.provider.JavaKeyStore$MultipleFormatJKS",
                 null, attrs);
         add(p, "KeyStore", "CaseExactJKS",
                 "sun.security.provider.JavaKeyStore$CaseExactJKS",
                 null, attrs);
         add(p, "KeyStore", "DKS", "sun.security.provider.DomainKeyStore$DKS",
                 null, attrs);
+        add(p, "KeyStore", "PEM", "sun.security.provider.PemKeyStore$MultipleFormatPEM",
+                null, attrs);
 
 
         /*
--- old/src/java.base/share/classes/sun/security/tools/keytool/Main.java	2019-08-14 00:18:11.000000000 +0800
+++ new/src/java.base/share/classes/sun/security/tools/keytool/Main.java	2019-08-14 00:18:11.000000000 +0800
@@ -936,6 +936,8 @@
             storetype = keyStore.getType();
             if (storetype.equalsIgnoreCase("pkcs12")) {
                 isPasswordlessKeyStore = PKCS12KeyStore.isPasswordless(ksfile);
+            } else if (storetype.equalsIgnoreCase("pem")) {
+                isPasswordlessKeyStore = true;
             }
         } else {
             if (storetype == null) {
@@ -2195,6 +2197,8 @@
                 srcstoretype = store.getType();
                 if (srcstoretype.equalsIgnoreCase("pkcs12")) {
                     srcIsPasswordless = PKCS12KeyStore.isPasswordless(srcksfile);
+                } else if (srcstoretype.equalsIgnoreCase("pem")) {
+                    srcIsPasswordless = true;
                 }
             } else {
                 if (srcstoretype == null) {
--- old/src/java.base/share/classes/sun/security/util/KeyStoreDelegator.java	2019-08-14 00:18:13.000000000 +0800
+++ new/src/java.base/share/classes/sun/security/util/KeyStoreDelegator.java	2019-08-14 00:18:13.000000000 +0800
@@ -46,10 +46,10 @@
     private static final Debug debug = Debug.getInstance("keystore");
 
     private String primaryType;   // the primary keystore's type
-    private String secondaryType; // the secondary keystore's type
+    private List<String> secondaryTypes; // the secondary keystore's type
     private Class<? extends KeyStoreSpi> primaryKeyStore;
                                   // the primary keystore's class
-    private Class<? extends KeyStoreSpi> secondaryKeyStore;
+    private List<Class<? extends KeyStoreSpi>> secondaryKeyStores;
                                   // the secondary keystore's class
     private String type; // the delegate's type
     private KeyStoreSpi keystore; // the delegate
@@ -58,8 +58,8 @@
     public KeyStoreDelegator(
         String primaryType,
         Class<? extends KeyStoreSpi> primaryKeyStore,
-        String secondaryType,
-        Class<? extends KeyStoreSpi> secondaryKeyStore) {
+        List<String> secondaryTypes,
+        List<Class<? extends KeyStoreSpi>> secondaryKeyStores) {
 
         // Check whether compatibility mode has been disabled
         compatModeEnabled = "true".equalsIgnoreCase(
@@ -68,18 +68,18 @@
 
         if (compatModeEnabled) {
             this.primaryType = primaryType;
-            this.secondaryType = secondaryType;
+            this.secondaryTypes = secondaryTypes;
             this.primaryKeyStore = primaryKeyStore;
-            this.secondaryKeyStore = secondaryKeyStore;
+            this.secondaryKeyStores = secondaryKeyStores;
         } else {
             this.primaryType = primaryType;
-            this.secondaryType = null;
+            this.secondaryTypes = Collections.emptyList();
             this.primaryKeyStore = primaryKeyStore;
-            this.secondaryKeyStore = null;
+            this.secondaryKeyStores = Collections.emptyList();
 
             if (debug != null) {
                 debug.println("WARNING: compatibility mode disabled for " +
-                    primaryType + " and " + secondaryType + " keystore types");
+                    primaryType + " and " + secondaryTypes + " keystore types");
             }
         }
     }
@@ -229,50 +229,47 @@
                     throw (IOException)e;
                 }
 
-                try {
-                    // Ignore secondary keystore when no compatibility mode
-                    if (!compatModeEnabled) {
-                        throw e;
-                    }
-
-                    @SuppressWarnings("deprecation")
-                    KeyStoreSpi tmp= secondaryKeyStore.newInstance();
-                    keystore = tmp;
-                    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;
-                    } else if (e instanceof RuntimeException){
-                        throw (RuntimeException)e;
+                if (compatModeEnabled) {
+                    for (int i = 0; i < secondaryTypes.size(); i++) {
+                        try {
+                            @SuppressWarnings("deprecation")
+                            KeyStoreSpi tmp = secondaryKeyStores.get(i).newInstance();
+                            keystore = tmp;
+                            type = secondaryTypes.get(i);
+                            bufferedStream.reset();
+                            keystore.engineLoad(bufferedStream, password);
+                            if (debug != null) {
+                                debug.println("WARNING: switching from " +
+                                        primaryType + " to " + type +
+                                        " keystore file format has altered the " +
+                                        "keystore security level");
+                            }
+                            e = null;
+                            break;
+                        } catch (InstantiationException |
+                                IllegalAccessException e2) {
+                            // can safely ignore
+                        } catch (IOException e3) {
+                            // incorrect password
+                            if (e3.getCause() instanceof UnrecoverableKeyException) {
+                                e = e3;
+                            }
+                        } catch (Exception e2) {
+                            // continue;
+                        }
                     }
                 }
+                if (e instanceof IOException) {
+                    throw (IOException) e;
+                } else if (e instanceof CertificateException) {
+                    throw (CertificateException) e;
+                } else if (e instanceof NoSuchAlgorithmException) {
+                    throw (NoSuchAlgorithmException) e;
+                } else if (e instanceof RuntimeException) {
+                    throw (RuntimeException)e;
+                } else if (e != null) {
+                    throw new IOException(e);
+                }
             }
 
             if (debug != null) {
--- old/src/java.base/share/conf/security/java.security	2019-08-14 00:18:15.000000000 +0800
+++ new/src/java.base/share/conf/security/java.security	2019-08-14 00:18:15.000000000 +0800
@@ -297,12 +297,11 @@
 keystore.type=pkcs12
 
 #
-# Controls compatibility mode for JKS and PKCS12 keystore types.
+# Controls compatibility mode for PEM, JKS and PKCS12 keystore types.
 #
-# When set to 'true', both JKS and PKCS12 keystore types support loading
-# keystore files in either JKS or PKCS12 format. When set to 'false' the
-# JKS keystore type supports loading only JKS keystore files and the PKCS12
-# keystore type supports loading only PKCS12 keystore files.
+# When set to 'true', all PEM, JKS and PKCS12 keystore types support loading
+# keystore files in either PEM, JKS or PKCS12 format. When set to 'false' the
+# each keystore type supports loading only keystore files of its own.
 #
 keystore.type.compat=true
 
--- /dev/null	2019-08-14 00:18:16.000000000 +0800
+++ new/src/java.base/share/classes/sun/security/provider/PemKeyStore.java	2019-08-14 00:18:16.000000000 +0800
@@ -0,0 +1,264 @@
+/*
+ * Copyright (c) 2019, 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 sun.security.pkcs12.PKCS12KeyStore;
+import sun.security.util.KeyStoreDelegator;
+import sun.security.x509.X509CertImpl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreSpi;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This class provides the keystore implementation referred to as "PEM".
+ *
+ * @see java.security.KeyStoreSpi
+ * @since 14
+ */
+
+public class PemKeyStore extends KeyStoreSpi {
+
+    // special keystore that supports JKS/PKCS12/PEM file formats
+    public static final class MultipleFormatPEM extends KeyStoreDelegator {
+        public MultipleFormatPEM() {
+            super("PEM", PemKeyStore.class,
+                    List.of("JKS", "PKCS12"),
+                    List.of(JavaKeyStore.JKS.class, PKCS12KeyStore.class));
+        }
+    }
+
+    private Map<String,Certificate> certs = new LinkedHashMap<>();
+    private Map<String, Set<KeyStore.Entry.Attribute>> attrs = new LinkedHashMap<>();
+
+    @Override
+    public Key engineGetKey(String alias, char[] password) {
+        return null;
+    }
+
+    @Override
+    public Certificate[] engineGetCertificateChain(String alias) {
+        return null;
+    }
+
+    @Override
+    public Certificate engineGetCertificate(String alias) {
+        return certs.get(alias);
+    }
+
+    @Override
+    public Date engineGetCreationDate(String alias) {
+        Certificate c = certs.get(alias);
+        if (c instanceof X509Certificate) {
+            return ((X509Certificate) c).getNotBefore();
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public void engineSetKeyEntry(
+            String alias, Key key, char[] password, Certificate[] chain) {
+        throw new UnsupportedOperationException("cannot store key");
+    }
+
+    @Override
+    public void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) {
+        throw new UnsupportedOperationException("cannot store key");
+    }
+
+    @Override
+    public void engineSetCertificateEntry(String alias, Certificate cert) {
+        certs.put(alias, cert);
+    }
+
+    @Override
+    public void engineDeleteEntry(String alias) {
+        certs.remove(alias);
+    }
+
+    @Override
+    public Enumeration<String> engineAliases() {
+        return Collections.enumeration(certs.keySet());
+    }
+
+    @Override
+    public boolean engineContainsAlias(String alias) {
+        return certs.containsKey(alias);
+    }
+
+    @Override
+    public int engineSize() {
+        return certs.size();
+    }
+
+    @Override
+    public boolean engineIsKeyEntry(String alias) {
+        return false;
+    }
+
+    @Override
+    public boolean engineIsCertificateEntry(String alias) {
+        return engineContainsAlias(alias);
+    }
+
+    @Override
+    public String engineGetCertificateAlias(Certificate cert) {
+        for (var entry : certs.entrySet()) {
+            if (entry.getValue().equals(cert)) {
+                return entry.getKey();
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void engineStore(OutputStream stream, char[] password) {
+        throw new UnsupportedOperationException("cannot store keystore");
+    }
+
+    @Override
+    public void engineLoad(InputStream stream, char[] password)
+            throws IOException, CertificateException {
+
+        String alias = null;
+        StringBuilder block = new StringBuilder();
+        Set<KeyStore.Entry.Attribute> attr = new HashSet<>();
+
+        boolean inPEM = false;
+
+        try (BufferedReader r = new BufferedReader(
+                new InputStreamReader(stream, StandardCharsets.UTF_8))) {
+            while (true) {
+                String s = r.readLine();
+                if (s == null) {
+                    break;
+                }
+                s = s.trim();
+                if (s.equals(X509Factory.BEGIN_CERT)) {
+                    inPEM = true;
+                } else if (s.equals(X509Factory.END_CERT)) {
+                    byte[] data = Base64.getMimeDecoder().decode(block.toString());
+                    X509CertImpl c = new X509CertImpl(data);
+                    if (alias == null) {
+                        alias = c.getFingerprint("SHA-256");
+                    }
+                    certs.put(alias, c);
+                    attrs.put(alias, attr);
+                    alias = null;
+                    block.setLength(0);
+                    attr = new HashSet<>(); // must recreate
+                    inPEM = false;
+                } else if (inPEM) {
+                    block.append(s);
+                } else if (s.charAt(0) == '@') {
+                    String[] kv = s.substring(1).split(":\\s*", 2);
+                    if (kv[0].equals("alias") && kv.length == 2) {
+                        alias = kv[1];
+                    } else {
+                        attr.add(new PemAttribute(
+                                kv[0], kv.length == 2 ? kv[1] : "true"));
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public KeyStore.Entry engineGetEntry(
+            String alias, KeyStore.ProtectionParameter protParam) {
+        Certificate cert = certs.get(alias);
+        if (cert == null) {
+            return null;
+        } else {
+            return new KeyStore.TrustedCertificateEntry(cert, attrs.get(alias));
+        }
+    }
+
+    @Override
+    public boolean engineProbe(InputStream stream) throws IOException {
+        Objects.requireNonNull(stream);
+        // Ensure the file starts with 5 readable text chars
+        return isReadable(stream.read())
+                && isReadable(stream.read())
+                && isReadable(stream.read())
+                && isReadable(stream.read())
+                && isReadable(stream.read());
+    }
+
+    private static boolean isReadable(int i) {
+        return i >= 9 && i <= 13 || i > 31 && i < 128;
+    }
+
+    private static class PemAttribute implements KeyStore.Entry.Attribute {
+
+        private final String name, value;
+
+        public PemAttribute(String name, String value) {
+            this.name = Objects.requireNonNull(name);
+            this.value = Objects.requireNonNull(value);
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public String getValue() {
+            return value;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(name, value);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return Objects.equals(name, value);
+        }
+    }
+}