mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
Merge
This commit is contained in:
commit
0278846eaa
91 changed files with 1073 additions and 416 deletions
|
@ -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 = 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 final class JceKeyStore extends KeyStoreSpi {
|
|||
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 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) {
|
||||
|
@ -936,6 +957,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)) {
|
||||
|
|
|
@ -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 {
|
||||
|
@ -389,7 +392,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
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
@ -81,7 +81,7 @@ final class SealedObjectForKeyProtector extends SealedObject {
|
|||
.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 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(
|
||||
|
@ -121,26 +121,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;
|
||||
}
|
||||
|
|
|
@ -1858,6 +1858,8 @@ public class ObjectInputStream
|
|||
break;
|
||||
case TC_REFERENCE:
|
||||
descriptor = (ObjectStreamClass) readHandle(unshared);
|
||||
// Should only reference initialized class descriptors
|
||||
descriptor.checkInitialized();
|
||||
break;
|
||||
case TC_PROXYCLASSDESC:
|
||||
descriptor = readProxyDesc(unshared);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2020, 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
|
||||
|
@ -885,6 +885,17 @@ public class ObjectStreamClass implements Serializable {
|
|||
throw new InternalError("Unexpected call when not initialized");
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws InvalidClassException if not initialized.
|
||||
* To be called in cases where an uninitialized class descriptor indicates
|
||||
* a problem in the serialization stream.
|
||||
*/
|
||||
final void checkInitialized() throws InvalidClassException {
|
||||
if (!initialized) {
|
||||
throw new InvalidClassException("Class descriptor should be initialized");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an InvalidClassException if object instances referencing this
|
||||
* class descriptor should not be allowed to deserialize. This method does
|
||||
|
@ -1150,6 +1161,10 @@ public class ObjectStreamClass implements Serializable {
|
|||
} catch (IllegalAccessException ex) {
|
||||
// should not occur, as access checks have been suppressed
|
||||
throw new InternalError(ex);
|
||||
} catch (InstantiationError err) {
|
||||
var ex = new InstantiationException();
|
||||
ex.initCause(err);
|
||||
throw ex;
|
||||
}
|
||||
} else {
|
||||
throw new UnsupportedOperationException();
|
||||
|
|
|
@ -116,7 +116,8 @@ class MethodType
|
|||
|
||||
// The remaining fields are caches of various sorts:
|
||||
private @Stable MethodTypeForm form; // erased form, plus cached data about primitives
|
||||
private @Stable MethodType wrapAlt; // alternative wrapped/unwrapped version
|
||||
private @Stable Object wrapAlt; // alternative wrapped/unwrapped version and
|
||||
// private communication for readObject and readResolve
|
||||
private @Stable Invokers invokers; // cache of handy higher-order adapters
|
||||
private @Stable String methodDescriptor; // cache for toMethodDescriptorString
|
||||
|
||||
|
@ -711,7 +712,7 @@ class MethodType
|
|||
|
||||
private static MethodType wrapWithPrims(MethodType pt) {
|
||||
assert(pt.hasPrimitives());
|
||||
MethodType wt = pt.wrapAlt;
|
||||
MethodType wt = (MethodType)pt.wrapAlt;
|
||||
if (wt == null) {
|
||||
// fill in lazily
|
||||
wt = MethodTypeForm.canonicalize(pt, MethodTypeForm.WRAP, MethodTypeForm.WRAP);
|
||||
|
@ -723,7 +724,7 @@ class MethodType
|
|||
|
||||
private static MethodType unwrapWithNoPrims(MethodType wt) {
|
||||
assert(!wt.hasPrimitives());
|
||||
MethodType uwt = wt.wrapAlt;
|
||||
MethodType uwt = (MethodType)wt.wrapAlt;
|
||||
if (uwt == null) {
|
||||
// fill in lazily
|
||||
uwt = MethodTypeForm.canonicalize(wt, MethodTypeForm.UNWRAP, MethodTypeForm.UNWRAP);
|
||||
|
@ -1248,27 +1249,18 @@ s.writeObject(this.parameterArray());
|
|||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
|
||||
// Assign temporary defaults in case this object escapes
|
||||
MethodType_init(void.class, NO_PTYPES);
|
||||
// Assign defaults in case this object escapes
|
||||
UNSAFE.putReference(this, OffsetHolder.rtypeOffset, void.class);
|
||||
UNSAFE.putReference(this, OffsetHolder.ptypesOffset, NO_PTYPES);
|
||||
|
||||
s.defaultReadObject(); // requires serialPersistentFields to be an empty array
|
||||
|
||||
Class<?> returnType = (Class<?>) s.readObject();
|
||||
Class<?>[] parameterArray = (Class<?>[]) s.readObject();
|
||||
parameterArray = parameterArray.clone(); // make sure it is unshared
|
||||
|
||||
// Assign deserialized values
|
||||
MethodType_init(returnType, parameterArray);
|
||||
}
|
||||
|
||||
// Initialization of state for deserialization only
|
||||
private void MethodType_init(Class<?> rtype, Class<?>[] ptypes) {
|
||||
// In order to communicate these values to readResolve, we must
|
||||
// store them into the implementation-specific final fields.
|
||||
checkRtype(rtype);
|
||||
checkPtypes(ptypes);
|
||||
UNSAFE.putReference(this, OffsetHolder.rtypeOffset, rtype);
|
||||
UNSAFE.putReference(this, OffsetHolder.ptypesOffset, ptypes);
|
||||
// Verify all operands, and make sure ptypes is unshared
|
||||
// Cache the new MethodType for readResolve
|
||||
wrapAlt = new MethodType[]{MethodType.methodType(returnType, parameterArray)};
|
||||
}
|
||||
|
||||
// Support for resetting final fields while deserializing. Implement Holder
|
||||
|
@ -1291,12 +1283,10 @@ s.writeObject(this.parameterArray());
|
|||
// Do not use a trusted path for deserialization:
|
||||
// return makeImpl(rtype, ptypes, true);
|
||||
// Verify all operands, and make sure ptypes is unshared:
|
||||
try {
|
||||
return methodType(rtype, ptypes);
|
||||
} finally {
|
||||
// Re-assign defaults in case this object escapes
|
||||
MethodType_init(void.class, NO_PTYPES);
|
||||
}
|
||||
// Return a new validated MethodType for the rtype and ptypes passed from readObject.
|
||||
MethodType mt = ((MethodType[])wrapAlt)[0];
|
||||
wrapAlt = null;
|
||||
return mt;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2178,8 +2178,8 @@ class MutableBigInteger {
|
|||
}
|
||||
|
||||
/**
|
||||
* Calculate the multiplicative inverse of this mod mod, where mod is odd.
|
||||
* This and mod are not changed by the calculation.
|
||||
* Calculate the multiplicative inverse of this modulo mod, where the mod
|
||||
* argument is odd. This and mod are not changed by the calculation.
|
||||
*
|
||||
* This method implements an algorithm due to Richard Schroeppel, that uses
|
||||
* the same intermediate representation as Montgomery Reduction
|
||||
|
@ -2233,8 +2233,18 @@ class MutableBigInteger {
|
|||
k += trailingZeros;
|
||||
}
|
||||
|
||||
while (c.sign < 0)
|
||||
c.signedAdd(p);
|
||||
if (c.compare(p) >= 0) { // c has a larger magnitude than p
|
||||
MutableBigInteger remainder = c.divide(p,
|
||||
new MutableBigInteger());
|
||||
// The previous line ignores the sign so we copy the data back
|
||||
// into c which will restore the sign as needed (and converts
|
||||
// it back to a SignedMutableBigInteger)
|
||||
c.copyValue(remainder);
|
||||
}
|
||||
|
||||
if (c.sign < 0) {
|
||||
c.signedAdd(p);
|
||||
}
|
||||
|
||||
return fixup(c, p, k);
|
||||
}
|
||||
|
@ -2272,8 +2282,8 @@ class MutableBigInteger {
|
|||
}
|
||||
|
||||
// In theory, c may be greater than p at this point (Very rare!)
|
||||
while (c.compare(p) >= 0)
|
||||
c.subtract(p);
|
||||
if (c.compare(p) >= 0)
|
||||
c = c.divide(p, new MutableBigInteger());
|
||||
|
||||
return c;
|
||||
}
|
||||
|
|
|
@ -37,12 +37,12 @@ import java.security.PermissionCollection;
|
|||
import java.security.PrivilegedAction;
|
||||
import java.security.Security;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Vector;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.ConcurrentSkipListMap;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import sun.net.util.IPAddressUtil;
|
||||
import sun.net.PortConfig;
|
||||
import sun.security.util.RegisteredDomain;
|
||||
|
@ -1349,16 +1349,13 @@ final class SocketPermissionCollection extends PermissionCollection
|
|||
implements Serializable
|
||||
{
|
||||
// Not serialized; see serialization section at end of class
|
||||
// A ConcurrentSkipListMap is used to preserve order, so that most
|
||||
// recently added permissions are checked first (see JDK-4301064).
|
||||
private transient ConcurrentSkipListMap<String, SocketPermission> perms;
|
||||
private transient Map<String, SocketPermission> perms;
|
||||
|
||||
/**
|
||||
* Create an empty SocketPermissions object.
|
||||
*
|
||||
* Create an empty SocketPermissionCollection object.
|
||||
*/
|
||||
public SocketPermissionCollection() {
|
||||
perms = new ConcurrentSkipListMap<>(new SPCComparator());
|
||||
perms = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1431,6 +1428,18 @@ final class SocketPermissionCollection extends PermissionCollection
|
|||
int effective = 0;
|
||||
int needed = desired;
|
||||
|
||||
var hit = perms.get(np.getName());
|
||||
if (hit != null) {
|
||||
// fastpath, if the host was explicitly listed
|
||||
if (((needed & hit.getMask()) != 0) && hit.impliesIgnoreMask(np)) {
|
||||
effective |= hit.getMask();
|
||||
if ((effective & desired) == desired) {
|
||||
return true;
|
||||
}
|
||||
needed = (desired & ~effective);
|
||||
}
|
||||
}
|
||||
|
||||
//System.out.println("implies "+np);
|
||||
for (SocketPermission x : perms.values()) {
|
||||
//System.out.println(" trying "+x);
|
||||
|
@ -1512,22 +1521,9 @@ final class SocketPermissionCollection extends PermissionCollection
|
|||
// Get the one we want
|
||||
@SuppressWarnings("unchecked")
|
||||
Vector<SocketPermission> permissions = (Vector<SocketPermission>)gfields.get("permissions", null);
|
||||
perms = new ConcurrentSkipListMap<>(new SPCComparator());
|
||||
perms = new ConcurrentHashMap<>(permissions.size());
|
||||
for (SocketPermission sp : permissions) {
|
||||
perms.put(sp.getName(), sp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple comparator that orders new non-equal entries at the beginning.
|
||||
*/
|
||||
private static class SPCComparator implements Comparator<String> {
|
||||
@Override
|
||||
public int compare(String s1, String s2) {
|
||||
if (s1.equals(s2)) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,6 @@ class ByteBufferAs$Type$Buffer$RW$$BO$ // package-private
|
|||
public $Type$Buffer slice() {
|
||||
int pos = this.position();
|
||||
int lim = this.limit();
|
||||
assert (pos <= lim);
|
||||
int rem = (pos <= lim ? lim - pos : 0);
|
||||
long addr = byteOffset(pos);
|
||||
return new ByteBufferAs$Type$Buffer$RW$$BO$(bb, -1, 0, rem, rem, addr, segment);
|
||||
|
|
|
@ -213,7 +213,6 @@ class Direct$Type$Buffer$RW$$BO$
|
|||
public $Type$Buffer slice() {
|
||||
int pos = this.position();
|
||||
int lim = this.limit();
|
||||
assert (pos <= lim);
|
||||
int rem = (pos <= lim ? lim - pos : 0);
|
||||
int off = (pos << $LG_BYTES_PER_VALUE$);
|
||||
assert (off >= 0);
|
||||
|
|
|
@ -105,13 +105,15 @@ class Heap$Type$Buffer$RW$
|
|||
}
|
||||
|
||||
public $Type$Buffer slice() {
|
||||
int rem = this.remaining();
|
||||
int pos = this.position();
|
||||
int lim = this.limit();
|
||||
int rem = (pos <= lim ? lim - pos : 0);
|
||||
return new Heap$Type$Buffer$RW$(hb,
|
||||
-1,
|
||||
0,
|
||||
rem,
|
||||
rem,
|
||||
this.position() + offset, segment);
|
||||
pos + offset, segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -43,12 +43,15 @@ class StringCharBuffer // package-private
|
|||
}
|
||||
|
||||
public CharBuffer slice() {
|
||||
int pos = this.position();
|
||||
int lim = this.limit();
|
||||
int rem = (pos <= lim ? lim - pos : 0);
|
||||
return new StringCharBuffer(str,
|
||||
-1,
|
||||
0,
|
||||
this.remaining(),
|
||||
this.remaining(),
|
||||
offset + this.position());
|
||||
rem,
|
||||
rem,
|
||||
offset + pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -428,7 +428,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
|||
// here but what can we do? The final authority will be
|
||||
// whatever parse method is invoked, so ultimately the
|
||||
// Scanner will do the right thing
|
||||
String digit = "((?i)["+radixDigits+"]|\\p{javaDigit})";
|
||||
String digit = "((?i)["+radixDigits+"\\p{javaDigit}])";
|
||||
String groupedNumeral = "("+non0Digit+digit+"?"+digit+"?("+
|
||||
groupSeparator+digit+digit+digit+")+)";
|
||||
// digit++ is the possessive form which is necessary for reducing
|
||||
|
@ -478,7 +478,7 @@ public final class Scanner implements Iterator<String>, Closeable {
|
|||
private Pattern decimalPattern;
|
||||
private void buildFloatAndDecimalPattern() {
|
||||
// \\p{javaDigit} may not be perfect, see above
|
||||
String digit = "([0-9]|(\\p{javaDigit}))";
|
||||
String digit = "(([0-9\\p{javaDigit}]))";
|
||||
String exponent = "([eE][+-]?"+digit+"+)?";
|
||||
String groupedNumeral = "("+non0Digit+digit+"?"+digit+"?("+
|
||||
groupSeparator+digit+digit+digit+")+)";
|
||||
|
@ -1289,25 +1289,25 @@ public final class Scanner implements Iterator<String>, Closeable {
|
|||
|
||||
// These must be literalized to avoid collision with regex
|
||||
// metacharacters such as dot or parenthesis
|
||||
groupSeparator = "\\" + dfs.getGroupingSeparator();
|
||||
decimalSeparator = "\\" + dfs.getDecimalSeparator();
|
||||
groupSeparator = "\\x{" + Integer.toHexString(dfs.getGroupingSeparator()) + "}";
|
||||
decimalSeparator = "\\x{" + Integer.toHexString(dfs.getDecimalSeparator()) + "}";
|
||||
|
||||
// Quoting the nonzero length locale-specific things
|
||||
// to avoid potential conflict with metacharacters
|
||||
nanString = "\\Q" + dfs.getNaN() + "\\E";
|
||||
infinityString = "\\Q" + dfs.getInfinity() + "\\E";
|
||||
nanString = Pattern.quote(dfs.getNaN());
|
||||
infinityString = Pattern.quote(dfs.getInfinity());
|
||||
positivePrefix = df.getPositivePrefix();
|
||||
if (!positivePrefix.isEmpty())
|
||||
positivePrefix = "\\Q" + positivePrefix + "\\E";
|
||||
positivePrefix = Pattern.quote(positivePrefix);
|
||||
negativePrefix = df.getNegativePrefix();
|
||||
if (!negativePrefix.isEmpty())
|
||||
negativePrefix = "\\Q" + negativePrefix + "\\E";
|
||||
negativePrefix = Pattern.quote(negativePrefix);
|
||||
positiveSuffix = df.getPositiveSuffix();
|
||||
if (!positiveSuffix.isEmpty())
|
||||
positiveSuffix = "\\Q" + positiveSuffix + "\\E";
|
||||
positiveSuffix = Pattern.quote(positiveSuffix);
|
||||
negativeSuffix = df.getNegativeSuffix();
|
||||
if (!negativeSuffix.isEmpty())
|
||||
negativeSuffix = "\\Q" + negativeSuffix + "\\E";
|
||||
negativeSuffix = Pattern.quote(negativeSuffix);
|
||||
|
||||
// Force rebuilding and recompilation of locale dependent
|
||||
// primitive patterns
|
||||
|
|
|
@ -166,13 +166,23 @@ public class DatagramSocketAdaptor
|
|||
|
||||
@Override
|
||||
public SocketAddress getLocalSocketAddress() {
|
||||
try {
|
||||
return dc.getLocalAddress();
|
||||
} catch (ClosedChannelException e) {
|
||||
InetSocketAddress local = dc.localAddress();
|
||||
if (local == null || isClosed())
|
||||
return null;
|
||||
} catch (Exception x) {
|
||||
throw new Error(x);
|
||||
|
||||
InetAddress addr = local.getAddress();
|
||||
if (addr.isAnyLocalAddress())
|
||||
return local;
|
||||
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
try {
|
||||
sm.checkConnect(addr.getHostAddress(), -1);
|
||||
} catch (SecurityException x) {
|
||||
return new InetSocketAddress(local.getPort());
|
||||
}
|
||||
}
|
||||
return local;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -265,7 +265,7 @@ enum Alert {
|
|||
// It's OK to get a no_certificate alert from a client of
|
||||
// which we requested client authentication. However,
|
||||
// if we required it, then this is not acceptable.
|
||||
if (tc.sslConfig.isClientMode ||
|
||||
if (tc.sslConfig.isClientMode ||
|
||||
alert != Alert.NO_CERTIFICATE ||
|
||||
(tc.sslConfig.clientAuthType !=
|
||||
ClientAuthType.CLIENT_AUTH_REQUESTED)) {
|
||||
|
@ -273,8 +273,10 @@ enum Alert {
|
|||
"received handshake warning: " + alert.description);
|
||||
} else {
|
||||
// Otherwise ignore the warning but remove the
|
||||
// CertificateVerify handshake consumer so the state
|
||||
// machine doesn't expect it.
|
||||
// Certificate and CertificateVerify handshake
|
||||
// consumer so the state machine doesn't expect it.
|
||||
tc.handshakeContext.handshakeConsumers.remove(
|
||||
SSLHandshake.CERTIFICATE.id);
|
||||
tc.handshakeContext.handshakeConsumers.remove(
|
||||
SSLHandshake.CERTIFICATE_VERIFY.id);
|
||||
}
|
||||
|
|
|
@ -406,7 +406,7 @@ final class ClientHello {
|
|||
ProtocolVersion maxProtocolVersion = chc.maximumActiveProtocol;
|
||||
|
||||
// session ID of the ClientHello message
|
||||
SessionId sessionId = SSLSessionImpl.nullSession.getSessionId();
|
||||
SessionId sessionId = new SessionId(new byte[0]);
|
||||
|
||||
// a list of cipher suites sent by the client
|
||||
List<CipherSuite> cipherSuites = chc.activeCipherSuites;
|
||||
|
|
|
@ -90,6 +90,16 @@ final class ClientKeyExchange {
|
|||
ServerHandshakeContext shc = (ServerHandshakeContext)context;
|
||||
// clean up this consumer
|
||||
shc.handshakeConsumers.remove(SSLHandshake.CLIENT_KEY_EXCHANGE.id);
|
||||
|
||||
// Check for an unprocessed client Certificate message. If that
|
||||
// handshake consumer is still present then that expected message
|
||||
// was not sent.
|
||||
if (shc.handshakeConsumers.containsKey(
|
||||
SSLHandshake.CERTIFICATE.id)) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected ClientKeyExchange handshake message.");
|
||||
}
|
||||
|
||||
SSLKeyExchange ke = SSLKeyExchange.valueOf(
|
||||
shc.negotiatedCipherSuite.keyExchange,
|
||||
shc.negotiatedProtocol);
|
||||
|
|
|
@ -897,6 +897,8 @@ final class Finished {
|
|||
// has been received and processed.
|
||||
if (!chc.isResumption) {
|
||||
if (chc.handshakeConsumers.containsKey(
|
||||
SSLHandshake.CERTIFICATE.id) ||
|
||||
chc.handshakeConsumers.containsKey(
|
||||
SSLHandshake.CERTIFICATE_VERIFY.id)) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected Finished handshake message");
|
||||
|
@ -1029,6 +1031,8 @@ final class Finished {
|
|||
// has been received and processed.
|
||||
if (!shc.isResumption) {
|
||||
if (shc.handshakeConsumers.containsKey(
|
||||
SSLHandshake.CERTIFICATE.id) ||
|
||||
shc.handshakeConsumers.containsKey(
|
||||
SSLHandshake.CERTIFICATE_VERIFY.id)) {
|
||||
throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected Finished handshake message");
|
||||
|
|
|
@ -164,8 +164,10 @@ abstract class HandshakeContext implements ConnectionContext {
|
|||
this.conContext = conContext;
|
||||
this.sslConfig = (SSLConfiguration)conContext.sslConfig.clone();
|
||||
|
||||
this.algorithmConstraints = new SSLAlgorithmConstraints(
|
||||
sslConfig.userSpecifiedAlgorithmConstraints);
|
||||
this.activeProtocols = getActiveProtocols(sslConfig.enabledProtocols,
|
||||
sslConfig.enabledCipherSuites, sslConfig.algorithmConstraints);
|
||||
sslConfig.enabledCipherSuites, algorithmConstraints);
|
||||
if (activeProtocols.isEmpty()) {
|
||||
throw new SSLHandshakeException(
|
||||
"No appropriate protocol (protocol is disabled or " +
|
||||
|
@ -181,12 +183,10 @@ abstract class HandshakeContext implements ConnectionContext {
|
|||
}
|
||||
this.maximumActiveProtocol = maximumVersion;
|
||||
this.activeCipherSuites = getActiveCipherSuites(this.activeProtocols,
|
||||
sslConfig.enabledCipherSuites, sslConfig.algorithmConstraints);
|
||||
sslConfig.enabledCipherSuites, algorithmConstraints);
|
||||
if (activeCipherSuites.isEmpty()) {
|
||||
throw new SSLHandshakeException("No appropriate cipher suite");
|
||||
}
|
||||
this.algorithmConstraints =
|
||||
new SSLAlgorithmConstraints(sslConfig.algorithmConstraints);
|
||||
|
||||
this.handshakeConsumers = new LinkedHashMap<>();
|
||||
this.handshakeProducers = new HashMap<>();
|
||||
|
@ -209,7 +209,7 @@ abstract class HandshakeContext implements ConnectionContext {
|
|||
/**
|
||||
* Constructor for PostHandshakeContext
|
||||
*/
|
||||
HandshakeContext(TransportContext conContext) {
|
||||
protected HandshakeContext(TransportContext conContext) {
|
||||
this.sslContext = conContext.sslContext;
|
||||
this.conContext = conContext;
|
||||
this.sslConfig = conContext.sslConfig;
|
||||
|
@ -219,6 +219,7 @@ abstract class HandshakeContext implements ConnectionContext {
|
|||
this.handshakeOutput = new HandshakeOutStream(conContext.outputRecord);
|
||||
this.delegatedActions = new LinkedList<>();
|
||||
|
||||
this.handshakeConsumers = new LinkedHashMap<>();
|
||||
this.handshakeProducers = null;
|
||||
this.handshakeHash = null;
|
||||
this.activeProtocols = null;
|
||||
|
|
|
@ -336,7 +336,7 @@ final class KeyShareExtension {
|
|||
for (KeyShareEntry entry : spec.clientShares) {
|
||||
NamedGroup ng = NamedGroup.valueOf(entry.namedGroupId);
|
||||
if (ng == null || !SupportedGroups.isActivatable(
|
||||
shc.sslConfig.algorithmConstraints, ng)) {
|
||||
shc.algorithmConstraints, ng)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"Ignore unsupported named group: " +
|
||||
|
@ -620,7 +620,7 @@ final class KeyShareExtension {
|
|||
KeyShareEntry keyShare = spec.serverShare;
|
||||
NamedGroup ng = NamedGroup.valueOf(keyShare.namedGroupId);
|
||||
if (ng == null || !SupportedGroups.isActivatable(
|
||||
chc.sslConfig.algorithmConstraints, ng)) {
|
||||
chc.algorithmConstraints, ng)) {
|
||||
throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unsupported named group: " +
|
||||
NamedGroup.nameOf(keyShare.namedGroupId));
|
||||
|
@ -762,7 +762,7 @@ final class KeyShareExtension {
|
|||
NamedGroup selectedGroup = null;
|
||||
for (NamedGroup ng : shc.clientRequestedNamedGroups) {
|
||||
if (SupportedGroups.isActivatable(
|
||||
shc.sslConfig.algorithmConstraints, ng)) {
|
||||
shc.algorithmConstraints, ng)) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
||||
SSLLogger.fine(
|
||||
"HelloRetryRequest selected named group: " +
|
||||
|
|
|
@ -30,17 +30,11 @@ import java.nio.BufferOverflowException;
|
|||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A compact implementation of HandshakeContext for post-handshake messages
|
||||
*/
|
||||
final class PostHandshakeContext extends HandshakeContext {
|
||||
private final static Map<Byte, SSLConsumer> consumers = Map.of(
|
||||
SSLHandshake.KEY_UPDATE.id, SSLHandshake.KEY_UPDATE,
|
||||
SSLHandshake.NEW_SESSION_TICKET.id, SSLHandshake.NEW_SESSION_TICKET);
|
||||
|
||||
PostHandshakeContext(TransportContext context) throws IOException {
|
||||
super(context);
|
||||
|
||||
|
@ -49,10 +43,23 @@ final class PostHandshakeContext extends HandshakeContext {
|
|||
"Post-handshake not supported in " + negotiatedProtocol.name);
|
||||
}
|
||||
|
||||
this.localSupportedSignAlgs = new ArrayList<SignatureScheme>(
|
||||
this.localSupportedSignAlgs = new ArrayList<>(
|
||||
context.conSession.getLocalSupportedSignatureSchemes());
|
||||
|
||||
handshakeConsumers = new LinkedHashMap<>(consumers);
|
||||
// Add the potential post-handshake consumers.
|
||||
if (context.sslConfig.isClientMode) {
|
||||
handshakeConsumers.putIfAbsent(
|
||||
SSLHandshake.KEY_UPDATE.id,
|
||||
SSLHandshake.KEY_UPDATE);
|
||||
handshakeConsumers.putIfAbsent(
|
||||
SSLHandshake.NEW_SESSION_TICKET.id,
|
||||
SSLHandshake.NEW_SESSION_TICKET);
|
||||
} else {
|
||||
handshakeConsumers.putIfAbsent(
|
||||
SSLHandshake.KEY_UPDATE.id,
|
||||
SSLHandshake.KEY_UPDATE);
|
||||
}
|
||||
|
||||
handshakeFinished = true;
|
||||
handshakeSession = context.conSession;
|
||||
}
|
||||
|
@ -83,4 +90,21 @@ final class PostHandshakeContext extends HandshakeContext {
|
|||
SSLHandshake.nameOf(handshakeType), be);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isConsumable(TransportContext context, byte handshakeType) {
|
||||
if (handshakeType == SSLHandshake.KEY_UPDATE.id) {
|
||||
// The KeyUpdate handshake message does not apply to TLS 1.2 and
|
||||
// previous protocols.
|
||||
return context.protocolVersion.useTLS13PlusSpec();
|
||||
}
|
||||
|
||||
if (handshakeType == SSLHandshake.NEW_SESSION_TICKET.id) {
|
||||
// The new session ticket handshake message could be consumer in
|
||||
// client side only.
|
||||
return context.sslConfig.isClientMode;
|
||||
}
|
||||
|
||||
// No more post-handshake message supported currently.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,21 +71,21 @@ final class SSLAlgorithmConstraints implements AlgorithmConstraints {
|
|||
|
||||
SSLAlgorithmConstraints(SSLSocket socket,
|
||||
boolean withDefaultCertPathConstraints) {
|
||||
this.userSpecifiedConstraints = getConstraints(socket);
|
||||
this.userSpecifiedConstraints = getUserSpecifiedConstraints(socket);
|
||||
this.peerSpecifiedConstraints = null;
|
||||
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
|
||||
}
|
||||
|
||||
SSLAlgorithmConstraints(SSLEngine engine,
|
||||
boolean withDefaultCertPathConstraints) {
|
||||
this.userSpecifiedConstraints = getConstraints(engine);
|
||||
this.userSpecifiedConstraints = getUserSpecifiedConstraints(engine);
|
||||
this.peerSpecifiedConstraints = null;
|
||||
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
|
||||
}
|
||||
|
||||
SSLAlgorithmConstraints(SSLSocket socket, String[] supportedAlgorithms,
|
||||
boolean withDefaultCertPathConstraints) {
|
||||
this.userSpecifiedConstraints = getConstraints(socket);
|
||||
this.userSpecifiedConstraints = getUserSpecifiedConstraints(socket);
|
||||
this.peerSpecifiedConstraints =
|
||||
new SupportedSignatureAlgorithmConstraints(supportedAlgorithms);
|
||||
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
|
||||
|
@ -93,13 +93,14 @@ final class SSLAlgorithmConstraints implements AlgorithmConstraints {
|
|||
|
||||
SSLAlgorithmConstraints(SSLEngine engine, String[] supportedAlgorithms,
|
||||
boolean withDefaultCertPathConstraints) {
|
||||
this.userSpecifiedConstraints = getConstraints(engine);
|
||||
this.userSpecifiedConstraints = getUserSpecifiedConstraints(engine);
|
||||
this.peerSpecifiedConstraints =
|
||||
new SupportedSignatureAlgorithmConstraints(supportedAlgorithms);
|
||||
this.enabledX509DisabledAlgConstraints = withDefaultCertPathConstraints;
|
||||
}
|
||||
|
||||
private static AlgorithmConstraints getConstraints(SSLEngine engine) {
|
||||
private static AlgorithmConstraints getUserSpecifiedConstraints(
|
||||
SSLEngine engine) {
|
||||
if (engine != null) {
|
||||
// Note that the KeyManager or TrustManager implementation may be
|
||||
// not implemented in the same provider as SSLSocket/SSLEngine.
|
||||
|
@ -108,17 +109,18 @@ final class SSLAlgorithmConstraints implements AlgorithmConstraints {
|
|||
HandshakeContext hc =
|
||||
((SSLEngineImpl)engine).conContext.handshakeContext;
|
||||
if (hc != null) {
|
||||
return hc.sslConfig.algorithmConstraints;
|
||||
return hc.sslConfig.userSpecifiedAlgorithmConstraints;
|
||||
}
|
||||
} else {
|
||||
return engine.getSSLParameters().getAlgorithmConstraints();
|
||||
}
|
||||
|
||||
return engine.getSSLParameters().getAlgorithmConstraints();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static AlgorithmConstraints getConstraints(SSLSocket socket) {
|
||||
private static AlgorithmConstraints getUserSpecifiedConstraints(
|
||||
SSLSocket socket) {
|
||||
if (socket != null) {
|
||||
// Note that the KeyManager or TrustManager implementation may be
|
||||
// not implemented in the same provider as SSLSocket/SSLEngine.
|
||||
|
@ -127,11 +129,11 @@ final class SSLAlgorithmConstraints implements AlgorithmConstraints {
|
|||
HandshakeContext hc =
|
||||
((SSLSocketImpl)socket).conContext.handshakeContext;
|
||||
if (hc != null) {
|
||||
return hc.sslConfig.algorithmConstraints;
|
||||
return hc.sslConfig.userSpecifiedAlgorithmConstraints;
|
||||
}
|
||||
} else {
|
||||
return socket.getSSLParameters().getAlgorithmConstraints();
|
||||
}
|
||||
|
||||
return socket.getSSLParameters().getAlgorithmConstraints();
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -51,7 +51,7 @@ import sun.security.ssl.SSLExtension.ServerExtensions;
|
|||
*/
|
||||
final class SSLConfiguration implements Cloneable {
|
||||
// configurations with SSLParameters
|
||||
AlgorithmConstraints algorithmConstraints;
|
||||
AlgorithmConstraints userSpecifiedAlgorithmConstraints;
|
||||
List<ProtocolVersion> enabledProtocols;
|
||||
List<CipherSuite> enabledCipherSuites;
|
||||
ClientAuthType clientAuthType;
|
||||
|
@ -116,7 +116,8 @@ final class SSLConfiguration implements Cloneable {
|
|||
SSLConfiguration(SSLContextImpl sslContext, boolean isClientMode) {
|
||||
|
||||
// Configurations with SSLParameters, default values.
|
||||
this.algorithmConstraints = SSLAlgorithmConstraints.DEFAULT;
|
||||
this.userSpecifiedAlgorithmConstraints =
|
||||
SSLAlgorithmConstraints.DEFAULT;
|
||||
this.enabledProtocols =
|
||||
sslContext.getDefaultProtocolVersions(!isClientMode);
|
||||
this.enabledCipherSuites =
|
||||
|
@ -153,7 +154,7 @@ final class SSLConfiguration implements Cloneable {
|
|||
SSLParameters getSSLParameters() {
|
||||
SSLParameters params = new SSLParameters();
|
||||
|
||||
params.setAlgorithmConstraints(this.algorithmConstraints);
|
||||
params.setAlgorithmConstraints(this.userSpecifiedAlgorithmConstraints);
|
||||
params.setProtocols(ProtocolVersion.toStringArray(enabledProtocols));
|
||||
params.setCipherSuites(CipherSuite.namesOf(enabledCipherSuites));
|
||||
switch (this.clientAuthType) {
|
||||
|
@ -193,7 +194,7 @@ final class SSLConfiguration implements Cloneable {
|
|||
void setSSLParameters(SSLParameters params) {
|
||||
AlgorithmConstraints ac = params.getAlgorithmConstraints();
|
||||
if (ac != null) {
|
||||
this.algorithmConstraints = ac;
|
||||
this.userSpecifiedAlgorithmConstraints = ac;
|
||||
} // otherwise, use the default value
|
||||
|
||||
String[] sa = params.getCipherSuites();
|
||||
|
|
|
@ -77,11 +77,6 @@ import javax.net.ssl.SSLSessionContext;
|
|||
*/
|
||||
final class SSLSessionImpl extends ExtendedSSLSession {
|
||||
|
||||
/*
|
||||
* we only really need a single null session
|
||||
*/
|
||||
static final SSLSessionImpl nullSession = new SSLSessionImpl();
|
||||
|
||||
/*
|
||||
* The state of a single session, as described in section 7.1
|
||||
* of the SSLv3 spec.
|
||||
|
@ -153,7 +148,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
* be used either by a client or by a server, as a connection is
|
||||
* first opened and before handshaking begins.
|
||||
*/
|
||||
private SSLSessionImpl() {
|
||||
SSLSessionImpl() {
|
||||
this.protocolVersion = ProtocolVersion.NONE;
|
||||
this.cipherSuite = CipherSuite.C_NULL;
|
||||
this.sessionId = new SessionId(false, null);
|
||||
|
@ -1222,15 +1217,6 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||
public void invalidate() {
|
||||
sessionLock.lock();
|
||||
try {
|
||||
//
|
||||
// Can't invalidate the NULL session -- this would be
|
||||
// attempted when we get a handshaking error on a brand
|
||||
// new connection, with no "real" session yet.
|
||||
//
|
||||
if (this == nullSession) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (context != null) {
|
||||
context.remove(sessionId);
|
||||
context = null;
|
||||
|
|
|
@ -360,7 +360,7 @@ public final class SSLSocketImpl
|
|||
SSLLogger.severe("handshake failed", ioe);
|
||||
}
|
||||
|
||||
return SSLSessionImpl.nullSession;
|
||||
return new SSLSessionImpl();
|
||||
}
|
||||
|
||||
return conContext.conSession;
|
||||
|
|
|
@ -173,12 +173,24 @@ interface SSLTransport {
|
|||
|
||||
if (plainText == null) {
|
||||
plainText = Plaintext.PLAINTEXT_NULL;
|
||||
} else {
|
||||
// Fill the destination buffers.
|
||||
if ((dsts != null) && (dstsLength > 0) &&
|
||||
(plainText.contentType ==
|
||||
ContentType.APPLICATION_DATA.id)) {
|
||||
} else if (plainText.contentType ==
|
||||
ContentType.APPLICATION_DATA.id) {
|
||||
// check handshake status
|
||||
//
|
||||
// Note that JDK does not support 0-RTT yet. Otherwise, it is
|
||||
// needed to check early_data.
|
||||
if (!context.isNegotiated) {
|
||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,verbose")) {
|
||||
SSLLogger.warning("unexpected application data " +
|
||||
"before handshake completion");
|
||||
}
|
||||
|
||||
throw context.fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Receiving application data before handshake complete");
|
||||
}
|
||||
|
||||
// Fill the destination buffers.
|
||||
if ((dsts != null) && (dstsLength > 0)) {
|
||||
ByteBuffer fragment = plainText.fragment;
|
||||
int remains = fragment.remaining();
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ final class TransportContext implements ConnectionContext {
|
|||
this.isUnsureMode = isUnsureMode;
|
||||
|
||||
// initial security parameters
|
||||
this.conSession = SSLSessionImpl.nullSession;
|
||||
this.conSession = new SSLSessionImpl();
|
||||
this.protocolVersion = this.sslConfig.maximumProtocolVersion;
|
||||
this.clientVerifyData = emptyByteArray;
|
||||
this.serverVerifyData = emptyByteArray;
|
||||
|
@ -164,12 +164,13 @@ final class TransportContext implements ConnectionContext {
|
|||
" message: " +
|
||||
SSLHandshake.nameOf(type));
|
||||
}
|
||||
if (type == SSLHandshake.KEY_UPDATE.id &&
|
||||
!protocolVersion.useTLS13PlusSpec()) {
|
||||
|
||||
if (!PostHandshakeContext.isConsumable(this, type)) {
|
||||
throw fatal(Alert.UNEXPECTED_MESSAGE,
|
||||
"Unexpected post-handshake message: " +
|
||||
SSLHandshake.nameOf(type));
|
||||
}
|
||||
|
||||
handshakeContext = new PostHandshakeContext(this);
|
||||
} else {
|
||||
handshakeContext = sslConfig.isClientMode ?
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue