< prev index next >

src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java

Print this page
rev 52903 : 8234027: Better JCEKS key support
Reviewed-by: ahgross, mullan, rriggs, rhalade

@@ -77,10 +77,16 @@
 
     // Secret key
     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
     private static final class TrustedCertEntry {
         Date date; // the creation date of this entry

@@ -132,12 +138,12 @@
                                                     + "as PKCS #8 " +
                                                     "EncryptedPrivateKeyInfo");
             }
             key = keyProtector.recover(encrInfo);
         } else {
-            key =
-                keyProtector.unseal(((SecretKeyEntry)entry).sealedKey);
+            SecretKeyEntry ske = ((SecretKeyEntry)entry);
+            key = keyProtector.unseal(ske.sealedKey, ske.maxLength);
         }
 
         return key;
     }
 

@@ -278,10 +284,11 @@
                     SecretKeyEntry entry = new SecretKeyEntry();
                     entry.date = new Date();
 
                     // seal and store the key
                     entry.sealedKey = keyProtector.seal(key);
+                    entry.maxLength = Integer.MAX_VALUE;
                     entries.put(alias.toLowerCase(Locale.ENGLISH), entry);
                 }
 
             } catch (Exception e) {
                 throw new KeyStoreException(e.getMessage());

@@ -687,10 +694,14 @@
             int trustedKeyCount = 0, privateKeyCount = 0, secretKeyCount = 0;
 
             if (stream == null)
                 return;
 
+            byte[] allData = stream.readAllBytes();
+            int fullLength = allData.length;
+
+            stream = new ByteArrayInputStream(allData);
             if (password != null) {
                 md = getPreKeyedHash(password);
                 dis = new DataInputStream(new DigestInputStream(stream, md));
             } else {
                 dis = new DataInputStream(stream);

@@ -825,14 +836,15 @@
                             final ObjectInputStream ois2 = ois;
                             // Set a deserialization checker
                             AccessController.doPrivileged(
                                 (PrivilegedAction<Void>)() -> {
                                     ois2.setObjectInputFilter(
-                                        new DeserializationChecker());
+                                        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) {
                             throw new IOException(cnfe.getMessage());
                         } catch (InvalidClassException ice) {

@@ -921,20 +933,30 @@
     /*
      * An ObjectInputFilter that checks the format of the secret key being
      * 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) {
 
             // First run a custom filter
             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)) {
                 return Status.REJECTED;
             }
< prev index next >