Skip to content
Permalink
Browse files
8234027: Better JCEKS key support
Reviewed-by: ahgross, mullan, rriggs, rhalade
  • Loading branch information
wangweij committed Dec 11, 2019
1 parent ad09813 commit c182379f32d454ce2242753b605a4be92cfff3e2
Showing 3 changed files with 47 additions and 16 deletions.
@@ -81,6 +81,12 @@ private static final class PrivateKeyEntry {
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 Key engineGetKey(String alias, char[] password)
}
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 void engineSetKeyEntry(String alias, Key key, char[] password,

// 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 void engineLoad(InputStream stream, char[] password)
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));
@@ -829,10 +840,11 @@ public void engineLoad(InputStream stream, char[] password)
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) {
@@ -926,8 +938,17 @@ public boolean engineProbe(InputStream stream) throws IOException {
* 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) {
@@ -936,6 +957,7 @@ private static class DeserializationChecker implements ObjectInputFilter {
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)) {
@@ -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 @@ SealedObject seal(Key key)

/**
* 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 {
@@ -389,7 +392,7 @@ Key unseal(SealedObject so)
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
@@ -73,15 +73,15 @@ AlgorithmParameters getParameters() {
return params;
}

final Key getKey(Cipher c)
final Key getKey(Cipher c, int maxLength)
throws IOException, ClassNotFoundException, IllegalBlockSizeException,
BadPaddingException {

try (ObjectInputStream ois = SharedSecrets.getJavaxCryptoSealedObjectAccess()
.getExtObjectInputStream(this, c)) {
AccessController.doPrivileged(
(PrivilegedAction<Void>) () -> {
ois.setObjectInputFilter(DeserializationChecker.ONE_FILTER);
ois.setObjectInputFilter(new DeserializationChecker(maxLength));
return null;
});
try {
@@ -109,7 +109,7 @@ final Key getKey(Cipher c)
*/
private static class DeserializationChecker implements ObjectInputFilter {

private static final ObjectInputFilter ONE_FILTER;
private static final ObjectInputFilter OWN_FILTER;

static {
String prop = AccessController.doPrivileged(
@@ -121,26 +121,32 @@ private static class DeserializationChecker implements ObjectInputFilter {
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;
}

0 comments on commit c182379

Please sign in to comment.