public class IOTransport {

    private final Socket socket;
    private InputStream is;
    private OutputStream os;
    private byte[] toSendLenBytes;

    public IOTransport(Socket socket) throws SocketException, IOException {
        this.socket = socket;
        //  this.socket.setSoTimeout(Properties.timeout);
        set_outpuStreams();
    }

    private void set_outpuStreams() throws IOException {
        is = socket.getInputStream();
        os = socket.getOutputStream();
    }

    public void SendMessage(String toSend) throws IOException {
        byte[] toSendBytes = toSend.getBytes();
        int toSendLen = toSendBytes.length;
        toSendLenBytes = new byte[4];
        CreateLenBytes(toSendLen);
        os.write(toSendLenBytes);
        os.write(toSendBytes);
    }

    public String receiveMessage() throws IOException {
        byte[] lenBytes = new byte[4];
        is.read(lenBytes, 0, 4);
        int len = ReadteLenBytes(lenBytes);
        byte[] receivedBytes = new byte[len];
        is.read(receivedBytes, 0, len);
        String received = new String(receivedBytes, 0, len);
        return received;
    }

    private void CreateLenBytes(int toSendLen) {
        toSendLenBytes[0]
                = (byte) (toSendLen & 0xff);
        toSendLenBytes[1]
                = (byte) ((toSendLen >> 8) & 0xff);
        toSendLenBytes[2]
                = (byte) ((toSendLen >> 16) & 0xff);
        toSendLenBytes[3]
                = (byte) ((toSendLen >> 24) & 0xff);
    }

    private int ReadteLenBytes(byte[] lenBytes) {
        int len;
        len = (((lenBytes[3] & 0xff) << 24)
                | ((lenBytes[2] & 0xff) << 16)
                | ((lenBytes[1] & 0xff) << 8)
                | (lenBytes[0] & 0xff));

        return len;
    }

    public void close() throws IOException {
        is.close();
        os.close();
        socket.close();
    }

} public class RandomGenerator {

    private BigInteger PrivatePrimeNumber;

    public void setPrivatePrimeNumber(BigInteger PrivatePrimeNumber) {
        this.PrivatePrimeNumber = PrivatePrimeNumber;
    }

    public BigInteger getPrivatePrimeNumber() {
        return PrivatePrimeNumber;
    }

    public RandomGenerator() {
        PrimeNumberGenerator();
    }
    private static final byte skip1024ModulusBytes[]
            = {
                (byte) 0xF4, (byte) 0x88, (byte) 0xFD, (byte) 0x58,
                (byte) 0x4E, (byte) 0x49, (byte) 0xDB, (byte) 0xCD,
                (byte) 0x20, (byte) 0xB4, (byte) 0x9D, (byte) 0xE4,
                (byte) 0x91, (byte) 0x07, (byte) 0x36, (byte) 0x6B,
                (byte) 0x33, (byte) 0x6C, (byte) 0x38, (byte) 0x0D,
                (byte) 0x45, (byte) 0x1D, (byte) 0x0F, (byte) 0x7C,
                (byte) 0x88, (byte) 0xB3, (byte) 0x1C, (byte) 0x7C,
                (byte) 0x5B, (byte) 0x2D, (byte) 0x8E, (byte) 0xF6,
                (byte) 0xF3, (byte) 0xC9, (byte) 0x23, (byte) 0xC0,
                (byte) 0x43, (byte) 0xF0, (byte) 0xA5, (byte) 0x5B,
                (byte) 0x18, (byte) 0x8D, (byte) 0x8E, (byte) 0xBB,
                (byte) 0x55, (byte) 0x8C, (byte) 0xB8, (byte) 0x5D,
                (byte) 0x38, (byte) 0xD3, (byte) 0x34, (byte) 0xFD,
                (byte) 0x7C, (byte) 0x17, (byte) 0x57, (byte) 0x43,
                (byte) 0xA3, (byte) 0x1D, (byte) 0x18, (byte) 0x6C,
                (byte) 0xDE, (byte) 0x33, (byte) 0x21, (byte) 0x2C,
                (byte) 0xB5, (byte) 0x2A, (byte) 0xFF, (byte) 0x3C,
                (byte) 0xE1, (byte) 0xB1, (byte) 0x29, (byte) 0x40,
                (byte) 0x18, (byte) 0x11, (byte) 0x8D, (byte) 0x7C,
                (byte) 0x84, (byte) 0xA7, (byte) 0x0A, (byte) 0x72,
                (byte) 0xD6, (byte) 0x86, (byte) 0xC4, (byte) 0x03,
                (byte) 0x19, (byte) 0xC8, (byte) 0x07, (byte) 0x29,
                (byte) 0x7A, (byte) 0xCA, (byte) 0x95, (byte) 0x0C,
                (byte) 0xD9, (byte) 0x96, (byte) 0x9F, (byte) 0xAB,
                (byte) 0xD0, (byte) 0x0A, (byte) 0x50, (byte) 0x9B,
                (byte) 0x02, (byte) 0x46, (byte) 0xD3, (byte) 0x08,
                (byte) 0x3D, (byte) 0x66, (byte) 0xA4, (byte) 0x5D,
                (byte) 0x41, (byte) 0x9F, (byte) 0x9C, (byte) 0x7C,
                (byte) 0xBD, (byte) 0x89, (byte) 0x4B, (byte) 0x22,
                (byte) 0x19, (byte) 0x26, (byte) 0xBA, (byte) 0xAB,
                (byte) 0xA2, (byte) 0x5E, (byte) 0xC3, (byte) 0x55,
                (byte) 0xE9, (byte) 0x2F, (byte) 0x78, (byte) 0xC7,};

    private void PrimeNumberGenerator() {

        BigInteger randomServerPrimeNumber;

        Random r = new Random();
        byte[] random = new byte[16];

        BigInteger skip1024Modulus = new BigInteger(1, skip1024ModulusBytes);
        r.nextBytes(random);
        randomServerPrimeNumber = BigInteger.probablePrime(256, r);

        PrivatePrimeNumber = randomServerPrimeNumber;
    }

    public BigInteger GeneratePublicPrimeNumber() {
        return new BigInteger(Properties.exponent)
                .modPow(PrivatePrimeNumber,
                        new BigInteger(Properties.modulus));
    }

    //calculate diffie-helman g^(randomPrimeNumber) mod p
    public BigInteger SessionGenerator(String ClientResult) {

        //calculate diffie-helman g^(randomPrimeNumber) mod p
        BigInteger exponent = new BigInteger(ClientResult);
        return exponent.modPow(PrivatePrimeNumber, new BigInteger(Properties.modulus));
    }

} public final class JSonObject {

    public String EncryptedSymetricClientKey;
    public String RSAPublicKey;

    public String ClientEncryptedPrimeNumber;
    public String ServerPrimeNumber;

    public String EncryptedMessage;
    public String fingerPrint;
    public String HmacHash;

    public JSonObject() {
        Intialize();
    }

    public void setEncryptedSymetricClientKey(String EncryptedSymetricClientKey) {
        this.EncryptedSymetricClientKey = EncryptedSymetricClientKey;
    }

    public String getEncryptedSymetricClientKey() {
        return EncryptedSymetricClientKey;
    }

    public void setServerPrimeNumber(String ServerPrimeNumber) {
        this.ServerPrimeNumber = ServerPrimeNumber;
    }

    public void setClientEncryptedPrimeNumber(String ClientEncryptedPrimeNumber) {
        this.ClientEncryptedPrimeNumber = ClientEncryptedPrimeNumber;
    }

    public void setClientKey(String EncryptedSymetricClientKey) {
        this.EncryptedSymetricClientKey = EncryptedSymetricClientKey;
    }

    public String getServerPrimeNumber() {
        return ServerPrimeNumber;
    }

    public String getClientEncryptedPrimeNumber() {
        return ClientEncryptedPrimeNumber;
    }

    public void setMessage(String Message) {
        this.EncryptedSymetricClientKey = Message;
    }

    public String getMessage() {
        return EncryptedSymetricClientKey;
    }

    public void setRSAPublicKey(String RSAPublicKey) {
        this.RSAPublicKey = RSAPublicKey;
    }

    public String getRSAPublicKey() {
        return RSAPublicKey;
    }

    public void setEncryptedMessage(String EncryptedMessage) {
        this.EncryptedMessage = EncryptedMessage;
    }

    public String getEncryptedMessage() {
        return EncryptedMessage;
    }

    public String getFingerPrint() {
        return fingerPrint;
    }

    public void setFingerPrint(String fingerPrint) {
        this.fingerPrint = fingerPrint;
    }

    public void setHmacHash(String HmacHash) {
        this.HmacHash = HmacHash;
    }

    public String getHmacHash() {
        return HmacHash;
    }

    public void Intialize() {
        this.EncryptedSymetricClientKey = "";
        this.ClientEncryptedPrimeNumber = "";
        this.ServerPrimeNumber = "";
        this.RSAPublicKey = "";
        this.EncryptedMessage = "";
        this.fingerPrint = "";
    }
} public class Properties {

    //socket properties
    public static final int ConnectionPort = 5555;
    public static final int MaxConnections = 100;
    public static final int timeout = 10 * 1000;

    public static final String END_PROTOCOL = "EndSession";
    //Put your message which you want taken from Server
   public static final String PlainText_UTF8 = "Hello Client Send me again Enrypted Message";
    
    //encryption properties  
   

    //for more info check https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange
    //g^x mod p 
    //However, its very unlikely that anyone else listening on the channel 
    //can calculate the key, since the calculation of discrete logarithms under 
    //field arithmetic is very hard (see Galois Fields)
    //Prime numbers machine generator 
    public static final String exponent = "95632573769194905177488615436919317766582673020891665265323677789504596581977";
    public static final String modulus = "81554351438297688582888558141846154981885664956959015742153749206820791432251";

    public static final String AES_PROVIDER = "AES";
    public static final String AES_ALGORITHM = "AES/CBC/PKCS7Padding";

    //Rsa needed
    public static final String RSA_ALGORITHM = "RSA";
    public static final String RSA_CRYPTO_ALGORITHM = "RSA/ECB/PKCS1Padding";
    public static final String RSA_Provider = "BC";

    //store_keys
    public static final String PUBLIC_KEY_FILE = "Public.key";
    public static final String PRIVATE_KEY_FILE = "Private.key";

    //Encodes values
    public static final String CHAR_ENCODING = "UTF-8";
    public static final String HashingAlgorithm = "md5";

    //keysizes-length
    public static final int KeySizeLength = 16;
    public static final int RSA_KEYSIZE = 1024;
    
     //HmacAlgProvider hash Function
     public static final String HmacAlgProv="HmacSHA256";
    //Rsa Integrity Signature
     public static final String Signature="SHA256withRSA";
} public class AES_Encryption {

    public AES_Encryption() {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    }

    public static String encrypt(String plainText, String key) throws Exception {

        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

        byte[] keyBytes = Diggest.Diggest(key, Properties.HashingAlgorithm);
        // Use the first 16 bytes (or even less if key is shorter)
        byte[] keyBytes16 = new byte[Properties.KeySizeLength];
        System.arraycopy(keyBytes, 0, keyBytes16, 0, Math.min(keyBytes.length, Properties.KeySizeLength));

        // convert plain text to bytes
        byte[] plainBytes = plainText.getBytes(Properties.CHAR_ENCODING);

        SecretKeySpec skeySpec = new SecretKeySpec(keyBytes16, Properties.AES_PROVIDER);
        Cipher cipher = Cipher.getInstance(Properties.AES_ALGORITHM);
        byte[] iv = new byte[16]; // initialization vector with all 0 just like openssll
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(iv));

        // encrypt
        byte[] encrypted = cipher.doFinal(plainBytes);
        String encryptedString = new String(Base64.encodeBase64(encrypted));

        return encryptedString;
    }

    public static String Decrypt(String EncryptedText, String key) throws Exception {

        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

        byte[] keyBytes = Diggest.Diggest(key, Properties.HashingAlgorithm);
        // Use the first 16 bytes (or even less if key is shorter)
        byte[] keyBytes16 = new byte[Properties.KeySizeLength];

        System.arraycopy(keyBytes, 0, keyBytes16, 0, Math.min(keyBytes.length, Properties.KeySizeLength));

        // convert plain text to bytes
        byte[] plainBytes = Base64.decodeBase64(EncryptedText.getBytes(Properties.CHAR_ENCODING));
        SecretKeySpec skeySpec = new SecretKeySpec(keyBytes16, Properties.AES_PROVIDER);
        Cipher cipher = Cipher.getInstance(Properties.AES_ALGORITHM);
        byte[] iv = new byte[16]; // initialization vector with all 0
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(iv));

        byte[] decrypteed = cipher.doFinal(plainBytes);

        return new String(decrypteed, Properties.CHAR_ENCODING);
    }
} public class ClientKey extends Keystore {

    private String ClientKey;

    public ClientKey() {
        super(Type.ClientKey);
        System.out.println(this.toString() + " created!");
    }

    public ClientKey(String ClientKey) {
        super(Type.ClientKey);
        this.ClientKey = ClientKey;
        System.out.println(this.toString() + " created!");
    }

    public String getClientKey() {
        return ClientKey;
    }

    public void setClientKey(String ClientKey) {
        this.ClientKey = ClientKey;
    }

    @Override
    public String toString() {
        return super.toString(); //To change body of generated methods, choose Tools | Templates.
    }

} public class HMacAlgoProvider {

    public static String HmacSha256Sign(String Encrypted, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance(Properties.HmacAlgProv);
        byte[] keybytes = key.getBytes(Properties.CHAR_ENCODING);
        SecretKeySpec secret_key = new SecretKeySpec(keybytes, Properties.HmacAlgProv);
        sha256_HMAC.init(secret_key);

        byte[] EncryptedBytes = Encrypted.getBytes(Properties.CHAR_ENCODING);
        String hash = new String(Base64.encodeBase64(sha256_HMAC.doFinal(EncryptedBytes)));
        return hash;
    }

    public static boolean HmacSha256Verify(String Encrypted, String key, String HmacMsg) throws Exception {

        String ServerHmacSign = HmacSha256Sign(Encrypted, key);
        if (HmacMsg.equals(ServerHmacSign)) {
            System.out.println("Integrity verified successfully");
            return true;
        } else {
            System.out.println("Integrity Of Message can not be verified");
            return false;
        }

    }
} public class IntegrityException extends Exception {

    public IntegrityException() {
        super();
    }

    public IntegrityException(String message) {
        super(message);
    }

    public IntegrityException(String message, Throwable cause) {
        super(message, cause);
    }

    public IntegrityException(Throwable cause) {
        super(cause);
    }
} public class SessionKey extends Keystore {

    private String SessionKey;

    public SessionKey() {
        super(Type.SessionKey);
        System.out.println(this.toString() + " created!");
    }

    public SessionKey(String SessionKey) {
        super(Type.SessionKey);
        this.SessionKey = SessionKey;
        System.out.println(this.toString() + " created!");
    }

    public void setSessionKey(String SessionKey) {
        this.SessionKey = SessionKey;
    }

    public String getSessionKey() {
        return SessionKey;
    }

    @Override
    public String toString() {
        return super.toString(); //To change body of generated methods, choose Tools | Templates.
    }

} public class Fingerprint {

    public static String siganture(String encrypted) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException, NoSuchProviderException, IOException {

        Signature mySign = Signature.getInstance(Properties.Signature);

        mySign.initSign(IdentityKeyPair.readPrivateKeyFromFile(Properties.PRIVATE_KEY_FILE));
        mySign.update(encrypted.getBytes(Properties.CHAR_ENCODING));

        byte[] byteSignedData = mySign.sign();

        String encryptedString = new String(Base64.encodeBase64(byteSignedData));
        return encryptedString;
    }
} public class IdentityKeyPair {

    static void SaveKeys(String fileName, BigInteger mod, BigInteger exp) throws IOException {
        // File f = new File(fileName); 
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;

        try {
            //System.out.println("Generating " + fileName + "...");
            fos = new FileOutputStream(fileName);
            oos = new ObjectOutputStream(new BufferedOutputStream(fos));

            oos.writeObject(mod);
            oos.writeObject(exp);

            // System.out.println(fileName + " generated successfully");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (oos != null) {
                oos.close();

                if (fos != null) {
                    fos.close();
                }
            }
        }
    }

    public static PublicKey readPublicKeyFromFile(String fileName) throws IOException {
        FileInputStream fis = null;
        ObjectInputStream ois = null;
        try {
            fis = new FileInputStream(new File(fileName));
            ois = new ObjectInputStream(fis);

            BigInteger modulus = (BigInteger) ois.readObject();
            BigInteger exponent = (BigInteger) ois.readObject();

            //Get Public Key
            RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(modulus, exponent);
            KeyFactory fact = KeyFactory.getInstance(Properties.RSA_ALGORITHM);
            PublicKey publicKey = fact.generatePublic(rsaPublicKeySpec);

            return publicKey;

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ois != null) {
                ois.close();
                if (fis != null) {
                    fis.close();
                }
            }
        }
        return null;
    }

    public static PrivateKey readPrivateKeyFromFile(String fileName) throws IOException {
        FileInputStream fis = null;
        ObjectInputStream ois = null;
        try {
            fis = new FileInputStream(new File(fileName));
            ois = new ObjectInputStream(fis);

            BigInteger modulus = (BigInteger) ois.readObject();
            BigInteger exponent = (BigInteger) ois.readObject();

            //Get Private Key
            RSAPrivateKeySpec rsaPrivateKeySpec = new RSAPrivateKeySpec(modulus, exponent);
            KeyFactory fact = KeyFactory.getInstance(Properties.RSA_ALGORITHM);
            PrivateKey privateKey = fact.generatePrivate(rsaPrivateKeySpec);

            return privateKey;

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ois != null) {
                ois.close();
                if (fis != null) {
                    fis.close();
                }
            }
        }
        return null;
    }

    public static String PublicKeyString() throws IOException {
        byte[] encoded = readPublicKeyFromFile(Properties.PUBLIC_KEY_FILE).getEncoded();
        SubjectPublicKeyInfo subjectPublicKeyInfo = new SubjectPublicKeyInfo(ASN1Sequence.getInstance(encoded));
        byte[] otherEncoded = Base64.encodeBase64(subjectPublicKeyInfo.getPublicKey().getEncoded());
        String publikey = new String(otherEncoded);
        return publikey;
    }

    public static boolean Key_Files() {

        File privateKey = new File(Properties.PRIVATE_KEY_FILE);
        File publicKey = new File(Properties.PUBLIC_KEY_FILE);

        if (privateKey.exists() && publicKey.exists()) {
            return false;
        }
        return true;
    }
} public class RSA_Encryption {

    public static String RsaEecrypt(PublicKey pubKey, String plainText) throws Exception {
        byte[] plainBytes = plainText.getBytes(Properties.CHAR_ENCODING);
        Cipher cipher = Cipher.getInstance(Properties.RSA_CRYPTO_ALGORITHM, Properties.RSA_Provider);

        byte[] iv = new byte[16];
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);

        byte[] encrypted = cipher.doFinal(plainBytes);
        String encryptedString = new String(Base64.encodeBase64(encrypted));

        return encryptedString;
    }

    public static String RsaDecrypt(PrivateKey privateKey, String EncryptedText) throws Exception {
        byte[] plainBytes = Base64.decodeBase64(EncryptedText.getBytes(Properties.CHAR_ENCODING));
        byte[] iv = new byte[16]; // initialization vector with all 0

        Cipher cipher = Cipher.getInstance(Properties.RSA_CRYPTO_ALGORITHM, Properties.RSA_Provider);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);

        byte[] decrypteed = cipher.doFinal(plainBytes);
        String DecryptedString = new String(decrypteed, "UTF-8");

        return DecryptedString;
    }
}
import IOSocket.IOTransport;
import Properties.RandomGenerator;
import Properties.JSonObject;
import Properties.Properties;
import java.io.IOException;
import java.math.BigInteger;
import java.net.SocketException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.AES_Encryption.AES_Encryption;
import org.AES_Encryption.ClientKey;
import org.AES_Encryption.HMacAlgoProvider;
import org.AES_Encryption.IntegrityException;
import org.AES_Encryption.SessionKey;
import org.RSA_Encryption.Fingerprint;
import org.RSA_Encryption.IdentityKeyPair;
import org.RSA_Encryption.RSA_Encryption;
package Handshake;


public final class DHkeyExchange implements IOSynAck {

    private static IOMessageExhange Builder;
    private static RandomGenerator Genarator;

    private final IOTransport socketChanel;
    private final SessionKey Sessionkey;
    private ClientKey ClientKey;

    public DHkeyExchange(IOTransport socketChanel, SessionKey Sessionkey) throws SocketException {
        this.socketChanel = socketChanel;
        this.Sessionkey = Sessionkey;
        Inintialize();
    }

    //More readable is more efficient. Temporary expressions and local variables need the same space and 
    //from CPU/JVM perspective it doesn't make much difference. JVM will do a better job 
    //optimizing/inling it
    @Override
    public void SendPublicKey() {
        try {
            //send PublicKey to client
            JSonObject ObjToSend = new JSonObject();