diff --git a/src/share/classes/com/sun/crypto/provider/JceKeyStore.java b/src/share/classes/com/sun/crypto/provider/JceKeyStore.java index 2800f24b6e8ff08aace0c1e35d0a22759aa82d12..cd1362a60514ecde81f4725e137589395fd59ea2 100644 --- a/src/share/classes/com/sun/crypto/provider/JceKeyStore.java +++ b/src/share/classes/com/sun/crypto/provider/JceKeyStore.java @@ -81,6 +81,12 @@ public final class JceKeyStore extends KeyStoreSpi { 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 @@ -136,8 +142,8 @@ public final class JceKeyStore extends KeyStoreSpi { } key = keyProtector.recover(encrInfo); } else { - key = - keyProtector.unseal(((SecretKeyEntry)entry).sealedKey); + SecretKeyEntry ske = ((SecretKeyEntry)entry); + key = keyProtector.unseal(ske.sealedKey, ske.maxLength); } return key; @@ -282,6 +288,7 @@ public final class JceKeyStore extends KeyStoreSpi { // seal and store the key entry.sealedKey = keyProtector.seal(key); + entry.maxLength = Integer.MAX_VALUE; entries.put(alias.toLowerCase(Locale.ENGLISH), entry); } @@ -691,6 +698,10 @@ public final class JceKeyStore extends KeyStoreSpi { if (stream == null) return; + byte[] allData = IOUtils.readAllBytes(stream); + int fullLength = allData.length; + + stream = new ByteArrayInputStream(allData); if (password != null) { md = getPreKeyedHash(password); dis = new DataInputStream(new DigestInputStream(stream, md)); @@ -829,10 +840,11 @@ public final class JceKeyStore extends KeyStoreSpi { AccessController.doPrivileged( (PrivilegedAction)() -> { 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) { @@ -909,8 +921,17 @@ public final class JceKeyStore extends KeyStoreSpi { * 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) { @@ -919,6 +940,7 @@ public final class JceKeyStore extends KeyStoreSpi { 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)) { diff --git a/src/share/classes/com/sun/crypto/provider/KeyProtector.java b/src/share/classes/com/sun/crypto/provider/KeyProtector.java index 053c3c900e01f7c47e8c47fba7a275b6c699c1b2..63a06a3110ca4917fefbc6ad6540509f40edb5f7 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, 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 @@ -352,8 +352,11 @@ final class KeyProtector { /** * 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 { @@ -388,7 +391,7 @@ final class KeyProtector { 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 diff --git a/src/share/classes/com/sun/crypto/provider/SealedObjectForKeyProtector.java b/src/share/classes/com/sun/crypto/provider/SealedObjectForKeyProtector.java index c95770e91d22f7b7c6e407a9f0ca112adfb30c11..7a22629a9efce821048ee9bdc9c9cac6a208ecec 100644 --- a/src/share/classes/com/sun/crypto/provider/SealedObjectForKeyProtector.java +++ b/src/share/classes/com/sun/crypto/provider/SealedObjectForKeyProtector.java @@ -73,7 +73,7 @@ final class SealedObjectForKeyProtector extends SealedObject { return params; } - final Key getKey(Cipher c) + final Key getKey(Cipher c, int maxLength) throws IOException, ClassNotFoundException, IllegalBlockSizeException, BadPaddingException { @@ -82,7 +82,7 @@ final class SealedObjectForKeyProtector extends SealedObject { AccessController.doPrivileged( (PrivilegedAction) () -> { ObjectInputFilter.Config.setObjectInputFilter(ois, - DeserializationChecker.ONE_FILTER); + new DeserializationChecker(maxLength)); return null; }); try { @@ -110,7 +110,7 @@ final class SealedObjectForKeyProtector extends SealedObject { */ private static class DeserializationChecker implements ObjectInputFilter { - private static final ObjectInputFilter ONE_FILTER; + private static final ObjectInputFilter OWN_FILTER; static { String prop = AccessController.doPrivileged( @@ -122,26 +122,32 @@ final class SealedObjectForKeyProtector extends SealedObject { return Security.getProperty(KEY_SERIAL_FILTER); } }); - 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; }