I have a file encrypted with java application using AES. I also have a key file was encrypted with. But i can't understand how to use the key to decrypt file. Most tutorials and examples create temporary random key, encrypt file and decrypt it in one place.
So, question is how to specify a key which have to be used for decryption?
EDIT:
Samples i found use following code to generate key. I have no idea where i can use my key here.
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
SecretKey key = kgen.generateKey();
Just to summarise my comments to Lucifer's answer.
If you don't know what padding was used to encrypt, then decrypt with 'no padding' set. That will decrypt everything, including the padding, and won't throw an error because of mismatched padding.
When you have decrypted the cyphertext, have a look at the last block of the output and see what padding was used. Different paddings leave different byte patterns, so it is usually easy enough to tell.
Set your decryption method to expect the correct type of padding, and it will be automatically removed for you.
The answer could be simply to put the key data as bytes into a SecretKeySpec like this:
SecretKeySpec aesKey = new SecretKeySpec(myKeyData, "AES");
Note that SecretKeySpec implements the Key interface, so you can use it directly in a Cipher.init() method. So there is no SecretKeyFactory needed, which you would use otherwise.
Please try following methods, if might helpful for you.
private static byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data)
throws Exception
{
int minSize = cipher.getOutputSize(data.length);
byte[] outBuf = new byte[minSize];
int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
int length2 = cipher.doFinal(outBuf, length1);
int actualLength = length1 + length2;
byte[] result = new byte[actualLength];
System.arraycopy(outBuf, 0, result, 0, result.length);
return result;
}
private static byte[] decrypt(byte[] cipher, byte[] key, byte[] iv) throws Exception
{
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new AESEngine()));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(false, ivAndKey);
return cipherData(aes, cipher);
}
private static byte[] encrypt(byte[] plain, byte[] key, byte[] iv) throws Exception
{
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(
new AESEngine()));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(true, ivAndKey);
return cipherData(aes, plain);
}
Complete example of encrypting/Decrypting a huge video without throwing Java OutOfMemoryException and using Java SecureRandom for Initialization Vector generation. Also depicted storing key bytes to database and then reconstructing same key from those bytes.
https://stackoverflow.com/a/18892960/185022
Related
I am looking for settings/parameters of CryptoKit which will allow me to share data between iOS App and a Java Application. The flow would be something like below:
- Use CryptoKit to encrypt a text using a fixed key and random initialization vector (IV).
- In the Java application use standard javax libraries to perform the decryption using the same fixed key. The random IV will be transported/shared with the application along with the encrypted text.
Similarly, the reverse is also required, where text is encrypted using JavaX libraries using a fixed key and random IV. The random IV and encrypted text is shared with the iOS app where it should use CryptoKit to decrypt it.
Below is the code for Encrypt and Decrypt in Java
public static byte[] encrypt(byte[] plaintext, byte[] key, byte[] IV) throws Exception
{
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES_256/GCM/NoPadding");
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
// Initialize Cipher for ENCRYPT_MODE
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Encryption
byte[] cipherText = cipher.doFinal(plaintext);
return cipherText;
}
public static String decrypt(byte[] cipherText, byte[] key, byte[] IV) throws Exception
{
// Get Cipher Instance
Cipher cipher = Cipher.getInstance("AES_256/GCM/NoPadding");
// Create SecretKeySpec
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
// Create GCMParameterSpec
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, IV);
// Initialize Cipher for DECRYPT_MODE
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
// Perform Decryption
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
}
The CryptoKit commands as below:
let mykey = SymmetricKey(data: passhash)
let myiv = try AES.GCM.Nonce()
let mySealedBox = try AES.GCM.seal(source.data(using: .utf8)!, using: mykey, nonce: myiv)
let myNewSealedBox = try AES.GCM.SealedBox(nonce: myiv, ciphertext: mySealedBox.ciphertext, tag: mySealedBox.tag)
let myText = try String(decoding: AES.GCM.open(myNewSealedBox, using: mykey), as: UTF8.self)
Below are the steps to generate an encrypted text in Java:
int GCM_IV_LENGTH = 12;
//Generate Key
MessageDigest md = MessageDigest.getInstance("SHA265");
byte[] key = md.digest("pass".getBytes(StandardCharsets.UTF_8));
// Generate IV
SecureRandom sr = new SecureRandom(pass.getBytes(StandardCharsets.UTF_8));
byte[] IV = new byte[GCM_IV_LENGTH];
sr.nextBytes(IV);
//Encrypt
byte[] cipherText = encrypt("Text to encrypt".getBytes(), key, IV);
//Base64 Encoded CipherText
String cipherTextBase64 = Base64.getEncoder().encodeToString(cipherText);
To Decrypt this in SWIFT CryptoKit, I first need to create a sealed box with this CipherText however, the CryptoKit API to create a sealed box requires the following:
Nonce/IV (Available above)
CipherText (Available above)
Tag (NO IDEA FROM WHERE TO GET THIS????)
AES.GCM.SealedBox(nonce: , ciphertext: , tag: )
The other way, lets first encrypt data in CryptoKit
let mykey = SymmetricKey(data: SHA256.hash(data: "12345".data(using: .utf8)!))
let myiv = AES.GCM.Nonce()
let mySealedBox = try AES.GCM.seal("Text to encrypt".data(using: .utf8)!, using: mykey, nonce: myiv)
let cipherText = mySealedBox.cipherText.base64EncodedString()
let iv = myiv.withUnsafeBytes{
return Data(Array($0)).base64EncodedString()
}
If i pass this IV and CipherText to Java Decrypt function along with key (SHA265 hash of "12345" string), i get a TAG mismatch error.
This is the final set of code in SWIFT:
let pass = “Password”
let data = “Text to encrypt”.data(using: .utf8)!
let key = SymmetricKey(data: SHA256.hash(data: pass.datat(using: .utf8)!))
let iv = AES.GCM.Nonce()
let mySealedBox = try AES.GCM.seal(data, using: key, nonce: iv)
dataToShare = mySealedBox.combined?.base64EncodedData()
Write this data to a file (I am using google APIs to write this data to a file on google drive)
Read this data from the file in java and pass it to the functions as defined in the question using the below code:
byte[] iv = Base64.getDecoder().decode(text.substring(0,16));
cipher[] = Base64.getDecoder().decode(text.substring(16));
byte[] key = md.digest(pass.getBytes(StandardCharsets.UTF_8));
String plainText = decrypt(cipher, key, iv);
I'm trying to make an encryption-decryption app. I've got two classes - one with functions to generate the key, encrypt and decrypt, second one for JavaFX GUI. In the GUI class I've got 4 textareas: 1st to write text to encrypt, 2nd for encrypted text, 3rd for the key (String encodedKey = Base64.getEncoder().encodeToString(klucz.getEncoded());) and 4th for decrypted text.
The problem is, I am not able to decrypt the text. I'm trying to recreate the SecretKey like this:
String encodedKey = textAreaKey.getText();
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
SecretKey klucz = new SecretKeySpec(decodedKey, "DESede");
When I encrypt the key looks like this: com.sun.crypto.provider.DESedeKey#4f964d80 and when I try to recreate it: javax.crypto.spec.SecretKeySpec#4f964d80 and I'm getting javax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher
Here is my 1st class:
public class Encryption {
public static SecretKey generateKey() throws NoSuchAlgorithmException {
Security.addProvider(new com.sun.crypto.provider.SunJCE());
KeyGenerator keygen = KeyGenerator.getInstance("DESede");
keygen.init(168);
SecretKey klucz = keygen.generateKey();
return klucz;
}
static byte[] encrypt(byte[] plainTextByte, SecretKey klucz)
throws Exception {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, klucz);
byte[] encryptedBytes = cipher.doFinal(plainTextByte);
return encryptedBytes;
}
static byte[] decrypt(byte[] encryptedBytes, SecretKey klucz)
throws Exception {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, klucz);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return decryptedBytes;
}
}
edit
btnEncrypt.setOnAction((ActionEvent event) -> {
try {
String plainText = textAreaToEncrypt.getText();
SecretKey klucz = Encryption.generateKey();
byte[] plainTextByte = plainText.getBytes();
byte[] encryptedBytes = Encryption.encrypt(plainTextByte, klucz);
String encryptedText = Base64.getEncoder().encodeToString(encryptedBytes);
textAreaEncryptedText.setText(encryptedText);
byte[] byteKey = klucz.getEncoded();
String stringKey = Base64.getEncoder().encodeToString(byteKey);
textAreaKey.setTextstringKey
} catch (Exception ex) {
ex.printStackTrace();
}
});
btnDecrypt.setOnAction((ActionEvent event) -> {
try {
String stringKey = textAreaKey.getText();
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
SecretKey klucz2 = new SecretKeySpec(decodedKey, "DESede");
String encryptedText = textAreaEncryptedText.getText();
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText.getBytes());
byte[] decryptedBytes = Encryption.decrypt(encryptedBytes, klucz2;
String decryptedText = Base64.getEncoder().encodeToString(decryptedBytes);
textAreaDecryptedText.setText(decryptedText);
} catch (Exception ex) {
ex.printStackTrace();
}
});
One of your problems is here:
String encryptedText = new String(encryptedBytes, "UTF8");
Generally, many byte sequences in cipher text are not valid UTF-8–encoded characters. When you try to create a String, this malformed sequences will be replaced with the "replacement character", and then information from the the cipher text is irretrievably lost. When you convert the String back to bytes and try to decrypt it, the corrupt cipher text raises an error.
If you need to represent the cipher text as a character string, use base-64 encoding, just as you do for the key.
The other principal problem is that you are aren't specifying the full transformation. You should specify the "mode" and "padding" of the cipher explicitly, like "DESede/ECB/PKCS5Padding".
The correct mode will depend on your assignment. ECB is generally not secure, but more secure modes add a bit of complexity that may be outside the scope of your assignment. Study your instructions and clarify the requirements with your teacher if necessary.
There are two main issues:
You should not use user entered password as a key (there are difference between them). The key must have specific size depending on the cipher (16 or 24 bytes for 3des)
Direct 3DES (DESede) is a block cipher encrypting 8 bytes at once. To encrypt multiple blocks, there are some methods defined how to do that properly. It is calls Block cipher mode.
For proper encryption you need to take care of a few more things
Creating a key from the password
Let's assume you want to use DESede (3des). The key must have fixed size - 16 or 24 bytes. To properly generate a key from password you should use PBKDF. Some people are sensitive to "must use", however neglecting this step really compromises the encryption security mainly using user-entered passwords.
For 3DES you can use :
int keySize = 16*8;
int iterations = 800000;
char[] password = "password".toCharArray();
SecureRandom random = new SecureRandom();
byte[] salt = random.generateSeed(8);
SecretKeyFactory secKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
KeySpec spec = new PBEKeySpec(password, salt, iterations, keySize);
SecretKey pbeSecretKey = secKeyFactory.generateSecret(spec);
SecretKey desSecret = new SecretKeySpec(pbeSecretKey.getEncoded(), "DESede");
// iv needs to have block size
// we will use the salt for simplification
IvParameterSpec ivParam = new IvParameterSpec(salt);
Cipher cipher = Cipher.getInstance("DESEde/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, desSecret, ivParam);
System.out.println("salt: "+Base64.getEncoder().encodeToString(salt));
System.out.println(cipher.getIV().length+" iv: "+Base64.getEncoder().encodeToString(cipher.getIV()));
byte[] ciphertext = cipher.doFinal("plaintext input".getBytes());
System.out.println("encrypted: "+Base64.getEncoder().encodeToString(ciphertext));
if you can ensure that your password has good entropy (is long and random enough) you may be good with a simple hash
MessageDigest dgst = MessageDigest.getInstance("sha-1");
byte[] hash = dgst.digest("some long, complex and random password".getBytes());
byte[] keyBytes = new byte[keySize/8];
System.arraycopy(hash, 0, keyBytes, 0, keySize/8);
SecretKey desSecret = new SecretKeySpec(keyBytes, "DESede");
The salt serves to randomize the output and should be used.
The output of the encryption should be salt | cipthertext | tag (not necessarily in this order, but you will need all of these for proper encryption).
To decrypt the output, you will need to split the output to salt, ciphertext and the tag.
I see zero vectors ( static salt or iv ) very often in examples from StackOverflow, but in many cases it may lead to broken ciphers revelaling key or plaintext.
The initialization vector iv is needed for block chain modes (encrypting longer input than a single block), we could use the salt from the key as well
when having the same size ( 8 bytes in our case). For really secure solution the password salt should be longer.
The tag is an authentication tag, to ensure that nobody has manipulated with the ciphertext. You could use HMAC of the plaintext or ciphertext. It is important you should use different key for HMAC than for encryption. However - I believe in your case your homework will be ok even without the hmac tag
This is the code in question:
decrypt(self):
"""Decrypt one block"""
data = self.source.read(1024)
if not data:
return ""
iv = data[:16]
encrypted = data[16:]
counter = Crypto.Util.Counter.new(64, prefix=iv[:8], initial_value=struct.unpack(">Q", iv[8:])[0])
cipher = Crypto.Cipher.AES.new(self.info["CpData"], Crypto.Cipher.AES.MODE_CTR, counter=counter)
return cipher.decrypt(encrypted)
This is the line I have problems understanding:
counter = Crypto.Util.Counter.new(64, prefix=iv[:8], initial_value=struct.unpack(">Q", iv[8:])[0])
What does it do and how do I replicate it in Java? Currently I have this, but the result is not what I expect:
public static byte[] decrypt(byte[] encryptedData) throws Exception {
Key key = new SecretKeySpec(keyBytes, "AES");
Cipher c = Cipher.getInstance("AES/CTR/NoPadding");
byte[] iv = Arrays.copyOfRange(encryptedData, 0, 16) ; // first 16 bytes
byte[] data = Arrays.copyOfRange(encryptedData, 16, 1024); // rest
IvParameterSpec ivSpec = new IvParameterSpec(iv);
c.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decValue = c.doFinal(data);
return decValue;
}
Hours of googling have not yielded a useable result. How do I use this counter thing in Java?
Thanks!
On a quick look it appears that there is 8 bytes of IV prepended to the incoming cyphertext. Extract the first 8 bytes and use them as an IV to set up AES-CTR. Then decrypt the rest of the cyphertext.
I need to encrypt an image of size 151*15 with RSA.
This is the java code to encrypt the image file
import javax.crypto.Cipher;
plaintext = time;
cipher = Cipher.getInstance('RSA');
keygen = java.security.KeyPairGenerator.getInstance('RSA');
keyPair = keygen.genKeyPair();
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate())
plaintextUnicodeVals = uint16(plaintext)
plaintextBytes = typecast(plaintextUnicodeVals, 'int8')
ciphertext = cipher.doFinal(plaintextBytes);
this is the image file to be encrypted
I got the folllowing error
Java exception occurred:
javax.crypto.IllegalBlockSizeException: Data must not be longer than 117 bytes
at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
please give me the hint Or procedure so that i can approach in right direction.
Thanks
You need to use AES for encrypting large data. RSA cannot encrypt data larger than key's size.
So you can encrypt an AES key with RSA, and the whole image with AES (256 bit) (i.e. generate different AES key for each image).
Also RSA is very slow and thus is not good for encrypting large data, if you want to split up an image to a large number of blocks of size 117 bytes and encrypt them one by one.
For example:
public static byte[] encrypt(byte[] data) {
try {
KeyPair keyPair = initalizeKeyPair();
final javax.crypto.Cipher rsa = javax.crypto.Cipher.getInstance("RSA");
rsa.init(javax.crypto.Cipher.ENCRYPT_MODE, keyPair.getPublic());
SecureRandom random = new SecureRandom();
final byte[] secretKey = new byte[16];
random.nextBytes(secretKey);
final javax.crypto.Cipher aes = javax.crypto.Cipher.getInstance("AES");
SecretKeySpec k = new SecretKeySpec(secretKey, "AES");
aes.init(javax.crypto.Cipher.ENCRYPT_MODE, k);
final byte[] ciphedKey = rsa.doFinal(secretKey);
final byte[] ciphedData = aes.doFinal(data);
byte[] result = new byte[256 + ciphedData.length];
System.arraycopy(ciphedKey, 0, result, 0, 256);
System.arraycopy(ciphedData, 0, result, 256, ciphedData.length);
return result;
} catch (... e) {
throw new SomeException(e);
}
}
I've been stuck on a bug in my code, it will not let me decrypt properly!
I am only passing eight bytes of data to dataBytes and I am passing
a 24 byte key to keyBytes.
I am trying to return the decrypted data as an array of bytes.
I keep getting the bad padding exception.
Thanks!
Here is the code snippet:
private static byte[] DESEdeDecrypt(byte[] keyBytes, byte[] dataBytes){
byte[] decryptedData = null;
try{
DESedeKeySpec keySpec = new DESedeKeySpec(keyBytes, 0);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
SecretKey key = keyFactory.generateSecret(keySpec);
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.DECRYPT_MODE, key);
decryptedData = cipher.doFinal(dataBytes);
}
catch(Exception e){System.out.println(e);}
return decryptedData;
You must use the same padding to decrypt as you did to encrypt. It is better to set it explicitly rather than to rely on defaults. Best also to specify the mode at both ends as well:
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
DESede is slow and obsolescent. You shouldn't use it except for compatibility with old code. For new work it is better to use AES.