I'm getting an error everytime I run this
"Error: Given final block not properly padded"
Basically I'm trying to brute force the last 3 bytes of the key, the first 13 bytes are correct.
Any idea what am I doing wrong? I tried removing the padding and it works but it couldn't find the plaintext that I'm sure it exists and contains the word "Mary had".
Note: I'm using sun.misc.BASE64Decoder
here's a part of my code.
String myiv = new String(new byte[] {
0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x31,0x30,0x31,0x31,0x31,0x32,0x33
});
char [] mykeyarray = new char[] {0x86,0xe5,0x30,0x90,0xff,0x62,0xa0,0x9a,0x81,0x00,0xad,0x9e,0x8f,0x00,0x00,0x00};
String encoded = "dm8cfvs+c7pKM+WR+fde8b06SB+lqWLS4sZW+PfQSKtTfgPknzYzpTVOtJP3JBoU2Uo/7XWopjoPDOlPr24duuck0z+vAx91bYTwQo4INnIIBkj/lhJMWmvAKaUIO3qzBoGg8ynQOhuG6LY7Wo0uww==";
IvParameterSpec ivspec = new IvParameterSpec(myiv.getBytes());
byte [] decoded;
FileWriter fstream = new FileWriter("out.txt");
BufferedWriter out = new BufferedWriter(fstream);
String mykey;
int repeat = 256;
outerloop:
for(int i=0;i<repeat;i++){
for(int j=0;j<repeat;j++){
for(int k=0;k<repeat;k++){
mykey = new String(mykeyarray);
SecretKeySpec keyspec = new SecretKeySpec(mykey.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
System.out.println("I: "+i+" J: "+j+" K: "+k);
decoded = new BASE64Decoder().decodeBuffer(encoded);
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte [] decrypted = cipher.doFinal(decoded);
String dec = new String(decrypted);
if(dec.contains("Mary")){
out.write(dec);
out.write("\n");
System.out.println(dec);
break outerloop;
}
mykeyarray[15]++;
}
mykeyarray[14]++;
mykeyarray[15]=0x00;
}
mykeyarray[13]++;
mykeyarray[14]=0x00;
mykeyarray[15]=0x00;
}
out.close();
}
catch(Exception e){
System.out.println("Error: " + e.getMessage());
}
}
}
Your code makes many many mistakes, and I don't know what you are trying to accomplish. So I'll explain why you may receive a BadPaddingException for a CBC cipher:
your key is incorrect
one or both of the last two blocks of ciphertext have been altered
one or more blocks have been removed from the end of the ciphertext
the IV is incorrect and the ciphertext consists of a single block
Good luck hunting down the cause of the exception.
Try to learn more about PKCS#5 padding. It's a special bytes beeing added to plain text before encryption. It can't be correct if the text was decrypted with a wrong key. If you brute-force a key, you will take this error on each key except correct one.
Since decrypting with a random key gives you a random message, you usually don't get correct padding. Just catch the exception and move on.
You will get padding errors approximately 93% of the time when brute forcing a PKCS5 padded message. PKCS5 padding pads out your message with bytes containing the length of the padding. So valid padding is 0x01, 0x2 0x02, 0x03 0x03 0x03, ...., 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF. The odds of correct padding happening in a random message are 1/16 + (1/16)^2 ... (1/16)^16 <.067. Which means you get incorrect padding about 1- %6.7 = 93% of the time.
Related
I'm trying to encrypt and decrypt plain text with AES("AES/CBC/PKCS7Padding") with Cipher.
Made secretKey by KeyGenerator and encrypt/decrypt with it.
This works well at first.
However, if plain text is a large string(such as 183244 characters), part of it is decrypted and not all of it.
"part of it" means
If plain text is "abcdefghijklmn", decrypted text comes like "abcdefgh"
(this is an example. actually this happens only with too long characters)
Code
// get key from Android kyestore
SecretKey secretKey = (SecretKey)mKeyStore.getKey(KEY_ALIAS, null);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
IvParameterSpec ivParams = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
//
// Encrypt
//
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher);
cipherOutputStream.write(plainString.getBytes("UTF-8"));
cipherOutputStream.close();
String encryptedString = Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT);
//
// Decrypt
//
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
decryptCipher.init(Cipher.DECRYPT_MODE, secretKey, ivParams);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.decode(encryptedString, Base64.DEFAULT));
CipherInputStream cipherInputStream = new CipherInputStream(byteArrayInputStream, decryptCipher);
outputStream = new ByteArrayOutputStream();
int b;
while ((b = cipherInputStream.read()) != -1) {
outputStream.write(b);
}
outputStream.close();
String decryptedString = outputStream.toString("UTF-8");
//
// results. decrypted string is part of source string and short.
//
int sourceStringLength = plainString.length(); // is 183244;
int decryptedStringLength = decryptedString.length()); // is UNEXPECTED 68553;
Could you please let know the point to fix this?
Edited
I've checked number of bytes on encryption/decryption.
encryption output bytes : "outputStream.toByteArray().length" is 68560
decryption input bytes : "Base64.decode(encryptedString, Base64.DEFAULT).length" is also 68560
I think this means decryption process doesn't lost any part of it and encryption process may truncate part of input plain text.
is this because of restriction of AES algorithm?
Solved
As #zaph commented, not all of the bytes is written to steam.
Replacing
cipherOutputStream.write(plainString.getBytes("UTF-8"));
cipherOutputStream.close();
with
// NOTE: workaround. too bit bytes doesn't writted correctly.
byte[] bytes = plainString.getBytes("UTF-8");
int oneBulkSize = 1024;// temp value for proof of concept. might be bigger one.
int numOfBulk = (bytes.length / oneBulkSize);
for (int i = 0; i < numOfBulk; i++) {
cipherOutputStream.write(bytes, oneBulkSize * i, oneBulkSize);
}
if ((bytes.length % oneBulkSize) != 0) {
cipherOutputStream.write(bytes, oneBulkSize * numOfBulk, bytes.length % oneBulkSize);
}
cipherOutputStream.close();
solved the issue.
AES does not have a data length limitation. The encrypted data should be the same length as the input plus up to an additional 16-bytes for padding.
Add some debugging code in the encryption loop and verify that the entire input is being read. Perhaps the encryption stream needs a loop as does the decryption stream.
Another possibility is that the 183244 is the byte size with UTF-16 encoding but you are reading as UTF-8, most of the UTF-16 characters can be as one UTF-8 byte.
The following is the actual solution code by the OP #nsht, included here for completeness (from a deleted answer by #nsht):
Replacing
cipherOutputStream.write(plainString.getBytes("UTF-8"));
cipherOutputStream.close();
with
// NOTE: workaround. too bit bytes doesn't writted correctly.
byte[] bytes = plainString.getBytes("UTF-8");
int oneBulkSize = 1024;// temp value for proof of concept. might be bigger one.
int numOfBulk = (bytes.length / oneBulkSize);
for (int i = 0; i < numOfBulk; i++) {
cipherOutputStream.write(bytes, oneBulkSize * i, oneBulkSize);
}
if ((bytes.length % oneBulkSize) != 0) {
cipherOutputStream.write(bytes, oneBulkSize * numOfBulk, bytes.length % oneBulkSize);
}
cipherOutputStream.close();
solved the issue.
I'm trying to encrypt a message in Java and decrypt it in Python. Unfortunately i'm just starting with python and am not able to get the decryption working.
That's my Java Code:
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
byte[] iv = sr.generateSeed(16);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
SecretKey aesKey = keygen.generateKey();
//save byte array in text file to recreate key later
byte[] encodedKey = aesKey.getEncoded();
new File("myPath\\AESKey.txt");
FileOutputStream fos = new FileOutputStream("myPath\\AESKey.txt");
//save AesKey in first 16 bytes and Initial Vector in next 16 bytes
fos.write(encodedKey);
fos.write(iv);
fos.close();
String secretText = "Hello cryptography";
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, aesKey, ivSpec);
byte[] encrypted = cipher.doFinal(secretText.getBytes());
BASE64Encoder myEncoder = new BASE64Encoder();
String encodedSecretText = myEncoder.encode(encrypted);
new File("myPath\\encodedSecretText.txt");
FileOutputStream fos2 = new FileOutputStream("myPath\\encodedSecretText.txt");
fos2.write(encodedSecretText.getBytes());
fos2.close();
I was able to decrypt the message with java, but not with python. I hope someone can show me how to do this.i copied the part with padding from another answer and assume that's the problem.
I get the message: ord() expected string of length 1, but int found.
Python:
from Crypto import Random
from Crypto.Cipher import AES
import base64
BS = 16
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-ord(s[-1])]
#read bytes of aesKey
file = open("myPath/AESKey.txt","rb")
aesKey = file.read(16)
iv = file.read(16)
file.close()
sec = open("myPath/encodedSecretText.txt")
for line in sec:
encodedSecretText = line.rstrip()
sec.close()
class AESCipher:
def __init__( self, key ):
self.key = key
def encrypt( self, raw ):
raw = pad(raw)
iv = Random.new().read( AES.block_size )
cipher = AES.new( self.key, AES.MODE_CBC, iv )
return base64.b64encode( iv + cipher.encrypt( raw ) )
def decrypt( self, enc ):
enc = base64.b64decode(enc)
cipher = AES.new(self.key, AES.MODE_CBC, iv )
return unpad(cipher.decrypt( enc[16:] ))
aes = AESCipher(aesKey)
print(aes.decrypt(encodedSecretText))
Thanks for any hint.
You're calling ord on an integer. Which is obviously illegal. The whole point of ord is that you give it a Unicode character, as a string, and it gives you back the numerical value of the code point.
So, why do you have a number? Well, I'm not sure what you expected to have, but let's look at what you actually have. If s[-1] is an integer, then s is some kind of sequence of integers. And s is the result of calling cipher.decrypt(). As the documentation for that function says, it returns a byte string. This isn't a specific type, just a description of a type—you can find out what the actual return value is with some basic debugging, maybe like this:
cipher = AES.new(self.key, AES.MODE_CBC, iv )
plaintext = cipher.decrypt(enc[16:])
print(type(plaintext), repr(plaintext))
return unpad(plaintext)
But I'm going to guess that it's a bytes object, which (quoting from http://docs.python.org/3/library/functions.html#bytes) …
is an immutable sequence of integers in the range 0 <= x < 256.
So, s[-1] is an integer in the range [0, 256). Hence the error.
So, what should you be doing instead? Well, why are you trying to call ord? You have a byte. Presumably what you want is a byte. So… just don't call anything there.
Meanwhile, there's at least one other serious error in your code:
for line in sec:
encodedSecretText = line.rstrip()
sec.close()
As pasted, this will raise an IndentationError. And if you indent both the second and third lines, you'll get an error for reading from a closed file. So presumably you want to indent just the second one. In which case, what you're doing is going through all of the lines, stripping the trailing whitespace off each, and then doing nothing with them. At the end of the loop, encodedSecretText holds the last line of encoded text, and all of the other lines are long forgotten and unrecoverable.
If you want to read all of the text into a list of lines, you will want something like this:
encodedSecretText = []
for line in sec:
encodedSecretText.append(line.rstrip())
Or, if you want to read it into one big string, with the newlines removed, you could do the above and then encodedSecretText = b''.join(encodedSecretText), or just do skip the whole loop and do encodedSecretText = sec.read().replace(b'\n', b'').
Just wondering why same plain text encrypted in Java and C doesn't generate same cipher text.
For DES algo I noticed
input text = "text", key = "test"
C cipher text len = 24
Java generates cipher text len = 8
For 256Bit AES I noticed similar difference
C cipher text len = 32
Java generates cipher text len = 16
here is my "C" code
char* enc(const char* text, const char* keyStr)
{
EVP_CIPHER_CTX ctx;
unsigned char key[32] = {0};
unsigned char iv[16] = {0};
unsigned char in[16] = {0};
unsigned char out[32]; /* at least one block longer than in[] */
memset(out, 0, 32);
int outlen1, outlen2;
memcpy(in, text, strlen(text));
memcpy(key, keyStr, strlen(keyStr));
EVP_EncryptInit(&ctx, EVP_aes_256_cbc(), key, iv);
EVP_EncryptUpdate(&ctx, out, &outlen1, in, sizeof(in));
EVP_EncryptFinal(&ctx, out + outlen1, &outlen2);
char* ret = (char*)malloc(outlen1 + outlen2+1);
memset(ret, 0, outlen1 + outlen2+1);
memcpy(ret, out, outlen1 + outlen2);
EVP_CIPHER_CTX_cleanup(&ctx);
return ret;
}
Here is the "Java" code
public static byte[] enc(byte[] input, byte[] keyStr){
byte[] output = null;
try {
byte[] newKey = getByteArrays(keyStr, 0, 32);
SecretKey secretKey = new SecretKeySpec(newKey, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
//Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
String ivStr = "";
byte[] ivKey = getByteArrays(ivStr.getBytes("UTF-8"), 0, 16);
IvParameterSpec ips = new IvParameterSpec(ivKey);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ips, null);
output = cipher.doFinal(input);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return output;
}
I have list of entries encrypted using C , I want to decrypt those entries in Java.
UPDATED
following update helped to get same length , both in 'C' and 'JAVA'
EVP_EncryptUpdate(&ctx, out, &outlen1, in, strlen(text));
No Just wondering why ciphertext returned by "C" and "JAVA" are different for same text and key, I'm expecting them tobe same
The reason the C code outputs 32 bytes of ciphertext is because you are padding your input string with zeros before passing it to the encryption function.
In this line:
EVP_EncryptUpdate(&ctx, out, &outlen1, in, sizeof(in));
the function has no idea that you padded your data with zeros. Since sizeof(in) is 16, it takes 16 as the length of your plaintext. And with PKCS5 padding (and AES), 16 bytes will pad to 32 bytes.
In your Java code you didn't manually pad your input, you just passed it straight to the encryption function. So the encryption function sees your plaintext size as 4 bytes, which pads to 16 with PKCS5 padding.
So your solution is simple: don't manually pad your input in the C code. Just pass the plaintext directly to EVP_EncryptUpdate. It's already designed to handle unpadded plaintext.
Based on recent feedback and findings on this problem, I've rewritten the question to get rid of noise.
I have 2 separate code paths, one in Java (Android), one and Python which accomplish the following for the purposes of negotiating a pairing between an Android device and a Python/Django.
Java:
Generate a syncKey
Hash a concatenated string of various values using the presharedKey (including the syncKey)
Encrypt the syncKey using a presharedKey
Send the Hash, encrypted syncKey, DeviceId and arbitrary variables to web server
Python
Get the presharedKey from the deviceId
Decrypt the encrypted syncKey
Hash a concatenated string of various values using the presharedKey (including the decrypted syncKey)
Make sure the hash matches, which confirms that the syncKey was decrypted successfully, and that the deviceId holds the correct presharedKey.
Now this process works if I send the syncKey unencrypted. The final hash matches, which proves the deviceId has the correct preshared-key, however as soon as I add the en/decryption into the process, the hash no longer matches, despite the fact that both the syncKey and concatenated string appear to match perfectly character for character from the debug output of both Java/Python.
One quirk of the process is that a 256bit key is necessary for the AES256 encryption algorithm, so I'm chopping the 512bit presharedKey in half. The alternative of using only a 256bit key across the board was requiring that I pass the key through encode('ascii') on the python side, or else it was throwing up errors during hashing with the shorter key.
Here is the relevant code:
Java:
String presharedKey = getKey();
// f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd0c1c8e36fddba501ef92e72c95b47e07f98f7fd9cb63da75c008a3201124ea5d
String deviceId = getDeviceId();
// 1605788742789230
SyncKey syncKey = generateSyncKey();
// 824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9
String concat = syncKey.hexString();
// 824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9
String ALGORITHM = "HmacSHA256";
String hash = null;
try {
SecretKeySpec keySpec = new SecretKeySpec(
presharedKey.getBytes(),
ALGORITHM);
Mac mac = Mac.getInstance(ALGORITHM);
mac.init(keySpec);
byte[] result = mac.doFinal(concat.getBytes());
hash = Base64.encodeToString(result, Base64.DEFAULT);
// FpDE2JLmCBr+/rW+n/jBHH13F8AV80sUM2fQAY2IpRs=
} catch (NoSuchAlgorithmException x) {
} catch (InvalidKeyException x) {
}
String encKey = presharedKey.substring(0, presharedKey.length() / 2);
// f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd
int len = encKey.length();
byte[] encKeyBytes = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
encKeyBytes[i / 2] = (byte) ((Character.digit(encKey.charAt(i), 16) << 4)
+ Character.digit(encKey.charAt(i+1), 16));
}
String encryptedSyncKey = null;
try {
byte[] iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
SecretKeySpec encKeySpec = new SecretKeySpec(encKeyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, encKeySpec, ivSpec);
byte[] encryptedSyncKeyBytes = cipher.doFinal(syncKey.hexString().getBytes());
encryptedSyncKey = Base64.encodeToString(encryptedSyncKeyBytes, Base64.DEFAULT);
/*
Yrl0/SuTUUTC6oJ8o4TCOy65EwO0JzoXfEi9kLq0AOlf6rH+nN7+BEc0s5uE7TIo1UlJb/DvR2Ca
ACmQVXXhgpZUTB4sQ0eSo+t32lg0EEb9xKI5CZ4l9QO5raw0xBn7r/tfIdVm8AIFkN9QCcthS0DF
KH3oWhpwNS+tfEuibLPgGqP/zGTozmido9U9lb4n
*/
} catch (InvalidAlgorithmParameterException e) {
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchPaddingException e) {
} catch (InvalidKeyException e) {
} catch (IllegalBlockSizeException e) {
} catch (BadPaddingException e) {
}
sendStuffToWeb(encryptedSyncKey, deviceId, hash);
Python:
hash = getHash(request)
# hash from Java: FpDE2JLmCBr+/rW+n/jBHH13F8AV80sUM2fQAY2IpRs=
encrypted_sync_key = getEncSyncKey(request)
# encryptedSyncKey from Java:
# Yrl0/SuTUUTC6oJ8o4TCOy65EwO0JzoXfEi9kLq0AOlf6rH+nN7+BEc0s5uE7TIo1UlJb/DvR2Ca
# ACmQVXXhgpZUTB4sQ0eSo+t32lg0EEb9xKI5CZ4l9QO5raw0xBn7r/tfIdVm8AIFkN9QCcthS0DF
# KH3oWhpwNS+tfEuibLPgGqP/zGTozmido9U9lb4n
device_id = getDeviceId(request)
# 1605788742789230
preshared_key = getPresharedKeyFromDevice(deviceId)
# f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd0c1c8e36fddba501ef92e72c95b47e07f98f7fd9cb63da75c008a3201124ea5d
enc_key = preshared_key[:len(preshared_key)/2]
# f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd
aes = AES.new(enc_key.decode('hex'), AES.MODE_CBC, IV="\x00"*16)
sync_key = aes.decrypt(base64.b64decode(encrypted_sync_key))
# 824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9
concat = sync_key
# 824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9
import hashlib
from hmac import new as hmac
verify_hash = hmac(preshared_key, concat, hashlib.sha256).digest().encode('base64')
# IoSc2w2sQ4/fwhJTdUQHw/Hdyjy+ranzQ1z3J5LfYbA=
From the debug output below you can see the syncKey is encrypted and decrypted successfully, and the concat is identical. However the resulting hash ends up being different.
Your Python code is wrong. I can reproduce, in Python, the answer you got in Java.
If I use your inputs:
>>> preshared_key_hex
b'f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd0c1c8e36fddba501ef92e72c95b47e07f98f7fd9cb63da75c008a3201124ea5d'
>>> concat_hex
b'824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9'
I get the same value you get in Java:
>>> base64.b64encode(hmac.new(preshared_key_hex, concat_hex, hashlib.sha256).digest())
b'FpDE2JLmCBr+/rW+n/jBHH13F8AV80sUM2fQAY2IpRs='
However, that value is probably also wrong. You should almost certainly be hex decoding the input values.
I'm unable to reproduce what you got in Python; one of the values you're passing to hmac.new isn't what you think it is. print them immediately before calling hmac.new and you should see what doesn't match.
I'm working on a project and I need to encrypt a String with AES. The program needs to take be able to either take in a String and output an encrypted string in hex, along with a key, or, using a user-specified key and string, output unencrypted text (that is, the program needs to be able to do both of these things in different instances i.e. I should be able to put in "1234" on my machine and get out "Encrypted text: asdf Key: ghjk"; my friend should be able to put in "Encrypted text: asdf KEy: ghjk" on his and get out "1234" )
Here's what I have so far:
package betterencryption;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.Scanner;
public class BetterEncryption {
public static String asHex (byte buf[]) { //asHex works just fine, it's the main that's
//giving me trouble
StringBuffer strbuf = new StringBuffer(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10)
strbuf.append("0");
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(System.in);
KeyGenerator kgen = KeyGenerator.getInstance("AES");kgen.init(128);
SecretKey skey = kgen.generateKey();
byte[] bytes = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(bytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
System.out.print("Do you want to encrypt or unencrypt?\n");/*This is a weird way of doing it,*/
String choice = sc.next(); char cc = choice.charAt(2); /*I know, but this part checks to see if*/
if(cc=='c'){ /*the program is to encrypt or unencrypt*/
System.out.print("Enter a string to encrypt: "); /* a string. The 'encrypt' function works.*/
String message = sc.next();
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal((args.length == 0 ? message : args[0]).getBytes());
System.out.println("Encrypted string: " + asHex(encrypted)+"\nKey: "+asHex(bytes));
//^This^ section actually works! The code outputs an encrypted string and everything.
//It's beautiful
//Unfortunately getting that string back into readable text has been problematic
//Which is where you guys come in!
//Hopefully
}
if(true){
System.out.print("\nEnter the encrypted string: "); String encryptedString = sc.next();
System.out.print("\nEnter the key: "); String keyString = sc.next();
int len = encryptedString.length(); /*this section converts the user-input string*/
byte[] encrypted = new byte[len / 2]; /*into an array of bytes*/
for (int i = 0; i < len; i += 2) { /*I'm not sure if it works, though*/
encrypted[i / 2] = (byte) ((Character.digit(encryptedString.charAt(i), 16) << 4)+
Character.digit(encryptedString.charAt(i+1), 16));
cipher.init(Cipher.DECRYPT_MODE, skeySpec); /*as you can see, I haven't even begun to implement*/
byte[] original = cipher.doFinal(encrypted);/*a way to allow the user-input key to be used.*/
String originalString = new String(original);
System.out.println("\nOriginal string: "+originalString); //I'm really quite stuck.
//can you guys help?
}
}
}
}
Well, hopefully someone can help me.
EDIT:
My biggest problems are converting String encryptedString into an sKeySpec and figuring out how to prevent the 'unencrypt' function from giving the user an error saying that the String they input was not properly padded. I know this isn't true because I've tried encrypting a String and then pasting what the encrypted form of it is into the unencryptor only to get an error. The program works fine if I eliminate all the "if" conditions and just have it encrypt a String and then unencrypt it in the same instance; I think this is due to the preservation of keyGen's Random Key
Your problem is this:
KeyGenerator kgen = KeyGenerator.getInstance("AES");kgen.init(128);
SecretKey skey = kgen.generateKey();
byte[] bytes = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(bytes, "AES");
As you've written it, your program is generating a new, random key every time it's run, which is never saved or displayed anywhere. Anything that you're encrypting with this key is effectively impossible to decrypt.
What you'll need to do is come up with some scheme for generating a secret key from the user input, rather than randomly generating it using KeyGenerator. How that scheme will work is up to you.
Depending on which AES Variant you use, your key needs to be 128, 192 or 256Bit long.
You can use a HashAlgorithm to generate a key with the specific length from the user-input.
String key;
byte[] keydata = hashFunctionToMakeToKeytheRightSize(key);
SecretKeySpec secretKeySpec = new SecretKeySpec(keydata, "AES");
Also see: java-aes-and-using-my-own-key