changeset 1807:96a0ed531b5e

8189997: Enhance keystore mechanisms 8194259: keytool error: java.io.IOException: Invalid secret key format Reviewed-by: mullan, valeriep, rriggs, ahgross
author igerasim
date Tue, 06 Feb 2018 09:04:46 -0800
parents 165f080ecc69
children e32be26dcc8c
files src/share/classes/com/sun/crypto/provider/JceKeyStore.java src/share/classes/com/sun/crypto/provider/KeyProtector.java src/share/classes/com/sun/crypto/provider/SealedObjectForKeyProtector.java src/share/classes/com/sun/crypto/provider/ai.java src/share/classes/javax/crypto/SealedObject.java src/share/classes/sun/misc/JavaxCryptoSealedObjectAccess.java src/share/classes/sun/misc/SharedSecrets.java src/share/lib/security/java.security-linux src/share/lib/security/java.security-solaris src/share/lib/security/java.security-windows
diffstat 10 files changed, 253 insertions(+), 96 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/crypto/provider/JceKeyStore.java	Tue Oct 23 15:51:11 2012 -0700
+++ b/src/share/classes/com/sun/crypto/provider/JceKeyStore.java	Tue Feb 06 09:04:46 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
@@ -928,8 +928,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/share/classes/com/sun/crypto/provider/KeyProtector.java	Tue Oct 23 15:51:11 2012 -0700
+++ b/src/share/classes/com/sun/crypto/provider/KeyProtector.java	Tue Feb 06 09:04:46 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,11 +26,10 @@
 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;
+import java.security.Security;
 import java.security.KeyFactory;
 import java.security.MessageDigest;
 import java.security.GeneralSecurityException;
@@ -44,7 +43,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;
@@ -348,7 +346,7 @@
             Cipher cipher = new CipherForKeyProtector(cipherSpi, PROV,
                                                    "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/share/classes/com/sun/crypto/provider/SealedObjectForKeyProtector.java	Tue Oct 23 15:51:11 2012 -0700
+++ b/src/share/classes/com/sun/crypto/provider/SealedObjectForKeyProtector.java	Tue Feb 06 09:04:46 2018 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2012, 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,9 @@
 
 package com.sun.crypto.provider;
 
+import sun.misc.ObjectInputFilter;
+import sun.misc.SharedSecrets;
+
 import java.io.*;
 import java.security.*;
 import javax.crypto.*;
@@ -33,6 +36,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 sun.misc.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);
@@ -58,4 +71,99 @@
         }
         return params;
     }
+
+    final Key getKey(Cipher c)
+            throws IOException, ClassNotFoundException, IllegalBlockSizeException,
+            BadPaddingException {
+
+        final ObjectInputStream ois = SharedSecrets.getJavaxCryptoSealedObjectAccess()
+                .getExtObjectInputStream(this, c);
+        try {
+            AccessController.doPrivileged(
+                    new PrivilegedAction<Void>() {
+                        @Override
+                        public Void run() {
+                            ObjectInputFilter.Config.setObjectInputFilter(ois,
+                                    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;
+                }
+            }
+        } finally {
+            if (ois != null) {
+                ois.close();
+            }
+        }
+    }
+
+    /**
+     * 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(
+                    new PrivilegedAction<String>() {
+                        @Override
+                        public String run() {
+                            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/share/classes/com/sun/crypto/provider/ai.java	Tue Oct 23 15:51:11 2012 -0700
+++ /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/share/classes/javax/crypto/SealedObject.java	Tue Oct 23 15:51:11 2012 -0700
+++ b/src/share/classes/javax/crypto/SealedObject.java	Tue Feb 06 09:04:46 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,9 @@
 
 package javax.crypto;
 
+import sun.misc.JavaxCryptoSealedObjectAccess;
+import sun.misc.SharedSecrets;
+
 import java.io.*;
 import java.security.AlgorithmParameters;
 import java.security.Key;
@@ -289,17 +292,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;
@@ -421,17 +414,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;
@@ -454,6 +437,25 @@
         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(new JavaxCryptoSealedObjectAccess() {
+            @Override
+            public ObjectInputStream getExtObjectInputStream(SealedObject obj, Cipher c)
+                    throws BadPaddingException, IllegalBlockSizeException, IOException {
+                return obj.getExtObjectInputStream(c);
+            }
+        });
+    }
 }
 
 final class extObjectInputStream extends ObjectInputStream {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/misc/JavaxCryptoSealedObjectAccess.java	Tue Feb 06 09:04:46 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 sun.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/share/classes/sun/misc/SharedSecrets.java	Tue Oct 23 15:51:11 2012 -0700
+++ b/src/share/classes/sun/misc/SharedSecrets.java	Tue Feb 06 09:04:46 2018 -0800
@@ -25,6 +25,7 @@
 
 package sun.misc;
 
+import javax.crypto.SealedObject;
 import java.io.ObjectInputStream;
 import java.util.jar.JarFile;
 import java.io.Console;
@@ -59,6 +60,7 @@
     private static JavaOISAccess javaOISAccess;
     private static JavaIOFileAccess javaIOFileAccess;
     private static JavaObjectInputStreamAccess javaObjectInputStreamAccess;
+    private static JavaxCryptoSealedObjectAccess javaxCryptoSealedObjectAccess;
 
     public static JavaUtilJarAccess javaUtilJarAccess() {
         if (javaUtilJarAccess == null) {
@@ -193,6 +195,17 @@
         javaIOFileAccess = access;
     }
 
+    public static void setJavaxCryptoSealedObjectAccess(JavaxCryptoSealedObjectAccess jcsoa) {
+        javaxCryptoSealedObjectAccess = jcsoa;
+    }
+
+    public static JavaxCryptoSealedObjectAccess getJavaxCryptoSealedObjectAccess() {
+        if (javaxCryptoSealedObjectAccess == null) {
+            unsafe.ensureClassInitialized(SealedObject.class);
+        }
+        return javaxCryptoSealedObjectAccess;
+    }
+
     public static JavaIOFileAccess getJavaIOFileAccess() {
         if (javaIOFileAccess == null) {
             unsafe.ensureClassInitialized(File.class);
--- a/src/share/lib/security/java.security-linux	Tue Oct 23 15:51:11 2012 -0700
+++ b/src/share/lib/security/java.security-linux	Tue Feb 06 09:04:46 2018 -0800
@@ -786,6 +786,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 sequence of patterns.
@@ -848,6 +851,23 @@
 # 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.lang.Enum;java.security.KeyRep;\
+  java.security.KeyRep$Type;javax.crypto.spec.SecretKeySpec;!*
 #
 # RMI Distributed Garbage Collector (DGC) Serial Filter
 #
--- a/src/share/lib/security/java.security-solaris	Tue Oct 23 15:51:11 2012 -0700
+++ b/src/share/lib/security/java.security-solaris	Tue Feb 06 09:04:46 2018 -0800
@@ -746,6 +746,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 sequence of patterns.
@@ -820,6 +823,23 @@
 #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.lang.Enum;java.security.KeyRep;\
+  java.security.KeyRep$Type;javax.crypto.spec.SecretKeySpec;!*
+
+#
 # Serial number of the OCSP responder's certificate
 #
 # By default, the certificate of the OCSP responder is that of the issuer
--- a/src/share/lib/security/java.security-windows	Tue Oct 23 15:51:11 2012 -0700
+++ b/src/share/lib/security/java.security-windows	Tue Feb 06 09:04:46 2018 -0800
@@ -763,6 +763,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 sequence of patterns.
@@ -835,3 +838,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.lang.Enum;java.security.KeyRep;\
+  java.security.KeyRep$Type;javax.crypto.spec.SecretKeySpec;!*