|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package sun.security.ssl; |
|
|
|
import javax.crypto.SecretKey; |
|
import javax.crypto.spec.SecretKeySpec; |
|
import javax.net.ssl.SSLHandshakeException; |
|
|
|
import java.io.IOException; |
|
import java.security.AccessControlContext; |
|
import java.security.AccessController; |
|
import java.security.PrivilegedActionException; |
|
import java.security.PrivilegedExceptionAction; |
|
import java.security.SecureRandom; |
|
import java.security.spec.AlgorithmParameterSpec; |
|
|
|
final class KrbKeyExchange { |
|
static final SSLPossessionGenerator poGenerator = |
|
new KrbPossessionGenerator(); |
|
static final SSLKeyAgreementGenerator kaGenerator = |
|
new KrbKAGenerator(); |
|
|
|
static final |
|
class KrbPossessionGenerator implements SSLPossessionGenerator { |
|
|
|
|
|
|
|
|
|
*/ |
|
@Override |
|
public SSLPossession createPossession(HandshakeContext handshakeContext) { |
|
Object serviceCreds = null; |
|
try { |
|
final AccessControlContext acc = handshakeContext.conContext.acc; |
|
serviceCreds = AccessController.doPrivileged( |
|
|
|
new PrivilegedExceptionAction<Object>() { |
|
@Override |
|
public Object run() throws Exception { |
|
|
|
return Krb5Helper.getServiceCreds(acc); |
|
}}); |
|
|
|
// check permission to access and use the secret key of the |
|
|
|
if (serviceCreds != null) { |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine("Using Kerberos creds"); |
|
} |
|
String serverPrincipal = |
|
Krb5Helper.getServerPrincipalName(serviceCreds); |
|
if (serverPrincipal != null) { |
|
// When service is bound, we check ASAP. Otherwise, |
|
// will check after client request is received |
|
|
|
SecurityManager sm = System.getSecurityManager(); |
|
try { |
|
if (sm != null) { |
|
|
|
sm.checkPermission(Krb5Helper.getServicePermission( |
|
serverPrincipal, "accept"), acc); |
|
} |
|
} catch (SecurityException se) { |
|
|
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine("Permission to access Kerberos" |
|
+ " secret key denied"); |
|
} |
|
return null; |
|
} |
|
} |
|
return new KrbServiceCreds(serviceCreds); |
|
} |
|
} catch (PrivilegedActionException e) { |
|
|
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine("Attempt to obtain Kerberos key failed: " |
|
+ e.toString()); |
|
} |
|
} |
|
return null; |
|
} |
|
} |
|
|
|
static final |
|
class KrbServiceCreds implements SSLPossession { |
|
final Object serviceCreds; |
|
|
|
KrbServiceCreds(Object serviceCreds) { |
|
this.serviceCreds = serviceCreds; |
|
} |
|
} |
|
|
|
static final |
|
class KrbPremasterSecret implements SSLPossession, SSLCredentials { |
|
final byte[] preMaster; |
|
|
|
KrbPremasterSecret(byte[] premasterSecret) { |
|
preMaster = premasterSecret; |
|
} |
|
|
|
static KrbPremasterSecret createPremasterSecret( |
|
ProtocolVersion protocolVersion, SecureRandom rand) { |
|
byte[] pm = new byte[48]; |
|
rand.nextBytes(pm); |
|
pm[0] = protocolVersion.major; |
|
pm[1] = protocolVersion.minor; |
|
return new KrbPremasterSecret(pm); |
|
} |
|
|
|
static KrbPremasterSecret decode(ProtocolVersion protocolVersion, |
|
ProtocolVersion clientVersion, byte[] preMaster, |
|
SecureRandom rand) { |
|
KrbPremasterSecret preMasterSecret = null; |
|
boolean versionMismatch = true; |
|
ProtocolVersion preMasterProtocolVersion = null; |
|
if (preMaster != null && preMaster.length == 48) { |
|
preMasterProtocolVersion = |
|
ProtocolVersion.valueOf(preMaster[0], preMaster[1]); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine("Kerberos pre-master secret protocol" + |
|
" version: " + preMasterProtocolVersion); |
|
} |
|
|
|
// Check if the pre-master secret protocol version is valid. |
|
// The specification states that it must be the maximum version |
|
// supported by the client from its ClientHello message. However, |
|
// many old implementations send the negotiated version, so accept |
|
|
|
versionMismatch = |
|
(preMasterProtocolVersion.compare(clientVersion) != 0); |
|
if (versionMismatch && |
|
(clientVersion.compare(ProtocolVersion.TLS10) <= 0)) { |
|
versionMismatch = |
|
(preMasterProtocolVersion.compare(protocolVersion) != 0); |
|
} |
|
} |
|
|
|
if (versionMismatch) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
preMasterSecret = createPremasterSecret(clientVersion, rand); |
|
if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { |
|
SSLLogger.fine("Kerberos pre-master secret error," + |
|
" generating random secret for safe failure."); |
|
} |
|
} else { |
|
preMasterSecret = new KrbPremasterSecret(preMaster); |
|
} |
|
return preMasterSecret; |
|
} |
|
} |
|
|
|
private static final class KrbKAGenerator implements SSLKeyAgreementGenerator { |
|
|
|
private KrbKAGenerator() { |
|
// blank |
|
} |
|
|
|
@Override |
|
public SSLKeyDerivation createKeyDerivation( |
|
HandshakeContext context) throws IOException { |
|
|
|
KrbPremasterSecret premaster = null; |
|
if (context instanceof ClientHandshakeContext) { |
|
for (SSLPossession possession : context.handshakePossessions) { |
|
if (possession instanceof KrbPremasterSecret) { |
|
premaster = (KrbPremasterSecret)possession; |
|
break; |
|
} |
|
} |
|
} else { |
|
for (SSLCredentials credential : context.handshakeCredentials) { |
|
if (credential instanceof KrbPremasterSecret) { |
|
premaster = (KrbPremasterSecret)credential; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if (premaster == null) { |
|
throw context.conContext.fatal(Alert.HANDSHAKE_FAILURE, |
|
"No sufficient KRB key agreement parameters negotiated"); |
|
} |
|
|
|
return new KRBKAKeyDerivation(context, premaster.preMaster); |
|
|
|
} |
|
|
|
private static final |
|
class KRBKAKeyDerivation implements SSLKeyDerivation { |
|
private final HandshakeContext context; |
|
private final byte[] secretBytes; |
|
|
|
KRBKAKeyDerivation(HandshakeContext context, |
|
byte[] secret) { |
|
this.context = context; |
|
this.secretBytes = secret; |
|
} |
|
|
|
@Override |
|
public SecretKey deriveKey(String algorithm, |
|
AlgorithmParameterSpec params) throws IOException { |
|
try { |
|
SecretKey preMasterSecret = new SecretKeySpec(secretBytes, |
|
"TlsPremasterSecret"); |
|
|
|
SSLMasterKeyDerivation mskd = |
|
SSLMasterKeyDerivation.valueOf( |
|
context.negotiatedProtocol); |
|
if (mskd == null) { |
|
|
|
throw new SSLHandshakeException( |
|
"No expected master key derivation for protocol: " + |
|
context.negotiatedProtocol.name); |
|
} |
|
SSLKeyDerivation kd = mskd.createKeyDerivation( |
|
context, preMasterSecret); |
|
return kd.deriveKey("MasterSecret", params); |
|
} catch (Exception gse) { |
|
throw (SSLHandshakeException) new SSLHandshakeException( |
|
"Could not generate secret").initCause(gse); |
|
} |
|
} |
|
} |
|
} |
|
} |