Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
halo
external_conscrypt
Commits
7e3bd54e
Commit
7e3bd54e
authored
10 years ago
by
Alex Klyubin
Committed by
Gerrit Code Review
10 years ago
Browse files
Options
Download
Plain Diff
Merge "Support duck-typed PSKKeyManager instances in SSLContext.init."
parents
4372784a
0b4bf3b3
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
451 additions
and
0 deletions
+451
-0
src/main/java/org/conscrypt/DuckTypedPSKKeyManager.java
src/main/java/org/conscrypt/DuckTypedPSKKeyManager.java
+133
-0
src/main/java/org/conscrypt/SSLParametersImpl.java
src/main/java/org/conscrypt/SSLParametersImpl.java
+4
-0
src/test/java/org/conscrypt/DuckTypedPSKKeyManagerTest.java
src/test/java/org/conscrypt/DuckTypedPSKKeyManagerTest.java
+314
-0
No files found.
src/main/java/org/conscrypt/DuckTypedPSKKeyManager.java
0 → 100644
View file @
7e3bd54e
/*
* Copyright 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org.conscrypt
;
import
java.lang.reflect.Method
;
import
java.net.Socket
;
import
javax.crypto.SecretKey
;
import
javax.net.ssl.SSLEngine
;
/**
* Reflection-based {@link PSKKeyManager} adaptor for objects which expose all the methods of the
* {@code PSKKeyManager} interface but do not implement the interface.
*
* <p>This is expected to be useful on platforms where there are multiple instances of the
* {@code PSKKeyManager} interface.
*/
class
DuckTypedPSKKeyManager
implements
PSKKeyManager
{
private
final
Object
mDelegate
;
private
DuckTypedPSKKeyManager
(
Object
delegate
)
{
mDelegate
=
delegate
;
}
/**
* Gets an instance of {@code DuckTypedPSKKeyManager} which delegates all invocations of methods
* of the {@link PSKKeyManager} interface to the same methods of the provided object.
*
* @throws NoSuchMethodException if {@code obj} does not implement a method of the
* {@code PSKKeyManager} interface.
*/
public
static
DuckTypedPSKKeyManager
getInstance
(
Object
obj
)
throws
NoSuchMethodException
{
Class
<?>
sourceClass
=
obj
.
getClass
();
for
(
Method
targetMethod
:
PSKKeyManager
.
class
.
getMethods
())
{
if
(
targetMethod
.
isSynthetic
())
{
continue
;
}
// Check that obj exposes the target method (same name and parameter types)
Method
sourceMethod
=
sourceClass
.
getMethod
(
targetMethod
.
getName
(),
targetMethod
.
getParameterTypes
());
// Check that the return type of obj's method matches the target method.
Class
<?>
sourceReturnType
=
sourceMethod
.
getReturnType
();
Class
<?>
targetReturnType
=
targetMethod
.
getReturnType
();
if
(!
targetReturnType
.
isAssignableFrom
(
sourceReturnType
))
{
throw
new
NoSuchMethodException
(
sourceMethod
+
" return value ("
+
sourceReturnType
+
") incompatible with target return value ("
+
targetReturnType
+
")"
);
}
}
return
new
DuckTypedPSKKeyManager
(
obj
);
}
@Override
public
String
chooseServerKeyIdentityHint
(
Socket
socket
)
{
try
{
return
(
String
)
mDelegate
.
getClass
()
.
getMethod
(
"chooseServerKeyIdentityHint"
,
Socket
.
class
)
.
invoke
(
mDelegate
,
socket
);
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
"Failed to invoke chooseServerKeyIdentityHint"
,
e
);
}
}
@Override
public
String
chooseServerKeyIdentityHint
(
SSLEngine
engine
)
{
try
{
return
(
String
)
mDelegate
.
getClass
()
.
getMethod
(
"chooseServerKeyIdentityHint"
,
SSLEngine
.
class
)
.
invoke
(
mDelegate
,
engine
);
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
"Failed to invoke chooseServerKeyIdentityHint"
,
e
);
}
}
@Override
public
String
chooseClientKeyIdentity
(
String
identityHint
,
Socket
socket
)
{
try
{
return
(
String
)
mDelegate
.
getClass
()
.
getMethod
(
"chooseClientKeyIdentity"
,
String
.
class
,
Socket
.
class
)
.
invoke
(
mDelegate
,
identityHint
,
socket
);
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
"Failed to invoke chooseClientKeyIdentity"
,
e
);
}
}
@Override
public
String
chooseClientKeyIdentity
(
String
identityHint
,
SSLEngine
engine
)
{
try
{
return
(
String
)
mDelegate
.
getClass
()
.
getMethod
(
"chooseClientKeyIdentity"
,
String
.
class
,
SSLEngine
.
class
)
.
invoke
(
mDelegate
,
identityHint
,
engine
);
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
"Failed to invoke chooseClientKeyIdentity"
,
e
);
}
}
@Override
public
SecretKey
getKey
(
String
identityHint
,
String
identity
,
Socket
socket
)
{
try
{
return
(
SecretKey
)
mDelegate
.
getClass
()
.
getMethod
(
"getKey"
,
String
.
class
,
String
.
class
,
Socket
.
class
)
.
invoke
(
mDelegate
,
identityHint
,
identity
,
socket
);
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
"Failed to invoke getKey"
,
e
);
}
}
@Override
public
SecretKey
getKey
(
String
identityHint
,
String
identity
,
SSLEngine
engine
)
{
try
{
return
(
SecretKey
)
mDelegate
.
getClass
()
.
getMethod
(
"getKey"
,
String
.
class
,
String
.
class
,
SSLEngine
.
class
)
.
invoke
(
mDelegate
,
identityHint
,
identity
,
engine
);
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
"Failed to invoke getKey"
,
e
);
}
}
}
This diff is collapsed.
Click to expand it.
src/main/java/org/conscrypt/SSLParametersImpl.java
View file @
7e3bd54e
...
...
@@ -847,6 +847,10 @@ public class SSLParametersImpl implements Cloneable {
for
(
KeyManager
km
:
kms
)
{
if
(
km
instanceof
PSKKeyManager
)
{
return
(
PSKKeyManager
)
km
;
}
else
if
(
km
!=
null
)
{
try
{
return
DuckTypedPSKKeyManager
.
getInstance
(
km
);
}
catch
(
NoSuchMethodException
ignored
)
{}
}
}
return
null
;
...
...
This diff is collapsed.
Click to expand it.
src/test/java/org/conscrypt/DuckTypedPSKKeyManagerTest.java
0 → 100644
View file @
7e3bd54e
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org.conscrypt
;
import
junit.framework.TestCase
;
import
java.lang.reflect.InvocationHandler
;
import
java.lang.reflect.Method
;
import
java.lang.reflect.Proxy
;
import
java.net.Socket
;
import
java.security.Key
;
import
java.util.Arrays
;
import
javax.crypto.SecretKey
;
import
javax.crypto.spec.SecretKeySpec
;
import
javax.net.ssl.KeyManager
;
import
javax.net.ssl.SSLContext
;
import
javax.net.ssl.SSLEngine
;
import
javax.net.ssl.SSLSocket
;
import
javax.net.ssl.SSLSocketFactory
;
public
class
DuckTypedPSKKeyManagerTest
extends
TestCase
{
private
SSLSocket
mSSLSocket
;
private
SSLEngine
mSSLEngine
;
@Override
protected
void
setUp
()
throws
Exception
{
super
.
setUp
();
SSLContext
sslContext
=
SSLContext
.
getDefault
();
SSLSocketFactory
sslSocketFactory
=
sslContext
.
getSocketFactory
();
mSSLSocket
=
(
SSLSocket
)
sslSocketFactory
.
createSocket
();
mSSLEngine
=
sslContext
.
createSSLEngine
();
}
@Override
protected
void
tearDown
()
throws
Exception
{
try
{
if
(
mSSLSocket
!=
null
)
{
try
{
mSSLSocket
.
close
();
}
catch
(
Exception
ignored
)
{}
}
}
finally
{
super
.
tearDown
();
}
}
public
void
testDuckTypingFailsWhenOneMethodMissing
()
throws
Exception
{
try
{
DuckTypedPSKKeyManager
.
getInstance
(
new
AlmostPSKKeyManager
());
fail
();
}
catch
(
NoSuchMethodException
expected
)
{}
}
public
void
testDuckTypingFailsWhenOneMethodReturnTypeIncompatible
()
throws
Exception
{
try
{
assertNotNull
(
DuckTypedPSKKeyManager
.
getInstance
(
new
KeyManagerOfferingAllPSKKeyManagerMethodsWithIncompatibleReturnTypes
()));
fail
();
}
catch
(
NoSuchMethodException
expected
)
{}
}
public
void
testDuckTypingSucceedsWhenAllMethodsPresentWithExactReturnTypes
()
throws
Exception
{
assertNotNull
(
DuckTypedPSKKeyManager
.
getInstance
(
new
KeyManagerOfferingAllPSKKeyManagerMethodsWithExactReturnTypes
()));
}
public
void
testDuckTypingSucceedsWhenAllMethodsPresentWithDifferentButCompatibleReturnTypes
()
throws
Exception
{
assertNotNull
(
DuckTypedPSKKeyManager
.
getInstance
(
new
KeyManagerOfferingAllPSKKeyManagerMethodsWithCompatibleReturnTypes
()));
}
public
void
testMethodInvocationDelegation
()
throws
Exception
{
// IMPLEMENTATION NOTE: We create a DuckTypedPSKKeyManager wrapping a Reflection Proxy,
// invoke each method of the PSKKeyManager interface on the DuckTypedPSKKeyManager instance,
// and assert that invocations on the Proxy are as expected and that values returned by the
// Proxy are returned to us.
MockInvocationHandler
mockInvocationHandler
=
new
MockInvocationHandler
();
PSKKeyManager
delegate
=
(
PSKKeyManager
)
Proxy
.
newProxyInstance
(
DuckTypedPSKKeyManager
.
class
.
getClassLoader
(),
new
Class
[]
{
PSKKeyManager
.
class
},
mockInvocationHandler
);
PSKKeyManager
pskKeyManager
=
DuckTypedPSKKeyManager
.
getInstance
(
delegate
);
String
identityHint
=
"hint"
;
String
identity
=
"identity"
;
mockInvocationHandler
.
returnValue
=
identityHint
;
assertSame
(
identityHint
,
pskKeyManager
.
chooseServerKeyIdentityHint
(
mSSLSocket
));
assertEquals
(
"chooseServerKeyIdentityHint"
,
mockInvocationHandler
.
lastInvokedMethod
.
getName
());
assertEquals
(
Arrays
.
asList
(
new
Class
[]
{
Socket
.
class
}),
Arrays
.
asList
(
mockInvocationHandler
.
lastInvokedMethod
.
getParameterTypes
()));
assertEquals
(
1
,
mockInvocationHandler
.
lastInvokedMethodArgs
.
length
);
assertSame
(
mSSLSocket
,
mockInvocationHandler
.
lastInvokedMethodArgs
[
0
]);
mockInvocationHandler
.
returnValue
=
identityHint
;
assertSame
(
identityHint
,
pskKeyManager
.
chooseServerKeyIdentityHint
(
mSSLEngine
));
assertEquals
(
"chooseServerKeyIdentityHint"
,
mockInvocationHandler
.
lastInvokedMethod
.
getName
());
assertEquals
(
Arrays
.
asList
(
new
Class
[]
{
SSLEngine
.
class
}),
Arrays
.
asList
(
mockInvocationHandler
.
lastInvokedMethod
.
getParameterTypes
()));
assertEquals
(
1
,
mockInvocationHandler
.
lastInvokedMethodArgs
.
length
);
assertSame
(
mSSLEngine
,
mockInvocationHandler
.
lastInvokedMethodArgs
[
0
]);
mockInvocationHandler
.
returnValue
=
identity
;
assertSame
(
identity
,
pskKeyManager
.
chooseClientKeyIdentity
(
identityHint
,
mSSLSocket
));
assertEquals
(
"chooseClientKeyIdentity"
,
mockInvocationHandler
.
lastInvokedMethod
.
getName
());
assertEquals
(
Arrays
.
asList
(
new
Class
[]
{
String
.
class
,
Socket
.
class
}),
Arrays
.
asList
(
mockInvocationHandler
.
lastInvokedMethod
.
getParameterTypes
()));
assertEquals
(
2
,
mockInvocationHandler
.
lastInvokedMethodArgs
.
length
);
assertSame
(
identityHint
,
mockInvocationHandler
.
lastInvokedMethodArgs
[
0
]);
assertSame
(
mSSLSocket
,
mockInvocationHandler
.
lastInvokedMethodArgs
[
1
]);
mockInvocationHandler
.
returnValue
=
identity
;
assertSame
(
identity
,
pskKeyManager
.
chooseClientKeyIdentity
(
identityHint
,
mSSLEngine
));
assertEquals
(
"chooseClientKeyIdentity"
,
mockInvocationHandler
.
lastInvokedMethod
.
getName
());
assertEquals
(
Arrays
.
asList
(
new
Class
[]
{
String
.
class
,
SSLEngine
.
class
}),
Arrays
.
asList
(
mockInvocationHandler
.
lastInvokedMethod
.
getParameterTypes
()));
assertEquals
(
2
,
mockInvocationHandler
.
lastInvokedMethodArgs
.
length
);
assertSame
(
identityHint
,
mockInvocationHandler
.
lastInvokedMethodArgs
[
0
]);
assertSame
(
mSSLEngine
,
mockInvocationHandler
.
lastInvokedMethodArgs
[
1
]);
SecretKey
key
=
new
SecretKeySpec
(
"arbitrary"
.
getBytes
(),
"RAW"
);
mockInvocationHandler
.
returnValue
=
key
;
assertSame
(
key
,
pskKeyManager
.
getKey
(
identityHint
,
identity
,
mSSLSocket
));
assertEquals
(
"getKey"
,
mockInvocationHandler
.
lastInvokedMethod
.
getName
());
assertEquals
(
Arrays
.
asList
(
new
Class
[]
{
String
.
class
,
String
.
class
,
Socket
.
class
}),
Arrays
.
asList
(
mockInvocationHandler
.
lastInvokedMethod
.
getParameterTypes
()));
assertEquals
(
3
,
mockInvocationHandler
.
lastInvokedMethodArgs
.
length
);
assertSame
(
identityHint
,
mockInvocationHandler
.
lastInvokedMethodArgs
[
0
]);
assertSame
(
identity
,
mockInvocationHandler
.
lastInvokedMethodArgs
[
1
]);
assertSame
(
mSSLSocket
,
mockInvocationHandler
.
lastInvokedMethodArgs
[
2
]);
mockInvocationHandler
.
returnValue
=
key
;
assertSame
(
key
,
pskKeyManager
.
getKey
(
identityHint
,
identity
,
mSSLEngine
));
assertEquals
(
"getKey"
,
mockInvocationHandler
.
lastInvokedMethod
.
getName
());
assertEquals
(
Arrays
.
asList
(
new
Class
[]
{
String
.
class
,
String
.
class
,
SSLEngine
.
class
}),
Arrays
.
asList
(
mockInvocationHandler
.
lastInvokedMethod
.
getParameterTypes
()));
assertEquals
(
3
,
mockInvocationHandler
.
lastInvokedMethodArgs
.
length
);
assertSame
(
identityHint
,
mockInvocationHandler
.
lastInvokedMethodArgs
[
0
]);
assertSame
(
identity
,
mockInvocationHandler
.
lastInvokedMethodArgs
[
1
]);
assertSame
(
mSSLEngine
,
mockInvocationHandler
.
lastInvokedMethodArgs
[
2
]);
}
public
void
testMethodInvocationDelegationWithDifferentButCompatibleReturnType
()
throws
Exception
{
// Check that nothing blows up when we invoke getKey which is declared to return
// SecretKeySpec rather than SecretKey as declared in the PSKKeyManager interface.
PSKKeyManager
pskKeyManager
=
DuckTypedPSKKeyManager
.
getInstance
(
new
KeyManagerOfferingAllPSKKeyManagerMethodsWithCompatibleReturnTypes
());
pskKeyManager
.
getKey
(
null
,
""
,
mSSLSocket
);
pskKeyManager
.
getKey
(
null
,
""
,
mSSLEngine
);
}
/**
* {@link KeyManager} which implements all methods of {@link PSKKeyManager} except for one.
*/
@SuppressWarnings
(
"unused"
)
private
static
class
AlmostPSKKeyManager
implements
KeyManager
{
public
String
chooseServerKeyIdentityHint
(
Socket
socket
)
{
return
null
;
}
public
String
chooseServerKeyIdentityHint
(
SSLEngine
engine
)
{
return
null
;
}
public
String
chooseClientKeyIdentity
(
String
identityHint
,
Socket
socket
)
{
return
null
;
}
public
String
chooseClientKeyIdentity
(
String
identityHint
,
SSLEngine
engine
)
{
return
null
;
}
public
SecretKey
getKey
(
String
identityHint
,
String
identity
,
Socket
socket
)
{
return
null
;
}
// Missing method from the PSKKeyManager interface:
// SecretKey getKey(String identityHint, String identity, SSLEngine engine);
}
/**
* {@link KeyManager} which exposes all methods of the {@link PSKKeyManager} interface but does
* not implement the interface.
*/
@SuppressWarnings
(
"unused"
)
private
static
class
KeyManagerOfferingAllPSKKeyManagerMethodsWithExactReturnTypes
implements
KeyManager
{
public
String
chooseServerKeyIdentityHint
(
Socket
socket
)
{
return
null
;
}
public
String
chooseServerKeyIdentityHint
(
SSLEngine
engine
)
{
return
null
;
}
public
String
chooseClientKeyIdentity
(
String
identityHint
,
Socket
socket
)
{
return
null
;
}
public
String
chooseClientKeyIdentity
(
String
identityHint
,
SSLEngine
engine
)
{
return
null
;
}
public
SecretKey
getKey
(
String
identityHint
,
String
identity
,
Socket
socket
)
{
return
null
;
}
public
SecretKey
getKey
(
String
identityHint
,
String
identity
,
SSLEngine
engine
)
{
return
null
;
}
}
/**
* {@link KeyManager} which exposes all methods of the {@link PSKKeyManager} interface but does
* not implement the interface. Additionally, the return types of some methods are different
* but compatible with the {@code PSKKeyManager} interface.
*/
@SuppressWarnings
(
"unused"
)
private
static
class
KeyManagerOfferingAllPSKKeyManagerMethodsWithCompatibleReturnTypes
implements
KeyManager
{
public
String
chooseServerKeyIdentityHint
(
Socket
socket
)
{
return
null
;
}
public
String
chooseServerKeyIdentityHint
(
SSLEngine
engine
)
{
return
null
;
}
public
String
chooseClientKeyIdentity
(
String
identityHint
,
Socket
socket
)
{
return
null
;
}
public
String
chooseClientKeyIdentity
(
String
identityHint
,
SSLEngine
engine
)
{
return
null
;
}
// PSKKeyManager's return type: SecretKey
public
SecretKeySpec
getKey
(
String
identityHint
,
String
identity
,
Socket
socket
)
{
return
null
;
}
// PSKKeyManager's return type: SecretKey
public
SecretKeySpec
getKey
(
String
identityHint
,
String
identity
,
SSLEngine
engine
)
{
return
null
;
}
}
/**
* {@link KeyManager} which exposes all methods of the {@link PSKKeyManager} interface but does
* not implement the interface. Additionally, the return types of some methods are incompatible
* with the {@code PSKKeyManager} interface.
*/
@SuppressWarnings
(
"unused"
)
private
static
class
KeyManagerOfferingAllPSKKeyManagerMethodsWithIncompatibleReturnTypes
implements
KeyManager
{
public
String
chooseServerKeyIdentityHint
(
Socket
socket
)
{
return
null
;
}
public
String
chooseServerKeyIdentityHint
(
SSLEngine
engine
)
{
return
null
;
}
public
String
chooseClientKeyIdentity
(
String
identityHint
,
Socket
socket
)
{
return
null
;
}
public
String
chooseClientKeyIdentity
(
String
identityHint
,
SSLEngine
engine
)
{
return
null
;
}
public
SecretKey
getKey
(
String
identityHint
,
String
identity
,
Socket
socket
)
{
return
null
;
}
// PSKKeyManager's return type: SecretKey
public
Key
getKey
(
String
identityHint
,
String
identity
,
SSLEngine
engine
)
{
return
null
;
}
}
static
class
MockInvocationHandler
implements
InvocationHandler
{
Object
returnValue
;
Method
lastInvokedMethod
;
Object
[]
lastInvokedMethodArgs
;
@Override
public
Object
invoke
(
Object
proxy
,
Method
method
,
Object
[]
args
)
throws
Throwable
{
lastInvokedMethod
=
method
;
lastInvokedMethodArgs
=
args
;
return
returnValue
;
}
}
}
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment