--- 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);
+ }
+ }
+}