8189997: Enhance keystore mechanisms
8194259: keytool error: java.io.IOException: Invalid secret key format
Reviewed-by: mullan, valeriep, rriggs, ahgross
--- a/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java Wed Jan 03 09:05:11 2018 -0800
+++ b/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java Fri Jan 12 08:06:24 2018 +0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2018, 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
@@ -943,8 +943,10 @@
// First run a custom filter
long nestedDepth = info.depth();
if ((nestedDepth == 1 &&
- info.serialClass() != SealedObjectForKeyProtector.class) ||
- nestedDepth > MAX_NESTED_DEPTH) {
+ info.serialClass() != SealedObjectForKeyProtector.class) ||
+ (nestedDepth > MAX_NESTED_DEPTH &&
+ info.serialClass() != null &&
+ info.serialClass() != Object.class)) {
return Status.REJECTED;
}
--- a/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java Wed Jan 03 09:05:11 2018 -0800
+++ b/src/java.base/share/classes/com/sun/crypto/provider/KeyProtector.java Fri Jan 12 08:06:24 2018 +0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2018, 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
@@ -26,8 +26,6 @@
package com.sun.crypto.provider;
import java.io.IOException;
-import java.io.Serializable;
-import java.security.Security;
import java.security.Key;
import java.security.PrivateKey;
import java.security.Provider;
@@ -35,7 +33,6 @@
import java.security.MessageDigest;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
import java.security.UnrecoverableKeyException;
import java.security.AlgorithmParameters;
import java.security.spec.InvalidParameterSpecException;
@@ -44,7 +41,6 @@
import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
import javax.crypto.SecretKey;
-import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SealedObject;
import javax.crypto.spec.*;
import sun.security.x509.AlgorithmId;
@@ -347,7 +343,7 @@
SunJCE.getInstance(),
"PBEWithMD5AndTripleDES");
cipher.init(Cipher.DECRYPT_MODE, skey, params);
- return (Key)soForKeyProtector.getObject(cipher);
+ return soForKeyProtector.getKey(cipher);
} catch (NoSuchAlgorithmException ex) {
// Note: this catch needed to be here because of the
// later catch of GeneralSecurityException
--- a/src/java.base/share/classes/com/sun/crypto/provider/SealedObjectForKeyProtector.java Wed Jan 03 09:05:11 2018 -0800
+++ b/src/java.base/share/classes/com/sun/crypto/provider/SealedObjectForKeyProtector.java Fri Jan 12 08:06:24 2018 +0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2018, 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
@@ -25,6 +25,8 @@
package com.sun.crypto.provider;
+import jdk.internal.misc.SharedSecrets;
+
import java.io.*;
import java.security.*;
import javax.crypto.*;
@@ -33,6 +35,16 @@
static final long serialVersionUID = -3650226485480866989L;
+ /**
+ * The InputStreamFilter for a Key object inside this SealedObject. It can
+ * be either provided as a {@link Security} property or a system property
+ * (when provided as latter, it shadows the former). If the result of this
+ * filter is {@link java.io.ObjectInputFilter.Status.UNDECIDED}, the system
+ * level filter defined by jdk.serialFilter will be consulted. The value
+ * of this property uses the same format of jdk.serialFilter.
+ */
+ private static final String KEY_SERIAL_FILTER = "jceks.key.serialFilter";
+
SealedObjectForKeyProtector(Serializable object, Cipher c)
throws IOException, IllegalBlockSizeException {
super(object, c);
@@ -59,4 +71,87 @@
}
return params;
}
+
+ final Key getKey(Cipher c)
+ throws IOException, ClassNotFoundException, IllegalBlockSizeException,
+ BadPaddingException {
+
+ try (ObjectInputStream ois = SharedSecrets.getJavaxCryptoSealedObjectAccess()
+ .getExtObjectInputStream(this, c)) {
+ AccessController.doPrivileged(
+ (PrivilegedAction<Void>) () -> {
+ ois.setObjectInputFilter(DeserializationChecker.ONE_FILTER);
+ return null;
+ });
+ try {
+ @SuppressWarnings("unchecked")
+ Key t = (Key) ois.readObject();
+ return t;
+ } catch (InvalidClassException ice) {
+ String msg = ice.getMessage();
+ if (msg.contains("REJECTED")) {
+ throw new IOException("Rejected by the"
+ + " jceks.key.serialFilter or jdk.serialFilter"
+ + " property", ice);
+ } else {
+ throw ice;
+ }
+ }
+ }
+ }
+
+ /**
+ * The filter for the content of a SealedObjectForKeyProtector.
+ *
+ * First, the jceks.key.serialFilter will be consulted. If the result
+ * is UNDECIDED, the system level jdk.serialFilter will be consulted.
+ */
+ private static class DeserializationChecker implements ObjectInputFilter {
+
+ private static final ObjectInputFilter ONE_FILTER;
+
+ static {
+ String prop = AccessController.doPrivileged(
+ (PrivilegedAction<String>) () -> {
+ String tmp = System.getProperty(KEY_SERIAL_FILTER);
+ if (tmp != null) {
+ return tmp;
+ } else {
+ return Security.getProperty(KEY_SERIAL_FILTER);
+ }
+ });
+ ONE_FILTER = new DeserializationChecker(prop == null ? null
+ : ObjectInputFilter.Config.createFilter(prop));
+ }
+
+ private final ObjectInputFilter base;
+
+ private DeserializationChecker(ObjectInputFilter base) {
+ this.base = base;
+ }
+
+ @Override
+ public ObjectInputFilter.Status checkInput(
+ ObjectInputFilter.FilterInfo info) {
+
+ if (info.serialClass() == Object.class) {
+ return Status.UNDECIDED;
+ }
+
+ if (base != null) {
+ Status result = base.checkInput(info);
+ if (result != Status.UNDECIDED) {
+ return result;
+ }
+ }
+
+ ObjectInputFilter defaultFilter =
+ ObjectInputFilter.Config.getSerialFilter();
+ if (defaultFilter != null) {
+ return defaultFilter.checkInput(info);
+ }
+
+ return Status.UNDECIDED;
+ }
+ }
}
--- a/src/java.base/share/classes/com/sun/crypto/provider/ai.java Wed Jan 03 09:05:11 2018 -0800
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2001, 2007, 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 com.sun.crypto.provider;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.io.ObjectStreamException;
-import java.security.AlgorithmParameters;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.SealedObject;
-import javax.crypto.spec.*;
-
-/**
- * This class is introduced to workaround a problem in
- * the SunJCE provider shipped in JCE 1.2.1: the class
- * SealedObjectForKeyProtector was obfuscated due to a mistake.
- *
- * In order to retrieve secret keys in a JCEKS KeyStore written
- * by the SunJCE provider in JCE 1.2.1, this class will be used.
- *
- * @author Valerie Peng
- *
- *
- * @see JceKeyStore
- */
-
-final class ai extends javax.crypto.SealedObject {
-
- static final long serialVersionUID = -7051502576727967444L;
-
- ai(SealedObject so) {
- super(so);
- }
-
- Object readResolve() throws ObjectStreamException {
- return new SealedObjectForKeyProtector(this);
- }
-}
--- a/src/java.base/share/classes/javax/crypto/SealedObject.java Wed Jan 03 09:05:11 2018 -0800
+++ b/src/java.base/share/classes/javax/crypto/SealedObject.java Fri Jan 12 08:06:24 2018 +0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, 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
@@ -25,6 +25,8 @@
package javax.crypto;
+import jdk.internal.misc.SharedSecrets;
+
import java.io.*;
import java.security.AlgorithmParameters;
import java.security.Key;
@@ -287,17 +289,7 @@
throws IOException, ClassNotFoundException, IllegalBlockSizeException,
BadPaddingException
{
- /*
- * Unseal the object
- */
- byte[] content = c.doFinal(this.encryptedContent);
-
- /*
- * De-serialize it
- */
- // creating a stream pipe-line, from b to a
- ByteArrayInputStream b = new ByteArrayInputStream(content);
- ObjectInput a = new extObjectInputStream(b);
+ ObjectInput a = getExtObjectInputStream(c);
try {
Object obj = a.readObject();
return obj;
@@ -417,17 +409,7 @@
throw new RuntimeException(iape.getMessage());
}
- /*
- * Unseal the object
- */
- byte[] content = c.doFinal(this.encryptedContent);
-
- /*
- * De-serialize it
- */
- // creating a stream pipe-line, from b to a
- ByteArrayInputStream b = new ByteArrayInputStream(content);
- ObjectInput a = new extObjectInputStream(b);
+ ObjectInput a = getExtObjectInputStream(c);
try {
Object obj = a.readObject();
return obj;
@@ -450,6 +432,19 @@
if (encodedParams != null)
encodedParams = encodedParams.clone();
}
+
+ // This method is also called inside SealedObjectForKeyProtector.java.
+ private ObjectInputStream getExtObjectInputStream(Cipher c)
+ throws BadPaddingException, IllegalBlockSizeException, IOException {
+
+ byte[] content = c.doFinal(this.encryptedContent);
+ ByteArrayInputStream b = new ByteArrayInputStream(content);
+ return new extObjectInputStream(b);
+ }
+
+ static {
+ SharedSecrets.setJavaxCryptoSealedObjectAccess((obj,c) -> obj.getExtObjectInputStream(c));
+ }
}
final class extObjectInputStream extends ObjectInputStream {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaxCryptoSealedObjectAccess.java Fri Jan 12 08:06:24 2018 +0800
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018, 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 jdk.internal.misc;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.SealedObject;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+public interface JavaxCryptoSealedObjectAccess {
+ ObjectInputStream getExtObjectInputStream(
+ SealedObject sealed, Cipher cipher)
+ throws BadPaddingException, IllegalBlockSizeException, IOException;
+}
--- a/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java Wed Jan 03 09:05:11 2018 -0800
+++ b/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java Fri Jan 12 08:06:24 2018 +0800
@@ -25,6 +25,7 @@
package jdk.internal.misc;
+import javax.crypto.SealedObject;
import java.io.ObjectInputFilter;
import java.lang.module.ModuleDescriptor;
import java.util.ResourceBundle;
@@ -71,6 +72,7 @@
private static JavaObjectInputStreamAccess javaObjectInputStreamAccess;
private static JavaObjectInputFilterAccess javaObjectInputFilterAccess;
private static JavaIORandomAccessFileAccess javaIORandomAccessFileAccess;
+ private static JavaxCryptoSealedObjectAccess javaxCryptoSealedObjectAccess;
public static JavaUtilJarAccess javaUtilJarAccess() {
if (javaUtilJarAccess == null) {
@@ -324,4 +326,15 @@
}
return javaIORandomAccessFileAccess;
}
+
+ public static void setJavaxCryptoSealedObjectAccess(JavaxCryptoSealedObjectAccess jcsoa) {
+ javaxCryptoSealedObjectAccess = jcsoa;
+ }
+
+ public static JavaxCryptoSealedObjectAccess getJavaxCryptoSealedObjectAccess() {
+ if (javaxCryptoSealedObjectAccess == null) {
+ unsafe.ensureClassInitialized(SealedObject.class);
+ }
+ return javaxCryptoSealedObjectAccess;
+ }
}
--- a/src/java.base/share/conf/security/java.security Wed Jan 03 09:05:11 2018 -0800
+++ b/src/java.base/share/conf/security/java.security Fri Jan 12 08:06:24 2018 +0800
@@ -895,6 +895,9 @@
# Patterns are separated by ";" (semicolon).
# Whitespace is significant and is considered part of the pattern.
#
+# If the system property jdk.serialFilter is also specified, it supersedes
+# the security property value defined here.
+#
# If a pattern includes a "=", it sets a limit.
# If a limit appears more than once the last value is used.
# Limits are checked before classes regardless of the order in the
@@ -1005,3 +1008,20 @@
# It is not guaranteed to be examined and used by other implementations.
#
#com.sun.CORBA.ORBIorTypeCheckRegistryFilter=binary_class_name;binary_class_name
+
+#
+# JCEKS Encrypted Key Serial Filter
+#
+# This filter, if configured, is used by the JCEKS KeyStore during the
+# deserialization of the encrypted Key object stored inside a key entry.
+# If not configured or the filter result is UNDECIDED (i.e. none of the patterns
+# matches), the filter configured by jdk.serialFilter will be consulted.
+#
+# If the system property jceks.key.serialFilter is also specified, it supersedes
+# the security property value defined here.
+#
+# The filter pattern uses the same format as jdk.serialFilter. The default
+# pattern allows java.lang.Enum, java.security.KeyRep, java.security.KeyRep$Type,
+# and javax.crypto.spec.SecretKeySpec and rejects all the others.
+jceks.key.serialFilter = java.base/java.lang.Enum;java.base/java.security.KeyRep;\
+ java.base/java.security.KeyRep$Type;java.base/javax.crypto.spec.SecretKeySpec;!*