diff options
-rw-r--r-- | luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java | 372 | ||||
-rw-r--r-- | support/src/test/java/libcore/java/security/TestKeyStore.java | 44 |
2 files changed, 416 insertions, 0 deletions
diff --git a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java index 8b424caeb5..e70a67ac9a 100644 --- a/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java +++ b/luni/src/test/java/libcore/javax/net/ssl/SSLSocketTest.java @@ -27,19 +27,36 @@ import java.io.InputStream; import java.io.OutputStream; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Method; +import java.math.BigInteger; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.Signature; +import java.security.SignatureException; +import java.security.SignatureSpi; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECParameterSpec; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -50,7 +67,13 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.CipherSpi; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; import javax.crypto.spec.SecretKeySpec; import javax.net.ServerSocketFactory; import javax.net.SocketFactory; @@ -898,6 +921,355 @@ public class SSLSocketTest extends TestCase { c.close(); } + public void test_SSLSocket_clientAuth_OpaqueKey_RSA() throws Exception { + run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore.getClientCertificate()); + } + + public void test_SSLSocket_clientAuth_OpaqueKey_EC_RSA() throws Exception { + run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore.getClientEcRsaCertificate()); + } + + public void test_SSLSocket_clientAuth_OpaqueKey_EC_EC() throws Exception { + run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore.getClientEcEcCertificate()); + } + + private void run_SSLSocket_clientAuth_OpaqueKey(TestKeyStore keyStore) throws Exception { + try { + Security.insertProviderAt(new OpaqueProvider(), 1); + + final TestSSLContext c = TestSSLContext.create(keyStore, TestKeyStore.getServer()); + SSLContext clientContext = SSLContext.getInstance("TLS"); + final X509KeyManager delegateKeyManager = (X509KeyManager) c.clientKeyManagers[0]; + X509KeyManager keyManager = new X509KeyManager() { + @Override + public String chooseClientAlias(String[] keyType, Principal[] issuers, + Socket socket) { + return delegateKeyManager.chooseClientAlias(keyType, issuers, socket); + } + + @Override + public String chooseServerAlias(String keyType, Principal[] issuers, + Socket socket) { + return delegateKeyManager.chooseServerAlias(keyType, issuers, socket); + } + + @Override + public X509Certificate[] getCertificateChain(String alias) { + return delegateKeyManager.getCertificateChain(alias); + } + + @Override + public String[] getClientAliases(String keyType, Principal[] issuers) { + return delegateKeyManager.getClientAliases(keyType, issuers); + } + + @Override + public String[] getServerAliases(String keyType, Principal[] issuers) { + return delegateKeyManager.getServerAliases(keyType, issuers); + } + + @Override + public PrivateKey getPrivateKey(String alias) { + PrivateKey privKey = delegateKeyManager.getPrivateKey(alias); + if (privKey instanceof RSAPrivateKey) { + return new OpaqueDelegatingRSAPrivateKey((RSAPrivateKey) privKey); + } else if (privKey instanceof ECPrivateKey) { + return new OpaqueDelegatingECPrivateKey((ECPrivateKey) privKey); + } else { + return null; + } + } + }; + clientContext.init(new KeyManager[] { + keyManager + }, new TrustManager[] { + c.clientTrustManager + }, null); + SSLSocket client = (SSLSocket) clientContext.getSocketFactory().createSocket(c.host, + c.port); + final SSLSocket server = (SSLSocket) c.serverSocket.accept(); + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future<Void> future = executor.submit(new Callable<Void>() { + @Override + public Void call() throws Exception { + server.setNeedClientAuth(true); + server.startHandshake(); + return null; + } + }); + executor.shutdown(); + client.startHandshake(); + assertNotNull(client.getSession().getLocalCertificates()); + TestKeyStore.assertChainLength(client.getSession().getLocalCertificates()); + TestSSLContext.assertClientCertificateChain(c.clientTrustManager, + client.getSession().getLocalCertificates()); + future.get(); + client.close(); + server.close(); + c.close(); + } finally { + Security.removeProvider(OpaqueProvider.NAME); + } + } + + @SuppressWarnings("serial") + public static class OpaqueProvider extends Provider { + public static final String NAME = "OpaqueProvider"; + + public OpaqueProvider() { + super(NAME, 1.0, "test provider"); + + put("Signature.NONEwithRSA", OpaqueSignatureSpi.RSA.class.getName()); + put("Signature.NONEwithECDSA", OpaqueSignatureSpi.ECDSA.class.getName()); + put("Cipher.RSA/ECB/NoPadding", OpaqueCipherSpi.class.getName()); + } + } + + protected static class OpaqueSignatureSpi extends SignatureSpi { + private final String algorithm; + + private Signature delegate; + + protected OpaqueSignatureSpi(String algorithm) { + this.algorithm = algorithm; + } + + public final static class RSA extends OpaqueSignatureSpi { + public RSA() { + super("NONEwithRSA"); + } + } + + public final static class ECDSA extends OpaqueSignatureSpi { + public ECDSA() { + super("NONEwithECDSA"); + } + } + + @Override + protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { + fail("Cannot verify"); + } + + @Override + protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { + DelegatingPrivateKey opaqueKey = (DelegatingPrivateKey) privateKey; + try { + delegate = Signature.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + throw new InvalidKeyException(e); + } + delegate.initSign(opaqueKey.getDelegate()); + } + + @Override + protected void engineUpdate(byte b) throws SignatureException { + delegate.update(b); + } + + @Override + protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { + delegate.update(b, off, len); + } + + @Override + protected byte[] engineSign() throws SignatureException { + return delegate.sign(); + } + + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + return delegate.verify(sigBytes); + } + + @SuppressWarnings("deprecation") + @Override + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + delegate.setParameter(param, value); + } + + @SuppressWarnings("deprecation") + @Override + protected Object engineGetParameter(String param) throws InvalidParameterException { + return delegate.getParameter(param); + } + } + + public static class OpaqueCipherSpi extends CipherSpi { + private Cipher delegate; + + public OpaqueCipherSpi() { + } + + @Override + protected void engineSetMode(String mode) throws NoSuchAlgorithmException { + fail(); + } + + @Override + protected void engineSetPadding(String padding) throws NoSuchPaddingException { + fail(); + } + + @Override + protected int engineGetBlockSize() { + return delegate.getBlockSize(); + } + + @Override + protected int engineGetOutputSize(int inputLen) { + return delegate.getOutputSize(inputLen); + } + + @Override + protected byte[] engineGetIV() { + return delegate.getIV(); + } + + @Override + protected AlgorithmParameters engineGetParameters() { + return delegate.getParameters(); + } + + @Override + protected void engineInit(int opmode, Key key, SecureRandom random) + throws InvalidKeyException { + getCipher(); + delegate.init(opmode, key, random); + } + + protected void getCipher() throws InvalidKeyException { + try { + delegate = Cipher.getInstance("RSA/ECB/NoPadding"); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new InvalidKeyException(e); + } + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + getCipher(); + delegate.init(opmode, key, params, random); + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + getCipher(); + delegate.init(opmode, key, params, random); + } + + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + return delegate.update(input, inputOffset, inputLen); + } + + @Override + protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, + int outputOffset) throws ShortBufferException { + return delegate.update(input, inputOffset, inputLen, output, outputOffset); + } + + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) + throws IllegalBlockSizeException, BadPaddingException { + return delegate.update(input, inputOffset, inputLen); + } + + @Override + protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, + int outputOffset) + throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + return delegate.doFinal(input, inputOffset, inputLen, output, outputOffset); + } + } + + private interface DelegatingPrivateKey { + PrivateKey getDelegate(); + } + + @SuppressWarnings("serial") + private static class OpaqueDelegatingECPrivateKey implements ECPrivateKey, DelegatingPrivateKey { + private final ECPrivateKey delegate; + + public OpaqueDelegatingECPrivateKey(ECPrivateKey delegate) { + this.delegate = delegate; + } + + @Override + public PrivateKey getDelegate() { + return delegate; + } + + @Override + public String getAlgorithm() { + return delegate.getAlgorithm(); + } + + @Override + public String getFormat() { + return null; + } + + @Override + public byte[] getEncoded() { + return null; + } + + @Override + public ECParameterSpec getParams() { + return delegate.getParams(); + } + + @Override + public BigInteger getS() { + return null; + } + } + + @SuppressWarnings("serial") + private static class OpaqueDelegatingRSAPrivateKey implements RSAPrivateKey, DelegatingPrivateKey { + private final RSAPrivateKey delegate; + + public OpaqueDelegatingRSAPrivateKey(RSAPrivateKey delegate) { + this.delegate = delegate; + } + + @Override + public BigInteger getPrivateExponent() { + return null; + } + + @Override + public String getAlgorithm() { + return delegate.getAlgorithm(); + } + + @Override + public String getFormat() { + return null; + } + + @Override + public byte[] getEncoded() { + return null; + } + + @Override + public BigInteger getModulus() { + return delegate.getModulus(); + } + + @Override + public PrivateKey getDelegate() { + return delegate; + } + } + public void test_SSLSocket_TrustManagerRuntimeException() throws Exception { TestSSLContext c = TestSSLContext.create(); SSLContext clientContext = SSLContext.getInstance("TLS"); diff --git a/support/src/test/java/libcore/java/security/TestKeyStore.java b/support/src/test/java/libcore/java/security/TestKeyStore.java index e537268450..ef62a44c17 100644 --- a/support/src/test/java/libcore/java/security/TestKeyStore.java +++ b/support/src/test/java/libcore/java/security/TestKeyStore.java @@ -132,10 +132,13 @@ public final class TestKeyStore extends Assert { private static TestKeyStore ROOT_CA; private static TestKeyStore INTERMEDIATE_CA; private static TestKeyStore INTERMEDIATE_CA_2; + private static TestKeyStore INTERMEDIATE_CA_EC; private static TestKeyStore SERVER; private static TestKeyStore CLIENT; private static TestKeyStore CLIENT_CERTIFICATE; + private static TestKeyStore CLIENT_EC_RSA_CERTIFICATE; + private static TestKeyStore CLIENT_EC_EC_CERTIFICATE; private static TestKeyStore CLIENT_2; @@ -209,6 +212,15 @@ public final class TestKeyStore extends Assert { .ca(true) .certificateSerialNumber(BigInteger.valueOf(1)) .build(); + INTERMEDIATE_CA_EC = new Builder() + .aliasPrefix("IntermediateCA-EC") + .keyAlgorithms("EC") + .subject("CN=Test Intermediate Certificate Authority ECDSA") + .ca(true) + .signer(ROOT_CA.getPrivateKey("RSA", "RSA")) + .rootCa(ROOT_CA.getRootCertificate("RSA")) + .certificateSerialNumber(BigInteger.valueOf(2)) + .build(); INTERMEDIATE_CA = new Builder() .aliasPrefix("IntermediateCA") .subject("CN=Test Intermediate Certificate Authority") @@ -225,6 +237,20 @@ public final class TestKeyStore extends Assert { .certificateSerialNumber(BigInteger.valueOf(3)) .build(); CLIENT = new TestKeyStore(createClient(INTERMEDIATE_CA.keyStore), null, null); + CLIENT_EC_RSA_CERTIFICATE = new Builder() + .aliasPrefix("client-ec") + .keyAlgorithms("EC") + .subject("emailAddress=test-ec@user") + .signer(INTERMEDIATE_CA.getPrivateKey("RSA", "RSA")) + .rootCa(INTERMEDIATE_CA.getRootCertificate("RSA")) + .build(); + CLIENT_EC_EC_CERTIFICATE = new Builder() + .aliasPrefix("client-ec") + .keyAlgorithms("EC") + .subject("emailAddress=test-ec@user") + .signer(INTERMEDIATE_CA_EC.getPrivateKey("EC", "RSA")) + .rootCa(INTERMEDIATE_CA_EC.getRootCertificate("RSA")) + .build(); CLIENT_CERTIFICATE = new Builder() .aliasPrefix("client") .subject("emailAddress=test@user") @@ -297,6 +323,24 @@ public final class TestKeyStore extends Assert { } /** + * Return a client keystore with a matched RSA certificate and + * private key as well as a CA certificate. + */ + public static TestKeyStore getClientEcRsaCertificate() { + initCerts(); + return CLIENT_EC_RSA_CERTIFICATE; + } + + /** + * Return a client keystore with a matched RSA certificate and + * private key as well as a CA certificate. + */ + public static TestKeyStore getClientEcEcCertificate() { + initCerts(); + return CLIENT_EC_EC_CERTIFICATE; + } + + /** * Return a keystore with a second CA certificate that does not * trust the server certificate returned by getServer for negative * testing. |