From ddccffa8bec20e0f47b590c8e11a4a38fad24cc6 Mon Sep 17 00:00:00 2001 From: igerasim Date: Tue, 6 Feb 2018 09:04:46 -0800 Subject: [PATCH] 8189997: Enhance keystore mechanisms 8194259: keytool error: java.io.IOException: Invalid secret key format Reviewed-by: mullan, valeriep, rriggs, ahgross --- .../com/sun/crypto/provider/JceKeyStore.java | 8 +- .../com/sun/crypto/provider/KeyProtector.java | 8 +- .../provider/SealedObjectForKeyProtector.java | 99 ++++++++++++++++++- .../classes/javax/crypto/SealedObject.java | 41 ++++---- .../misc/JavaxCryptoSealedObjectAccess.java} | 44 ++------- src/share/classes/sun/misc/SharedSecrets.java | 16 ++- src/share/lib/security/java.security-aix | 20 ++++ src/share/lib/security/java.security-linux | 20 ++++ src/share/lib/security/java.security-macosx | 20 ++++ src/share/lib/security/java.security-solaris | 20 ++++ src/share/lib/security/java.security-windows | 20 ++++ 11 files changed, 246 insertions(+), 70 deletions(-) rename src/share/classes/{com/sun/crypto/provider/ai.java => sun/misc/JavaxCryptoSealedObjectAccess.java} (56%) diff --git a/src/share/classes/com/sun/crypto/provider/JceKeyStore.java b/src/share/classes/com/sun/crypto/provider/JceKeyStore.java index 50fa3b73d..d47f5a9c9 100644 --- a/src/share/classes/com/sun/crypto/provider/JceKeyStore.java +++ b/src/share/classes/com/sun/crypto/provider/JceKeyStore.java @@ -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 @@ -929,8 +929,10 @@ public final class JceKeyStore extends KeyStoreSpi { // 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; } diff --git a/src/share/classes/com/sun/crypto/provider/KeyProtector.java b/src/share/classes/com/sun/crypto/provider/KeyProtector.java index 823d7bdb8..7f9d0b2ee 100644 --- a/src/share/classes/com/sun/crypto/provider/KeyProtector.java +++ b/src/share/classes/com/sun/crypto/provider/KeyProtector.java @@ -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.KeyFactory; 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 java.security.spec.PKCS8EncodedKeySpec; 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 @@ final class KeyProtector { 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 diff --git a/src/share/classes/com/sun/crypto/provider/SealedObjectForKeyProtector.java b/src/share/classes/com/sun/crypto/provider/SealedObjectForKeyProtector.java index d88e7668c..c95770e91 100644 --- a/src/share/classes/com/sun/crypto/provider/SealedObjectForKeyProtector.java +++ b/src/share/classes/com/sun/crypto/provider/SealedObjectForKeyProtector.java @@ -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,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 @@ final class SealedObjectForKeyProtector extends SealedObject { 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); @@ -59,4 +72,88 @@ final class SealedObjectForKeyProtector extends SealedObject { } return params; } + + final Key getKey(Cipher c) + throws IOException, ClassNotFoundException, IllegalBlockSizeException, + BadPaddingException { + + try (ObjectInputStream ois = SharedSecrets.getJavaxCryptoSealedObjectAccess() + .getExtObjectInputStream(this, c)) { + AccessController.doPrivileged( + (PrivilegedAction) () -> { + 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; + } + } + } + } + + /** + * 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 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; + } + } } diff --git a/src/share/classes/javax/crypto/SealedObject.java b/src/share/classes/javax/crypto/SealedObject.java index 5c548323c..cdf9bd346 100644 --- a/src/share/classes/javax/crypto/SealedObject.java +++ b/src/share/classes/javax/crypto/SealedObject.java @@ -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 sun.misc.SharedSecrets; + import java.io.*; import java.security.AlgorithmParameters; import java.security.Key; @@ -287,17 +289,7 @@ public class SealedObject implements Serializable { 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 @@ public class SealedObject implements Serializable { 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 @@ public class SealedObject implements Serializable { 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 { diff --git a/src/share/classes/com/sun/crypto/provider/ai.java b/src/share/classes/sun/misc/JavaxCryptoSealedObjectAccess.java similarity index 56% rename from src/share/classes/com/sun/crypto/provider/ai.java rename to src/share/classes/sun/misc/JavaxCryptoSealedObjectAccess.java index 853307396..664ec05fb 100644 --- a/src/share/classes/com/sun/crypto/provider/ai.java +++ b/src/share/classes/sun/misc/JavaxCryptoSealedObjectAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2007, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -22,43 +22,17 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +package sun.misc; -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.BadPaddingException; 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); - } +import java.io.IOException; +import java.io.ObjectInputStream; - Object readResolve() throws ObjectStreamException { - return new SealedObjectForKeyProtector(this); - } +public interface JavaxCryptoSealedObjectAccess { + ObjectInputStream getExtObjectInputStream( + SealedObject sealed, Cipher cipher) + throws BadPaddingException, IllegalBlockSizeException, IOException; } diff --git a/src/share/classes/sun/misc/SharedSecrets.java b/src/share/classes/sun/misc/SharedSecrets.java index 18accf8eb..5f8ee1b4a 100644 --- a/src/share/classes/sun/misc/SharedSecrets.java +++ b/src/share/classes/sun/misc/SharedSecrets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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,7 +25,7 @@ package sun.misc; -import java.io.ObjectInputStream; +import javax.crypto.SealedObject; import java.util.jar.JarFile; import java.io.Console; import java.io.FileDescriptor; @@ -58,6 +58,7 @@ public class SharedSecrets { private static JavaUtilZipFileAccess javaUtilZipFileAccess; private static JavaAWTAccess javaAWTAccess; private static JavaOISAccess javaOISAccess; + private static JavaxCryptoSealedObjectAccess javaxCryptoSealedObjectAccess; public static JavaUtilJarAccess javaUtilJarAccess() { if (javaUtilJarAccess == null) { @@ -199,4 +200,15 @@ public class SharedSecrets { } return javaAWTAccess; } + + public static void setJavaxCryptoSealedObjectAccess(JavaxCryptoSealedObjectAccess jcsoa) { + javaxCryptoSealedObjectAccess = jcsoa; + } + + public static JavaxCryptoSealedObjectAccess getJavaxCryptoSealedObjectAccess() { + if (javaxCryptoSealedObjectAccess == null) { + unsafe.ensureClassInitialized(SealedObject.class); + } + return javaxCryptoSealedObjectAccess; + } } diff --git a/src/share/lib/security/java.security-aix b/src/share/lib/security/java.security-aix index 7cb04ba23..5a6b9f3fe 100644 --- a/src/share/lib/security/java.security-aix +++ b/src/share/lib/security/java.security-aix @@ -860,6 +860,9 @@ jdk.xml.dsig.secureValidationPolicy=\ # 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. @@ -955,3 +958,20 @@ jdk.xml.dsig.secureValidationPolicy=\ # 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;!* diff --git a/src/share/lib/security/java.security-linux b/src/share/lib/security/java.security-linux index 2b9336c6f..455eb977e 100644 --- a/src/share/lib/security/java.security-linux +++ b/src/share/lib/security/java.security-linux @@ -861,6 +861,9 @@ jdk.xml.dsig.secureValidationPolicy=\ # 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. @@ -961,3 +964,20 @@ jdk.xml.dsig.secureValidationPolicy=\ # 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;!* diff --git a/src/share/lib/security/java.security-macosx b/src/share/lib/security/java.security-macosx index 5850b8a8c..6891bfbf1 100644 --- a/src/share/lib/security/java.security-macosx +++ b/src/share/lib/security/java.security-macosx @@ -864,6 +864,9 @@ jdk.xml.dsig.secureValidationPolicy=\ # 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. @@ -959,3 +962,20 @@ jdk.xml.dsig.secureValidationPolicy=\ # 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;!* diff --git a/src/share/lib/security/java.security-solaris b/src/share/lib/security/java.security-solaris index 16feb7a3b..5dcb58444 100644 --- a/src/share/lib/security/java.security-solaris +++ b/src/share/lib/security/java.security-solaris @@ -863,6 +863,9 @@ jdk.xml.dsig.secureValidationPolicy=\ # 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. @@ -958,3 +961,20 @@ jdk.xml.dsig.secureValidationPolicy=\ # 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;!* diff --git a/src/share/lib/security/java.security-windows b/src/share/lib/security/java.security-windows index 375c8b2b1..724b6967a 100644 --- a/src/share/lib/security/java.security-windows +++ b/src/share/lib/security/java.security-windows @@ -864,6 +864,9 @@ jdk.xml.dsig.secureValidationPolicy=\ # 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. @@ -959,3 +962,20 @@ jdk.xml.dsig.secureValidationPolicy=\ # 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;!* -- GitLab