What i am making is a chat program that encrypts/decrypts messages send. So far i've established the basic all-to-all communication with clients and have put a server-to-client simple encryption with AES, basically i have given them the same key. Now i want to establish a key exchange algorithm that will allow every client to have its own key. I have read multiple algorithms, at first i wanted to establish a Diffie-Hellman but i found some RSA examples that made more sence. This is what i have:
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
Key aesKey = keyGenerator.generateKey();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
byte[] aesKeyBytes = aesKey.getEncoded();
System.out.println("1. aesKeyBytes= "+ bytesToHex(aesKeyBytes));
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] cipherText = cipher.doFinal(aesKeyBytes);
System.out.println("2. cipherText= "+bytesToHex(cipherText));
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
byte[] decryptedKeyBytes = cipher.doFinal(cipherText);
System.out.println("3. decryptedKeyBytes= "+bytesToHex(decryptedKeyBytes));
//use symmetric with the decrypted key
SecretKey newAesKey = new SecretKeySpec(decryptedKeyBytes, "AES");
Basically from the point this code ends i just use the SecretKey to initialize my AES cipher and go on from there. Now my question is how to distribute the keys from the RSA through sockets without losing bytes or whatever so that all clients can have unique keypairs with the server. Also is there a way to not use a keygenerator and give my own keys like Strings, since i plan on using setters so that the user can change his/her key on whish, otherwise what's the point in using keyexchange if all the clients end up having the same keys in the first place? And one last thing purely on curiosity, is it possible to modify this RSA to DH, and how are they different code-wise?
Lastly the "bytesToHex" method is as follows and was provided to me by my teacher and after tested it works fine so there are no problems there:
public static String bytesToHex(byte[] data)
{
if (data==null)
return null;
else
{
int len = data.length;
String str = "";
for (int i=0; i<len; i++)
{
if ((data[i]&0xFF)<16){
str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF);
}
else{
str = str + java.lang.Integer.toHexString(data[i]&0xFF);
}
}
return str.toUpperCase();
}
}
I know there might be answers like "look at this" and "this is a good example", but trust me i've looked at all of them i just got more confused. Just to be clear i don't want to use Files to store my keys or anything like that, i want simply the client and server to send each other their public keys so that along with their private key to create a secretKey, i know that this is basically DH but honestly i am so fed up right now that i'll take what i can get so anything you have to say is much appreciated.
Related
Background: I have two devices which communicate via IP/port connection establishing live voice encrypting communication thanks to Diffie-Hellman key-exchange and encrypting it thanks to AES algorithm. Now some of the code is written and some just taken to use as an example of the prototype implementation.
Problem: Now even when understanding how my classes work just like the title states: I can not figure out how to take the key from DH class and declare in AES class that this is the key it must use to encrypt.
P.s. Advice on code optimization, better practices and general tips are most welcome, please.
Thank you for your time.
public class DH extends Thread {
int bitLength=512;
int certainty=20;//
private static final SecureRandom rnd = new SecureRandom();
public DH() throws Exception{
Random randomGenerator = new Random();
BigInteger generatorValue,primeValue,publicA,publicB,secretA,secretB,sharedKeyA,sharedKeyB;
primeValue = findPrime();// BigInteger.valueOf((long)g);
System.out.println("the prime is "+primeValue);
generatorValue = findPrimeRoot(primeValue);//BigInteger.valueOf((long)p);
System.out.println("the generator of the prime is "+generatorValue);
// on machine 1
secretA = new BigInteger(bitLength-2,randomGenerator);
// on machine 2
secretB = new BigInteger(bitLength-2,randomGenerator);
// to be published:
publicA=generatorValue.modPow(secretA, primeValue);
publicB=generatorValue.modPow(secretB, primeValue);
sharedKeyA = publicB.modPow(secretA,primeValue);// should always be same as:
sharedKeyB = publicA.modPow(secretB,primeValue);
String getAValue=sharedKeyA.toString();
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(getAValue.getBytes());
byte byteData[] = md.digest();
StringBuffer sb = new StringBuffer();
for(int i=0;i<byteData.length;i++)
{
sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1));// ??
}
String getHexValue = sb.toString();
System.out.println("hex format in SHA-256 is "+getHexValue);
byte [] initkey = getAValue.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-256");
initkey = sha.digest(initkey);
initkey = Arrays.copyOf(initkey, 16);
SecretKeySpec secretKeySpec = new SecretKeySpec(initkey,"AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
}
As you can see i have coded AES key and IV statically but want for the generated AES key in DH to be assigned in this class
public class AES {
static String IV = "AAAAAAAAAAAAAAAA";
static String initkey = "13B_0(wcXNGkHAR[";
public static byte[] encrypt(byte[] plainData, int offset, int length) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");//CBC
SecretKeySpec key = new SecretKeySpec(initkey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(plainData, offset, length);
}
public static byte[] decrypt(byte[] cipherSound, int offset, int length) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");//CBC
SecretKeySpec key = new SecretKeySpec(initkey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(cipherSound, offset, length);
}
}
figure out how to take the key from DH class and declare in AES class that this is the key it must use to encrypt
Please check this tutorial
Then you can use the returned secret:
byte[] masterKey = aKeyAgree.generateSecret();
// maybe hash the master key too
SecretKeySpec key = new SecretKeySpec(masterKey, 0, 16, "AES");
However, if you have two way communication, you either need random IV or different keys for each direction (derived from the master)
Additional hint :
I have two devices which communicate via IP/port connection establishing live voice encrypting communication
Maybe your devices are powerful enough to establish proper TLS, what would solve many things for you
I have to implement basic encryption in my program. I can use Base64 it was rejected by the client. So I am using the following methods. The problem which I am facing is the there are special characters in the encrypted which are resulting in exceptions. Can I change this code to somehow encrypt into plain text without special characters.
protected static byte[] encrypt(String text)
{
try
{
String key = "6589745268754125";
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
// encrypt the text
cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encrypted = cipher.doFinal(text.getBytes());
return encrypted;
}
catch(Exception ex)
{
WriteLog("Encryption Failed");
WriteLog(ex.getMessage());
return null;
}
}
protected static String decrypt(byte[] pass)
{
try
{
String key = "6589745268754125";
// Create key and cipher
Key aesKey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
// decrypt the text
cipher.init(Cipher.DECRYPT_MODE, aesKey);
String decrypted = new String(cipher.doFinal(pass));
return decrypted;
}
catch(Exception ex)
{
WriteLog("Encryption Failed");
WriteLog(ex.getMessage());
return null;
}
}
The exception message says "Given final block not properly padded"
javax.crypto.BadPaddingException: Given final block not properly padded
so, basically you don't know about encryption and have the problem that your client wants encryption
ok, a quick headsup:
encoding: transforming an input to an output that holds identical information but in another representation ... ex: 1,2,3 -> a,b,c
as you can see the output looks differently but holds the same information
please note that no secret information is necessary to encode/decode
encryption: might look similar at first glance but here you need some secrets ... an encryption takes 2 inputs ... a secret and the input data
the resulting output can be decrypted, but ONLY if you have the corresponding secret
if your client wants you to encrypt something, make sure that thing can be represented as bytes ... encrypting a string... not good... encrypting a string that has been transformed into < insert arbitrary byte encoding here, for example unicode > ... ok
encryptions usually handle bytes (let's not care about historic ciphers here)
when you decide for an encryption/cipher you have to know that there are essentially 2 distinct groups: symetric and asymetric
symetric: the same key (read secret) you use to encrypt will be needed for decryption
asymetric: there are keypairs consisting of a public and a private part (public/private key) the public part is used for encryption, the private part is used for decryption ... makes no sense unless you have different parties that need to exchange keys
asymetric ciphers are usually used to encrypt decrypt the keys for symetric ciphers because they are SLOW while symetric ciphers usually are FAST
asymetric ciphers are not intended to encrypt large amounts of data
symetric ciphers are intended for bulk data
if your goal is just to keep an information encrypted while it is laying around on a harddisk, a symetric cipher is what you want
you will need a key for the cipher to operate ... and... you will have the problem where to store it ... so if you can, have the user enter a sufficiently complex password ... use the password and a function called PBKDF2 with a sufficiently high iteration count (sufficiently high= increase this number until the process takes either a few seconds if you only need this on startup, or until your users start complaining about the delay) to make binary key from the password.
use this key for AES in GCM mode (symetric cipher)
the cipher will want something called IV or initialization vector ...
the iv is no secret, you may prepend this thing to your ciphertext as clear text information
the iv needs to be the size of one block of your cipher, so in the case of AES 128 bit = 16 byte
so your IV when encrypting is a 16 byte (unique) random number (means that you may not use an IV two times or more: persist the used IVs and when getting a new one, check if it was already stored, if yes startover IV generation, if no, store it and then use it)
when decrypting, read the prepended cleartext IV from your file (first 16 byte)
if you just want to store the ciphertext on disk, write it into a binary file
if the file has to contain only printable text apply an encoding like base16/32/64 before writing your bytes to the file and decode into a byte array before decrypting (unless your data is too big for that, then you will have to find/write a stream wrapper that will add/strip encoding for you)
If the client doesn't like Base64, then try Base32 or Base16 (= hex). They are less common but well defined alternatives to Base64.
You might also find out exactly why the client doesn't want you to use Base64.
You should Base64 the encrypted content. It's usual technique by the way.
I guess the client's problem wasn't Base64 format itself but the fact, that Base64 isn't (a strong) encryption.
The problem was padding. I had use AES/CBC/NoPadding and make sure that my strings are multiple of 16 bytes. So in addition to changing the ecryption and decryption I had to add two methods. One to add \0 i.e. implicit null terminators to the end end of the text to make it a multiple of 16 and another to remove them after decryption. So the final version is like this.
public class crypto {
static String IV = "AAAAAAAAAAAAAAAA";
static String plaintext = "my non padded text";
static String encryptionKey = "0123456789abcdef";
public static void main(String[] args)
{
byte[] cipher = encrypt(plaintext);
String decrypted = decrypt(cipher);
}
protected static String covertto16Byte(String plainText)
{
while(plainText.length()%16 != 0)
plainText += "\0";
return plainText;
}
protected static String removePadding(String plainText)
{
return plainText.replace("\0","");
}
protected static byte[] encrypt(String plainText)
{
try
{
String _plaintText_16 = covertto16Byte(plainText);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(_plaintText_16.getBytes("UTF-8"));
} catch (Exception ex)
{
//catch mechanism
return null;
}
}
protected static String decrypt(byte[] cipherText)
{
try
{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return removePadding(new String(cipher.doFinal(cipherText), "UTF-8"));
} catch (Exception ex)
{
//catch mechanism
return null;
}
}
}
I am aware of a question very similar to this (How do I encrypt in Python and decrypt in Java?) but I have a different problem.
My problem is, I am not able to decrypt in Java correctly. Despite using the correct key and IV, I still get garbage characters after decryption. I don't have any compile/run-time errors or exceptions in Java so I believe I am using the right parameters for decryption.
Python Encryption Code -
from Crypto.Cipher import AES
import base64
key = '0123456789012345'
iv = 'RandomInitVector'
raw = 'samplePlainText'
cipher = AES.new(key,AES.MODE_CFB,iv)
encrypted = base64.b64encode(iv + cipher.encrypt(raw))
Java Decryption Code -
private static String KEY = "0123456789012345";
public static String decrypt(String encrypted_encoded_string) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
String plain_text = "";
try{
byte[] encrypted_decoded_bytes = Base64.getDecoder().decode(encrypted_encoded_string);
String encrypted_decoded_string = new String(encrypted_decoded_bytes);
String iv_string = encrypted_decoded_string.substring(0,16); //IV is retrieved correctly.
IvParameterSpec iv = new IvParameterSpec(iv_string.getBytes());
SecretKeySpec skeySpec = new SecretKeySpec(KEY.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
plain_text = new String(cipher.doFinal(encrypted_decoded_bytes));//Returns garbage characters
return plain_text;
} catch (Exception e) {
System.err.println("Caught Exception: " + e.getMessage());
}
return plain_text;
}
Is there anything obvious that I am missing?
The Cipher Feedback (CFB) mode of operation is a family of modes. It is parametrized by the segment size (or register size). PyCrypto has a default segment size of 8 bit and Java (actually OpenJDK) has a default segment size the same as the block size (128 bit for AES).
If you want CFB-128 in pycrypto, you can use AES.new(key, AES.MODE_CFB, iv, segment_size=128). If you want CFB-8 in Java, you can use Cipher.getInstance("AES/CFB8/NoPadding");.
Now that we have that out the way, you have other problems:
Always specify the character set you're using, because it can change between different JVMs: new String(someBytes, "UTF-8") and someString.getBytes("UTF-8"). When you do, be consistent.
Never use a String to store binary data (new String(encrypted_decoded_bytes);). You can copy the bytes directly: IvParameterSpec iv = new IvParameterSpec(Arrays.copyOf(encrypted_decoded_bytes, 16)); and cipher.doFinal(Arrays.copyOfRange(encrypted_decoded_bytes, 16, encrypted_decoded_bytes.length)).
In Java, you're assuming that the IV is written in front of the ciphertext and then encoded together, but in Python, you're never doing anything with the IV. I guess you posted incomplete code.
It is crucial for CFB mode to use a different IV every time if the key stays the same. If you don't change the IV for every encryption, you will create a multi-time pad which enables an attacker to deduce the plaintext even without knowing the key.
Can we encrypt large files with ECC or it is like RSA works for small only? can anyone please recommend a good website for ECC Java implementation.
Thanks
In general you are required to perform hybrid encryption with ECC. ECIES for instance is basically a key agreement followed by symmetric encryption. So you cannot directly encrypt anything with ECIES, which is the most common ECC method for encryption. Basically you should couple it to a symmetric cipher. This is actually the best scheme for RSA encryption as well, most of the time.
As you can see you can use this directly as a Cipher using CBC mode & PKCS#7 padding, but beware of the large header (117 bytes for a 384 curve, no less). This is required to perform the key derivation. Make sure that the public key is properly validated (I'm not sure about the Bouncy Castle code in this regards, haven't taken a look at it).
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator ecKeyGen = KeyPairGenerator.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
ecKeyGen.initialize(new ECGenParameterSpec("brainpoolP384r1"));
// doesn't work, which means we are dancing on the leading edge :)
// KeyPairGenerator ecKeyGen = KeyPairGenerator.getInstance("EC");
// ecKeyGen.initialize(new ECGenParameterSpec("secp384r1"));
KeyPair ecKeyPair = ecKeyGen.generateKeyPair();
System.out.println("What is slow?");
Cipher iesCipher = Cipher.getInstance("ECIESwithAES");
iesCipher.init(Cipher.ENCRYPT_MODE, ecKeyPair.getPublic());
byte[] ciphertext = iesCipher.doFinal(com.google.common.base.Strings.repeat("owlstead", 1000).getBytes());
iesCipher.init(Cipher.DECRYPT_MODE, ecKeyPair.getPrivate());
byte[] plaintext = iesCipher.doFinal(ciphertext);
System.out.println(Hex.toHexString(ciphertext));
System.out.println(new String(plaintext));
}
public static void main(String[] args) throws Exception
{
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator ecKeyGen = KeyPairGenerator.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
ecKeyGen.initialize(new ECGenParameterSpec("secp256r1"));
KeyPair ecKeyPair = ecKeyGen.generateKeyPair();
System.out.println("What is slow?");
Cipher iesCipher = Cipher.getInstance("ECIESwithAES-CBC");
Cipher iesDecipher = Cipher.getInstance("ECIESwithAES-CBC");
iesCipher.init(Cipher.ENCRYPT_MODE, ecKeyPair.getPublic());
String message = "Hello World";
byte[] ciphertext = iesCipher.doFinal(message.getBytes());
System.out.println(Hex.toHexString(ciphertext));
iesDecipher.init(Cipher.DECRYPT_MODE, ecKeyPair.getPrivate(), iesCipher.getParameters());
byte[] plaintext = iesDecipher.doFinal(ciphertext);
System.out.println(new String(plaintext));
}
I've googled and I've experimented, and I'm having no luck.
I'm writing a program that talks between android, windows/linux java and a raspberry pi and I want to encrypt something on one side and decrypt on the other, in all directions, as in, I want it to work in each possible platform case.
Blowfish, and DES eventually give me the dreaded "Given final block not properly padded"
on one platform or another and AES goes so slow on the raspberry as to be useless.
I've tried various secretkeyfactorys and keygenerators, and everything my master-cut-and-paste skills allowed for, and nothing works.
Very frustrating, I'm considering using ROT13. At least I know that works.
I know somebody's going to ask for a code sample so here it is... it's the same exact code everybody else shows examples of.
public static SecretKey generatedessecretkey(String password) throws InvalidKeyException, UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeySpecException
{
DESKeySpec keySpec = new DESKeySpec(password.getBytes("UTF8"));
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(keySpec);
return key;
}
public static void encrypt(IOLogger log, byte[] datablock, String grouppw, ArrayList<byte[]> resp)
{
try
{
SecretKey ks = generatedessecretkey(grouppw);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, ks);
byte[] b = cipher.doFinal(datablock);
resp.clear();
resp.add(b);
return;
}
catch (Exception e)
{
}
}
edit: here's the decrypt, it's the same thing backwards
public static void desdecrypt(IOLogger log, byte[] datablock, String grouppw, ArrayList<byte[]> resp)
{
try
{
SecretKey ks = generatedessecretkey(grouppw);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, ks);
byte[] b = cipher.doFinal(datablock);
resp.clear();
resp.add(b);
return;
}
catch (Exception e)
{
}
}
the question I really want to ask is what encryption scheme is supported on most platforms out of the box
That question is answered in full by the JCA Specification and the list of standard algorithms given here.
It seems to me that you just haven't specified the chaining and padding modes correctly, or at all.