I'm using a RC4 stream cipher to encrypt/decrypt data sent through a Java UDP client-server program. My code works as intended on the host program and displays the correct plaintext and ciphertext, but when the data is sent over to the client via UDP, the ciphertext in particular get a super long ciphertext appended to it.
The client is still able to decrypt it properly, I just don't understand where the random appended ciphertext is coming from.
Host program:
public class Host {
private static final String ENCRYPTION_ALGORITHM = "ARCFOUR"; // or "RC4"
public static void main(String[] args) throws Exception {
//Use Java's built-in DatagramSocket and DatagramPacket to implement UDP
DatagramSocket socket = new DatagramSocket(1500); //Create a socket to listen at port 1500
byte[] buf = new byte[65535]; //Byte array to wrap
//Initialize P, G, H(PW), A variables
BigInteger P = null;
BigInteger G = null;
String hashedPW = null; //Hashed PW using SHA-1
BigInteger A = new BigInteger(10, new Random()); //Random number selected by Alice, maximum limit of 1000
BigInteger aliceKey = null;
//Read in Diffie-Hellman parameters from param.txt
File file = new File("param.txt");
Scanner scan = new Scanner(file);
//Loop through the entire file
while(scan.hasNextLine()) {
int counter = 0;
String delimiter = ","; //Content in the file is separated using a comma
String[] tokenValue = scan.nextLine().split(delimiter);
for (String token : tokenValue) {
switch (counter++) {
case 0: //Diffie-Hellman's P
P = new BigInteger(token);
break;
case 1: //Diffie-Hellman's G
G = new BigInteger(token);
break;
case 2: //Password hashed using SHA-1
hashedPW = token;
break;
}
}
}
scan.close(); //Close scanner to prevent memory leaks
aliceKey = G.modPow(A, P); //Calculate g^a mod p to obtain Alice's public key
byte[] passwordBytes = hashedPW.getBytes();
//Setting up RC4 stream cipher
SecretKey secretKey = new SecretKeySpec(passwordBytes, 0 , passwordBytes.length, "RC4"); //Generate secret key using common password
Cipher rc4 = Cipher.getInstance(ENCRYPTION_ALGORITHM);
System.out.println("Parameters successfully read. Listening on port 1500...");
//While-loop to keep host running until terminated
while (true) {
DatagramPacket message = new DatagramPacket(buf, buf.length); //Create a packet to receive message
socket.receive(message); //Receive the message from Bob
//If Alice receives a packet with the message "Bob"
if(new String(message.getData(),0,message.getLength()).equals("Bob")) {
System.out.println("Bob has sent a connection request.");
String msgToBob = hashedPW + "," + P + "," + G + "," + aliceKey; //H(PW), P, G, G^A mod P
message.setData(encrypt(msgToBob, secretKey, rc4)); //Encrypt data using RC4
System.out.println("Ciphertext sent: " + new String(message.getData(),0,message.getLength()));
System.out.println("Decrypted text: " + decrypt(secretKey, rc4, message.getData()));
socket.send(message);
}
}
}
Client program:
public class Client {
private static final String ENCRYPTION_ALGORITHM = "ARCFOUR"; // or "RC4"
public static void main(String[] args) throws Exception {
//Use Java's built-in DatagramSocket and DatagramPacket to implement UDP
DatagramSocket socket = new DatagramSocket(); //Create socket object to send data
byte[] buffer = new byte[65535];
socket.setSoTimeout(5000); //Throw an exception if no data received within 5000ms
//Create scanner to grab user input
Scanner input = new Scanner(System.in);
System.out.print("Please enter the common password: ");
String commonPW = input.nextLine(); //Storing the password
commonPW = encryptThisString(commonPW); //Hash using SHA-1
byte[] passwordBytes = commonPW.getBytes(); //Convert password to byte-array
//Setting up RC4 stream cipher
SecretKey secretKey = new SecretKeySpec(passwordBytes, 0 , passwordBytes.length, "RC4"); //Generate secret key using common password
Cipher rc4 = Cipher.getInstance(ENCRYPTION_ALGORITHM);
System.out.print("Enter a message to send to Alice: ");
String bobInput = input.nextLine();
//Create packet containing message
DatagramPacket message = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("localhost"), 1500);
message.setData(bobInput.getBytes());
socket.send(message); //Send message to host
//Create separate packet to receive response (so that its length is big enough to accept)
DatagramPacket response = new DatagramPacket(buffer, buffer.length);
socket.receive(response);
System.out.println("Received from Alice: " + new String(response.getData(),0,response.getLength()));
System.out.println("Decrypted text: " + decrypt(secretKey, rc4, response.getData()));
socket.close();
}
And here's the same methods in both programs used to encrypt/decrypt data:
private static byte[] encrypt(String plaintext, SecretKey secretKey, Cipher rc4) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
rc4.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] plaintextBytes = plaintext.getBytes();
byte[] ciphertextBytes = rc4.doFinal(plaintextBytes);
//System.out.println("RC4 ciphertext base64 encoded: " + Base64.encodeBase64String(ciphertextBytes));
return ciphertextBytes;
}
private static String decrypt(SecretKey secretKey, Cipher rc4, byte[] ciphertextBytes) throws InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
rc4.init(Cipher.DECRYPT_MODE, secretKey, rc4.getParameters());
byte[] byteDecryptedText = rc4.doFinal(ciphertextBytes);
String plaintextBack = new String(byteDecryptedText);
return plaintextBack;
}
The console output for host:
Parameters successfully read. Listening on port 1500...
Bob has sent a connection request.
q?Md|-A????±?h??♥?a????h¶^O eÆá<±?ÿ.♂/?9§}5?aaJj??fë???7óIp§T▬??ñk??lümµÅ⌂»??=L?rúk?§}x☻?.☻j:A·æu?8ë∟2äq*k∟♥?C?$??VZIù?!0|?á=I?X{♥²"‼°▲|c??2²ht??²☻P=?T?"?,9?ƒZ▼?X??M=d uöåä$M☼ñ?"♂♀ö«2&??SC?î?Å?|?#?B??⌂b9?♀??T?lƃî D^í?àêoO3{N3♦^L²¼?=ë??-?|?O{???B?♣ #Æ??½.ìV:ó?±]Ü4▲?|rA£jEGJé??♠?$K"'=«??D»m←ÿ4?J↕¥§y¶[£↨?U¬y-?ÿpëñù«←x→↨[²äueN???↨}☻zƒy??si??z?U[q??Q
?+?÷¡,7Ü?S{6?l???²??↨?%?+üá???
'½?J??«?¶?T.±#=Q!ó??>½ëY2?P☻a♂**?iLO???☺⌂♫?ñ=N?ö?k$4??äîóí←?si?↓ìÜg∟Z6↕?µáù½Rû▲b?9µ,???g?6ù?→nv?ûyµö?M¿U♥â?
e[?V?Zô$¿?¢ôq♥j????G??|?~0ÇGZ?z♥68&MoK??v·$<?âZ^??Ys??[g?? ½å\▬ß¿?·↔!N???.?'?AD?Vg?ën¢-6ô ???0,ö???}lä??Y?D?Xñ¬?º??²☺r¥ï??1?#pq?►p?▬??FMÜ-m>ßñè,?¡r??µ±#?C?¶£?¿→?oi O??♥óeòM£?N ë_?▲èv¡ï?Ü(?öW♥w²ù]q???e♠à♠íô?í^??QM?^?5éÑ??V(??? V?>è?à%?D-r∟Ü1??
N↑??·*?A?tTßæi?[??ú¬c¢ƒl??↓▲ÿie→?♠tL¡??'Ñe÷∟ ƒí?♠↑4 Ä?R;}?:'¡!n6◄▲
☻'??r?I???↨¬z?→XÄ??☼Ä{ñ:â⌂$ê{????àß?FRí???☻ª3?#9‼/¿?$/ï?{ç
öûu½Rz?▼?ô,??◄K[P∟►§PS??))?â?d▲í♥?9??Ü?¡? ««+?☼☺?<<°n¼?♂%óî↔úög
Decrypted text:
62a91401ef53536f5e4a282ca75ce2505b7f8dfe,133369106234410027239548844977121529173266794839390492022055432118963330212454946189658890281117740419231366102782626716256746218028068963594259491015958826219687167085932845323487318056081767660358673429760060452507829995471137991271021787608959982667244524929925606086596447565032707095625628293300812984091,15635507126291796032175381295261956049808038047523713881770866167494167302066120851790523759612392465402233123769308400911864440257625348397936936978922581645934310469147780992255584968935730234568508327975497520199946653504137607154138430285573487119797346741445139288140860073660395495603540644148828628224,96950246366083756365146382678616471348261490848687079335489625506702434220187716384031671233394087317377823909861019537627642960196032766557467991541000545780675424518198042877768900211198791646583260247888450303207379340549434158094990174523043323964083031376798844776809887666868545542060025549549427788304
Output for Client is similar except for the decrypted plaintext:
Please enter the common password: Popcorn
Enter a message to send to Alice: Bob
q?Md|-A????±?h??♥?a????h¶^O eÆá<±?ÿ.♂/?9§}5?aaJj??fë???7óIp§T▬??ñk??lümµÅ⌂»??=L?rúk?§}x☻?.☻j:A·æu?8ë∟2äq*k∟♥?C?$??VZIù?!0|?á=I?X{♥²"‼°▲|c??2²ht??²☻P=?T?"?,9?ƒZ▼?X??M=d uöåä$M☼ñ?"♂♀ö«2&??SC?î?Å?|?#?B??⌂b9?♀??T?lƃî D^í?àêoO3{N3♦^L²¼?=ë??-?|?O{???B?♣ #Æ??½.ìV:ó?±]Ü4▲?|rA£jEGJé??♠?$K"'=«??D»m←ÿ4?J↕¥§y¶[£↨?U¬y-?ÿpëñù«←x→↨[²äueN???↨}☻zƒy??si??z?U[q??Q
?+?÷¡,7Ü?S{6?l???²??↨?%?+üá???
'½?J??«?¶?T.±#=Q!ó??>½ëY2?P☻a♂**?iLO???☺⌂♫?ñ=N?ö?k$4??äîóí←?si?↓ìÜg∟Z6↕?µáù½Rû▲b?9µ,???g?6ù?→nv?ûyµö?M¿U♥â?
e[?V?Zô$¿?¢ôq♥j????G??|?~0ÇGZ?z♥68&MoK??v·$<?âZ^??Ys??[g?? ½å\▬ß¿?·↔!N???.?'?AD?Vg?ën¢-6ô ???0,ö???}lä??Y?D?Xñ¬?º??²☺r¥ï??1?#pq?►p?▬??FMÜ-m>ßñè,?¡r??µ±#?C?¶£?¿→?oi O??♥óeòM£?N ë_?▲èv¡ï?Ü(?öW♥w²ù]q???e♠à♠íô?í^??QM?^?5éÑ??V(??? V?>è?à%?D-r∟Ü1??
N↑??·*?A?tTßæi?[??ú¬c¢ƒl??↓▲ÿie→?♠tL¡??'Ñe÷∟ ƒí?♠↑4 Ä?R;}?:'¡!n6◄▲
☻'??r?I???↨¬z?→XÄ??☼Ä{ñ:â⌂$ê{????àß?FRí???☻ª3?#9‼/¿?$/ï?{ç
öûu½Rz?▼?ô,??◄K[P∟►§PS??))?â?d▲í♥?9??Ü?¡? ««+?☼☺?<<°n¼?♂%óî↔úög
Decrypted text:
62a91401ef53536f5e4a282ca75ce2505b7f8dfe,133369106234410027239548844977121529173266794839390492022055432118963330212454946189658890281117740419231366102782626716256746218028068963594259491015958826219687167085932845323487318056081767660358673429760060452507829995471137991271021787608959982667244524929925606086596447565032707095625628293300812984091,15635507126291796032175381295261956049808038047523713881770866167494167302066120851790523759612392465402233123769308400911864440257625348397936936978922581645934310469147780992255584968935730234568508327975497520199946653504137607154138430285573487119797346741445139288140860073660395495603540644148828628224,96950246366083756365146382678616471348261490848687079335489625506702434220187716384031671233394087317377823909861019537627642960196032766557467991541000545780675424518198042877768900211198791646583260247888450303207379340549434158094990174523043323964083031376798844776809887666868545542060025549549427788304?úmB½Ä????←ê???►\d⌂h♦4LM♣☼»??*Ñ?ì?çtº???ì?.◄☺????xK♥??Ä4öó↑↨
4?á☼mëQ«a
The actual output is much longer for the client program, I didn't copy the entire appended ciphertext. Not sure if it's relevant, but the console makes some Windows notification/error noise when it runs as well, although it doesn't actually give any compilation errors. I'm running my program using command prompt.
I don't think the issue lies with the decryption as I tested with some text on both the host and client programs and I managed to get the same decrypted plaintext just fine. Only when I send the data over UDP it causes this issue it seems.
The text infront of the original ciphertext such as "Ciphertext sent: " gets omitted in the output as well.
What am I missing?
I was trying to decrypt the entire packet buffer instead of that fraction of it that contains the actual message, which resulted in the large wall of text after the actual decrypted message.
Altering the decrypt function used to instead take in the appropriate packet size fixed the issue.
Related
I am currently creating a UDP program in Java to simulate a secure communication channel. Between the Client and Server, I want to encrypt data before it is sent over to the other side. The problem I am facing now is that there are a lot of weird characters appearing after the decrypted message(example below). Why is this happening and how can I get rid of these characters?
Server-side code:
String testMessage = "this is a test message";
// initialise the key for RC4 encryption
Key key = new SecretKeySpec("password".getBytes(StandardCharsets.UTF_8), "RC4");
System.out.println("key: " + key);
// initialise cipher for encryption
Cipher encryptCipher = Cipher.getInstance("RC4");
encryptCipher.init(Cipher.ENCRYPT_MODE, key);
// encrypt the message
byte[] encryptedMessage = encryptCipher.doFinal(testMessage.getBytes(StandardCharsets.UTF_8));
// array of bytes to store encrypted data to be sent
byte[] sendData = encryptedMessage;
// send message
// byte[] testMessageBytes = testMessage.getBytes(StandardCharsets.UTF_8);
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, inet, senderPort);
socket.send(sendPacket);
Client-side code:
// initialise the key for RC4 encryption
Key key = new SecretKeySpec("password".getBytes(StandardCharsets.UTF_8), "RC4");
System.out.println("key: " + key);
// Array of bytes to store data received in a packet
byte[] receiveData = new byte[1024];
// DatagramPacket to receive data
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
// receive data and print it to the console
socket.receive(receivePacket);
String receiveMessage = new String(receivePacket.getData(), StandardCharsets.UTF_8);
System.out.println("Message before decryption: " + receiveMessage);
// initialise cipher for decryption
Cipher decryptCipher = Cipher.getInstance("RC4");
decryptCipher.init(Cipher.DECRYPT_MODE, key);
// decrypt the received message
byte[] decryptedMessage = decryptCipher.doFinal(receivePacket.getData());
receiveMessage = new String(decryptedMessage, StandardCharsets.UTF_8);
System.out.println("Message after decryption: " + receiveMessage);
This is the result i am getting:
Message before decryption: ??Q}lW?q?↓A??♂?r?(?
Message after decryption: this is a test message&☺rL;?kj?W?♥?q???;g5z$'D??RC?e?gj??P?n[J?|J#K}#?y&??S??R♦?9??X?/m▬????♣J?M??]??|♫???AN
??P?(?♂{^M???q?K?▬s?T♥?Ø????l?R☼☻z ?♂V??}?49?P?????ZTG????/?/???J"#9z???♀??I??9??{??????K?z?♠?.♣?????mH →???↓☻???g?5??{?A1\-f*?♥n%??1 V?q=C►?§9♦U???☺??*?"R%?#♦??☺?g? 4???x??Pht$◄?F►??◄g◄l?P9???▲?>???IG?t???H??t↓??V?bC?:?▲?y#U)g↑g,t?f?U#?r?♠??[7%?e??XR↑??a??Vv??►_?u???#B?☻??FU?¶???????y▲?$??x=44?}??/?cn?↨??+{???↕?6?:?¶?B?]?)=+8d↨?p\?}3?q∟?▬?k-??o????}?W?M?Z◄¦¿?h??L?9?N?\♥V??☻^~?e?W3J?↑?^↔▲m%?>??P?H♫tCm☼*v☺?}??U']T?a 2?↑?↕???CN???fL?#C???dUCZ_↓?)]???▬DZc?⌂x?⌂??????#? ??wHt????b ?#??(→?KMZ[wx?6???b?Y."c??☻?Q?☻é☻??c?-.??☼V9???☺6???ep~?q?↨&???§?? />??c???♥??C36???►K???↨?.??Lr
I am very new to socket programming and cryptography, so pardon any inefficient code. I do know that RC4 is deprecated and should not be used anymore, but this is just for educational purposes. Please let me know if there is any information that I am lacking for this post. I appreciate any and all help.
You need to ensure that you're only dealing with the portion of the packet that contains the message as opposed to its entire buffer, so:
byte[] decryptedMessage = decryptCipher.doFinal(receivePacket.getData(), 0, receivePacket.getLength());
I am creating a client/server program to perform encryption with 256 bit AES. I am deriving my key from ECDH. I send the size of the byte arrays I am using to represent my various keys and strings. The issue I am having is that when I try to send the size of my encrypted string from my client to my server, my server says that I sent a much larger size than I actually did. Sending sizes worked for all other byte arrays that needed to be sent. The size of the encrypted string sent from the client is 16 bytes. The server receives an integer size of 276032497 bytes. I have checked that I am in fact sending 16 bytes from the client end.
Any idea of what the issue could be?
Server code:
//generate public key for server
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(256);
KeyPair kp = kpg.generateKeyPair();
byte[] ourPk = kp.getPublic().getEncoded();
String format = kp.getPublic().getFormat();
int ourPkLength = ourPk.length;
int arrSize;
//send client our pk
out.writeInt(ourPkLength);
out.write(ourPk);
System.out.println("sent PK!");
//receive pk from client
arrSize = fromClient.readInt();
byte[] otherPk = new byte[arrSize];
fromClient.read(otherPk);
System.out.println("recived client PK!");
KeyFactory kf = KeyFactory.getInstance("EC");
X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(otherPk);
PublicKey otherPublicKey = kf.generatePublic(pkSpec);
//Perform key agreement
KeyAgreement ka = KeyAgreement.getInstance("ECDH");
ka.init(kp.getPrivate());
ka.doPhase(otherPublicKey, true);
// Send shared secret
byte[] sharedSecret = ka.generateSecret();
// Derive a key from the shared secret and both public keys
MessageDigest hash = MessageDigest.getInstance("SHA-256");
hash.update(sharedSecret);
// Simple deterministic ordering
List<ByteBuffer> keys = Arrays.asList(ByteBuffer.wrap(ourPk), ByteBuffer.wrap(otherPk));
Collections.sort(keys);
hash.update(keys.get(0));
hash.update(keys.get(1));
byte[] derivedKey = hash.digest();
System.out.println("derived key: " + derivedKey + " length: " + derivedKey.length);
//Convert byte [] to secret key
//Define cipher
SecretKeySpec symmetricKey = new SecretKeySpec(derivedKey, 0, 32, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, symmetricKey, new IvParameterSpec(new byte[16]));
//receive encrypted message from client and try to decrypt.
arrSize = fromClient.readInt();
System.out.println("array size sent: " + arrSize);
byte[] decryptArr = new byte[arrSize];
fromClient.read(decryptArr);
System.out.println("Recieved encrypted string: " + decryptArr + " length: " + decryptArr.length);
String decryptStr = Base64.getEncoder().encodeToString(cipher.doFinal(decryptArr));
System.out.println("Decrypted String: " + decryptStr);
Client code:
//generate public key for client
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(256);
KeyPair kp = kpg.generateKeyPair();
byte[] ourPk = kp.getPublic().getEncoded();
//String format = kp.getPublic().getFormat();
int ourPkLength = ourPk.length;
int arrSize;
//Receive generated public key from the Server
arrSize = fromServ.readInt();
byte[] otherPk = new byte[arrSize];
fromServ.read(otherPk);
System.out.println("recived server PK!");
//Send the server our public key
out.writeInt(ourPkLength);
out.write(ourPk);
System.out.println("sent PK!");
KeyFactory kf = KeyFactory.getInstance("EC");
X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(otherPk);
PublicKey otherPublicKey = kf.generatePublic(pkSpec);
//Perform key agreement
KeyAgreement ka = KeyAgreement.getInstance("ECDH");
ka.init(kp.getPrivate());
ka.doPhase(otherPublicKey, true);
// Generate a shared secret
byte[] sharedSecret = ka.generateSecret();
// Derive a key from the shared secret and both public keys
MessageDigest hash = MessageDigest.getInstance("SHA-256");
hash.update(sharedSecret);
// Simple deterministic ordering
List<ByteBuffer> keys = Arrays.asList(ByteBuffer.wrap(ourPk), ByteBuffer.wrap(otherPk));
Collections.sort(keys);
hash.update(keys.get(0));
hash.update(keys.get(1));
byte[] derivedKey = hash.digest();
System.out.println("derived key: " + derivedKey + " length: " + derivedKey.length);
//Convert the derivedkey from a byte array to a Secret key Spec of type AES
SecretKeySpec secretKey = new SecretKeySpec(derivedKey, 0, 32, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(new byte[16]));
String plainText = "Testing!";
byte[] cipherText = cipher.doFinal(plainText.getBytes());
System.out.println("Encrypted str: " + cipherText + " length: "+ cipherText.length);
//Send encrypted string to Server
int len = cipherText.length;
System.out.println("length: " + len);
out.write(len);
out.write(cipherText);
System.out.println("Sent encrypted string!");
In your client, you wrote: out.write(len). Your code snippet does not explain what out is, but I'm guessing that .write(someIntValue) is a pass-through from java.io.OutputStream which has that method (.write(int)). The thing is, this writes a single byte, lopping off all bits in your int except the bottom 8.
The matching call in your server code is:
arrSize = fromClient.readInt();
this is not a call that InputStream has (I'm guessing you have some class that extends InputStream and adds these), presumably, what readInt does, is read 4 bytes, and reconstitutes them into a single java int by assuming Big Endian ordering.
So, you send 1 byte from the client and then the string, but the server will read 4 bytes for the length: That 1 byte (the actual length sent by client), plus the first 3 of your string, and tries to interpret that as a length, causing wildly different numbers.
The fact that 276032497 is a number, that, if put in bytes via big endian ordering, starts with a byte with value 16, which is exactly the length you sent, is strongly suggestive that this is your problem.
The fix seems rather trivial; turn out.write(len) into out.writeInt(len). And if you're going to be doing byte-level protocol like this, you need a better testing and debugging plan than ¯\_(ツ)_/¯ I guess I'll ask StackOverflow. This is probably why most folks use different solutions that handrolling raw byte protocols (so, investigate ProtoBuf and friends). At the very least, make the actual pipe a pluggable concept, so that you can plug in a dummy pipe that doesn't encrypt anything, so you can eyeball the bytes flying over the wire to find issues like this; it is unlikely this will be the last time you mismatch your server and client's code.
I'm a newbie in Java, I'm trying to sent a byte [] array via socket, but it itself convert my array to string and then sends it. so far it's ok, but the problem is that I need to either receive the message in array type, OR I have to convert the string to array, in order to make the decrypt method be able to decrypt the array (the input of decrypt method must be byte array). how can I do that?
my serverside related code is:
private void IssuingTickets() throws Exception{
String socketUsername = reader.readLine();//rcv username
String socketPassword = reader.readLine();//rcv password
writer.println("Lemme Check!");
Properties prop = new Properties();
prop.load(new FileInputStream("input"));
String fileUsername = prop.getProperty("username");
String filePassword = null;
if (prop.getProperty("password") != null) {
filePassword = prop.getProperty("password");}
if (socketPassword.equals(filePassword)){
String sessionKeyBobKdc = new Scanner(new File("sBOBandKDC")).useDelimiter("\\Z").next();
byte[] ClientTicket = encrypt(sessionKeyBobKdc, filePassword);
System.out.println("clietn ticket = " + ClientTicket+" ArraytoString " + Arrays.toString(ClientTicket));
writer.println(ClientTicket);
String KDCkey = new Scanner(new File("KDCkey")).useDelimiter("\\Z").next();
String UnEncTGT= sessionKeyBobKdc.concat(socketUsername);
byte[] TGT = encrypt(UnEncTGT, KDCkey);
writer.println(TGT);
}else
{writer.println("Please try again later!");}
}
public static byte[] encrypt(String plainText1, String encryptionKey) throws Exception {
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(plainText1.getBytes("UTF-8"));
}
public static String decrypt(byte[] cipherText, String encryptionKey) throws Exception{
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 new String(cipher.doFinal(cipherText),"UTF-8");
}
}
the Clientside related code is:
private void recieveTickts() throws IOException, InterruptedException, Exception{
String msg = reader.readLine(); //Lemme check
System.out.println("Server :" + msg);
TimeUnit.SECONDS.sleep(3);
String Sth = reader.readLine(); //please try again
if(Sth.equals("Please try again later!")){
System.out.println(Sth);
System.exit(1);
}else{
ClientTicket = Sth;
String TGT = reader.readLine();
System.out.println("Encrypted key between Client and KDC is " + ClientTicket + " and the TGT is " + TGT);
TimeUnit.SECONDS.sleep(3);
System.out.println("SUCCESSFUL"); }
byte[] b = ClientTicket.getBytes();
System.out.println("b= " + b);
String sessionKeyBobKdc = decrypt(b, Password);
System.out.println(Password + "session key is "+ sessionKeyBobKdc);
}
thanx in advanced guys..
I documented the code to help you out but that is basically a quick and dirty way to never stop listening for data and then also return the received as byte[]. If you don't need to listen at all times just modify this code to stop listening once you received your message
You can make this more dynamic with using parameters for defining the sign that a message is finished etc.
I used a StringBuilder to act as a little buffer. And defined that a message is finished once I receive a LF. Everything else get's put into the "buffer". Additionally I filter out CR since I don't want them in my message.
Socket client;
StringBuilder sb = new StringBuilder();
String result = "";
int c;
try
{
InputStream inputStream = client.getInputStream();
while ( ( c = inputStream.read() ) >= 0 ) // Loop to listen for data until the connection is dead
{
if ( c == 0x0a ) // 0x0a is the hex for a LineFeed ( LF )
{
result = sb.toString(); // put everything you received so far as a final message
sb.delete( 0, sb.length() ); // clear your message "buffer"
}
else if ( c != 0x0d /* <CR> */ ) // Not necessary but it helps keeping the message clear
{
sb.append( (char) c ); // add the received integer as char to the message "buffer"
}
if ( !result.isEmpty() ) // Catch that a message is received
{
log.fine( "received message: " + result ); // just log for tracing
return result.getBytes(StandardCharsets.UTF_8); // return the byte[] of the message with the needed Charset.
}
}
}
catch ( Exception e )
{
log.warning( e.getMessage() );
}
I am trying to implement a basic Diffie-Hellman protocol and the code succeeds up to the point when it needs to decrypt the sent value using DES. I have looked at a lot of examples in which it was a matter of the keys not matching up, but I am printing their values on both ends of the connection and they are both exactly the same. I have also tried multiple padding schemes as well as changing how the keys were generated.
My last attempt was in adding the parameter IvParameterSpec to the cipher init, but that only solved one of the errors.
I am running this on a single machine where the socket is connecting over the localhost and I have been checking for any issues on either side with the sent data not exactly matching the received data, but nothing is altered in the sending. I did notice, however, that when printing each of the byte arrays on either side of the socket that the client side is much longer than the server with what appears to be padding(?)
The error I am getting is saying that the final block is padded incorrectly and so decryption fails
My Server code (the side that is not working as intended):
public static void main(String[] args) {
ServerSocket welcomeSocket = null;
// Creates a connectable socket on port 6789
try {
welcomeSocket = new ServerSocket(6789);
} catch (IOException e) {
e.printStackTrace();
}
while(true){
try{
double k2, B, A;
double n = 13;
double g = 61;
long y = 7;
B = (Math.pow(g, y))%n;
System.out.println("Accepting connections");
// Accept an incoming connection on the socket server
Socket connectionSocket = welcomeSocket.accept();
// Creates a read and write stream for that client
DataInputStream inFromClient = new DataInputStream(connectionSocket.getInputStream());
DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
// Sends the double to the client
outToClient.writeDouble(B);
System.out.println("Sent " + B);
// Reads the number sent by the Client
A = inFromClient.readDouble();
System.out.println("Received " + A);
// Modifies the number
k2 = (Math.pow(A, y))%n;
System.out.println("DES key seed = " + k2);
byte[] deskeydata = toByteArray(k2);
// Turns the bytes of the modified number into a DES key spec
DESKeySpec deskeyspec = new DESKeySpec(deskeydata);
// Makes a secret key (DES)
SecretKeyFactory keyF = SecretKeyFactory.getInstance("DES");
SecretKey keystuff = keyF.generateSecret(deskeyspec);
System.out.println(keystuff.toString());
// Gets an incoming string from the client and turns it into binary
byte[] incomingBytes = new byte[128];
try{
inFromClient.readFully(incomingBytes);
} catch(EOFException eof){
System.out.println("Finished reading");
}
System.out.println(new String(incomingBytes));
Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");
// Decrypts the string using the shared secret key
c.init(Cipher.DECRYPT_MODE, keystuff, new IvParameterSpec(new byte[8]));
byte[] ct2 = c.doFinal(incomingBytes);
// Decode it from base 64
//byte[] decodedBytes = Base64.getDecoder().decode(ct2);
// Prints the received string
System.out.println("Received: " + new String(ct2));
inFromClient.close();
outToClient.close();
} catch(Exception e){
e.printStackTrace();
}
}
}
My Client code:
public static void main(String[] args) {
// Creates a socket to the local host on port 6789
Socket clientSocket = null;
try {
clientSocket = new Socket("localhost", 6789);
} catch (IOException e1) {
e1.printStackTrace();
}
try{
double k1, B, A;
double n = 13;
double g = 61;
long x = 3;
// Sends an unencrypted number to the server
A = (Math.pow(g, x))%n;
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
DataInputStream inFromServer = new DataInputStream(clientSocket.getInputStream());
// Transforms A into a byte array and sends it over
outToServer.writeDouble(A);
outToServer.flush();
System.out.println("Sending " + A);
// Reads the incoming data from the server
B = inFromServer.readDouble();
System.out.println("Recieved " + B);
// Modifies the data to create the number for des key
k1 = (Math.pow(B, x))%n;
System.out.println("DES key seed = " + k1);
byte[] deskeydata = toByteArray(k1);
// Turns the bytes of the modified number into a DES key spec
DESKeySpec deskeyspec = new DESKeySpec(deskeydata);
// Makes a secret key (DES)
SecretKeyFactory keyF = SecretKeyFactory.getInstance("DES");
SecretKey keystuff = keyF.generateSecret(deskeyspec);
System.out.println(keystuff.toString());
// Takes in input from the user and turns it into binary
BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Enter a message:");
String sentence = inFromUser.readLine();
byte[] str2 = sentence.getBytes();
byte[] encodedMessage = Base64.getEncoder().encode(str2);
Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");
// Encrypts the user's input with the secret key
c.init(Cipher.ENCRYPT_MODE, keystuff, new IvParameterSpec(new byte[8]));
byte[] ct2 = c.doFinal(encodedMessage);
System.out.println("Initted the cipher and moving forward with " + new String(ct2));
// Writes the encrypted message to the user
outToServer.write(ct2);
outToServer.flush();
inFromServer.close();
outToServer.close();
} catch(Exception e){
e.printStackTrace();
}
}
Anything that could help me in getting this working would be immensely welcome since I have been working on this error alone for quite some time.
I managed to figure out a way to solve this (though I suspect it is very inefficient). The issue was due to the readFully method on the server side. I was reading the answer into a 128 byte array and the decrypt function saw the empty slots in the byte array as something instead of nothing.
To solve this I replaced the input section with the following, which reads each individual byte and creates an array of bytes with the exact length of the incoming message.
ArrayList<Byte> totalBytes = new ArrayList<Byte>();
while(true){
try{
byte in = inFromClient.readByte();
totalBytes.add(in);
} catch(EOFException eof){
System.out.println("Finished reading");
break;
}
}
int incomingSize = totalBytes.size();
byte[] receivedBytes = new byte[incomingSize];
for(int i = 0; i < incomingSize; i++){
receivedBytes[i] = totalBytes.get(i);
}
So I have been working with the Bouncycastle libraries in an attempt to connect with a remote server. This process has been problematic from the get go and now I'm close to getting everything working but some odd things are happening.
When I first started building out the encryption process I was told to use AES 256 with PKCS7Padding. After some nagging I was provided with a c++ example of the server code. It turned out that the IV is 256 bit so I had to use the RijndaelEngine instead. Also in order for this to work correctly I have to use ZeroBytePadding.
Here is my code:
socket = new Socket(remoteIP, port);
outputStream = new PrintWriter(socket.getOutputStream());
inputStream = new BufferedReader(new InputStreamReader(socket.getInputStream()));
byte[] base_64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".getBytes("UTF-8");
Security.addProvider(new BouncyCastleProvider());
public String AESEncrypt(String out) throws IOException, DataLengthException, IllegalStateException, InvalidCipherTextException {
byte[] EncKey = key;
byte randKey;
Random randNumber = new Random();
randKey = base_64[randNumber.nextInt(base_64.length)];
EncKey[randKey&0x1f] = randKey;
RijndaelEngine rijndaelEngine = new RijndaelEngine(256);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(rijndaelEngine), new ZeroBytePadding());
ParametersWithIV keyParameter = new ParametersWithIV(new KeyParameter(EncKey), iv);
cipher.init(true, keyParameter);
byte[] txt = out.getBytes();
byte[] encoded = new byte[cipher.getOutputSize(txt.length)];
int len = cipher.processBytes(txt, 0, txt.length, encoded, 0);
cipher.doFinal(encoded, len);
char keyChar = (char) randKey;
String encString = new String(Base64.encode(encoded));
encString = encString.substring(0, encString.length()-1) + randKey;
return encString;
}
public void AESDecrypt(String in) throws DataLengthException, IllegalStateException, IOException, InvalidCipherTextException {
byte[] decKey = key;
byte[] msg = in.getBytes();
byte randKey = msg[msg.length-1];
decKey[randKey&0x1f] = randKey;
byte[] trimMsg = new byte[msg.length-1];
System.arraycopy(msg, 0, trimMsg, 0, trimMsg.length);
in = new String(trimMsg);
RijndaelEngine rijndaelEngine = new RijndaelEngine(256);
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(rijndaelEngine), new ZeroBytePadding());
ParametersWithIV keyParameter = new ParametersWithIV(new KeyParameter(decKey), iv);
cipher.init(false, keyParameter);
byte[] encoded = Base64.decode(in.trim());
byte[] decoded = new byte[cipher.getOutputSize(encoded.length)];
int len = cipher.processBytes(encoded, 0, encoded.length, decoded, 0);
cipher.doFinal(decoded, len);
String decString = new String(decoded);
}
Here is a test function I am using to send and receive messages:
public void serverTest() throws DataLengthException, IllegalStateException, InvalidCipherTextException, IOException {
//out = AESEncrypt(out);
outputStream.write(out + "\n");
outputStream.flush();
String msg = "";
while ((msg = inputStream.readLine()) != null) {
AESDecrypt(msg);
}
}
The key and iv don't change with the exception of the last byte in the key. If I am encrypting I get a random base64 char and change the last byte to that. If its decryption I get the last byte from the message and set the last value of the key to it for decryption.
In the c++ example there was an unencrypted message and two encrypted messages. I could deal with those fine.
Here is the problem, when I send my message to the remote server "encrypted" the app waits for a response until the connection times out but never gets one. If I send the message unencrypted I get either 7 responses which I can successfully decrypt and finally
org.bouncycastle.util.encoders.DecoderException: unable to decode base64 string:
String index out of range: -4 at org.bouncycastle.util.encoders.Base64.decode(Unknown Source)
or my last line before the error will look like this:
?"??n?i???el????s???!_S=??ah????CR??l6??]?{?l??Y?????Gn???+?????9!'??gU&4>??{X????G?.$c=??0?5??GP???_Q5????8??Z\?~???<Kr?????[2\ ???a$?C??z%?W???{?.?????eR?j????~?B"$??"z??W;???<?Yu??Y*???Z?K?e!?????f?;O(?Zw0B??g<???????????,)?L>???A"?????<?????W??#\???f%??j ?EhY/?? ?5R?34r???#?1??I??????M
If I set the encryption/decryption to use PKCS7Padding I get no response when my message is encrypted still but with decryption from the server I get between 2 to 6 responses and then
org.bouncycastle.crypto.InvalidCipherTextException: pad block corrupted
I am at a loss with this. I don't know what I might be doing wrong so I have come here. I'm hoping the so community can point out my errors and guide me in the right direction.
I have a bit of an update I found my error in the encryption. I wasn't placing the random base64 value at the end of the encrypted string correctly so now I am doing like this.
encString += (char)randKey;
I can get response from the server now. Now the problem is I will some times get one or two readable lines but the rest are all garbage. I asked the individuals who run the server about it and they said in some c# code that they reference the have
return UTF8Encoding.UTF8.GetString(resultArray);
and thats all I have to go off of. I have tried UTF-8 encoding any place where I do getBytes or new String, and I have tried making the BurrferReader stream UTF-8 but it's still garbage.
Have you seedn the BCgit? this has bouncycastle code and examples. I am using the Csharp version in this repository. https://github.com/bcgit/bc-java
All crypto primitive examples are stored here: https://github.com/bcgit/bc-java/tree/master/core/src/test/java/org/bouncycastle/crypto/test
Try this code for testing Aes-CBC
private void testNullCBC()
throws InvalidCipherTextException
{
BufferedBlockCipher b = new BufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
KeyParameter kp = new KeyParameter(Hex.decode("5F060D3716B345C253F6749ABAC10917"));
b.init(true, new ParametersWithIV(kp, new byte[16]));
byte[] out = new byte[b.getOutputSize(tData.length)];
int len = b.processBytes(tData, 0, tData.length, out, 0);
len += b.doFinal(out, len);
if (!areEqual(outCBC1, out))
{
fail("no match on first nullCBC check");
}
b.init(true, new ParametersWithIV(null, Hex.decode("000102030405060708090a0b0c0d0e0f")));
len = b.processBytes(tData, 0, tData.length, out, 0);
len += b.doFinal(out, len);
if (!areEqual(outCBC2, out))
{
fail("no match on second nullCBC check");
}
}