blob: abe74fa9c91d75186c1ff12c98686db672b6f0ed [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.geode.internal.cache.tier.sockets;
import static org.apache.geode.internal.cache.tier.sockets.Handshake.CREDENTIALS_DHENCRYPT;
import static org.apache.geode.internal.cache.tier.sockets.Handshake.PRIVATE_KEY_ALIAS_PROP;
import static org.apache.geode.internal.cache.tier.sockets.Handshake.PRIVATE_KEY_FILE_PROP;
import static org.apache.geode.internal.cache.tier.sockets.Handshake.PRIVATE_KEY_PASSWD_PROP;
import static org.apache.geode.internal.cache.tier.sockets.Handshake.PUBLIC_KEY_FILE_PROP;
import static org.apache.geode.internal.cache.tier.sockets.Handshake.PUBLIC_KEY_PASSWD_PROP;
import static org.apache.geode.internal.cache.tier.sockets.Handshake.REPLY_AUTH_NOT_REQUIRED;
import static org.apache.geode.internal.cache.tier.sockets.Handshake.REPLY_OK;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyAgreement;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.geode.DataSerializer;
import org.apache.geode.LogWriter;
import org.apache.geode.annotations.internal.MakeNotStatic;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.distributed.internal.DistributionConfig;
import org.apache.geode.internal.ByteArrayDataInput;
import org.apache.geode.internal.HeapDataOutputStream;
import org.apache.geode.internal.Version;
import org.apache.geode.internal.cache.tier.Encryptor;
import org.apache.geode.internal.logging.InternalLogWriter;
import org.apache.geode.security.AuthenticationFailedException;
import org.apache.geode.security.GemFireSecurityException;
public class EncryptorImpl implements Encryptor {
// Parameters for the Diffie-Hellman key exchange
private static final BigInteger dhP =
new BigInteger("13528702063991073999718992897071702177131142188276542919088770094024269"
+ "73079899070080419278066109785292538223079165925365098181867673946"
+ "34756714063947534092593553024224277712367371302394452615862654308"
+ "11180902979719649450105660478776364198726078338308557022096810447"
+ "3500348898008043285865193451061481841186553");
private static final BigInteger dhG =
new BigInteger("13058345680719715096166513407513969537624553636623932169016704425008150"
+ "56576152779768716554354314319087014857769741104157332735258102835"
+ "93126577393912282416840649805564834470583437473176415335737232689"
+ "81480201869671811010996732593655666464627559582258861254878896534"
+ "1273697569202082715873518528062345259949959");
private static final int dhL = 1023;
private Cipher _encrypt;
private Cipher _decrypt;
private PublicKey clientPublicKey;
private String clientSKAlgo;
@MakeNotStatic
private static PrivateKey dhPrivateKey;
@MakeNotStatic
private static PublicKey dhPublicKey;
@MakeNotStatic
private static String dhSKAlgo;
// Members for server authentication using digital signature
// Members for server authentication using digital signature
@MakeNotStatic
private static String certificateFilePath;
@MakeNotStatic
private static HashMap certificateMap;
@MakeNotStatic
private static String privateKeyAlias;
@MakeNotStatic
private static String privateKeySubject;
@MakeNotStatic
private static PrivateKey privateKeyEncrypt;
@MakeNotStatic
private static String privateKeySignAlgo;
@MakeNotStatic
private static SecureRandom random;
private byte appSecureMode = (byte) 0;
private LogWriter logWriter;
EncryptorImpl(EncryptorImpl encryptor) {
this.appSecureMode = encryptor.appSecureMode;
this.logWriter = encryptor.logWriter;
}
public EncryptorImpl(LogWriter logWriter) {
this.logWriter = logWriter;
}
void setAppSecureMode(byte appSecureMode) {
this.appSecureMode = appSecureMode;
}
public static byte[] decryptBytes(byte[] data, Cipher decrypt) throws Exception {
return decrypt.doFinal(data);
}
protected Cipher getDecryptCipher(String dhSKAlgo, PublicKey publicKey) throws Exception {
if (_decrypt == null) {
KeyAgreement ka = KeyAgreement.getInstance("DH");
ka.init(dhPrivateKey);
ka.doPhase(publicKey, true);
Cipher decrypt;
int keysize = getKeySize(dhSKAlgo);
int blocksize = getBlockSize(dhSKAlgo);
if (keysize == -1 || blocksize == -1) {
SecretKey sKey = ka.generateSecret(dhSKAlgo);
decrypt = Cipher.getInstance(dhSKAlgo);
decrypt.init(Cipher.DECRYPT_MODE, sKey);
} else {
String algoStr = getDhAlgoStr(dhSKAlgo);
byte[] sKeyBytes = ka.generateSecret();
SecretKeySpec sks = new SecretKeySpec(sKeyBytes, 0, keysize, algoStr);
IvParameterSpec ivps = new IvParameterSpec(sKeyBytes, keysize, blocksize);
decrypt = Cipher.getInstance(algoStr + "/CBC/PKCS5Padding");
decrypt.init(Cipher.DECRYPT_MODE, sks, ivps);
}
_decrypt = decrypt;
}
return _decrypt;
}
/**
* Populate the available server public keys into a local static HashMap. This method is not
* thread safe.
*/
public static void initCertsMap(Properties props) throws Exception {
certificateMap = new HashMap();
certificateFilePath = props.getProperty(PUBLIC_KEY_FILE_PROP);
if (certificateFilePath != null && certificateFilePath.length() > 0) {
KeyStore ks = KeyStore.getInstance("JKS");
String keyStorePass = props.getProperty(PUBLIC_KEY_PASSWD_PROP);
char[] passPhrase = (keyStorePass != null ? keyStorePass.toCharArray() : null);
FileInputStream keystorefile = new FileInputStream(certificateFilePath);
try {
ks.load(keystorefile, passPhrase);
} finally {
keystorefile.close();
}
Enumeration aliases = ks.aliases();
while (aliases.hasMoreElements()) {
String alias = (String) aliases.nextElement();
Certificate cert = ks.getCertificate(alias);
if (cert instanceof X509Certificate) {
String subject = ((X509Certificate) cert).getSubjectDN().getName();
certificateMap.put(subject, cert);
}
}
}
}
/**
* Load the private key of the server. This method is not thread safe.
*/
public static void initPrivateKey(Properties props) throws Exception {
String privateKeyFilePath = props.getProperty(PRIVATE_KEY_FILE_PROP);
privateKeyAlias = "";
privateKeyEncrypt = null;
if (privateKeyFilePath != null && privateKeyFilePath.length() > 0) {
KeyStore ks = KeyStore.getInstance("PKCS12");
privateKeyAlias = props.getProperty(PRIVATE_KEY_ALIAS_PROP);
if (privateKeyAlias == null) {
privateKeyAlias = "";
}
String keyStorePass = props.getProperty(PRIVATE_KEY_PASSWD_PROP);
char[] passPhrase = (keyStorePass != null ? keyStorePass.toCharArray() : null);
FileInputStream privateKeyFile = new FileInputStream(privateKeyFilePath);
try {
ks.load(privateKeyFile, passPhrase);
} finally {
privateKeyFile.close();
}
Key key = ks.getKey(privateKeyAlias, passPhrase);
Certificate keyCert = ks.getCertificate(privateKeyAlias);
if (key instanceof PrivateKey && keyCert instanceof X509Certificate) {
privateKeyEncrypt = (PrivateKey) key;
privateKeySignAlgo = ((X509Certificate) keyCert).getSigAlgName();
privateKeySubject = ((X509Certificate) keyCert).getSubjectDN().getName();
}
}
}
/**
* Initialize the Diffie-Hellman keys. This method is not thread safe
*/
public static void initDHKeys(DistributionConfig config) throws Exception {
dhSKAlgo = config.getSecurityClientDHAlgo();
dhPrivateKey = null;
dhPublicKey = null;
// Initialize the keys when either the host is a client that has
// non-blank setting for DH symmetric algo, or this is a server
// that has authenticator defined.
if ((dhSKAlgo != null
&& dhSKAlgo.length() > 0) /* || securityService.isClientSecurityRequired() */) {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH");
DHParameterSpec dhSpec = new DHParameterSpec(dhP, dhG, dhL);
keyGen.initialize(dhSpec);
KeyPair keypair = keyGen.generateKeyPair();
// Get the generated public and private keys
dhPrivateKey = keypair.getPrivate();
dhPublicKey = keypair.getPublic();
random = new SecureRandom();
// Force the random generator to seed itself.
byte[] someBytes = new byte[48];
random.nextBytes(someBytes);
}
}
@Override
public byte[] decryptBytes(byte[] data) throws Exception {
if (this.appSecureMode == CREDENTIALS_DHENCRYPT) {
String algo = null;
if (this.clientSKAlgo != null) {
algo = this.clientSKAlgo;
} else {
algo = dhSKAlgo;
}
Cipher c = getDecryptCipher(algo, this.clientPublicKey);
return decryptBytes(data, c);
} else {
return data;
}
}
@Override
public byte[] encryptBytes(byte[] data) throws Exception {
if (this.appSecureMode == CREDENTIALS_DHENCRYPT) {
String algo = null;
if (this.clientSKAlgo != null) {
algo = this.clientSKAlgo;
} else {
algo = dhSKAlgo;
}
return encryptBytes(data, getEncryptCipher(algo, this.clientPublicKey));
} else {
return data;
}
}
public static byte[] encryptBytes(byte[] data, Cipher encrypt) throws Exception {
return encrypt.doFinal(data);
}
protected Cipher getEncryptCipher(String dhSKAlgo, PublicKey publicKey) throws Exception {
if (_encrypt == null) {
KeyAgreement ka = KeyAgreement.getInstance("DH");
ka.init(dhPrivateKey);
ka.doPhase(publicKey, true);
Cipher encrypt;
int keysize = getKeySize(dhSKAlgo);
int blocksize = getBlockSize(dhSKAlgo);
if (keysize == -1 || blocksize == -1) {
SecretKey sKey = ka.generateSecret(dhSKAlgo);
encrypt = Cipher.getInstance(dhSKAlgo);
encrypt.init(Cipher.ENCRYPT_MODE, sKey);
} else {
String dhAlgoStr = getDhAlgoStr(dhSKAlgo);
byte[] sKeyBytes = ka.generateSecret();
SecretKeySpec sks = new SecretKeySpec(sKeyBytes, 0, keysize, dhAlgoStr);
IvParameterSpec ivps = new IvParameterSpec(sKeyBytes, keysize, blocksize);
encrypt = Cipher.getInstance(dhAlgoStr + "/CBC/PKCS5Padding");
encrypt.init(Cipher.ENCRYPT_MODE, sks, ivps);
}
_encrypt = encrypt;
}
return _encrypt;
}
boolean isEnabled() {
return dhSKAlgo != null && dhSKAlgo.length() > 0;
}
byte writeEncryptedCredential(DataOutputStream dos, DataInputStream dis,
HeapDataOutputStream heapdos) throws IOException {
byte acceptanceCode;
try {
logWriter.fine("HandShake: using Diffie-Hellman key exchange with algo " + dhSKAlgo);
boolean requireAuthentication =
(certificateFilePath != null && certificateFilePath.length() > 0);
if (requireAuthentication) {
logWriter.fine("HandShake: server authentication using digital " + "signature required");
}
// Credentials with encryption indicator
heapdos.writeByte(CREDENTIALS_DHENCRYPT);
this.appSecureMode = CREDENTIALS_DHENCRYPT;
heapdos.writeBoolean(requireAuthentication);
// Send the symmetric encryption algorithm name
DataSerializer.writeString(dhSKAlgo, heapdos);
// Send the DH public key
byte[] keyBytes = dhPublicKey.getEncoded();
DataSerializer.writeByteArray(keyBytes, heapdos);
byte[] clientChallenge = null;
if (requireAuthentication) {
// Authentication of server should be with the client supplied
// challenge
clientChallenge = new byte[64];
random.nextBytes(clientChallenge);
DataSerializer.writeByteArray(clientChallenge, heapdos);
}
heapdos.flush();
dos.write(heapdos.toByteArray());
dos.flush();
// Expect the alias and signature in the reply
acceptanceCode = dis.readByte();
if (acceptanceCode == REPLY_OK) {
// Get the public key of the other side
keyBytes = DataSerializer.readByteArray(dis);
if (requireAuthentication) {
String subject = DataSerializer.readString(dis);
byte[] signatureBytes = DataSerializer.readByteArray(dis);
if (!certificateMap.containsKey(subject)) {
throw new AuthenticationFailedException(
String.format("HandShake failed to find public key for server with subject %s",
subject));
}
// Check the signature with the public key
X509Certificate cert = (X509Certificate) certificateMap.get(subject);
Signature sig = Signature.getInstance(cert.getSigAlgName());
sig.initVerify(cert);
sig.update(clientChallenge);
// Check the challenge string
if (!sig.verify(signatureBytes)) {
throw new AuthenticationFailedException(
"Mismatch in client " + "challenge bytes. Malicious server?");
}
logWriter.fine("HandShake: Successfully verified the " + "digital signature from server");
}
// Read server challenge bytes
byte[] serverChallenge = DataSerializer.readByteArray(dis);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFact = KeyFactory.getInstance("DH");
// PublicKey pubKey = keyFact.generatePublic(x509KeySpec);
this.clientPublicKey = keyFact.generatePublic(x509KeySpec);
try (HeapDataOutputStream hdos = new HeapDataOutputStream(Version.CURRENT)) {
// Add the challenge string
DataSerializer.writeByteArray(serverChallenge, hdos);
// byte[] encBytes = encrypt.doFinal(hdos.toByteArray());
byte[] encBytes =
encryptBytes(hdos.toByteArray(), getEncryptCipher(dhSKAlgo, this.clientPublicKey));
DataSerializer.writeByteArray(encBytes, dos);
}
}
} catch (IOException ex) {
throw ex;
} catch (GemFireSecurityException ex) {
throw ex;
} catch (Exception ex) {
throw new AuthenticationFailedException("HandShake failed in Diffie-Hellman key exchange",
ex);
}
return acceptanceCode;
}
byte writeEncryptedCredentials(DataOutputStream dos, DataInputStream dis,
Properties p_credentials, HeapDataOutputStream heapdos) throws IOException {
byte acceptanceCode;
try {
logWriter.fine("HandShake: using Diffie-Hellman key exchange with algo " + dhSKAlgo);
boolean requireAuthentication =
(certificateFilePath != null && certificateFilePath.length() > 0);
if (requireAuthentication) {
logWriter.fine("HandShake: server authentication using digital signature required");
}
// Credentials with encryption indicator
heapdos.writeByte(CREDENTIALS_DHENCRYPT);
heapdos.writeBoolean(requireAuthentication);
// Send the symmetric encryption algorithm name
DataSerializer.writeString(dhSKAlgo, heapdos);
// Send the DH public key
byte[] keyBytes = dhPublicKey.getEncoded();
DataSerializer.writeByteArray(keyBytes, heapdos);
byte[] clientChallenge = null;
if (requireAuthentication) {
// Authentication of server should be with the client supplied
// challenge
clientChallenge = new byte[64];
random.nextBytes(clientChallenge);
DataSerializer.writeByteArray(clientChallenge, heapdos);
}
heapdos.flush();
dos.write(heapdos.toByteArray());
dos.flush();
// Expect the alias and signature in the reply
acceptanceCode = dis.readByte();
if (acceptanceCode == REPLY_OK) {
// Get the public key of the other side
keyBytes = DataSerializer.readByteArray(dis);
if (requireAuthentication) {
String subject = DataSerializer.readString(dis);
byte[] signatureBytes = DataSerializer.readByteArray(dis);
if (!certificateMap.containsKey(subject)) {
throw new AuthenticationFailedException(
String.format("HandShake failed to find public key for server with subject %s",
subject));
}
// Check the signature with the public key
X509Certificate cert = (X509Certificate) certificateMap.get(subject);
Signature sig = Signature.getInstance(cert.getSigAlgName());
sig.initVerify(cert);
sig.update(clientChallenge);
// Check the challenge string
if (!sig.verify(signatureBytes)) {
throw new AuthenticationFailedException(
"Mismatch in client " + "challenge bytes. Malicious server?");
}
logWriter.fine("HandShake: Successfully verified the " + "digital signature from server");
}
byte[] challenge = DataSerializer.readByteArray(dis);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFact = KeyFactory.getInstance("DH");
// PublicKey pubKey = keyFact.generatePublic(x509KeySpec);
this.clientPublicKey = keyFact.generatePublic(x509KeySpec);
HeapDataOutputStream hdos = new HeapDataOutputStream(Version.CURRENT);
try {
DataSerializer.writeProperties(p_credentials, hdos);
// Also add the challenge string
DataSerializer.writeByteArray(challenge, hdos);
// byte[] encBytes = encrypt.doFinal(hdos.toByteArray());
byte[] encBytes =
encryptBytes(hdos.toByteArray(), getEncryptCipher(dhSKAlgo, this.clientPublicKey));
DataSerializer.writeByteArray(encBytes, dos);
} finally {
hdos.close();
}
}
} catch (IOException ex) {
throw ex;
} catch (GemFireSecurityException ex) {
throw ex;
} catch (Exception ex) {
throw new AuthenticationFailedException("HandShake failed in Diffie-Hellman key exchange",
ex);
}
return acceptanceCode;
}
void readEncryptedCredentials(DataInputStream dis, DataOutputStream dos, DistributedSystem system,
boolean requireAuthentication) throws Exception {
this.appSecureMode = CREDENTIALS_DHENCRYPT;
boolean sendAuthentication = dis.readBoolean();
InternalLogWriter securityLogWriter = (InternalLogWriter) system.getSecurityLogWriter();
// Get the symmetric encryption algorithm to be used
this.clientSKAlgo = DataSerializer.readString(dis);
// Get the public key of the other side
byte[] keyBytes = DataSerializer.readByteArray(dis);
byte[] challenge = null;
// PublicKey pubKey = null;
if (requireAuthentication) {
// Generate PublicKey from encoded form
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFact = KeyFactory.getInstance("DH");
this.clientPublicKey = keyFact.generatePublic(x509KeySpec);
// Send the public key to other side
keyBytes = dhPublicKey.getEncoded();
challenge = new byte[64];
random.nextBytes(challenge);
// If the server has to also authenticate itself then
// sign the challenge from client.
if (sendAuthentication) {
// Get the challenge string from client
byte[] clientChallenge = DataSerializer.readByteArray(dis);
if (privateKeyEncrypt == null) {
throw new AuthenticationFailedException(
"Server private key not available for creating signature.");
}
// Sign the challenge from client and send it to the client
Signature sig = Signature.getInstance(privateKeySignAlgo);
sig.initSign(privateKeyEncrypt);
sig.update(clientChallenge);
byte[] signedBytes = sig.sign();
dos.writeByte(REPLY_OK);
DataSerializer.writeByteArray(keyBytes, dos);
// DataSerializer.writeString(privateKeyAlias, dos);
DataSerializer.writeString(privateKeySubject, dos);
DataSerializer.writeByteArray(signedBytes, dos);
securityLogWriter.fine("HandShake: sent the signed client challenge");
} else {
// These two lines should not be moved before the if{} statement in
// a common block for both if...then...else parts. This is to handle
// the case when an AuthenticationFailedException is thrown by the
// if...then part when sending the signature.
dos.writeByte(REPLY_OK);
DataSerializer.writeByteArray(keyBytes, dos);
}
// Now send the server challenge
DataSerializer.writeByteArray(challenge, dos);
securityLogWriter.fine("HandShake: sent the public key and challenge");
dos.flush();
// Read and decrypt the credentials
byte[] encBytes = DataSerializer.readByteArray(dis);
Cipher c = getDecryptCipher(this.clientSKAlgo, this.clientPublicKey);
byte[] credentialBytes = decryptBytes(encBytes, c);
ByteArrayDataInput dinp = new ByteArrayDataInput(credentialBytes);
// credentials = DataSerializer.readProperties(dinp);//Hitesh: we don't send in handshake
// now
byte[] challengeRes = DataSerializer.readByteArray(dinp);
// Check the challenge string
if (!Arrays.equals(challenge, challengeRes)) {
throw new AuthenticationFailedException(
"Mismatch in challenge bytes. Malicious client?");
}
dinp.close();
} else {
if (sendAuthentication) {
// Read and ignore the client challenge
DataSerializer.readByteArray(dis);
}
dos.writeByte(REPLY_AUTH_NOT_REQUIRED);
dos.flush();
}
}
static Properties getDecryptedCredentials(DataInputStream dis, DataOutputStream dos,
DistributedSystem system, boolean requireAuthentication, Properties credentials)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, InvalidKeyException,
SignatureException, NoSuchPaddingException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException, ClassNotFoundException {
boolean sendAuthentication = dis.readBoolean();
InternalLogWriter securityLogWriter = (InternalLogWriter) system.getSecurityLogWriter();
// Get the symmetric encryption algorithm to be used
String skAlgo = DataSerializer.readString(dis);
// Get the public key of the other side
byte[] keyBytes = DataSerializer.readByteArray(dis);
byte[] challenge = null;
PublicKey pubKey = null;
if (requireAuthentication) {
// Generate PublicKey from encoded form
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFact = KeyFactory.getInstance("DH");
pubKey = keyFact.generatePublic(x509KeySpec);
// Send the public key to other side
keyBytes = dhPublicKey.getEncoded();
challenge = new byte[64];
random.nextBytes(challenge);
// If the server has to also authenticate itself then
// sign the challenge from client.
if (sendAuthentication) {
// Get the challenge string from client
byte[] clientChallenge = DataSerializer.readByteArray(dis);
if (privateKeyEncrypt == null) {
throw new AuthenticationFailedException(
"Server private key not available for creating signature.");
}
// Sign the challenge from client and send it to the client
Signature sig = Signature.getInstance(privateKeySignAlgo);
sig.initSign(privateKeyEncrypt);
sig.update(clientChallenge);
byte[] signedBytes = sig.sign();
dos.writeByte(REPLY_OK);
DataSerializer.writeByteArray(keyBytes, dos);
// DataSerializer.writeString(privateKeyAlias, dos);
DataSerializer.writeString(privateKeySubject, dos);
DataSerializer.writeByteArray(signedBytes, dos);
securityLogWriter.fine("HandShake: sent the signed client challenge");
} else {
// These two lines should not be moved before the if{} statement in
// a common block for both if...then...else parts. This is to handle
// the case when an AuthenticationFailedException is thrown by the
// if...then part when sending the signature.
dos.writeByte(REPLY_OK);
DataSerializer.writeByteArray(keyBytes, dos);
}
// Now send the server challenge
DataSerializer.writeByteArray(challenge, dos);
securityLogWriter.fine("HandShake: sent the public key and challenge");
dos.flush();
// Read and decrypt the credentials
byte[] encBytes = DataSerializer.readByteArray(dis);
KeyAgreement ka = KeyAgreement.getInstance("DH");
ka.init(dhPrivateKey);
ka.doPhase(pubKey, true);
Cipher decrypt;
int keysize = getKeySize(skAlgo);
int blocksize = getBlockSize(skAlgo);
if (keysize == -1 || blocksize == -1) {
SecretKey sKey = ka.generateSecret(skAlgo);
decrypt = Cipher.getInstance(skAlgo);
decrypt.init(Cipher.DECRYPT_MODE, sKey);
} else {
String algoStr = getDhAlgoStr(skAlgo);
byte[] sKeyBytes = ka.generateSecret();
SecretKeySpec sks = new SecretKeySpec(sKeyBytes, 0, keysize, algoStr);
IvParameterSpec ivps = new IvParameterSpec(sKeyBytes, keysize, blocksize);
decrypt = Cipher.getInstance(algoStr + "/CBC/PKCS5Padding");
decrypt.init(Cipher.DECRYPT_MODE, sks, ivps);
}
byte[] credentialBytes = decrypt.doFinal(encBytes);
ByteArrayDataInput dinp = new ByteArrayDataInput(credentialBytes);
credentials = DataSerializer.readProperties(dinp);
byte[] challengeRes = DataSerializer.readByteArray(dinp);
// Check the challenge string
if (!Arrays.equals(challenge, challengeRes)) {
throw new AuthenticationFailedException(
"Mismatch in challenge bytes. Malicious client?");
}
dinp.close();
} else {
if (sendAuthentication) {
// Read and ignore the client challenge
DataSerializer.readByteArray(dis);
}
dos.writeByte(REPLY_AUTH_NOT_REQUIRED);
dos.flush();
}
return credentials;
}
private static int getKeySize(String skAlgo) {
// skAlgo contain both algo and key size info
int colIdx = skAlgo.indexOf(':');
String algoStr;
int algoKeySize = 0;
if (colIdx >= 0) {
algoStr = skAlgo.substring(0, colIdx);
algoKeySize = Integer.parseInt(skAlgo.substring(colIdx + 1));
} else {
algoStr = skAlgo;
}
int keysize = -1;
if (algoStr.equalsIgnoreCase("DESede")) {
keysize = 24;
} else if (algoStr.equalsIgnoreCase("Blowfish")) {
keysize = algoKeySize > 128 ? algoKeySize / 8 : 16;
} else if (algoStr.equalsIgnoreCase("AES")) {
keysize = (algoKeySize != 192 && algoKeySize != 256) ? 16 : algoKeySize / 8;
}
return keysize;
}
private static String getDhAlgoStr(String skAlgo) {
int colIdx = skAlgo.indexOf(':');
String algoStr;
if (colIdx >= 0) {
algoStr = skAlgo.substring(0, colIdx);
} else {
algoStr = skAlgo;
}
return algoStr;
}
private static int getBlockSize(String skAlgo) {
int blocksize = -1;
String algoStr = getDhAlgoStr(skAlgo);
if (algoStr.equalsIgnoreCase("DESede")) {
blocksize = 8;
} else if (algoStr.equalsIgnoreCase("Blowfish")) {
blocksize = 8;
} else if (algoStr.equalsIgnoreCase("AES")) {
blocksize = 16;
}
return blocksize;
}
}