org.openjsse.sun.security.ssl.SignatureScheme Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openjsse Show documentation
Show all versions of openjsse Show documentation
OpenJSSE delivers a TLS 1.3 JSSE provider for Java SE 8
/*
* Copyright (c) 2015, 2018, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package org.openjsse.sun.security.ssl;
import java.security.*;
import java.security.interfaces.ECPrivateKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PSSParameterSpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.openjsse.sun.security.ssl.SupportedGroupsExtension.NamedGroup;
import org.openjsse.sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
import org.openjsse.sun.security.ssl.X509Authentication.X509Possession;
import sun.security.util.KeyUtil;
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_SECP256R1_SHA256 (0x0403, "ecdsa_secp256r1_sha256",
"SHA256withECDSA",
"EC",
NamedGroup.SECP256_R1,
ProtocolVersion.PROTOCOLS_TO_13),
ECDSA_SECP384R1_SHA384 (0x0503, "ecdsa_secp384r1_sha384",
"SHA384withECDSA",
"EC",
NamedGroup.SECP384_R1,
ProtocolVersion.PROTOCOLS_TO_13),
ECDSA_SECP512R1_SHA512 (0x0603, "ecdsa_secp512r1_sha512",
"SHA512withECDSA",
"EC",
NamedGroup.SECP521_R1,
ProtocolVersion.PROTOCOLS_TO_13),
// RSASSA-PSS algorithms with public key OID rsaEncryption
//
// The minimalKeySize is calculated as (See RFC 8017 for details):
// hash length + salt length + 16
RSA_PSS_RSAE_SHA256 (0x0804, "rsa_pss_rsae_sha256",
"RSASSA-PSS", "RSA",
SigAlgParamSpec.RSA_PSS_SHA256, 528,
ProtocolVersion.PROTOCOLS_12_13),
RSA_PSS_RSAE_SHA384 (0x0805, "rsa_pss_rsae_sha384",
"RSASSA-PSS", "RSA",
SigAlgParamSpec.RSA_PSS_SHA384, 784,
ProtocolVersion.PROTOCOLS_12_13),
RSA_PSS_RSAE_SHA512 (0x0806, "rsa_pss_rsae_sha512",
"RSASSA-PSS", "RSA",
SigAlgParamSpec.RSA_PSS_SHA512, 1040,
ProtocolVersion.PROTOCOLS_12_13),
// RSASSA-PSS algorithms with public key OID RSASSA-PSS
//
// The minimalKeySize is calculated as (See RFC 8017 for details):
// hash length + salt length + 16
RSA_PSS_PSS_SHA256 (0x0809, "rsa_pss_pss_sha256",
"RSASSA-PSS", "RSASSA-PSS",
SigAlgParamSpec.RSA_PSS_SHA256, 528,
ProtocolVersion.PROTOCOLS_12_13),
RSA_PSS_PSS_SHA384 (0x080A, "rsa_pss_pss_sha384",
"RSASSA-PSS", "RSASSA-PSS",
SigAlgParamSpec.RSA_PSS_SHA384, 784,
ProtocolVersion.PROTOCOLS_12_13),
RSA_PSS_PSS_SHA512 (0x080B, "rsa_pss_pss_sha512",
"RSASSA-PSS", "RSASSA-PSS",
SigAlgParamSpec.RSA_PSS_SHA512, 1040,
ProtocolVersion.PROTOCOLS_12_13),
// RSASSA-PKCS1-v1_5 algorithms
RSA_PKCS1_SHA256 (0x0401, "rsa_pkcs1_sha256", "SHA256withRSA",
"RSA", null, null, 511,
ProtocolVersion.PROTOCOLS_TO_13,
ProtocolVersion.PROTOCOLS_TO_12),
RSA_PKCS1_SHA384 (0x0501, "rsa_pkcs1_sha384", "SHA384withRSA",
"RSA", null, null, 768,
ProtocolVersion.PROTOCOLS_TO_13,
ProtocolVersion.PROTOCOLS_TO_12),
RSA_PKCS1_SHA512 (0x0601, "rsa_pkcs1_sha512", "SHA512withRSA",
"RSA", null, null, 768,
ProtocolVersion.PROTOCOLS_TO_13,
ProtocolVersion.PROTOCOLS_TO_12),
// Legacy algorithms
DSA_SHA256 (0x0402, "dsa_sha256", "SHA256withDSA",
"DSA",
ProtocolVersion.PROTOCOLS_TO_12),
ECDSA_SHA224 (0x0303, "ecdsa_sha224", "SHA224withECDSA",
"EC",
ProtocolVersion.PROTOCOLS_TO_12),
RSA_SHA224 (0x0301, "rsa_sha224", "SHA224withRSA",
"RSA", 511,
ProtocolVersion.PROTOCOLS_TO_12),
DSA_SHA224 (0x0302, "dsa_sha224", "SHA224withDSA",
"DSA",
ProtocolVersion.PROTOCOLS_TO_12),
ECDSA_SHA1 (0x0203, "ecdsa_sha1", "SHA1withECDSA",
"EC",
ProtocolVersion.PROTOCOLS_TO_13),
RSA_PKCS1_SHA1 (0x0201, "rsa_pkcs1_sha1", "SHA1withRSA",
"RSA", null, null, 511,
ProtocolVersion.PROTOCOLS_TO_13,
ProtocolVersion.PROTOCOLS_TO_12),
DSA_SHA1 (0x0202, "dsa_sha1", "SHA1withDSA",
"DSA",
ProtocolVersion.PROTOCOLS_TO_12),
RSA_MD5 (0x0101, "rsa_md5", "MD5withRSA",
"RSA", 511,
ProtocolVersion.PROTOCOLS_TO_12);
final int id; // hash + signature
final String name; // literal name
private final String algorithm; // signature algorithm
final String keyAlgorithm; // signature key algorithm
private final AlgorithmParameterSpec signAlgParameter;
private final NamedGroup namedGroup; // associated named group
// The minimal required key size in bits.
//
// Only need to check RSA algorithm at present. RSA keys of 512 bits
// have been shown to be practically breakable, it does not make much
// sense to use the strong hash algorithm for keys whose key size less
// than 512 bits. So it is not necessary to calculate the minimal
// required key size exactly for a hash algorithm.
//
// Note that some provider may use 511 bits for 512-bit strength RSA keys.
final int minimalKeySize;
final List supportedProtocols;
// Some signature schemes are supported in different versions for handshake
// messages and certificates. This field holds the supported protocols
// for handshake messages.
final List handshakeSupportedProtocols;
final boolean isAvailable;
private static final String[] hashAlgorithms = new String[] {
"none", "md5", "sha1", "sha224",
"sha256", "sha384", "sha512"
};
private static final String[] signatureAlgorithms = new String[] {
"anonymous", "rsa", "dsa", "ecdsa",
};
static enum SigAlgParamSpec { // support RSASSA-PSS only now
RSA_PSS_SHA256 ("SHA-256", 32),
RSA_PSS_SHA384 ("SHA-384", 48),
RSA_PSS_SHA512 ("SHA-512", 64);
final private AlgorithmParameterSpec parameterSpec;
final boolean isAvailable;
SigAlgParamSpec(String hash, int saltLength) {
// See RFC 8017
PSSParameterSpec pssParamSpec =
new PSSParameterSpec(hash, "MGF1",
new MGF1ParameterSpec(hash), saltLength, 1);
boolean mediator = true;
try {
Signature signer = JsseJce.getSignature("RSASSA-PSS");
signer.setParameter(pssParamSpec);
} catch (InvalidAlgorithmParameterException |
NoSuchAlgorithmException exp) {
mediator = false;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"RSASSA-PSS signature with " + hash +
" is not supported by the underlying providers", exp);
}
}
this.isAvailable = mediator;
this.parameterSpec = mediator ? pssParamSpec : null;
}
AlgorithmParameterSpec getParameterSpec() {
return parameterSpec;
}
}
// performance optimization
private static final Set SIGNATURE_PRIMITIVE_SET =
Collections.unmodifiableSet(EnumSet.of(CryptoPrimitive.SIGNATURE));
private SignatureScheme(int id, String name,
String algorithm, String keyAlgorithm,
ProtocolVersion[] supportedProtocols) {
this(id, name, algorithm, keyAlgorithm, -1, supportedProtocols);
}
private SignatureScheme(int id, String name,
String algorithm, String keyAlgorithm,
int minimalKeySize,
ProtocolVersion[] supportedProtocols) {
this(id, name, algorithm, keyAlgorithm,
null, minimalKeySize, supportedProtocols);
}
private SignatureScheme(int id, String name,
String algorithm, String keyAlgorithm,
SigAlgParamSpec signAlgParamSpec, int minimalKeySize,
ProtocolVersion[] supportedProtocols) {
this(id, name, algorithm, keyAlgorithm,
signAlgParamSpec, null, minimalKeySize,
supportedProtocols, supportedProtocols);
}
private SignatureScheme(int id, String name,
String algorithm, String keyAlgorithm,
NamedGroup namedGroup,
ProtocolVersion[] supportedProtocols) {
this(id, name, algorithm, keyAlgorithm,
null, namedGroup, -1,
supportedProtocols, supportedProtocols);
}
private SignatureScheme(int id, String name,
String algorithm, String keyAlgorithm,
SigAlgParamSpec signAlgParamSpec,
NamedGroup namedGroup, int minimalKeySize,
ProtocolVersion[] supportedProtocols,
ProtocolVersion[] handshakeSupportedProtocols) {
this.id = id;
this.name = name;
this.algorithm = algorithm;
this.keyAlgorithm = keyAlgorithm;
this.signAlgParameter =
signAlgParamSpec != null ? signAlgParamSpec.parameterSpec : null;
this.namedGroup = namedGroup;
this.minimalKeySize = minimalKeySize;
this.supportedProtocols = Arrays.asList(supportedProtocols);
this.handshakeSupportedProtocols =
Arrays.asList(handshakeSupportedProtocols);
boolean mediator = true;
if (signAlgParamSpec != null) {
mediator = signAlgParamSpec.isAvailable;
} else {
try {
JsseJce.getSignature(algorithm);
} catch (Exception e) {
mediator = false;
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Signature algorithm, " + algorithm +
", is not supported by the underlying providers");
}
}
}
if (mediator && ((id >> 8) & 0xFF) == 0x03) { // SHA224
// There are some problems to use SHA224 on Windows.
if (Security.getProvider("SunMSCAPI") != null) {
mediator = false;
}
}
this.isAvailable = mediator;
}
static SignatureScheme valueOf(int id) {
for (SignatureScheme ss: SignatureScheme.values()) {
if (ss.id == id) {
return ss;
}
}
return null;
}
static String nameOf(int id) {
for (SignatureScheme ss: SignatureScheme.values()) {
if (ss.id == id) {
return ss.name;
}
}
// Use TLS 1.2 style name for unknown signature scheme.
int hashId = ((id >> 8) & 0xFF);
int signId = (id & 0xFF);
String hashName = (hashId >= hashAlgorithms.length) ?
"UNDEFINED-HASH(" + hashId + ")" : hashAlgorithms[hashId];
String signName = (signId >= signatureAlgorithms.length) ?
"UNDEFINED-SIGNATURE(" + signId + ")" :
signatureAlgorithms[signId];
return signName + "_" + hashName;
}
// Return the size of a SignatureScheme structure in TLS record
static int sizeInRecord() {
return 2;
}
// Get local supported algorithm collection complying to algorithm
// constraints.
static List getSupportedAlgorithms(
AlgorithmConstraints constraints,
List activeProtocols) {
List supported = new LinkedList<>();
for (SignatureScheme ss: SignatureScheme.values()) {
if (!ss.isAvailable) {
continue;
}
boolean isMatch = false;
for (ProtocolVersion pv : activeProtocols) {
if (ss.supportedProtocols.contains(pv)) {
isMatch = true;
break;
}
}
if (isMatch) {
if (constraints.permits(
SIGNATURE_PRIMITIVE_SET, ss.algorithm, null)) {
supported.add(ss);
} else if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake,verbose")) {
SSLLogger.finest(
"Ignore disabled signature sheme: " + ss.name);
}
} else if (SSLLogger.isOn &&
SSLLogger.isOn("ssl,handshake,verbose")) {
SSLLogger.finest(
"Ignore inactive signature sheme: " + ss.name);
}
}
return supported;
}
static List getSupportedAlgorithms(
AlgorithmConstraints constraints,
ProtocolVersion protocolVersion, int[] algorithmIds) {
List supported = new LinkedList<>();
for (int ssid : algorithmIds) {
SignatureScheme ss = SignatureScheme.valueOf(ssid);
if (ss == null) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Unsupported signature scheme: " +
SignatureScheme.nameOf(ssid));
}
} else if (ss.isAvailable &&
ss.supportedProtocols.contains(protocolVersion) &&
constraints.permits(SIGNATURE_PRIMITIVE_SET,
ss.algorithm, null)) {
supported.add(ss);
} else {
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
SSLLogger.warning(
"Unsupported signature scheme: " + ss.name);
}
}
}
return supported;
}
static SignatureScheme getPreferableAlgorithm(
List schemes,
SignatureScheme certScheme,
ProtocolVersion version) {
for (SignatureScheme ss : schemes) {
if (ss.isAvailable &&
ss.handshakeSupportedProtocols.contains(version) &&
certScheme.keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) {
return ss;
}
}
return null;
}
static SignatureScheme getPreferableAlgorithm(
List schemes,
X509Possession x509Possession,
ProtocolVersion version) {
PrivateKey signingKey = x509Possession.popPrivateKey;
String keyAlgorithm = signingKey.getAlgorithm();
int keySize;
// Only need to check RSA algorithm at present.
if (keyAlgorithm.equalsIgnoreCase("RSA") ||
keyAlgorithm.equalsIgnoreCase("RSASSA-PSS")) {
keySize = KeyUtil.getKeySize(signingKey);
} else {
keySize = Integer.MAX_VALUE;
}
for (SignatureScheme ss : schemes) {
if (ss.isAvailable && (keySize >= ss.minimalKeySize) &&
ss.handshakeSupportedProtocols.contains(version) &&
keyAlgorithm.equalsIgnoreCase(ss.keyAlgorithm)) {
if (ss.namedGroup != null &&
ss.namedGroup.type == NamedGroupType.NAMED_GROUP_ECDHE) {
ECParameterSpec params =
x509Possession.getECParameterSpec();
if (params != null &&
ss.namedGroup == NamedGroup.valueOf(params)) {
return ss;
}
} else {
return ss;
}
}
}
return null;
}
static String[] getAlgorithmNames(Collection schemes) {
if (schemes != null) {
ArrayList names = new ArrayList<>(schemes.size());
for (SignatureScheme scheme : schemes) {
names.add(scheme.algorithm);
}
return names.toArray(new String[0]);
}
return new String[0];
}
Signature getSignature(Key key) throws NoSuchAlgorithmException,
InvalidAlgorithmParameterException, InvalidKeyException {
if (!isAvailable) {
return null;
}
Signature signer = JsseJce.getSignature(algorithm);
if (key instanceof PublicKey) {
signer.initVerify((PublicKey)(key));
} else {
signer.initSign((PrivateKey)key);
}
// Important note: Please don't set the parameters before signature
// or verification initialization, so that the crypto provider can
// be selected properly.
if (signAlgParameter != null) {
signer.setParameter(signAlgParameter);
}
return signer;
}
}