I'm a fresh IOS developer, and facing a problem in encryption and decryption.
decrypt code(java)
byte[] keyPass = pass.getBytes("ASCII");
byte[] aesIV = new byte[16];
byte[] Decryptdata = Base64.decode(encodedString, Base64.NO_WRAP);
System.arraycopy(Decryptdata, 0, aesIV, 0, 16);
byte[] data = new byte[Decryptdata.length - 16];
System.arraycopy(Decryptdata, 16, data, 0, dataToDecrypt.length - 16);
Key aesKey = new SecretKeySpec(keyPass, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(aesIV);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivSpec);
Text = new String(cipher.doFinal(data), "UTF-8");
encrypt code(java)
byte[] keyPass = pass.getBytes("ASCII");
final Key key = new SecretKeySpec(keyPass, "AES");
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
byte[] byteMessage = text.getBytes("UTF-8");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = cipher.doFinal(byteMessage);
byte[] ivByte = cipher.getIV();
byte[] bytesTotal = new byte[ivByte.length+cipherText.length];
System.arraycopy(ivByte, 0, bytesTotal, 0, ivByte.length);
System.arraycopy(cipherText, 0, bytesTotal, ivByte.length, cipherText.length);
encyoted = Base64.encodeToString(bytesTotal, Base64.NO_WRAP);
How can I create encrypt and decrypt in objective-c?
"how can i create function from java to objective-c"
For iOS use Common Crypto.
Study, write code and debug. Repeat as necessary.
Related
I'm trying to encrypt and decrypt a string in java using AES/CBC/PKCS5Padding. It encrypts/decrypts without any compilation errors but the string after decryption is just random characters. this is my code:
Encryption:
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(key.toCharArray(), "salt".getBytes(), 65536, 256);
SecretKey secret =new SecretKeySpec(factory.generateSecret(spec).getEncoded(),"AES");
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivp = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret, ivp);
byte[] encryptedText = cipher.doFinal(input.getBytes("UTF-8));
return Base64.getEncoder().encodeToString(encryptedText);
Decryption:
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(key.toCharArray(), "salt".getBytes(), 65536, 256);
SecretKey secret =new SecretKeySpec(factory.generateSecret(spec).getEncoded(),"AES");
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivp = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, ivp);
byte[] decryptedtext = cipher.doFinal(Base64.getDecoder().decode(input));
return new String(decryptedtext, "UTF-8");
The given text is the same every time but the result changes and is filled with random characters.
Thanks in advance!
I am currently developing an Android app that requests specific properties from my webserver, which look like this:
<properties>
<property name="Approved-IP" value="SomeIPAddresses"/>
</properties>
The app saves the site as a temporary XML file and tries to check these IPs.
I want to let the server encrypt the properties using AES and IF the app doesn't have an encryption password set use a default key like "test123".
The app then decrypts this information via the EasyCrypt library and uses it.
The problem is that I don't really know how to do it server-side, so that the information won't be passed in plain text.
So I found a solution for my program (beware, this is just for exercise, so I wouldn't call it safe in ANY way)
I am using this method to encrypt the String on my WebServer:
private static byte[] encrypt(String plain, String key) throws Exception {
byte[] clean = plain.getBytes();
int ivSize = 16;
byte[] iv = new byte[ivSize];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(key.getBytes("UTF-8"));
byte[] keyBytes = new byte[16];
System.arraycopy(digest.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(clean);
byte[] encryptedIVAndText = new byte[ivSize + encrypted.length];
System.arraycopy(iv, 0, encryptedIVAndText, 0, ivSize);
System.arraycopy(encrypted, 0, encryptedIVAndText, ivSize, encrypted.length);
return encryptedIVAndText;
}
Then I am encoding the generated String with Base64
Base64.getEncoder().encodeToString(encryptedString)
And return the Base64 String.
My App decodes the returned String using the getDecoder function and decrypts it with this method:
public static String decrypt(byte[] encryptedIvTextBytes, String key) throws Exception {
int ivSize = 16;
int keySize = 16;
byte[] iv = new byte[ivSize];
System.arraycopy(encryptedIvTextBytes, 0, iv, 0, iv.length);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
int encryptedSize = encryptedIvTextBytes.length - ivSize;
byte[] encryptedBytes = new byte[encryptedSize];
System.arraycopy(encryptedIvTextBytes, ivSize, encryptedBytes, 0, encryptedSize);
byte[] keyBytes = new byte[keySize];
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(key.getBytes());
System.arraycopy(md.digest(), 0, keyBytes, 0, keyBytes.length);
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
Cipher cipherDecrypt = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipherDecrypt.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decrypted = cipherDecrypt.doFinal(encryptedBytes);
return new String(decrypted);
}
And the result is the normal properties file.
I am attempting to implement a basic example of AES encryption across two devices in Java. However using the same password(128 bit) on both devices to generate an AES key results in different keys on both devices, each time we run the application. So we are unable to decrypt the text that we send between devices.
The methods I'm using are below, they are slightly modified versions of code I've found elsewhere:
public String encryptMessage(String message, String password) throws Exception {
// Creating key and cipher
SecretKeySpec aesKey = new SecretKeySpec(password.getBytes("UTF-8"), "AES");
byte[] iv = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
//AES cipher
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
// encrypt the text
cipher.init(Cipher.ENCRYPT_MODE, aesKey, ivspec);
byte[] encrypted;
encrypted = cipher.doFinal(message.getBytes());
return new String(encrypted, "UTF-8");
}
public String decryptMessage(String encryptedMessage, String password) throws Exception {
// Creating key and cipher
byte[] passwordBytes = password.getBytes("UTF-8");
SecretKeySpec aesKey = new SecretKeySpec(passwordBytes, "AES");
byte[] iv = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
//AES cipher
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
// decrypting the text
cipher.init(Cipher.DECRYPT_MODE, aesKey, ivspec);
String decrypted = new String(cipher.doFinal(encryptedMessage.getBytes(Charset.forName("UTF-8"))));
//returning decrypted text
return decrypted;
}
Every time I run this code and print out aesKey, it is different.
My understanding of AES and symmetric encryption is that given the same password it should generate the same key, otherwise how is it able to decrypt the artefact?
Have I got the wrong end of the stick wrt AES or can someone suggest what might be going on?
Your understanding is correct and the key in your code is the same.
You can't "print" the aesKey since SecretKeySpec has no toString() method. So the built-in Object.toString() will be called, which just prints the address of the object in memory
javax.crypto.spec.SecretKeySpec#14c7f // <--- useless info //
There are just a few issues with your code:
Don't convert encrypted bytes to a UTF-8 string. There can be combinations which are invalid in UTF-8, as well as 00 bytes. Use Base64 or Hex encoding for printing encrypted data.
You shouldn't use ASCII bytes as a key, that greatly reduces security of the key. Derive a key from the password, at least with SHA-256, but preferably PBKDF2 or scrypt.
Use a high entropy random IV and store it with the ciphertext.
Here's an updated version demonstrating that it's working:
public static String encryptMessageGH(String message, String password) throws Exception {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
byte[] key = sha.digest(password.getBytes("UTF-8"));
SecretKeySpec aesKey = new SecretKeySpec(key, "AES");
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, aesKey, new IvParameterSpec(iv));
byte[] ciphertext = cipher.doFinal(message.getBytes());
byte[] encrypted = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, encrypted, 0, iv.length);
System.arraycopy(ciphertext, 0, encrypted, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(encrypted);
}
public static String decryptMessageGH(String encryptedMessage, String password) throws Exception {
MessageDigest sha = MessageDigest.getInstance("SHA-256");
byte[] key = sha.digest(password.getBytes("UTF-8"));
SecretKeySpec aesKey = new SecretKeySpec(key, "AES");
byte[] encrypted = Base64.getDecoder().decode(encryptedMessage);
byte[] iv = new byte[16];
System.arraycopy(encrypted, 0, iv, 0, iv.length);
byte[] ciphertext = new byte[encrypted.length - iv.length];
System.arraycopy(encrypted, iv.length, ciphertext, 0, ciphertext.length);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, aesKey, new IvParameterSpec(iv));
return new String(cipher.doFinal(ciphertext), "UTF-8");
}
public static void main(String[] args) throws Exception {
String orig = "Test message";
String enc = encryptMessageGH(orig, "abcdef123");
System.out.println("Encrypted: " + enc);
String dec = decryptMessageGH(enc, "abcdef123");
System.out.println("Decrypted: " + dec);
}
Output:
Encrypted: lcqcd9UZpjLSY9SsQ/N7kV/cpdzL3c7HQcCSiIs6p/k=
Decrypted: Test message
So at the moment I'm trying to write some encrypted text to a file and then be able to read that back in, decrypt it and display it to the user. I'm currently using AES-256 with PBKDF2 password derivation as I'd like to be able to use a user's password to encrypt/decrypt the files. The files are simple text files. The code I am currently using to encrypt some text and save it to a file is below. As far as I can tell, from having a look using adb, this works correctly.
FileOutputStream out = new FileOutputStream(mypath);
String defaultMessage = "Empty File";
int iterationCount = 1000;
int keyLength = 256;
int saltLength = keyLength / 8;
SecureRandom randomGenerator = new SecureRandom();
byte[] salt = new byte[saltLength];
randomGenerator.nextBytes(salt);
KeySpec keySpec = new PBEKeySpec(submittedPassword.toCharArray(), salt, iterationCount, keyLength);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[cipher.getBlockSize()];
randomGenerator.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivParams);
byte[] ciphertext = cipher.doFinal(defaultMessage.getBytes("UTF-8"));
String finalMessage = ciphertext.toString() + "]" + iv.toString() + "]" + salt.toString();
out.write(finalMessage.getBytes());
out.close();
P.S The above is within a Try/Except.
The code below is what I'm currently trying to use to read in the file and then decrypt it, however, when I try to display the decrypted contents via the test view at the end, it does not show up.
FileInputStream fileInputStream = new FileInputStream(mypath);
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuffer stringBuffer = new StringBuffer();
while ((fileContents = bufferedReader.readLine()) != null) {
stringBuffer.append(fileContents + "\n");
}
String fileContentsString = stringBuffer.toString();
String[] fileContentsList = fileContentsString.split("]");
byte[] cipherText = fileContentsList[0].getBytes();
Toast.makeText(getApplicationContext(), fileContentsList[0], Toast.LENGTH_LONG).show();
byte[] iv = fileContentsList[1].getBytes();
byte[] salt = fileContentsList[2].getBytes();
KeySpec keySpec = new PBEKeySpec(submittedPassword.toCharArray(), salt, 1000, 256);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivParams);
byte[] plaintext = cipher.doFinal(cipherText);
String plainrStr = new String(plaintext , "UTF-8");
textEdit.setText(plainrStr);
Hopefully someone can provide me with some assistance here. Again, the second code segment is within a Try/Except statement.
You have multiple problems with your code.
Encryption
This code
String finalMessage = ciphertext.toString() + "]" + iv.toString() + "]" + salt.toString();
does not produce a ciphertext. See here: Java: Syntax and meaning behind "[B#1ef9157"? Binary/Address?
The IV and salt have fixed sizes, so they can be placed in front of the ciphertext. After you've written the whole ciphertext, you need to use something like Base64 or Hex in order to get a String. Modern ciphers like AES produce ciphertexts that can contain bytes of any value which don't always constitute valid character encodings such as UTF-8. Strings are no containers for arbitrary byte[] contents.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(iv);
baos.write(salt);
baos.write(ciphertext);
String finalMessage = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);
But you don't need that at all, because you can directly write your ciphertext into the file:
out.write(iv);
out.write(salt);
out.write(ciphertext);
Decryption
Don't use InputStreamReader, a BufferedReader and a StringBuffer for binary data. Otherwise, you'll corrupt your binary ciphertext.
You only need this:
byte[] iv = new byte[16];
byte[] salt = new byte[32];
byte[] ctChunk = new byte[8192]; // not for whole ciphertext, just a buffer
if (16 != fileInputStream.read(iv) || 32 != fileInputStream.read(salt)) {
throw new Exception("IV or salt too short");
}
KeySpec keySpec = new PBEKeySpec(submittedPassword.toCharArray(), salt, 1000, 256);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivParams);
int read;
ByteArrayOutputStream ctBaos = new ByteArrayOutputStream();
while((read = fileInputStream.read(ctChunk)) > 0) {
ctBaos.write(cipher.update(cipherText, 0, read));
}
ctBaos.write(cipher.doFinal());
String plainrStr = new String(ctBaos.toByteArray(), "UTF-8");
textEdit.setText(plainrStr);
This handles randomization properly but doesn't provide integrity. If you want to detect (malicious) manipulations of your ciphertexts (and generally you'll want that to prevent some attacks), you'd need to use an authenticated mode like GCM or EAX, or employ an encrypt-then-MAC scheme with a strong MAC like HMAC-SHA256.
Use a library like tozny/java-aes-crypto in order to use good defaults.
How to write some encrypted text to a file and then be able to read that back in?
public static byte[] generateKey(String password) throws Exception
{
byte[] keyStart = password.getBytes("UTF-8");
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(keyStart);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
return skey.getEncoded();
}
public static byte[] encodeFile(byte[] key, byte[] fileData) throws Exception
{
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(fileData);
return encrypted;
}
public static byte[] decodeFile(byte[] key, byte[] fileData) throws Exception
{
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(fileData);
return decrypted;
}
To save a encrypted file to sd do:
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "your_folder_on_sd", "file_name");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
byte[] yourKey = generateKey("password");
byte[] filesBytes = encodeFile(yourKey, yourByteArrayContainigDataToEncrypt);
bos.write(fileBytes);
bos.flush();
bos.close();
To decode a file use:
byte[] yourKey = generateKey("password");
byte[] decodedData = decodeFile(yourKey, bytesOfYourFile);
For reading in a file to a byte Array there a different way out there. A Example: http://examples.javacodegeeks.com/core-java/io/fileinputstream/read-file-in-byte-array-with-fileinputstream/
I wrote my SecretKey to a file using the following code. Similarly, I have to write my ivParameterSpec to another file. How can I do this?
SecretKey key = KeyGenerator.getInstance("AES").generateKey();
ObjectOutputStream secretkeyOS = new ObjectOutputStream(new FileOutputStream("publicKeyFile"));
secretkeyOS.writeObject(key);
secretkeyOS.close();
AlgorithmParameterSpec paramSpec1 = new IvParameterSpec(iv);
session.setAttribute("secParam", paramSpec1);
ObjectOutputStream paramOS = new ObjectOutputStream(new FileOutputStream("paramFile"));
paramOS.writeObject(paramSpec1);
paramOS.close();
Don't try to store the IvParameterSpec object. It is not serializable, because it is not intended to be stored.
The IV is the important part. Store this and create a new IvSpec from the IV. I have changed example code from here for AES encryption to store the IV and use the loaded IV to decrypt the ciphertext so you can see a possible workflow.
Please be aware that this is a minimal example. In a real usecase you would store and load the key as well and exception handling should also be reconsidered :-D
public class Test {
public static void main(String[] args) throws Exception {
String message = "This string contains a secret message.";
// generate a key
KeyGenerator keygen = KeyGenerator.getInstance("AES");
keygen.init(128);
byte[] key = keygen.generateKey().getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
byte[] iv = { 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8 };
IvParameterSpec ivspec = new IvParameterSpec(iv);
// initialize the cipher for encrypt mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivspec);
// encrypt the message
byte[] encrypted = cipher.doFinal(message.getBytes());
System.out.println("Ciphertext: " + hexEncode(encrypted) + "\n");
// Write IV
FileOutputStream fs = new FileOutputStream(new File("paramFile"));
BufferedOutputStream bos = new BufferedOutputStream(fs);
bos.write(iv);
bos.close();
// Read IV
byte[] fileData = new byte[16];
DataInputStream dis = null;
dis = new DataInputStream(new FileInputStream(new File("paramFile")));
dis.readFully(fileData);
if (dis != null) {
dis.close();
}
// reinitialize the cipher for decryption
cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(fileData));
// decrypt the message
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println("Plaintext: " + new String(decrypted) + "\n");
}
[...]
}