/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/**
* The new X509 key manager implementation. The main differences to the
* old SunX509 key manager are:
* . it is based around the KeyStore.Builder API. This allows it to use
* other forms of KeyStore protection or password input (e.g. a
* CallbackHandler) or to have keys within one KeyStore protected by
* different keys.
* . it can use multiple KeyStores at the same time.
* . it is explicitly designed to accomodate KeyStores that change over
* the lifetime of the process.
* . it makes an effort to choose the key that matches best, i.e. one that
* is not expired and has the appropriate certificate extensions.
*
* Note that this code is not explicitly performance optimzied yet.
*
* @author Andreas Sterbenz
*/
implements X509KeyManager {
private final static boolean useDebug =
// for unit testing only, set via privileged reflection
// list of the builders
// counter to generate unique ids for the aliases
// cached entries
}
uidCounter = new AtomicLong();
}
// LinkedHashMap with a max size of 10
// see LinkedHashMap JavaDocs
return size() > 10;
}
}
//
// public methods
//
}
}
}
}
}
}
}
}
//
// implementation private methods
//
// Gets algorithm constraints of the socket.
if (session instanceof ExtendedSSLSession) {
}
return new SSLAlgorithmConstraints(
sslSocket, peerSupportedSignAlgs, true);
}
}
return new SSLAlgorithmConstraints(sslSocket, true);
}
}
// Gets algorithm constraints of the engine.
if (session instanceof ExtendedSSLSession) {
}
return new SSLAlgorithmConstraints(
engine, peerSupportedSignAlgs, true);
}
}
}
return new SSLAlgorithmConstraints(engine, true);
}
// we construct the alias we return to JSSE as seen in the code below
// a unique id is included to allow us to reliably cache entries
// between the calls to getCertificateChain() and getPrivateKey()
// even if tokens are inserted or removed
}
// if the alias is null, return immediately
return null;
}
// try to get the entry from cache
return entry;
}
// parse the alias
// invalid alias
return null;
}
try {
if (newEntry instanceof PrivateKeyEntry == false) {
// unexpected type of entry
return null;
}
return entry;
} catch (Exception e) {
// ignore
return null;
}
}
// Class to help verify that the public key algorithm (and optionally
// the signature algorithm) of a certificate matches what we need.
private static class KeyType {
// In TLS 1.2, the signature algorithm has been obsoleted by the
// supported_signature_algorithms, and the certificate type no longer
// restricts the algorithm used to sign the certificate.
// However, because we don't support certificate type checking other
// than rsa_sign, dss_sign and ecdsa_sign, we don't have to check the
// protocol version here.
if (k == -1) {
} else {
}
}
return false;
}
if (sigKeyAlgorithm == null) {
return true;
}
// if possible, check the public key in the issuer cert
return sigKeyAlgorithm.equals(
} else {
// Check the signature algorithm of the certificate itself.
// Look for the "withRSA" in "SHA1withRSA", etc.
}
}
}
return null;
}
}
return list;
}
/*
* Return the best alias that fits the given parameters.
* The algorithm we use is:
* . scan through all the aliases in all builders in order
* . as soon as we find a perfect match, return
* (i.e. a match with a cert that has appropriate key usage
* and is not expired).
* . if we do not find a perfect match, keep looping and remember
* the imperfect matches
* . at the end, sort the imperfect matches. we prefer expired certs
* with appropriate key usage to certs with the wrong key usage.
* return the first one of them.
*/
return null;
}
try {
// the results will either be a single perfect match
// or 1 or more imperfect matches
// if it's a perfect match, return immediately
if (useDebug) {
}
}
if (allResults == null) {
}
}
} catch (Exception e) {
// ignore
}
}
if (allResults == null) {
if (useDebug) {
}
return null;
}
if (useDebug) {
+ "returning best match out of:");
}
}
/*
* Return all aliases that (approximately) fit the parameters.
* These are perfect matches plus imperfect matches (expired certificates
* and certificates with the wrong extensions).
* The perfect matches will be first in the array.
*/
return null;
}
try {
if (allResults == null) {
}
}
} catch (Exception e) {
// ignore
}
}
if (useDebug) {
}
return null;
}
if (useDebug) {
}
return toAliases(allResults);
}
// turn candidate entries into unique aliases we can return to JSSE
int i = 0;
}
return s;
}
// make a Set out of the array
} else {
return null;
}
}
// a candidate match
// identifies the entry by builder and alias
// and includes the result of the certificate check
final int builderIndex;
final int keyIndex;
this.builderIndex = builderIndex;
this.checkResult = checkResult;
}
}
if (builderIndex == 0) {
return s;
} else {
}
}
}
// enum for the type of certificate check we want to perform
// (client or server)
// also includes the check code itself
private static enum CheckType {
// enum constant for "no check" (currently not used)
// enum constant for "tls client" check
// valid EKU for TLS client: any, tls_client
"2.5.29.37.0", "1.3.6.1.5.5.7.3.2" }))),
// enum constant for "tls server" check
// valid EKU for TLS server: any, tls_server, ns_sgc, ms_sgc
"2.5.29.37.0", "1.3.6.1.5.5.7.3.1", "2.16.840.1.113730.4.1",
"1.3.6.1.4.1.311.10.3.3" })));
// set of valid EKU values for this type
}
}
// check if this certificate is appropriate for this type of use
// first check extensions, if they match, check expiration
// note: we may want to move this code into the sun.security.validator
// package
if (this == NONE) {
return CheckResult.OK;
}
// check extensions
try {
// check extended key usage
// if extension present and it does not contain any of
// the valid EKU OIDs, return extension_mismatch
return CheckResult.EXTENSION_MISMATCH;
}
// check key usage
// require either signature bit
// or if server also allow key encipherment bit
if (kuSignature == false) {
return CheckResult.EXTENSION_MISMATCH;
}
}
// require signature bit
if (kuSignature == false) {
return CheckResult.EXTENSION_MISMATCH;
}
// require keyagreement bit
return CheckResult.EXTENSION_MISMATCH;
}
// require signature bit
if (kuSignature == false) {
return CheckResult.EXTENSION_MISMATCH;
}
// For servers, also require key agreement.
// This is not totally accurate as the keyAgreement bit
// is only necessary for static ECDH key exchange and
// not ephemeral ECDH. We leave it in for now until
// there are signs that this check causes problems
// for real world EC certificates.
return CheckResult.EXTENSION_MISMATCH;
}
}
}
} catch (CertificateException e) {
// extensions unparseable, return failure
return CheckResult.EXTENSION_MISMATCH;
}
try {
return CheckResult.OK;
} catch (CertificateException e) {
return CheckResult.EXPIRED;
}
}
}
// enum for the result of the extension check
// NOTE: the order of the constants is important as they are used
// for sorting, i.e. OK is best, followed by EXPIRED and EXTENSION_MISMATCH
private static enum CheckResult {
}
/*
* Return a List of all candidate matches in the specified builder
* that fit the parameters.
* We exclude entries in the KeyStore if they are not:
* . private key entries
* . the certificates are not X509 certificates
* . the algorithm of the key in the EE cert doesn't match one of keyTypes
* . none of the certs is issued by a Principal in issuerSet
* Using those entries would not be possible or they would almost
* certainly be rejected by the peer.
*
* In addition to those checks, we also check the extensions in the EE
* cert and its expiration. Even if there is a mismatch, we include
* such certificates because they technically work and might be accepted
* by the peer. This leads to more graceful failure and better error
* messages if the cert expires from one day to the next.
*
* The return values are:
* . null, if there are no matching entries at all
* . if 'findAll' is 'false' and there is a perfect match, a List
* with a single element (early return)
* . if 'findAll' is 'false' and there is NO perfect match, a List
* with all the imperfect matches (expired, wrong extensions)
* . if 'findAll' is 'true', a List with all perfect and imperfect
* matches
*/
boolean preferred = false;
// check if it is a key entry (private key or secret key)
continue;
}
// must be secret key entry, ignore
continue;
}
boolean incompatible = false;
if (cert instanceof X509Certificate == false) {
// not an X509Certificate, ignore this alias
incompatible = true;
break;
}
}
if (incompatible) {
continue;
}
// check keytype
int keyIndex = -1;
int j = 0;
keyIndex = j;
break;
}
j++;
}
if (keyIndex == -1) {
if (useDebug) {
+ ": key algorithm does not match");
}
continue;
}
// check issuers
boolean found = false;
found = true;
break;
}
}
if (found == false) {
if (useDebug) {
+ ": issuers do not match");
}
continue;
}
}
// check the algorithm constraints
if (constraints != null &&
if (useDebug) {
": certificate list does not conform to " +
"algorithm constraints");
}
continue;
}
}
preferred = true;
}
// if we have a good match and do not need all matches,
// return immediately
} else {
}
}
}
return results;
}
private static boolean conformsToAlgorithmConstraints(
try {
} catch (CertPathValidatorException cpve) {
// unlikely to happen
return false;
}
// It is a forward checker, so we need to check from trust to target.
try {
// We don't care about the unresolved critical extensions.
} catch (CertPathValidatorException cpve) {
return false;
}
}
return true;
}
}