changeset 9929:ff52524db7f5

8234027: Better JCEKS key support Reviewed-by: andrew
author yan
date Sun, 12 Apr 2020 06:25:42 +0100
parents ce46c9dcfd63
children fe2a830bf68a
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
diffstat 3 files changed, 47 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/crypto/provider/JceKeyStore.java	Sun Apr 12 05:14:13 2020 +0100
+++ b/src/share/classes/com/sun/crypto/provider/JceKeyStore.java	Sun Apr 12 06:25:42 2020 +0100
@@ -78,6 +78,12 @@
     private static final class SecretKeyEntry {
         Date date; // the creation date of this entry
         SealedObject sealedKey;
+
+        // Maximum possible length of sealedKey. Used to detect malicious
+        // input data. This field is set to the file length of the keystore
+        // at loading. It is useless when creating a new SecretKeyEntry
+        // to be store in a keystore.
+        int maxLength;
     }
 
     // Trusted certificate
@@ -133,8 +139,8 @@
             }
             key = keyProtector.recover(encrInfo);
         } else {
-            key =
-                keyProtector.unseal(((SecretKeyEntry)entry).sealedKey);
+            SecretKeyEntry ske = ((SecretKeyEntry)entry);
+            key = keyProtector.unseal(ske.sealedKey, ske.maxLength);
         }
 
         return key;
@@ -279,6 +285,7 @@
 
                     // seal and store the key
                     entry.sealedKey = keyProtector.seal(key);
+                    entry.maxLength = Integer.MAX_VALUE;
                     entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
                 }
 
@@ -687,6 +694,10 @@
             if (stream == null)
                 return;
 
+            byte[] allData = IOUtils.readAllBytes(stream);
+            final int fullLength = allData.length;
+
+            stream = new ByteArrayInputStream(allData);
             if (password != null) {
                 md = getPreKeyedHash(password);
                 dis = new DataInputStream(new DigestInputStream(stream, md));
@@ -826,11 +837,12 @@
                                 @Override
                                 public Void run() {
                                     ObjectInputFilter.Config.setObjectInputFilter(
-                                        ois2, new DeserializationChecker());
+                                        ois2, new DeserializationChecker(fullLength));
                                     return null;
                                 }
                             });
                             entry.sealedKey = (SealedObject)ois.readObject();
+                            entry.maxLength = fullLength;
                             // NOTE: don't close ois here since we are still
                             // using dis!!!
                         } catch (ClassNotFoundException cnfe) {
@@ -899,8 +911,17 @@
      * deserialized.
      */
     private static class DeserializationChecker implements ObjectInputFilter {
+
         private static final int MAX_NESTED_DEPTH = 2;
 
+        // Full length of keystore, anything inside a SecretKeyEntry should not
+        // be bigger. Otherwise, must be illegal.
+        private final int fullLength;
+
+        public DeserializationChecker(int fullLength) {
+            this.fullLength = fullLength;
+        }
+
         @Override
         public ObjectInputFilter.Status
             checkInput(ObjectInputFilter.FilterInfo info) {
@@ -909,6 +930,7 @@
             long nestedDepth = info.depth();
             if ((nestedDepth == 1 &&
                         info.serialClass() != SealedObjectForKeyProtector.class) ||
+                    info.arrayLength() > fullLength ||
                     (nestedDepth > MAX_NESTED_DEPTH &&
                         info.serialClass() != null &&
                         info.serialClass() != Object.class)) {
--- a/src/share/classes/com/sun/crypto/provider/KeyProtector.java	Sun Apr 12 05:14:13 2020 +0100
+++ b/src/share/classes/com/sun/crypto/provider/KeyProtector.java	Sun Apr 12 06:25:42 2020 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 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
@@ -319,8 +319,11 @@
 
     /**
      * Unseals the sealed key.
+     *
+     * @param maxLength Maximum possible length of so.
+     *                  If bigger, must be illegal.
      */
-    Key unseal(SealedObject so)
+    Key unseal(SealedObject so, int maxLength)
         throws NoSuchAlgorithmException, UnrecoverableKeyException {
         SecretKey sKey = null;
         try {
@@ -355,7 +358,7 @@
                                                       SunJCE.getInstance(),
                                                       "PBEWithMD5AndTripleDES");
             cipher.init(Cipher.DECRYPT_MODE, sKey, params);
-            return soForKeyProtector.getKey(cipher);
+            return soForKeyProtector.getKey(cipher, maxLength);
         } 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	Sun Apr 12 05:14:13 2020 +0100
+++ b/src/share/classes/com/sun/crypto/provider/SealedObjectForKeyProtector.java	Sun Apr 12 06:25:42 2020 +0100
@@ -73,7 +73,7 @@
         return params;
     }
 
-    final Key getKey(Cipher c)
+    final Key getKey(Cipher c, final int maxLength)
             throws IOException, ClassNotFoundException, IllegalBlockSizeException,
             BadPaddingException {
 
@@ -83,7 +83,7 @@
                 @Override
                 public Void run() {
                     ObjectInputFilter.Config.setObjectInputFilter(ois,
-                        DeserializationChecker.ONE_FILTER);
+                        new DeserializationChecker(maxLength));
                     return null;
                 }
             });
@@ -112,7 +112,7 @@
      */
     private static class DeserializationChecker implements ObjectInputFilter {
 
-        private static final ObjectInputFilter ONE_FILTER;
+        private static final ObjectInputFilter OWN_FILTER;
 
         static {
             String prop = AccessController.doPrivileged(new PrivilegedAction<String>() {
@@ -126,26 +126,32 @@
                     }
                 }
             });
-            ONE_FILTER = new DeserializationChecker(prop == null ? null
-                    : ObjectInputFilter.Config.createFilter(prop));
+            OWN_FILTER = prop == null
+                    ? null
+                    : ObjectInputFilter.Config.createFilter(prop);
         }
 
-        private final ObjectInputFilter base;
+        // Maximum possible length of anything inside
+        private final int maxLength;
 
-        private DeserializationChecker(ObjectInputFilter base) {
-            this.base = base;
+        private DeserializationChecker(int maxLength) {
+            this.maxLength = maxLength;
         }
 
         @Override
         public ObjectInputFilter.Status checkInput(
                 ObjectInputFilter.FilterInfo info) {
 
+            if (info.arrayLength() > maxLength) {
+                return Status.REJECTED;
+            }
+
             if (info.serialClass() == Object.class) {
                 return Status.UNDECIDED;
             }
 
-            if (base != null) {
-                Status result = base.checkInput(info);
+            if (OWN_FILTER != null) {
+                Status result = OWN_FILTER.checkInput(info);
                 if (result != Status.UNDECIDED) {
                     return result;
                 }