/* * 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.*; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Iterator; import java.util.List; 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.DistributionConfig; public class GMSEncrypt implements Cloneable { public static long encodingsPerformed; public static long decodingsPerformed; // 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 PrivateKey dhPrivateKey = null; private PublicKey dhPublicKey = null; private String dhSKAlgo = null; private Services services; private InternalDistributedMember localMember; private NetView view; public static final int numberOfPeerEncryptorCopies = Integer.getInteger("GMSEncrypt.MAX_ENCRYPTORS", Math.max(Runtime.getRuntime().availableProcessors() * 4, 16)).intValue(); /** * Keeps multiple copies for peer */ private ConcurrentHashMap<InternalDistributedMember, PeerEncryptor>[] copyOfPeerEncryptors; /** * Keeps multiple copies of cluster keys */ private ClusterEncryptor[] copyOfClusterEncryptors; /** * it keeps PK for peers */ private Map<InternalDistributedMemberWrapper, byte[]> memberToPeerEncryptor = new ConcurrentHashMap<>(); private ClusterEncryptor clusterEncryptor; protected void installView(NetView view) { this.view = view; this.view.setPublicKey(services.getJoinLeave().getMemberID(), getPublicKeyBytes()); } protected void installView(NetView view, InternalDistributedMember mbr) { this.view = view; } protected byte[] getClusterSecretKey() { return this.clusterEncryptor.secretBytes; } protected synchronized void initClusterSecretKey() throws Exception { if (this.clusterEncryptor == null) { this.clusterEncryptor = new ClusterEncryptor(this); } } protected synchronized void addClusterKey(byte[] secretBytes) { this.clusterEncryptor = new ClusterEncryptor(secretBytes); } protected GMSEncrypt() { initEncryptors(); } private byte[] getRegisteredPublicKey(InternalDistributedMember mbr) { return services.getPublicKey(mbr); } public GMSEncrypt(Services services) throws Exception { this.services = services; initEncryptors(); initDHKeys(services.getConfig().getDistributionConfig()); } public GMSEncrypt(Services services, InternalDistributedMember mbr) throws Exception { this.services = services; this.localMember = mbr; initEncryptors(); initDHKeys(services.getConfig().getDistributionConfig()); } void initEncryptors() { copyOfPeerEncryptors = new ConcurrentHashMap[numberOfPeerEncryptorCopies]; copyOfClusterEncryptors = new ClusterEncryptor[numberOfPeerEncryptorCopies]; } public byte[] decryptData(byte[] data, InternalDistributedMember member) throws Exception { return getPeerEncryptor(member).decryptBytes(data); } public byte[] encryptData(byte[] data, InternalDistributedMember member) throws Exception { return getPeerEncryptor(member).encryptBytes(data); } public byte[] decryptData(byte[] data) throws Exception { return getClusterEncryptor().decryptBytes(data); } public byte[] decryptData(byte[] data, byte[] pkBytes) throws Exception { PeerEncryptor encryptor = new PeerEncryptor(pkBytes); return encryptor.decryptBytes(data); } public byte[] encryptData(byte[] data) throws Exception { return getClusterEncryptor().encryptBytes(data); } protected byte[] getPublicKeyBytes() { return dhPublicKey.getEncoded(); } protected byte[] getPublicKey(InternalDistributedMember member) { try { InternalDistributedMember localMbr = services.getMessenger().getMemberID(); if (localMbr != null && localMbr.equals(member)) { return this.dhPublicKey.getEncoded();// local one } return getPeerEncryptor(member).peerPublicKey.getEncoded(); } catch (Exception e) { throw new RuntimeException("Not found public key for member " + member, e); } } protected void setPublicKey(byte[] publickey, InternalDistributedMember mbr) { try { // createPeerEncryptor(mbr, publickey); memberToPeerEncryptor.put(new InternalDistributedMemberWrapper(mbr), publickey); synchronized (copyOfPeerEncryptors) { // remove all the existing keys.. for (Map m : copyOfPeerEncryptors) { if (m != null) m.remove(mbr); } } } catch (Exception e) { throw new RuntimeException("Unable to create peer encryptor " + mbr, e); } } @Override protected GMSEncrypt clone() throws CloneNotSupportedException { try { GMSEncrypt gmsEncrypt = new GMSEncrypt(); gmsEncrypt.localMember = this.localMember; gmsEncrypt.dhSKAlgo = this.dhSKAlgo; gmsEncrypt.services = this.services; X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(this.dhPublicKey.getEncoded()); KeyFactory keyFact = KeyFactory.getInstance("DH"); gmsEncrypt.dhPublicKey = keyFact.generatePublic(x509KeySpec); final String format = this.dhPrivateKey.getFormat(); System.out.println("private key format " + format); System.out.println("public ksy format " + this.dhPublicKey.getFormat()); PKCS8EncodedKeySpec x509KeySpecPKey = new PKCS8EncodedKeySpec(this.dhPrivateKey.getEncoded()); keyFact = KeyFactory.getInstance("DH"); // PublicKey pubKey = keyFact.generatePublic(x509KeySpec); gmsEncrypt.dhPrivateKey = keyFact.generatePrivate(x509KeySpecPKey); return gmsEncrypt; } catch (Exception e) { throw new RuntimeException("Unable to clone", e); } } /** * Initialize the Diffie-Hellman keys. This method is not thread safe */ private void initDHKeys(DistributionConfig config) throws Exception { dhSKAlgo = config.getSecurityUDPDHAlgo(); // 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 ((dhSKAlgo != null && 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(); } } protected PeerEncryptor getPeerEncryptor(InternalDistributedMember member) throws Exception { Map<InternalDistributedMember, PeerEncryptor> m = getPeerEncryptorMap(); PeerEncryptor result = m.get(member); if (result == null) { synchronized (this) { result = m.get(member); if (result == null) { byte[] pk = (byte[]) memberToPeerEncryptor.get(new InternalDistributedMemberWrapper(member)); if (pk == null) { pk = getRegisteredPublicKey(member); } result = createPeerEncryptor(member, pk != null ? pk : (byte[]) view.getPublicKey(member)); m.put(member, result); } } } return result; } private Map<InternalDistributedMember, PeerEncryptor> getPeerEncryptorMap() { int h = Math.abs(Thread.currentThread().getName().hashCode() % numberOfPeerEncryptorCopies); ConcurrentHashMap<InternalDistributedMember, PeerEncryptor> m = copyOfPeerEncryptors[h]; if (m == null) { synchronized (copyOfPeerEncryptors) { m = copyOfPeerEncryptors[h]; if (m == null) { m = new ConcurrentHashMap<InternalDistributedMember, PeerEncryptor>(); copyOfPeerEncryptors[h] = m; } } } return m; } private ClusterEncryptor getClusterEncryptor() { int h = Math.abs(Thread.currentThread().getName().hashCode() % numberOfPeerEncryptorCopies); ClusterEncryptor c = copyOfClusterEncryptors[h]; if (c == null) { synchronized (copyOfClusterEncryptors) { c = copyOfClusterEncryptors[h]; if (c == null) { c = new ClusterEncryptor(getClusterSecretKey()); copyOfClusterEncryptors[h] = c; } } } return c; } private PeerEncryptor createPeerEncryptor(InternalDistributedMember member, byte[] peerKeyBytes) throws Exception { PeerEncryptor result = new PeerEncryptor(peerKeyBytes); return result; } 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; } static public byte[] encryptBytes(byte[] data, Cipher encrypt) throws Exception { return encrypt.doFinal(data); } static public byte[] decryptBytes(byte[] data, Cipher decrypt) throws Exception { try { byte[] decryptBytes = decrypt.doFinal(data); return decryptBytes; } catch (Exception ex) { throw ex; } } protected class PeerEncryptor { private PublicKey peerPublicKey = null; private String peerSKAlgo = null; private Cipher encrypt; private Cipher decrypt = null; protected PeerEncryptor(byte[] peerPublicKeyBytes) throws Exception { this.peerPublicKey = getPublicKey(peerPublicKeyBytes); } public synchronized byte[] encryptBytes(byte[] data) throws Exception { String algo = null; if (this.peerSKAlgo != null) { algo = this.peerSKAlgo; } else { algo = dhSKAlgo; } return GMSEncrypt.encryptBytes(data, getEncryptCipher(algo)); } private Cipher getEncryptCipher(String dhSKAlgo) throws Exception { try { if (encrypt == null) { encrypt = GMSEncrypt.getEncryptCipher(dhSKAlgo, dhPrivateKey, this.peerPublicKey); } } catch (Exception ex) { throw ex; } return encrypt; } public synchronized byte[] decryptBytes(byte[] data) throws Exception { String algo = null; if (this.peerSKAlgo != null) { algo = this.peerSKAlgo; } else { algo = dhSKAlgo; } Cipher c = getDecryptCipher(algo, this.peerPublicKey); return GMSEncrypt.decryptBytes(data, c); } private Cipher getDecryptCipher(String dhSKAlgo, PublicKey publicKey) throws Exception { if (decrypt == null) { decrypt = GMSEncrypt.getDecryptCipher(dhSKAlgo, dhPrivateKey, this.peerPublicKey); } return decrypt; } } // this needs to synchronize as it uses private key of that member protected static synchronized Cipher getEncryptCipher(String dhSKAlgo, PrivateKey privateKey, PublicKey peerPublicKey) throws Exception { KeyAgreement ka = KeyAgreement.getInstance("DH"); ka.init(privateKey); ka.doPhase(peerPublicKey, 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); } return encrypt; } protected static Cipher getEncryptCipher(String dhSKAlgo, 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; } // this needs to synchronize as it uses private key of that member protected static synchronized Cipher getDecryptCipher(String dhSKAlgo, PrivateKey privateKey, PublicKey publicKey) throws Exception { KeyAgreement ka = KeyAgreement.getInstance("DH"); ka.init(privateKey); 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); } return decrypt; } protected static Cipher getDecryptCipher(String dhSKAlgo, byte[] secretBytes) throws Exception { Cipher decrypt = null; 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; } protected 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(); } } protected static PublicKey getPublicKey(byte[] publicKeyBytes) throws Exception { X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(publicKeyBytes); KeyFactory keyFact = KeyFactory.getInstance("DH"); // PublicKey pubKey = keyFact.generatePublic(x509KeySpec); return keyFact.generatePublic(x509KeySpec); } protected static void initEncryptCipher(KeyAgreement ka, List<PublicKey> publicKeys) throws Exception { Iterator<PublicKey> itr = publicKeys.iterator(); while (itr.hasNext()) { ka.doPhase(itr.next(), !itr.hasNext()); } } /*** * this will hold the common key for cluster */ protected class ClusterEncryptor { byte[] secretBytes; Cipher encrypt; Cipher decrypt; public ClusterEncryptor(GMSEncrypt other) throws Exception { GMSEncrypt mine = new GMSEncrypt(other.services); this.secretBytes = GMSEncrypt.generateSecret(mine.dhSKAlgo, mine.dhPrivateKey, other.dhPublicKey); } public ClusterEncryptor(byte[] sb) { this.secretBytes = sb; } public synchronized byte[] encryptBytes(byte[] data) throws Exception { String algo = dhSKAlgo; return GMSEncrypt.encryptBytes(data, getEncryptCipher(algo)); } private Cipher getEncryptCipher(String dhSKAlgo) throws Exception { try { if (encrypt == null) { synchronized (this) { if (encrypt == null) { encrypt = GMSEncrypt.getEncryptCipher(dhSKAlgo, secretBytes); } } } } catch (Exception ex) { throw ex; } return encrypt; } public synchronized byte[] decryptBytes(byte[] data) throws Exception { String algo = dhSKAlgo; Cipher c = getDecryptCipher(algo); return GMSEncrypt.decryptBytes(data, c); } private Cipher getDecryptCipher(String dhSKAlgo) throws Exception { if (decrypt == null) { synchronized (this) { if (decrypt == null) { decrypt = GMSEncrypt.getDecryptCipher(dhSKAlgo, secretBytes); } } } return decrypt; } } }