blob: 1b2e035a9f8a8acd842f154d0bb29a6392a70cab [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.distributed.internal.membership.gms.messenger;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.spec.DHParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
import org.apache.geode.distributed.internal.membership.InternalDistributedMember.InternalDistributedMemberWrapper;
import org.apache.geode.distributed.internal.membership.NetView;
import org.apache.geode.distributed.internal.membership.gms.Services;
import org.apache.geode.distributed.internal.membership.gms.locator.GMSLocator;
import org.apache.geode.internal.util.JavaWorkarounds;
public final class GMSEncrypt {
// 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 final PrivateKey dhPrivateKey;
private final PublicKey dhPublicKey;
private final String dhSKAlgo;
private Services services;
private NetView view;
/**
* it keeps PK for peers
*/
private final Map<InternalDistributedMemberWrapper, byte[]> memberToPublicKey =
new ConcurrentHashMap<>();
private final ConcurrentHashMap<InternalDistributedMember, GMSEncryptionCipherPool> peerEncryptors =
new ConcurrentHashMap<>();
private GMSEncryptionCipherPool clusterEncryptor;
protected void installView(NetView view) {
this.view = view;
this.view.setPublicKey(services.getJoinLeave().getMemberID(), getPublicKeyBytes());
}
void overrideInstallViewForTest(NetView view) {
this.view = view;
}
protected byte[] getClusterSecretKey() {
if (this.clusterEncryptor != null) {
return this.clusterEncryptor.getSecretBytes();
} else {
return null;
}
}
protected synchronized void initClusterSecretKey() throws Exception {
if (this.clusterEncryptor == null) {
this.clusterEncryptor = new GMSEncryptionCipherPool(this, generateSecret(dhPublicKey));
}
}
protected synchronized void setClusterKey(byte[] secretBytes) {
this.clusterEncryptor = new GMSEncryptionCipherPool(this, secretBytes);
}
private byte[] getPublicKeyIfIAmLocator(InternalDistributedMember mbr) {
GMSLocator locator = (GMSLocator) services.getLocator();
if (locator != null) {
return locator.getPublicKey(mbr);
}
return null;
}
GMSEncrypt(Services services, String dhSKAlgo) throws Exception {
this.services = services;
this.dhSKAlgo = dhSKAlgo;
// Initialize the keys when either the host is a peer that has
// non-blank setting for DH symmetric algo, or this is a server
// that has authenticator defined.
if ((this.dhSKAlgo != null && this.dhSKAlgo.length() > 0)) {
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();
} else {
dhPrivateKey = null;
dhPublicKey = null;
}
}
byte[] decryptData(byte[] data, InternalDistributedMember member) throws Exception {
return getPeerEncryptor(member).decryptBytes(data);
}
byte[] encryptData(byte[] data, InternalDistributedMember member) throws Exception {
return getPeerEncryptor(member).encryptBytes(data);
}
byte[] decryptData(byte[] data) throws Exception {
return clusterEncryptor.decryptBytes(data);
}
byte[] decryptData(byte[] data, byte[] pkBytes) throws Exception {
GMSEncryptionCipherPool encryptor = new GMSEncryptionCipherPool(this, generateSecret(pkBytes));
return encryptor.decryptBytes(data);
}
byte[] encryptData(byte[] data) throws Exception {
return clusterEncryptor.encryptBytes(data);
}
byte[] getPublicKeyBytes() {
return dhPublicKey.getEncoded();
}
private byte[] lookupKeyByMember(InternalDistributedMember member) {
byte[] pk = memberToPublicKey.get(new InternalDistributedMemberWrapper(member));
if (pk == null) {
pk = getPublicKeyIfIAmLocator(member);
}
if (pk == null) {
pk = (byte[]) view.getPublicKey(member);
}
if (pk == null) {
throw new IllegalStateException("unable to find public key for " + member);
}
return pk;
}
protected byte[] getPublicKey(InternalDistributedMember member) {
try {
InternalDistributedMember localMbr = services.getMessenger().getMemberID();
if (localMbr != null && localMbr.equals(member)) {
return this.dhPublicKey.getEncoded();// local one
}
return lookupKeyByMember(member);
} catch (Exception e) {
throw new RuntimeException("Not found public key for member " + member, e);
}
}
protected void setPublicKey(byte[] publickey, InternalDistributedMember mbr) {
try {
memberToPublicKey.put(new InternalDistributedMemberWrapper(mbr), publickey);
peerEncryptors.replace(mbr, new GMSEncryptionCipherPool(this, generateSecret(publickey)));
} catch (Exception e) {
throw new RuntimeException("Unable to create peer encryptor " + mbr, e);
}
}
private GMSEncryptionCipherPool getPeerEncryptor(InternalDistributedMember member)
throws Exception {
return JavaWorkarounds.computeIfAbsent(peerEncryptors, member, (mbr) -> {
try {
return new GMSEncryptionCipherPool(this, generateSecret(lookupKeyByMember(member)));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
});
}
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;
}
Cipher getEncryptCipher(byte[] secretBytes) throws Exception {
Cipher encrypt = null;
int keysize = getKeySize(dhSKAlgo);
int blocksize = getBlockSize(dhSKAlgo);
if (keysize == -1 || blocksize == -1) {
SecretKeySpec sks = new SecretKeySpec(secretBytes, dhSKAlgo);
encrypt = Cipher.getInstance(dhSKAlgo);
encrypt.init(Cipher.ENCRYPT_MODE, sks);
} else {
String dhAlgoStr = getDhAlgoStr(dhSKAlgo);
SecretKeySpec sks = new SecretKeySpec(secretBytes, 0, keysize, dhAlgoStr);
IvParameterSpec ivps = new IvParameterSpec(secretBytes, keysize, blocksize);
encrypt = Cipher.getInstance(dhAlgoStr + "/CBC/PKCS5Padding");
encrypt.init(Cipher.ENCRYPT_MODE, sks, ivps);
}
return encrypt;
}
Cipher getDecryptCipher(byte[] secretBytes) throws Exception {
Cipher decrypt;
int keysize = getKeySize(dhSKAlgo);
int blocksize = getBlockSize(dhSKAlgo);
if (keysize == -1 || blocksize == -1) {
SecretKeySpec sks = new SecretKeySpec(secretBytes, dhSKAlgo);
decrypt = Cipher.getInstance(dhSKAlgo);
decrypt.init(Cipher.DECRYPT_MODE, sks);
} else {
String algoStr = getDhAlgoStr(dhSKAlgo);
SecretKeySpec sks = new SecretKeySpec(secretBytes, 0, keysize, algoStr);
IvParameterSpec ivps = new IvParameterSpec(secretBytes, keysize, blocksize);
decrypt = Cipher.getInstance(algoStr + "/CBC/PKCS5Padding");
decrypt.init(Cipher.DECRYPT_MODE, sks, ivps);
}
return decrypt;
}
private byte[] generateSecret(byte[] peerKeyBytes) throws Exception {
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(peerKeyBytes);
KeyFactory keyFact = KeyFactory.getInstance("DH");
PublicKey peerKey = keyFact.generatePublic(x509KeySpec);
return generateSecret(dhSKAlgo, dhPrivateKey, peerKey);
}
private byte[] generateSecret(PublicKey peerKey) throws Exception {
return generateSecret(dhSKAlgo, dhPrivateKey, peerKey);
}
private static byte[] generateSecret(String dhSKAlgo, PrivateKey privateKey,
PublicKey otherPublicKey) throws Exception {
KeyAgreement ka = KeyAgreement.getInstance("DH");
ka.init(privateKey);
ka.doPhase(otherPublicKey, true);
int keysize = getKeySize(dhSKAlgo);
int blocksize = getBlockSize(dhSKAlgo);
if (keysize == -1 || blocksize == -1) {
SecretKey sKey = ka.generateSecret(dhSKAlgo);
return sKey.getEncoded();
} else {
return ka.generateSecret();
}
}
}