mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8166596
: TLS support for the EdDSA signature algorithm
Reviewed-by: xuelei
This commit is contained in:
parent
3932527288
commit
d80ae05f61
9 changed files with 825 additions and 60 deletions
|
@ -67,8 +67,8 @@ final class CertificateRequest {
|
||||||
// TLS 1.2 and prior versions
|
// TLS 1.2 and prior versions
|
||||||
private static enum ClientCertificateType {
|
private static enum ClientCertificateType {
|
||||||
// RFC 2246
|
// RFC 2246
|
||||||
RSA_SIGN ((byte)0x01, "rsa_sign", "RSA", true),
|
RSA_SIGN ((byte)0x01, "rsa_sign", List.of("RSA"), true),
|
||||||
DSS_SIGN ((byte)0x02, "dss_sign", "DSA", true),
|
DSS_SIGN ((byte)0x02, "dss_sign", List.of("DSA"), true),
|
||||||
RSA_FIXED_DH ((byte)0x03, "rsa_fixed_dh"),
|
RSA_FIXED_DH ((byte)0x03, "rsa_fixed_dh"),
|
||||||
DSS_FIXED_DH ((byte)0x04, "dss_fixed_dh"),
|
DSS_FIXED_DH ((byte)0x04, "dss_fixed_dh"),
|
||||||
|
|
||||||
|
@ -77,9 +77,10 @@ final class CertificateRequest {
|
||||||
DSS_EPHEMERAL_DH ((byte)0x06, "dss_ephemeral_dh"),
|
DSS_EPHEMERAL_DH ((byte)0x06, "dss_ephemeral_dh"),
|
||||||
FORTEZZA_DMS ((byte)0x14, "fortezza_dms"),
|
FORTEZZA_DMS ((byte)0x14, "fortezza_dms"),
|
||||||
|
|
||||||
// RFC 4492
|
// RFC 4492 and 8442
|
||||||
ECDSA_SIGN ((byte)0x40, "ecdsa_sign",
|
ECDSA_SIGN ((byte)0x40, "ecdsa_sign",
|
||||||
"EC", JsseJce.isEcAvailable()),
|
List.of("EC", "EdDSA"),
|
||||||
|
JsseJce.isEcAvailable()),
|
||||||
RSA_FIXED_ECDH ((byte)0x41, "rsa_fixed_ecdh"),
|
RSA_FIXED_ECDH ((byte)0x41, "rsa_fixed_ecdh"),
|
||||||
ECDSA_FIXED_ECDH ((byte)0x42, "ecdsa_fixed_ecdh");
|
ECDSA_FIXED_ECDH ((byte)0x42, "ecdsa_fixed_ecdh");
|
||||||
|
|
||||||
|
@ -95,7 +96,7 @@ final class CertificateRequest {
|
||||||
|
|
||||||
final byte id;
|
final byte id;
|
||||||
final String name;
|
final String name;
|
||||||
final String keyAlgorithm;
|
final List<String> keyAlgorithm;
|
||||||
final boolean isAvailable;
|
final boolean isAvailable;
|
||||||
|
|
||||||
private ClientCertificateType(byte id, String name) {
|
private ClientCertificateType(byte id, String name) {
|
||||||
|
@ -103,7 +104,7 @@ final class CertificateRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClientCertificateType(byte id, String name,
|
private ClientCertificateType(byte id, String name,
|
||||||
String keyAlgorithm, boolean isAvailable) {
|
List<String> keyAlgorithm, boolean isAvailable) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.keyAlgorithm = keyAlgorithm;
|
this.keyAlgorithm = keyAlgorithm;
|
||||||
|
@ -134,7 +135,11 @@ final class CertificateRequest {
|
||||||
for (byte id : ids) {
|
for (byte id : ids) {
|
||||||
ClientCertificateType cct = ClientCertificateType.valueOf(id);
|
ClientCertificateType cct = ClientCertificateType.valueOf(id);
|
||||||
if (cct.isAvailable) {
|
if (cct.isAvailable) {
|
||||||
keyTypes.add(cct.keyAlgorithm);
|
cct.keyAlgorithm.forEach(key -> {
|
||||||
|
if (!keyTypes.contains(key)) {
|
||||||
|
keyTypes.add(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -459,6 +459,9 @@ final class CertificateVerify {
|
||||||
case "EC":
|
case "EC":
|
||||||
signer = Signature.getInstance(JsseJce.SIGNATURE_RAWECDSA);
|
signer = Signature.getInstance(JsseJce.SIGNATURE_RAWECDSA);
|
||||||
break;
|
break;
|
||||||
|
case "EdDSA":
|
||||||
|
signer = Signature.getInstance(JsseJce.SIGNATURE_EDDSA);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new SignatureException("Unrecognized algorithm: "
|
throw new SignatureException("Unrecognized algorithm: "
|
||||||
+ algorithm);
|
+ algorithm);
|
||||||
|
|
|
@ -424,6 +424,9 @@ final class ECDHServerKeyExchange {
|
||||||
case "EC":
|
case "EC":
|
||||||
signer = Signature.getInstance(JsseJce.SIGNATURE_ECDSA);
|
signer = Signature.getInstance(JsseJce.SIGNATURE_ECDSA);
|
||||||
break;
|
break;
|
||||||
|
case "EdDSA":
|
||||||
|
signer = Signature.getInstance(JsseJce.SIGNATURE_EDDSA);
|
||||||
|
break;
|
||||||
case "RSA":
|
case "RSA":
|
||||||
signer = RSASignature.getInstance();
|
signer = RSASignature.getInstance();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -90,6 +90,11 @@ final class JsseJce {
|
||||||
*/
|
*/
|
||||||
static final String SIGNATURE_ECDSA = "SHA1withECDSA";
|
static final String SIGNATURE_ECDSA = "SHA1withECDSA";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JCA identifier for EdDSA signatures.
|
||||||
|
*/
|
||||||
|
static final String SIGNATURE_EDDSA = "EdDSA";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JCA identifier string for Raw DSA, i.e. a DSA signature without
|
* JCA identifier string for Raw DSA, i.e. a DSA signature without
|
||||||
* hashing where the application provides the SHA-1 hash of the data.
|
* hashing where the application provides the SHA-1 hash of the data.
|
||||||
|
|
|
@ -274,11 +274,11 @@ enum SSLExtension implements SSLStringizer {
|
||||||
SIGNED_CERT_TIMESTAMP (0x0012, "signed_certificate_timestamp"),
|
SIGNED_CERT_TIMESTAMP (0x0012, "signed_certificate_timestamp"),
|
||||||
|
|
||||||
// extensions defined in RFC 7250
|
// extensions defined in RFC 7250
|
||||||
CLIENT_CERT_TYPE (0x0013, "padding"),
|
CLIENT_CERT_TYPE (0x0013, "client_certificate_type"),
|
||||||
SERVER_CERT_TYPE (0x0014, "server_certificate_type"),
|
SERVER_CERT_TYPE (0x0014, "server_certificate_type"),
|
||||||
|
|
||||||
// extensions defined in RFC 7685
|
// extensions defined in RFC 7685
|
||||||
PADDING (0x0015, "client_certificate_type"),
|
PADDING (0x0015, "padding"),
|
||||||
|
|
||||||
// extensions defined in RFC 7366
|
// extensions defined in RFC 7366
|
||||||
ENCRYPT_THEN_MAC (0x0016, "encrypt_then_mac"),
|
ENCRYPT_THEN_MAC (0x0016, "encrypt_then_mac"),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -29,18 +29,23 @@ import java.io.IOException;
|
||||||
import java.util.AbstractMap.SimpleImmutableEntry;
|
import java.util.AbstractMap.SimpleImmutableEntry;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
import sun.security.ssl.SupportedGroupsExtension.SupportedGroups;
|
||||||
import sun.security.ssl.X509Authentication.X509Possession;
|
import sun.security.ssl.X509Authentication.X509Possession;
|
||||||
|
|
||||||
final class SSLKeyExchange implements SSLKeyAgreementGenerator,
|
final class SSLKeyExchange implements SSLKeyAgreementGenerator,
|
||||||
SSLHandshakeBinding {
|
SSLHandshakeBinding {
|
||||||
private final SSLAuthentication authentication;
|
private final List<SSLAuthentication> authentication;
|
||||||
private final SSLKeyAgreement keyAgreement;
|
private final SSLKeyAgreement keyAgreement;
|
||||||
|
|
||||||
SSLKeyExchange(X509Authentication authentication,
|
SSLKeyExchange(List<X509Authentication> authentication,
|
||||||
SSLKeyAgreement keyAgreement) {
|
SSLKeyAgreement keyAgreement) {
|
||||||
this.authentication = authentication;
|
if (authentication != null) {
|
||||||
|
this.authentication = List.copyOf(authentication);
|
||||||
|
} else {
|
||||||
|
this.authentication = null;
|
||||||
|
}
|
||||||
this.keyAgreement = keyAgreement;
|
this.keyAgreement = keyAgreement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +53,15 @@ final class SSLKeyExchange implements SSLKeyAgreementGenerator,
|
||||||
// authentication
|
// authentication
|
||||||
SSLPossession authPossession = null;
|
SSLPossession authPossession = null;
|
||||||
if (authentication != null) {
|
if (authentication != null) {
|
||||||
authPossession = authentication.createPossession(context);
|
// Loop through potential authentication types and end at
|
||||||
|
// the first non-null possession.
|
||||||
|
for (SSLAuthentication authType : authentication) {
|
||||||
|
if ((authPossession = authType.createPossession(context))
|
||||||
|
!= null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (authPossession == null) {
|
if (authPossession == null) {
|
||||||
return new SSLPossession[0];
|
return new SSLPossession[0];
|
||||||
} else if (context instanceof ServerHandshakeContext) {
|
} else if (context instanceof ServerHandshakeContext) {
|
||||||
|
@ -109,12 +122,14 @@ final class SSLKeyExchange implements SSLKeyAgreementGenerator,
|
||||||
@Override
|
@Override
|
||||||
public SSLHandshake[] getRelatedHandshakers(
|
public SSLHandshake[] getRelatedHandshakers(
|
||||||
HandshakeContext handshakeContext) {
|
HandshakeContext handshakeContext) {
|
||||||
SSLHandshake[] auHandshakes;
|
SSLHandshake[] auHandshakes = null;
|
||||||
if (authentication != null) {
|
if (authentication != null) {
|
||||||
auHandshakes =
|
for (SSLAuthentication authType : authentication) {
|
||||||
authentication.getRelatedHandshakers(handshakeContext);
|
auHandshakes = authType.getRelatedHandshakers(handshakeContext);
|
||||||
} else {
|
if (auHandshakes != null && auHandshakes.length > 0) {
|
||||||
auHandshakes = null;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SSLHandshake[] kaHandshakes =
|
SSLHandshake[] kaHandshakes =
|
||||||
|
@ -136,12 +151,14 @@ final class SSLKeyExchange implements SSLKeyAgreementGenerator,
|
||||||
@Override
|
@Override
|
||||||
public Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers(
|
public Map.Entry<Byte, HandshakeProducer>[] getHandshakeProducers(
|
||||||
HandshakeContext handshakeContext) {
|
HandshakeContext handshakeContext) {
|
||||||
Map.Entry<Byte, HandshakeProducer>[] auProducers;
|
Map.Entry<Byte, HandshakeProducer>[] auProducers = null;
|
||||||
if (authentication != null) {
|
if (authentication != null) {
|
||||||
auProducers =
|
for (SSLAuthentication authType : authentication) {
|
||||||
authentication.getHandshakeProducers(handshakeContext);
|
auProducers = authType.getHandshakeProducers(handshakeContext);
|
||||||
} else {
|
if (auProducers != null && auProducers.length > 0) {
|
||||||
auProducers = null;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map.Entry<Byte, HandshakeProducer>[] kaProducers =
|
Map.Entry<Byte, HandshakeProducer>[] kaProducers =
|
||||||
|
@ -163,12 +180,14 @@ final class SSLKeyExchange implements SSLKeyAgreementGenerator,
|
||||||
@Override
|
@Override
|
||||||
public Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers(
|
public Map.Entry<Byte, SSLConsumer>[] getHandshakeConsumers(
|
||||||
HandshakeContext handshakeContext) {
|
HandshakeContext handshakeContext) {
|
||||||
Map.Entry<Byte, SSLConsumer>[] auConsumers;
|
Map.Entry<Byte, SSLConsumer>[] auConsumers = null;
|
||||||
if (authentication != null) {
|
if (authentication != null) {
|
||||||
auConsumers =
|
for (SSLAuthentication authType : authentication) {
|
||||||
authentication.getHandshakeConsumers(handshakeContext);
|
auConsumers = authType.getHandshakeConsumers(handshakeContext);
|
||||||
} else {
|
if (auConsumers != null && auConsumers.length > 0) {
|
||||||
auConsumers = null;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map.Entry<Byte, SSLConsumer>[] kaConsumers =
|
Map.Entry<Byte, SSLConsumer>[] kaConsumers =
|
||||||
|
@ -247,37 +266,37 @@ final class SSLKeyExchange implements SSLKeyAgreementGenerator,
|
||||||
|
|
||||||
private static class SSLKeyExRSA {
|
private static class SSLKeyExRSA {
|
||||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||||
X509Authentication.RSA, T12KeyAgreement.RSA);
|
List.of(X509Authentication.RSA), T12KeyAgreement.RSA);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SSLKeyExRSAExport {
|
private static class SSLKeyExRSAExport {
|
||||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||||
X509Authentication.RSA, T12KeyAgreement.RSA_EXPORT);
|
List.of(X509Authentication.RSA), T12KeyAgreement.RSA_EXPORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SSLKeyExDHEDSS {
|
private static class SSLKeyExDHEDSS {
|
||||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||||
X509Authentication.DSA, T12KeyAgreement.DHE);
|
List.of(X509Authentication.DSA), T12KeyAgreement.DHE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SSLKeyExDHEDSSExport {
|
private static class SSLKeyExDHEDSSExport {
|
||||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||||
X509Authentication.DSA, T12KeyAgreement.DHE_EXPORT);
|
List.of(X509Authentication.DSA), T12KeyAgreement.DHE_EXPORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SSLKeyExDHERSA {
|
private static class SSLKeyExDHERSA {
|
||||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||||
X509Authentication.RSA, T12KeyAgreement.DHE);
|
List.of(X509Authentication.RSA), T12KeyAgreement.DHE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SSLKeyExDHERSAOrPSS {
|
private static class SSLKeyExDHERSAOrPSS {
|
||||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||||
X509Authentication.RSA_OR_PSS, T12KeyAgreement.DHE);
|
List.of(X509Authentication.RSA_OR_PSS), T12KeyAgreement.DHE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SSLKeyExDHERSAExport {
|
private static class SSLKeyExDHERSAExport {
|
||||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||||
X509Authentication.RSA, T12KeyAgreement.DHE_EXPORT);
|
List.of(X509Authentication.RSA), T12KeyAgreement.DHE_EXPORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SSLKeyExDHANON {
|
private static class SSLKeyExDHANON {
|
||||||
|
@ -292,27 +311,28 @@ final class SSLKeyExchange implements SSLKeyAgreementGenerator,
|
||||||
|
|
||||||
private static class SSLKeyExECDHECDSA {
|
private static class SSLKeyExECDHECDSA {
|
||||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||||
X509Authentication.EC, T12KeyAgreement.ECDH);
|
List.of(X509Authentication.EC), T12KeyAgreement.ECDH);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SSLKeyExECDHRSA {
|
private static class SSLKeyExECDHRSA {
|
||||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||||
X509Authentication.EC, T12KeyAgreement.ECDH);
|
List.of(X509Authentication.EC), T12KeyAgreement.ECDH);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SSLKeyExECDHEECDSA {
|
private static class SSLKeyExECDHEECDSA {
|
||||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||||
X509Authentication.EC, T12KeyAgreement.ECDHE);
|
List.of(X509Authentication.EC, X509Authentication.EDDSA),
|
||||||
|
T12KeyAgreement.ECDHE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SSLKeyExECDHERSA {
|
private static class SSLKeyExECDHERSA {
|
||||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||||
X509Authentication.RSA, T12KeyAgreement.ECDHE);
|
List.of(X509Authentication.RSA), T12KeyAgreement.ECDHE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SSLKeyExECDHERSAOrPSS {
|
private static class SSLKeyExECDHERSAOrPSS {
|
||||||
private static SSLKeyExchange KE = new SSLKeyExchange(
|
private static SSLKeyExchange KE = new SSLKeyExchange(
|
||||||
X509Authentication.RSA_OR_PSS, T12KeyAgreement.ECDHE);
|
List.of(X509Authentication.RSA_OR_PSS), T12KeyAgreement.ECDHE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SSLKeyExECDHANON {
|
private static class SSLKeyExECDHANON {
|
||||||
|
|
|
@ -47,14 +47,6 @@ import sun.security.util.KeyUtil;
|
||||||
import sun.security.util.SignatureUtil;
|
import sun.security.util.SignatureUtil;
|
||||||
|
|
||||||
enum SignatureScheme {
|
enum SignatureScheme {
|
||||||
// EdDSA algorithms
|
|
||||||
ED25519 (0x0807, "ed25519", "ed25519",
|
|
||||||
"ed25519",
|
|
||||||
ProtocolVersion.PROTOCOLS_OF_13),
|
|
||||||
ED448 (0x0808, "ed448", "ed448",
|
|
||||||
"ed448",
|
|
||||||
ProtocolVersion.PROTOCOLS_OF_13),
|
|
||||||
|
|
||||||
// ECDSA algorithms
|
// ECDSA algorithms
|
||||||
ECDSA_SECP256R1_SHA256 (0x0403, "ecdsa_secp256r1_sha256",
|
ECDSA_SECP256R1_SHA256 (0x0403, "ecdsa_secp256r1_sha256",
|
||||||
"SHA256withECDSA",
|
"SHA256withECDSA",
|
||||||
|
@ -72,6 +64,14 @@ enum SignatureScheme {
|
||||||
NamedGroup.SECP521_R1,
|
NamedGroup.SECP521_R1,
|
||||||
ProtocolVersion.PROTOCOLS_TO_13),
|
ProtocolVersion.PROTOCOLS_TO_13),
|
||||||
|
|
||||||
|
// EdDSA algorithms
|
||||||
|
ED25519 (0x0807, "ed25519", "Ed25519",
|
||||||
|
"EdDSA",
|
||||||
|
ProtocolVersion.PROTOCOLS_12_13),
|
||||||
|
ED448 (0x0808, "ed448", "Ed448",
|
||||||
|
"EdDSA",
|
||||||
|
ProtocolVersion.PROTOCOLS_12_13),
|
||||||
|
|
||||||
// RSASSA-PSS algorithms with public key OID rsaEncryption
|
// RSASSA-PSS algorithms with public key OID rsaEncryption
|
||||||
//
|
//
|
||||||
// The minimalKeySize is calculated as (See RFC 8017 for details):
|
// The minimalKeySize is calculated as (See RFC 8017 for details):
|
||||||
|
@ -275,16 +275,6 @@ enum SignatureScheme {
|
||||||
|
|
||||||
boolean mediator = true;
|
boolean mediator = true;
|
||||||
|
|
||||||
// Disable EdDSA algorithms for TLS. Remove this when support is added.
|
|
||||||
if (id == 0x0807 || id == 0x0808) {
|
|
||||||
mediator = false;
|
|
||||||
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
|
|
||||||
SSLLogger.warning(
|
|
||||||
"Signature algorithm, " + algorithm +
|
|
||||||
", not supported by JSSE");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// An EC provider, for example the SunEC provider, may support
|
// An EC provider, for example the SunEC provider, may support
|
||||||
// AlgorithmParameters but not KeyPairGenerator or Signature.
|
// AlgorithmParameters but not KeyPairGenerator or Signature.
|
||||||
//
|
//
|
||||||
|
|
|
@ -64,7 +64,10 @@ enum X509Authentication implements SSLAuthentication {
|
||||||
|
|
||||||
// Require EC public key
|
// Require EC public key
|
||||||
EC ("EC", new X509PossessionGenerator(
|
EC ("EC", new X509PossessionGenerator(
|
||||||
new String[] {"EC"}));
|
new String[] {"EC"})),
|
||||||
|
// Edwards-Curve key
|
||||||
|
EDDSA ("EdDSA", new X509PossessionGenerator(
|
||||||
|
new String[] {"EdDSA"}));
|
||||||
|
|
||||||
final String keyType;
|
final String keyType;
|
||||||
final SSLPossessionGenerator possessionGenerator;
|
final SSLPossessionGenerator possessionGenerator;
|
||||||
|
|
736
test/jdk/javax/net/ssl/TLSCommon/TLSWithEdDSA.java
Normal file
736
test/jdk/javax/net/ssl/TLSCommon/TLSWithEdDSA.java
Normal file
|
@ -0,0 +1,736 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 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
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SunJSSE does not support dynamic system properties, no way to re-use
|
||||||
|
* system properties in samevm/agentvm mode.
|
||||||
|
* For extra debugging output, add -Djavax.net.debug=ssl:handshake into the
|
||||||
|
* run directive below.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8166596
|
||||||
|
* @summary TLS support for the EdDSA signature algorithm
|
||||||
|
* @library /javax/net/ssl/templates /test/lib
|
||||||
|
* @run main/othervm TLSWithEdDSA
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
|
import java.security.cert.CertificateException;
|
||||||
|
import java.security.cert.CertificateFactory;
|
||||||
|
import java.security.cert.PKIXBuilderParameters;
|
||||||
|
import java.security.cert.X509CertSelector;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.security.interfaces.ECKey;
|
||||||
|
import java.security.interfaces.EdECKey;
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
|
import java.util.*;
|
||||||
|
import javax.net.ssl.CertPathTrustManagerParameters;
|
||||||
|
import javax.net.ssl.KeyManager;
|
||||||
|
import javax.net.ssl.KeyManagerFactory;
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||||
|
import javax.net.ssl.SSLServerSocket;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
import javax.net.ssl.SSLSocket;
|
||||||
|
import javax.net.ssl.TrustManagerFactory;
|
||||||
|
import javax.net.ssl.X509ExtendedKeyManager;
|
||||||
|
import javax.net.ssl.X509KeyManager;
|
||||||
|
import jdk.test.lib.security.SecurityUtils;
|
||||||
|
|
||||||
|
public class TLSWithEdDSA extends SSLSocketTemplate {
|
||||||
|
private static final String PASSWD = "passphrase";
|
||||||
|
private static final String DEF_TRUST_ANCHORS = "CA_DSA_1024:CA_DSA_2048:" +
|
||||||
|
"CA_ECDSA_SECP256R1:CA_ECDSA_SECP384R1:CA_ECDSA_SECP521R1:" +
|
||||||
|
"CA_ED25519:CA_ED448:CA_RSA_2048";
|
||||||
|
private static final String DEF_ALL_EE = "EE_ECDSA_SECP256R1:" +
|
||||||
|
"EE_ECDSA_SECP384R1:EE_ECDSA_SECP521R1:EE_RSA_2048:" +
|
||||||
|
"EE_EC_RSA_SECP256R1:EE_DSA_2048:EE_DSA_1024:EE_ED25519:EE_ED448";
|
||||||
|
private static final List<String> TEST_PROTOS = List.of(
|
||||||
|
"TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1");
|
||||||
|
|
||||||
|
private static CertificateFactory certFac;
|
||||||
|
private static final Map<ParamType, String> clientParameters =
|
||||||
|
new HashMap<>();
|
||||||
|
private static final Map<ParamType, String> serverParameters =
|
||||||
|
new HashMap<>();
|
||||||
|
|
||||||
|
private final SessionChecker clientChecker;
|
||||||
|
private final SessionChecker serverChecker;
|
||||||
|
private final Class<? extends Throwable> clientException;
|
||||||
|
private final Class<? extends Throwable> serverException;
|
||||||
|
|
||||||
|
interface SessionChecker {
|
||||||
|
public void check(SSLSocket socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to make sure the end-entity certificate presented by the
|
||||||
|
* peer uses and Ed25519 key.
|
||||||
|
*/
|
||||||
|
final static SessionChecker isPeerEd25519 = new SessionChecker() {
|
||||||
|
@Override
|
||||||
|
public void check(SSLSocket sock) {
|
||||||
|
try {
|
||||||
|
SSLSession session = sock.getSession();
|
||||||
|
System.out.println("Peer certificate check for Ed25519:\n" +
|
||||||
|
sessionDump(session));
|
||||||
|
Certificate[] serverCertChain = session.getPeerCertificates();
|
||||||
|
X509Certificate tlsCert = (X509Certificate)serverCertChain[0];
|
||||||
|
keyCheck(tlsCert.getPublicKey(), "EdDSA", "Ed25519");
|
||||||
|
} catch (SSLPeerUnverifiedException sslpe) {
|
||||||
|
throw new RuntimeException(sslpe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to make sure the end-entity certificate presented by the
|
||||||
|
* peer uses and Ed448 key.
|
||||||
|
*/
|
||||||
|
final static SessionChecker isPeerEd448 = new SessionChecker() {
|
||||||
|
@Override
|
||||||
|
public void check(SSLSocket sock) {
|
||||||
|
try {
|
||||||
|
SSLSession session = sock.getSession();
|
||||||
|
System.out.println("Peer certificate check for Ed448:\n" +
|
||||||
|
sessionDump(session));
|
||||||
|
Certificate[] serverCertChain = session.getPeerCertificates();
|
||||||
|
X509Certificate tlsCert = (X509Certificate)serverCertChain[0];
|
||||||
|
keyCheck(tlsCert.getPublicKey(), "EdDSA", "Ed448");
|
||||||
|
} catch (SSLPeerUnverifiedException sslpe) {
|
||||||
|
throw new RuntimeException(sslpe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to make sure the end-entity certificate presented by the
|
||||||
|
* peer uses an EC secp521r1 key.
|
||||||
|
*/
|
||||||
|
final static SessionChecker isPeerP521 = new SessionChecker() {
|
||||||
|
@Override
|
||||||
|
public void check(SSLSocket sock) {
|
||||||
|
try {
|
||||||
|
SSLSession session = sock.getSession();
|
||||||
|
System.out.println("Peer certificate check for secp521r1:\n" +
|
||||||
|
sessionDump(session));
|
||||||
|
Certificate[] serverCertChain = session.getPeerCertificates();
|
||||||
|
X509Certificate tlsCert = (X509Certificate)serverCertChain[0];
|
||||||
|
keyCheck(tlsCert.getPublicKey(), "EC", "secp521r1");
|
||||||
|
} catch (SSLPeerUnverifiedException sslpe) {
|
||||||
|
throw new RuntimeException(sslpe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a String summary of an SSLSession object
|
||||||
|
*
|
||||||
|
* @param sess the SSLSession object to be dumped
|
||||||
|
*
|
||||||
|
* @return a String representation of the test-relevant portions of the
|
||||||
|
* SSLSession object.
|
||||||
|
*/
|
||||||
|
private static String sessionDump(SSLSession sess) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("----- Session Info -----\n");
|
||||||
|
sb.append("Protocol: ").append(sess.getProtocol()).append("\n");
|
||||||
|
sb.append("Cipher Suite: ").append(sess.getCipherSuite());
|
||||||
|
Certificate[] localCerts = sess.getLocalCertificates();
|
||||||
|
if (localCerts != null) {
|
||||||
|
sb.append("\nLocal Certs:");
|
||||||
|
int i = 0;
|
||||||
|
for (Certificate cert : localCerts) {
|
||||||
|
sb.append(String.format("\n [%d]: %s", i++,
|
||||||
|
((X509Certificate)cert).getSubjectX500Principal()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Certificate[] peerCerts = sess.getPeerCertificates();
|
||||||
|
if (peerCerts != null) {
|
||||||
|
sb.append("\nPeer Certs:");
|
||||||
|
int i = 0;
|
||||||
|
for (Certificate cert : peerCerts) {
|
||||||
|
sb.append(String.format("\n [%d]: %s", i++,
|
||||||
|
((X509Certificate)cert).getSubjectX500Principal()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SSLPeerUnverifiedException sslex) {
|
||||||
|
throw new RuntimeException(sslex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to make sure the public key conforms to the expected key type
|
||||||
|
* and (where applicable) curve.
|
||||||
|
*
|
||||||
|
* @param pubKey the public key to be checked
|
||||||
|
* @param expPkType the expected key type (RSA/DSA/EC/EdDSA)
|
||||||
|
* @param expCurveName if an EC/EdDSA key, the expected curve
|
||||||
|
*/
|
||||||
|
private static void keyCheck(PublicKey pubKey, String expPkType,
|
||||||
|
String expCurveName) {
|
||||||
|
String curveName = null;
|
||||||
|
String pubKeyAlg = pubKey.getAlgorithm();
|
||||||
|
if (!expPkType.equalsIgnoreCase(pubKeyAlg)) {
|
||||||
|
throw new RuntimeException("Expected " + expPkType + " key, got " +
|
||||||
|
pubKeyAlg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the curve type
|
||||||
|
if (expCurveName != null) {
|
||||||
|
switch (pubKeyAlg) {
|
||||||
|
case "EdDSA":
|
||||||
|
curveName = ((EdECKey)pubKey).getParams().getName().
|
||||||
|
toLowerCase();
|
||||||
|
if (!expCurveName.equalsIgnoreCase(curveName)) {
|
||||||
|
throw new RuntimeException("Expected " + expCurveName +
|
||||||
|
" curve, " + "got " + curveName);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "EC":
|
||||||
|
curveName = ((ECKey)pubKey).getParams().toString().
|
||||||
|
toLowerCase();
|
||||||
|
if (!curveName.contains(expCurveName.toLowerCase())) {
|
||||||
|
throw new RuntimeException("Expected " + expCurveName +
|
||||||
|
" curve, " + "got " + curveName);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Unsupported key type: " + pubKeyAlg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.format("Found key: %s / %s\n", pubKeyAlg,
|
||||||
|
curveName != null ? curveName : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
TLSWithEdDSA(SessionChecker cliChk, Class<? extends Throwable> cliExpExc,
|
||||||
|
SessionChecker servChk, Class<? extends Throwable> servExpExc) {
|
||||||
|
super();
|
||||||
|
clientChecker = cliChk;
|
||||||
|
clientException = cliExpExc;
|
||||||
|
serverChecker = servChk;
|
||||||
|
serverException = servExpExc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an SSLContext for use with the client side of this test. This
|
||||||
|
* uses parameters held in the static client parameters map.
|
||||||
|
*
|
||||||
|
* @return an initialized SSLContext for use with the client.
|
||||||
|
*
|
||||||
|
* @throws Exception if any downstream errors occur during key store
|
||||||
|
* creation, key/trust manager factory creation or context
|
||||||
|
* initialization.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected SSLContext createClientSSLContext() throws Exception {
|
||||||
|
KeyStore clientKeyStore = createKeyStore(
|
||||||
|
clientParameters.getOrDefault(ParamType.KSENTRIES, ""),
|
||||||
|
PASSWD.toCharArray());
|
||||||
|
KeyStore clientTrustStore = createTrustStore(
|
||||||
|
clientParameters.getOrDefault(ParamType.TSENTRIES,
|
||||||
|
DEF_TRUST_ANCHORS));
|
||||||
|
return createCtxCommon(clientKeyStore,
|
||||||
|
clientParameters.get(ParamType.CERTALIAS), PASSWD.toCharArray(),
|
||||||
|
clientTrustStore, "jdk.tls.client.SignatureSchemes",
|
||||||
|
clientParameters.get(ParamType.SIGALGS));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an SSLContext for use with the server side of this test. This
|
||||||
|
* uses parameters held in the static server parameters map.
|
||||||
|
*
|
||||||
|
* @return an initialized SSLContext for use with the server.
|
||||||
|
*
|
||||||
|
* @throws Exception if any downstream errors occur during key store
|
||||||
|
* creation, key/trust manager factory creation or context
|
||||||
|
* initialization.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected SSLContext createServerSSLContext() throws Exception {
|
||||||
|
KeyStore serverKeyStore = createKeyStore(
|
||||||
|
serverParameters.getOrDefault(ParamType.KSENTRIES, ""),
|
||||||
|
PASSWD.toCharArray());
|
||||||
|
KeyStore serverTrustStore = createTrustStore(
|
||||||
|
serverParameters.getOrDefault(ParamType.TSENTRIES,
|
||||||
|
DEF_TRUST_ANCHORS));
|
||||||
|
return createCtxCommon(serverKeyStore,
|
||||||
|
serverParameters.get(ParamType.CERTALIAS), PASSWD.toCharArray(),
|
||||||
|
serverTrustStore, "jdk.tls.server.SignatureSchemes",
|
||||||
|
serverParameters.get(ParamType.SIGALGS));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a trust store containing any CA certificates designated as
|
||||||
|
* trust anchors.
|
||||||
|
*
|
||||||
|
* @return the trust store populated with the root CA certificate.
|
||||||
|
*
|
||||||
|
* @throws GeneralSecurityException if any certificates cannot be added to
|
||||||
|
* the key store.
|
||||||
|
*/
|
||||||
|
private static KeyStore createTrustStore(String certEnumNames)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
KeyStore.Builder keyStoreBuilder =
|
||||||
|
KeyStore.Builder.newInstance("PKCS12", null,
|
||||||
|
new KeyStore.PasswordProtection(PASSWD.toCharArray()));
|
||||||
|
KeyStore ks = keyStoreBuilder.getKeyStore();
|
||||||
|
for (String certName : certEnumNames.split(":")) {
|
||||||
|
try {
|
||||||
|
SSLSocketTemplate.Cert cert =
|
||||||
|
SSLSocketTemplate.Cert.valueOf(certName);
|
||||||
|
ks.setCertificateEntry(certName, pem2Cert(cert.certStr));
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
System.out.println("Unable to find Cert enum entry for " +
|
||||||
|
certName + ", skipping");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a key store containing any end-entity private keys/certs
|
||||||
|
* specified in the parameters.
|
||||||
|
*
|
||||||
|
* @param certEnumNames a colon-delimited list of String values that are
|
||||||
|
* the names of the SSLSocketTemplate.Cert enumeration entries.
|
||||||
|
* @param pass the desired password for the resulting KeyStore object.
|
||||||
|
*
|
||||||
|
* @return a populated, loaded KeyStore ready for use.
|
||||||
|
*
|
||||||
|
* @throws GeneralSecurityException if any issues occur while setting
|
||||||
|
* the private key or certificate entries.
|
||||||
|
*/
|
||||||
|
private static KeyStore createKeyStore(String certEnumNames, char[] pass)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
KeyStore.Builder keyStoreBuilder =
|
||||||
|
KeyStore.Builder.newInstance("PKCS12", null,
|
||||||
|
new KeyStore.PasswordProtection(pass));
|
||||||
|
KeyStore ks = keyStoreBuilder.getKeyStore();
|
||||||
|
if (certEnumNames != null && !certEnumNames.isEmpty()) {
|
||||||
|
for (String certName : certEnumNames.split(":")) {
|
||||||
|
try {
|
||||||
|
SSLSocketTemplate.Cert cert =
|
||||||
|
SSLSocketTemplate.Cert.valueOf(certName);
|
||||||
|
ks.setKeyEntry(certName,
|
||||||
|
pem2PrivKey(cert.privKeyStr, cert.keyAlgo), pass,
|
||||||
|
new Certificate[] { pem2Cert(cert.certStr) });
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
System.out.println("Unable to find Cert enum entry for " +
|
||||||
|
certName + ", skipping");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Covert a PEM-encoded certificate into a X509Certificate object.
|
||||||
|
*
|
||||||
|
* @param certPem the PEM encoding for the certificate.
|
||||||
|
*
|
||||||
|
* @return the corresponding X509Certificate object for the provided PEM.
|
||||||
|
*
|
||||||
|
* @throws CertificateException if any decoding errors occur.
|
||||||
|
*/
|
||||||
|
private static X509Certificate pem2Cert(String certPem)
|
||||||
|
throws CertificateException {
|
||||||
|
return (X509Certificate)certFac.generateCertificate(
|
||||||
|
new ByteArrayInputStream(certPem.getBytes(
|
||||||
|
Charset.forName("UTF-8"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Covert a PEM-encoded PKCS8 private key into a PrivateKey object.
|
||||||
|
*
|
||||||
|
* @param keyPem the PEM encoding for the certificate.
|
||||||
|
* @param keyAlg the algorithm for the private key contained in the PKCS8
|
||||||
|
* ` encoding.
|
||||||
|
*
|
||||||
|
* @return the corresponding PrivateKey object for the provided PEM.
|
||||||
|
*
|
||||||
|
* @throws GeneralSecurityException if any decoding errors occur.
|
||||||
|
*/
|
||||||
|
private static PrivateKey pem2PrivKey(String keyPem, String keyAlg)
|
||||||
|
throws GeneralSecurityException {
|
||||||
|
PKCS8EncodedKeySpec p8Spec = new PKCS8EncodedKeySpec(
|
||||||
|
Base64.getMimeDecoder().decode(keyPem));
|
||||||
|
KeyFactory keyFac = KeyFactory.getInstance(keyAlg);
|
||||||
|
return keyFac.generatePrivate(p8Spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an SSLContext for use with the client or server sides of this
|
||||||
|
* test.
|
||||||
|
*
|
||||||
|
* @param keys the key store object for this SSLContext.
|
||||||
|
* @param alias optional alias specifier to exclusively use that alias for
|
||||||
|
* TLS connections.
|
||||||
|
* @param pass the key store password
|
||||||
|
* @param trust the trust store object
|
||||||
|
* @param sigAlgProp the signature algorithm property name to set
|
||||||
|
* (reserved for future use pending the fix for JDK-8255867)
|
||||||
|
* @param sigAlgVal the property value to be applied.
|
||||||
|
*
|
||||||
|
* @return an initialized SSLContext object.
|
||||||
|
*
|
||||||
|
* @throws IOException if any IOExceptions during manager factory creation
|
||||||
|
* take place
|
||||||
|
* @throws GeneralSecurityException any other failure during SSLContext
|
||||||
|
* creation/initialization
|
||||||
|
*/
|
||||||
|
private static SSLContext createCtxCommon(KeyStore keys, String alias,
|
||||||
|
char[] pass, KeyStore trust, String sigAlgProp, String sigAlgVal)
|
||||||
|
throws IOException, GeneralSecurityException {
|
||||||
|
SSLContext ctx;
|
||||||
|
if (sigAlgVal != null && !sigAlgVal.isEmpty()) {
|
||||||
|
System.setProperty(sigAlgProp, sigAlgVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an alias is specified use our local AliasKeyManager
|
||||||
|
KeyManager[] kms = (alias != null && !alias.isEmpty()) ?
|
||||||
|
new KeyManager[] { new AliasKeyManager(keys, pass, alias) } :
|
||||||
|
createKeyManagerFactory(keys, pass).getKeyManagers();
|
||||||
|
|
||||||
|
ctx = SSLContext.getInstance("TLS");
|
||||||
|
ctx.init(kms, createTrustManagerFactory(trust).getTrustManagers(),
|
||||||
|
null);
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a KeyManagerFactory for use during SSLContext initialization.
|
||||||
|
*
|
||||||
|
* @param ks the KeyStore forming the base of the KeyManagerFactory
|
||||||
|
* @param passwd the password to use for the key store
|
||||||
|
*
|
||||||
|
* @return the initialized KeyManagerFactory
|
||||||
|
*
|
||||||
|
* @throws IOException any IOExceptions during key manager factory
|
||||||
|
* initialization.
|
||||||
|
* @throws GeneralSecurityException if any failures during instantiation
|
||||||
|
* take place.
|
||||||
|
*/
|
||||||
|
private static KeyManagerFactory createKeyManagerFactory(KeyStore ks,
|
||||||
|
char[] passwd) throws IOException, GeneralSecurityException {
|
||||||
|
KeyManagerFactory kmf;
|
||||||
|
kmf = KeyManagerFactory.getInstance("SunX509");
|
||||||
|
kmf.init(ks, passwd);
|
||||||
|
|
||||||
|
KeyManager[] kmgrs = kmf.getKeyManagers();
|
||||||
|
X509ExtendedKeyManager xkm = (X509ExtendedKeyManager)kmgrs[0];
|
||||||
|
return kmf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a TrustManagerFactory for use during SSLContext initialization.
|
||||||
|
*
|
||||||
|
* @param trustStrore the KeyStore forming the base of the
|
||||||
|
* TrustManagerFactory
|
||||||
|
*
|
||||||
|
* @return the initialized TrustManagerFactory
|
||||||
|
*
|
||||||
|
* @throws IOException any IOExceptions during trust manager factory
|
||||||
|
* initialization.
|
||||||
|
* @throws GeneralSecurityException if any failures during instantiation
|
||||||
|
* take place.
|
||||||
|
*/
|
||||||
|
private static TrustManagerFactory createTrustManagerFactory(
|
||||||
|
KeyStore trustStore) throws IOException, GeneralSecurityException {
|
||||||
|
TrustManagerFactory tmf;
|
||||||
|
PKIXBuilderParameters pkixParams =
|
||||||
|
new PKIXBuilderParameters(trustStore, new X509CertSelector());
|
||||||
|
pkixParams.setRevocationEnabled(false);
|
||||||
|
tmf = TrustManagerFactory.getInstance("PKIX");
|
||||||
|
tmf.init(new CertPathTrustManagerParameters(pkixParams));
|
||||||
|
return tmf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configure the client side socket.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void configureClientSocket(SSLSocket socket) {
|
||||||
|
String pVal;
|
||||||
|
if ((pVal = clientParameters.get(ParamType.PROTOS)) != null) {
|
||||||
|
socket.setEnabledProtocols(pVal.split(":"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((pVal = clientParameters.get(ParamType.CIPHERS)) != null) {
|
||||||
|
socket.setEnabledCipherSuites(pVal.split(":"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configure the server side socket.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void configureServerSocket(SSLServerSocket socket) {
|
||||||
|
String pVal;
|
||||||
|
try {
|
||||||
|
socket.setReuseAddress(true);
|
||||||
|
if ((pVal = serverParameters.get(ParamType.PROTOS)) != null) {
|
||||||
|
socket.setEnabledProtocols(pVal.split(":"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((pVal = serverParameters.get(ParamType.CIPHERS)) != null) {
|
||||||
|
socket.setEnabledCipherSuites(pVal.split(":"));
|
||||||
|
}
|
||||||
|
|
||||||
|
pVal = serverParameters.get(ParamType.CLIAUTH);
|
||||||
|
socket.setWantClientAuth("WANT".equalsIgnoreCase(pVal));
|
||||||
|
socket.setNeedClientAuth("NEED".equalsIgnoreCase(pVal));
|
||||||
|
} catch (SocketException se) {
|
||||||
|
throw new RuntimeException(se);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void runServerApplication(SSLSocket socket) throws Exception {
|
||||||
|
InputStream sslIS = socket.getInputStream();
|
||||||
|
OutputStream sslOS = socket.getOutputStream();
|
||||||
|
|
||||||
|
sslIS.read();
|
||||||
|
sslOS.write(85);
|
||||||
|
sslOS.flush();
|
||||||
|
|
||||||
|
if (serverChecker != null) {
|
||||||
|
serverChecker.check(socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void runClientApplication(SSLSocket socket) throws Exception {
|
||||||
|
InputStream sslIS = socket.getInputStream();
|
||||||
|
OutputStream sslOS = socket.getOutputStream();
|
||||||
|
|
||||||
|
sslOS.write(280);
|
||||||
|
sslOS.flush();
|
||||||
|
sslIS.read();
|
||||||
|
|
||||||
|
if (clientChecker != null) {
|
||||||
|
clientChecker.check(socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws Exception {
|
||||||
|
SecurityUtils.removeFromDisabledTlsAlgs("TLSv1.1", "TLSv1");
|
||||||
|
certFac = CertificateFactory.getInstance("X.509");
|
||||||
|
String testFormat;
|
||||||
|
|
||||||
|
System.out.println("===== Test KeyManager alias retrieval =====");
|
||||||
|
testKeyManager(DEF_ALL_EE, "EdDSA",
|
||||||
|
new String[] {"ee_ed25519", "ee_ed448"});
|
||||||
|
|
||||||
|
testFormat =
|
||||||
|
"===== Basic Ed25519 Server-side Authentication: %s =====\n";
|
||||||
|
serverParameters.put(ParamType.KSENTRIES, "EE_ED25519:EE_RSA_2048");
|
||||||
|
runtest(testFormat, isPeerEd25519, null, null, null);
|
||||||
|
|
||||||
|
testFormat =
|
||||||
|
"===== Basic Ed448 Server-side Authentication: %s =====\n";
|
||||||
|
serverParameters.put(ParamType.KSENTRIES, "EE_ED448:EE_RSA_2048");
|
||||||
|
runtest(testFormat, isPeerEd448, null, null, null);
|
||||||
|
|
||||||
|
testFormat = "===== EC favored over EdDSA by default: %s =====\n";
|
||||||
|
serverParameters.put(ParamType.KSENTRIES,
|
||||||
|
"EE_ED25519:EE_ECDSA_SECP521R1");
|
||||||
|
runtest(testFormat, isPeerP521, null, null, null);
|
||||||
|
|
||||||
|
testFormat = "===== Override EC favoring by alias: %s =====\n";
|
||||||
|
serverParameters.put(ParamType.CERTALIAS, "EE_ED25519");
|
||||||
|
runtest(testFormat, isPeerEd25519, null, null, null);
|
||||||
|
serverParameters.remove(ParamType.CERTALIAS);
|
||||||
|
|
||||||
|
testFormat = "===== EdDSA Client Authentication: %s =====\n";
|
||||||
|
serverParameters.put(ParamType.KSENTRIES, "EE_RSA_2048");
|
||||||
|
serverParameters.put(ParamType.CLIAUTH, "NEED");
|
||||||
|
clientParameters.put(ParamType.KSENTRIES, "EE_ED25519");
|
||||||
|
runtest(testFormat, null, null, isPeerEd25519, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testKeyManager(String keyStoreSpec, String keyType,
|
||||||
|
String[] expAliases)
|
||||||
|
throws GeneralSecurityException, IOException {
|
||||||
|
char[] passChar = PASSWD.toCharArray();
|
||||||
|
|
||||||
|
// Create the KeyManager factory and resulting KeyManager
|
||||||
|
KeyManagerFactory kmf = createKeyManagerFactory(
|
||||||
|
createKeyStore(keyStoreSpec, passChar), passChar);
|
||||||
|
KeyManager[] kMgrs = kmf.getKeyManagers();
|
||||||
|
X509KeyManager xkm = (X509KeyManager)kMgrs[0];
|
||||||
|
|
||||||
|
String[] cliEdDSAAlises = xkm.getClientAliases(keyType, null);
|
||||||
|
System.out.format("Client Aliases (%s): ", keyType);
|
||||||
|
for (String alias : cliEdDSAAlises) {
|
||||||
|
System.out.print(alias + " ");
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
String[] servEdDSAAliases = xkm.getServerAliases(keyType, null);
|
||||||
|
System.out.format("Server Aliases (%s): ", keyType);
|
||||||
|
for (String alias : servEdDSAAliases) {
|
||||||
|
System.out.print(alias + " ");
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
if (!Arrays.equals(cliEdDSAAlises, expAliases)) {
|
||||||
|
throw new RuntimeException("Client alias mismatch");
|
||||||
|
} else if (!Arrays.equals(servEdDSAAliases, expAliases)) {
|
||||||
|
throw new RuntimeException("Server alias mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void runtest(String testNameFmt, SessionChecker cliChk,
|
||||||
|
Class<? extends Throwable> cliExpExc, SessionChecker servChk,
|
||||||
|
Class<? extends Throwable> servExpExc) {
|
||||||
|
TEST_PROTOS.forEach(protocol -> {
|
||||||
|
clientParameters.put(ParamType.PROTOS, protocol);
|
||||||
|
TLSWithEdDSA testObj = new TLSWithEdDSA(cliChk, cliExpExc, servChk,
|
||||||
|
servExpExc);
|
||||||
|
System.out.format(testNameFmt, protocol);
|
||||||
|
try {
|
||||||
|
testObj.run();
|
||||||
|
if (testObj.clientException != null ||
|
||||||
|
testObj.serverException != null) {
|
||||||
|
throw new RuntimeException("Expected exception from " +
|
||||||
|
"either client or server but was missed");
|
||||||
|
}
|
||||||
|
} catch (Exception exc) {
|
||||||
|
if (testObj.clientException == null &&
|
||||||
|
testObj.serverException == null) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Expected test failure did not occur");
|
||||||
|
} else if (testObj.clientException != null &&
|
||||||
|
!testObj.clientException.isAssignableFrom(exc.getClass())) {
|
||||||
|
throw new RuntimeException("Unexpected client exception " +
|
||||||
|
"detected: Expected " +
|
||||||
|
testObj.clientException.getName() +
|
||||||
|
", got " + exc.getClass().getName());
|
||||||
|
|
||||||
|
} else if (testObj.serverException != null &&
|
||||||
|
!testObj.serverException.isAssignableFrom(exc.getClass())) {
|
||||||
|
throw new RuntimeException("Unexpected client exception " +
|
||||||
|
"detected: Expected " +
|
||||||
|
testObj.serverException.getName() +
|
||||||
|
", got " + exc.getClass().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Custom KeyManager that allows the user to specify a key/certificate
|
||||||
|
* by alias to be used for any TLS authentication actions.
|
||||||
|
*/
|
||||||
|
static class AliasKeyManager implements X509KeyManager {
|
||||||
|
private final String alias;
|
||||||
|
private final KeyStore keystore;
|
||||||
|
private final char[] pass;
|
||||||
|
|
||||||
|
public AliasKeyManager(KeyStore keystore, char[] pass, String alias) {
|
||||||
|
this.keystore = Objects.requireNonNull(keystore);
|
||||||
|
this.alias = Objects.requireNonNull(alias);
|
||||||
|
this.pass = Objects.requireNonNull(pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PrivateKey getPrivateKey(String alias) {
|
||||||
|
try {
|
||||||
|
return (PrivateKey)keystore.getKey(alias, pass);
|
||||||
|
} catch (GeneralSecurityException exc) {
|
||||||
|
throw new RuntimeException(exc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public X509Certificate[] getCertificateChain(String alias) {
|
||||||
|
try {
|
||||||
|
Certificate[] certAr = keystore.getCertificateChain(alias);
|
||||||
|
return (certAr != null) ? Arrays.copyOf(certAr, certAr.length,
|
||||||
|
X509Certificate[].class) : null;
|
||||||
|
} catch (KeyStoreException ke) {
|
||||||
|
throw new RuntimeException(ke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String chooseClientAlias(String[] keyType, Principal[] issuers,
|
||||||
|
Socket socket) {
|
||||||
|
// Blindly return the one selected alias.
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String chooseServerAlias(String keyType, Principal[] issuers,
|
||||||
|
Socket socket) {
|
||||||
|
// Blindly return the one selected alias.
|
||||||
|
return alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getClientAliases(String keyType, Principal[] issuers) {
|
||||||
|
// There can be only one!
|
||||||
|
return new String[] { alias };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getServerAliases(String keyType, Principal[] issuers) {
|
||||||
|
// There can be only one!
|
||||||
|
return new String[] { alias };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum ParamType {
|
||||||
|
PROTOS,
|
||||||
|
CIPHERS,
|
||||||
|
SIGALGS,
|
||||||
|
CLIAUTH,
|
||||||
|
KSENTRIES,
|
||||||
|
TSENTRIES,
|
||||||
|
CERTALIAS
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue