I've tried running the code for about 3 days now but I am not able to figure out the mistake I've done.
I 'am using AES/CFB/NOPadding in 128 bit with password salting
Salt_Len = 8 bytes and IV_Len = 16 bytes
package Firstage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import java.util.Random;
import java.util.Scanner;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class Thealgorithm1
{
static Scanner get = new Scanner(System.in);
private static final String ALGORITHM = "AES";
private static final String ALGORITHM_MODE ="AES/CFB/NoPadding";
private static String password;
public static void encrypt(File inputFile, File outputFile)
throws Exception
{
System.out.println("Enetr passprhase");
password=get.nextLine();
final Random ivspc = new SecureRandom();
byte[] ivspec = new byte[16];
ivspc.nextBytes(ivspec);
IvParameterSpec enciv = new IvParameterSpec(ivspec);
FileOutputStream outputstrm = new FileOutputStream(outputFile);
byte[] outputBytes = doCrypto(Cipher.ENCRYPT_MODE, inputFile,password,enciv);
System.arraycopy(ivspec, 0,outputBytes , 0, 16);
outputstrm.write(outputBytes);
outputstrm.close();
System.out.println("File encrypted successfully!");
}
public static void decrypt(File inputFile, File outputFile)
throws Exception
{
System.out.println("Enter password");
password=get.nextLine();
IvParameterSpec hj = null;
byte[]outpytBytes=doCrypto(Cipher.DECRYPT_MODE, inputFile,password,hj);
FileOutputStream outputstrm = new FileOutputStream(outputFile);
outputstrm.write(outpytBytes);
outputstrm.close();
System.out.println("File decrypted successfully!");
}
private static byte[] doCrypto(int cipherMode, File inputFile,String keyo ,IvParameterSpec ivespec)
throws Exception {
/* Derive the key, given password and salt. */
final Random slt = new SecureRandom();
byte[] salt = new byte[8];
slt.nextBytes(salt);
char[] passkeyo = keyo.toCharArray();
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(passkeyo, salt, 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), ALGORITHM);
FileInputStream fylin = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int)inputFile.length()];
fylin.read(inputBytes);
fylin.close();
Cipher cipher = Cipher.getInstance(ALGORITHM_MODE);
byte[] outputBytes;
if(cipherMode==2)
{
IvParameterSpec ivdec = new IvParameterSpec(inputBytes,0,16);
cipher.init(cipherMode, secret,ivdec);
}
else
{
cipher.init(cipherMode, secret, ivespec);
}
if(cipherMode==2)
{
outputBytes = cipher.doFinal(inputBytes, 16,(inputBytes.length-16));
}
else
{
outputBytes=cipher.doFinal(inputBytes);
}
return outputBytes;
}
public static void main(String[] args)
{
File inputFile = new File("C:/temp/File.txt");
File encryptedFile = new File("C:/temp/encryaes.enc");
File decryptedFile = new File("C:/temp/mydr.txt");
try {
Thealgorithm1.encrypt(inputFile, encryptedFile);
Thealgorithm1.decrypt(encryptedFile, decryptedFile);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
The code encrypts it properly and also decrypts it, but the problem is that what it decrypts is not proper i.e there's a fault in the code.
You have two problems:
during encryption, you're overwriting a the first 16 bytes of the ciphertext with the IV. An easy fix is to write the IV to the stream instead of using System.arraycopy(). Change
System.arraycopy(ivspec, 0,outputBytes , 0, 16);
to
outputstrm.write(ivspec);
You're using a different salts during encryption and decryption, because you're always generating a new one. You should also write the salt in front of the ciphertext beside the IV and read it back during decryption.
You could also just use one of the two: generate a 16 byte salt and write that in front of the ciphertext. Then you can use a static IV, because the semantic property is achieved from the random salt.
You have two main issues in your code:
The key must be exactly the same for encryption and decryption. When you decrypt you are generating a different (random) salt value so, even if you enter the same passphrase, the decryption key is going to be different. You can test this by making the salt a class attribute and only initializing it once.
When you do this inside the encrypt method:
System.arraycopy(ivspec, 0,outputBytes , 0, 16);
You are writing over the first 16 bytes of the encrypted output.
My simple suggestion is to do something like this:
byte[] outputBytes = doCrypto(Cipher.ENCRYPT_MODE, inputFile, password, enciv);
byte[] outputBytesWithIV = new byte[outputBytes.length + 16];
System.arraycopy(ivspec, 0, outputBytesWithIV, 0, 16);
System.arraycopy(outputBytes, 0, outputBytesWithIV, 16, outputBytes.length);
outputstrm.write(outputBytesWithIV);
Related
I'm writing a encryption and decryption code as follows
import java.io.*;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.BadPaddingException;
import java.nio.file.Files;
import java.util.Scanner;
public class EncryptFile
{
public static void main(String args[]) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
//Encrypt Mode
FileOutputStream outputStream = new FileOutputStream(new File("D:\\encryptedNewStringFile.txt"));
Key secretKey = new SecretKeySpec("encKey".getBytes(), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] getFileBytes = "writing a file using encryption ".getBytes();
byte[] outputBytes = cipher.doFinal(getFileBytes);
outputStream.write(outputBytes);
getFileBytes = "\n".getBytes();
outputBytes = cipher.doFinal(getFileBytes);
outputStream.write(outputBytes);
getFileBytes = "This is New Line 2 \nThis is NewLine 3".getBytes();
outputBytes = cipher.doFinal(getFileBytes);
outputStream.write(outputBytes);
outputStream.close();
//Decrypt Mode
File curFile = new File("D:\\encryptedNewStringFile.txt");
secretKey = new SecretKeySpec("encKey".getBytes(), "Blowfish");
cipher = Cipher.getInstance("Blowfish/ECB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
getFileBytes = Files.readAllBytes(curFile.toPath());
outputBytes = cipher.doFinal(getFileBytes);
InputStream bai = new ByteArrayInputStream(outputBytes);
BufferedReader bfReader = new BufferedReader(new InputStreamReader(bai));
Scanner scan = new Scanner(bfReader);
while(scan.hasNextLine())
{
System.out.println(scan.nextLine());
}
}
}
here i have a problem in output which is the printed output has some extra symbols (i.e question marks and box symbols)in it.
The output i received is
Any suggestions will be really helpful thanks in advance
Cipher cipher = Cipher.getInstance("Blowfish");
is equivalent to
Cipher cipher = Cipher.getInstance("Blowfish/ECB/PKCS5Padding");
which means that each time you call cipher.doFinal additional padding is produced.
In order to write a file without intermittent padding, you should be using
outputBytes = cipher.update(getFileBytes);
and use cipher.doFinal only when writing the last time to the file. Then you will be able to use PKCS5Padding instead of NoPadding during decryption in order to remove the valid padding at the end automatically.
Security considerations:
ECB mode is bad and should not be used. There are only very few use cases where this makes sense to use. At least use CBC mode with a randomly generated IV. The IV doesn't need to be secret but only unpredictable. We usually prepend it to the ciphertext and slice it off before decryption. Since it has always a predefined length, this is easy to do.
Use an authenticated mode of operation like GCM or use a message authentication code like HMAC-SHA256 in order to detect and react to (malicious) manipulation of the ciphertext.
Blowfish should not be used today. Although it has no direct vulnerability, its small block size may open you up to different protocol based vulnerabilities. It would be advisable to use a block cipher with a block size of 128-bit. AES comes to mind.
Combining the answers from #Artjom B. and #The 5th column mouse you get a file encryption program that will encrypt a file with Blowfish in CBC mode. The encryption and decryption is done in chunks so large files (up to some GB) could get encrypted and decrypted without "out of memory errors".
The key is generated randomly, and you should keep in mind - without knowledge of the key no decryption of the file is possible.
output:
file encryption with Blowfish CBC mode
used key (Base64): jsErS04so1NCC7Jmds6Grr+0tPkNoaj0hx/izLaW5H8=
result encryption: true
result decryption: true
Security warning: the code has no exception handling, no correct file handling (e.g. overwriting without notice) and is for educational purpose only:
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class BlowfishCbcFileEncryption {
public static void main(String[] args) throws NoSuchPaddingException, NoSuchAlgorithmException, IOException,
InvalidKeyException, InvalidAlgorithmParameterException {
System.out.println("file encryption with Blowfish CBC mode");
String uncryptedFilename = "uncrypted.txt";
String encryptedFilename = "encrypted.enc";
String decryptedFilename = "decrypted.txt";
// random blowfish 256 key
byte[] key = new byte[32];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(key);
System.out.println("used key (Base64): " + base64Encoding(key));
// random iv
byte[] iv = new byte[8]; // blowfish iv is 8 bytes long
secureRandom.nextBytes(iv);
boolean result;
result = encryptCbcFileBufferedCipherOutputStream(uncryptedFilename, encryptedFilename, key, iv);
System.out.println("result encryption: " + result);
result = decryptCbcFileBufferedCipherInputStream(encryptedFilename, decryptedFilename, key);
System.out.println("result decryption: " + result);
}
public static boolean encryptCbcFileBufferedCipherOutputStream(String inputFilename, String outputFilename, byte[] key, byte[] iv)
throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
try (FileInputStream in = new FileInputStream(inputFilename);
FileOutputStream out = new FileOutputStream(outputFilename);
CipherOutputStream encryptedOutputStream = new CipherOutputStream(out, cipher);) {
out.write(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "Blowfish");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] buffer = new byte[8096];
int nread;
while ((nread = in.read(buffer)) > 0) {
encryptedOutputStream.write(buffer, 0, nread);
}
encryptedOutputStream.flush();
}
if (new File(outputFilename).exists()) {
return true;
} else {
return false;
}
}
public static boolean decryptCbcFileBufferedCipherInputStream(String inputFilename, String outputFilename, byte[] key) throws
IOException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidAlgorithmParameterException {
byte[] iv = new byte[8]; // blowfish iv is 8 bytes long
Cipher cipher = Cipher.getInstance("Blowfish/CBC/PKCS5Padding");
try (FileInputStream in = new FileInputStream(inputFilename); // i don't care about the path as all is local
CipherInputStream cipherInputStream = new CipherInputStream(in, cipher);
FileOutputStream out = new FileOutputStream(outputFilename)) // i don't care about the path as all is local
{
byte[] buffer = new byte[8192];
in.read(iv);
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "Blowfish");
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
int nread;
while ((nread = cipherInputStream.read(buffer)) > 0) {
out.write(buffer, 0, nread);
}
out.flush();
}
if (new File(outputFilename).exists()) {
return true;
} else {
return false;
}
}
private static String base64Encoding(byte[] input) {
return Base64.getEncoder().encodeToString(input);
}
}
Each time you convert string into the byte array, you use default file encoding from your VM properties which is not UTF-8.
So, to fix this issue you have two options: to define the default encoding in java system properties:
System.setProperty("file.encoding", StandardCharsets.UTF_8.name());
or add the charset encoding by each converting of strings into bytes:
"writing a file using encryption ".getBytes(StandardCharsets.UTF_8);
I had a Java code to decrypt password stored in connections XML for SQL Developer V17.
Looks like the encryption method or details are changed for V19.
After updating to SQL Developer V19, the same old code is not working.
Anyone know how to decrypt the password stored in connections XML for SQL Developer V19?
Get encrypted password from connections.json file from location :
C:\Users<User name>\AppData\Roaming\SQL
Developer\system18.4.0.376.1900\o.jdeveloper.db.connection Example :
"password": "Ehi21wFkasdfc=",
Get the ecryption key from the file
product-preferences.xml in location C:\Users<user
name>\AppData\Roaming\SQL
Developer\system18.4.0.376.1900\o.sqldeveloper
Example :
decrypt the password using the below website.
https://abskmj.github.io/sqldev-pw-decryptor/
Alternative way to do it in local machine:
Get encrypted password from connections.json file from location :
C:\Users\AppData\Roaming\SQL
Developer\system18.4.0.376.1900\o.jdeveloper.db.connection Example :
"password": "Ehi21wFkasdfc=",
Get the ecryption key from the file product-preferences.xml in
location C:\Users\AppData\Roaming\SQL
Developer\system18.4.0.376.1900\o.sqldeveloper
Use the following code to decrypt the password
package utils;
import java.security.MessageDigest;
import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.util.Base64;
public class Decrypt_OracleSqlDevPW {
private static byte[] des_cbc_decrypt(
byte[] encrypted_password,
byte[] decryption_key,
byte[] iv)
throws GeneralSecurityException
{
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryption_key, "DES"), new IvParameterSpec(iv));
return cipher.doFinal(encrypted_password);
}
private static byte[] decrypt_v4(
byte[] encrypted,
byte[] db_system_id)
throws GeneralSecurityException
{
byte[] encrypted_password = Base64.getDecoder().decode(encrypted);
byte[] salt = DatatypeConverter.parseHexBinary("051399429372e8ad");
// key = db_system_id + salt
byte[] key = new byte[db_system_id.length + salt.length];
System.arraycopy(db_system_id, 0, key, 0, db_system_id.length);
System.arraycopy(salt, 0, key, db_system_id.length, salt.length);
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
for (int i=0; i<42; i++) {
key = md.digest(key);
}
// secret_key = key [0..7]
byte[] secret_key = new byte[8];
System.arraycopy(key, 0, secret_key, 0, 8);
// iv = key [8..]
byte[] iv = new byte[key.length - 8];
System.arraycopy(key, 8, iv, 0, key.length - 8);
return des_cbc_decrypt(encrypted_password, secret_key, iv);
}
public static void main(String[] argv) { try {
String encryptedPassword = "wwA5s2bU02lfY4aTg==";// password
String key = "8397631a-9e0f-42-afda-3fafcac1f193";//key in our local machine
byte[] encrypted = encryptedPassword.getBytes();
byte[] db_system_id = key.getBytes();
byte[] x = decrypt_v4(encrypted, db_system_id);
String password = new String(x);
System.out.println(password);
}
catch (Exception e) {
System.out.println(e.toString());
}
}
}
I found this well written example of how to use AES encryption, and I will admit some of the more advanced aspects are beyond me. The class works fine, provided I'm using the same instance object. If I create another object, using the same exact passPhrase - that object can no longer properly decode any kind of string or data that the previous object created. I can only conclude that since this code takes the rather weak passPhrase string, mixes SALT, and builds a stronger 128-bit key - that this process of key construction is somehow randomized each time around. The significance being:
new AESEncrypter("MyPassword") <> new AESEncrypter("MyPassword")
Could someone help me modify the class below to get the desired behavior:
AESEncrypter a = new AESEncrypter("MyPassword")
String encoded = a.encrypt("my message")
AESEncrypter b = new AESEncrypter("MyPassword")
b.decrypt(encoded) == "my message"
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class AESEncrypter {
private static final byte[] SALT = {
(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
(byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03
};
private static final int ITERATION_COUNT = 65536;
private static final int KEY_LENGTH = 128;
public Cipher ecipher;
public Cipher dcipher;
AESEncrypter(String passPhrase) throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), SALT, ITERATION_COUNT, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
// I Think the problem is here???
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
ecipher.init(Cipher.ENCRYPT_MODE, secret);
dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
}
public String encrypt(String encrypt) throws Exception {
byte[] bytes = encrypt.getBytes("UTF8");
byte[] encrypted = encrypt(bytes);
return new BASE64Encoder().encode(encrypted);
}
public byte[] encrypt(byte[] plain) throws Exception {
return ecipher.doFinal(plain);
}
public String decrypt(String encrypt) throws Exception {
byte[] bytes = new BASE64Decoder().decodeBuffer(encrypt);
byte[] decrypted = decrypt(bytes);
return new String(decrypted, "UTF8");
}
public byte[] decrypt(byte[] encrypt) throws Exception {
return dcipher.doFinal(encrypt);
}
public static void main(String[] args) throws Exception {
String message = "MESSAGE";
String password = "PASSWORD";
AESEncrypter encrypter1 = new AESEncrypter(password);
AESEncrypter encrypter2 = new AESEncrypter(password);
String encrypted1 = encrypter1.encrypt(message);
String encrypted2 = encrypter2.encrypt(message);
System.out.println("Display Encrypted from object 1 and 2..why do they differ?" );
System.out.println(encrypted1) ;
System.out.println(encrypted2) ;
System.out.println("Display Each object decrypting its own encrypted msg. Works as expected" );
System.out.println(encrypter1.decrypt(encrypted1)) ;
System.out.println(encrypter2.decrypt(encrypted2)) ;
System.out.println("Attempt to decrypt the each others msg.. will fail" );
System.out.println(encrypter1.decrypt(encrypted2)) ;
System.out.println(encrypter2.decrypt(encrypted1)) ;
}
}
Display Encrypted from object 1 and 2..why do they differ?
drGy+BNSHPy34NWkkcNqLQ==
9p06VfBgTuh7TizZSbvKjw==
Display Each object decrypting its own encrypted msg. Works as expected
MESSAGE
MESSAGE
Attempt to decrypt the each others msg.. will fail
Error:
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
The problem is that when you initialize a new Cipher in CBC mode, it generates a fresh and random IV for you. This Initialization Vector doesn't have to be secret, but it has to be unpredictable to provide semantic security. You can simply put the IV in front of the ciphertext and use it for decryption.
public byte[] encrypt(byte[] plain) throws Exception {
byte[] iv = ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
byte[] ct = ecipher.doFinal(plain);
byte[] result = new byte[ct.length + iv.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(ct, 0, result, iv.length, ct.length);
return result;
}
public byte[] decrypt(byte[] encrypt) throws Exception {
byte[] iv = new byte[dcipher.getBlockSize()];
byte[] ct = new byte[encrypt.length - dcipher.getBlockSize()];
System.arraycopy(encrypt, 0, iv, 0, dcipher.getBlockSize());
System.arraycopy(encrypt, dcipher.getBlockSize(), ct, 0, ct.length);
dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
return dcipher.doFinal(ct);
}
You will need to store secret during the initialization step in a private variable for the decryption to work.
Keep in mind that the salt for PBDKF2 should also be random and 16 bytes long. You can store it alongside of the IV.
I'm writing a program which takes as input from the console - the name of a zip file, name of a zip file to be made containing the (de/en)crypted files generated from the first zip and a file containing the public key. I get the exception when decrypting:
exception Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:380)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:291)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:363)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at com.Main.decrypt(Main.java:67)
at com.Main.main(Main.java:201)
Can't figure out why I get this exception?
Public key:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCE3pA746UfpC8sFk8ZJp0yupyJqj5jy6cjdxUYoP7mCm7c0mqQDeCcDNBYW2eSozCioPrH/9L+CDQEPLYakoem+jFnUKDH5+pru/0PJTJJF8Xh/ZT9eJlvsYBr1/qSfICf6RTs7kzwq9IuSZBw7/tfNEF9i0A8FVox6HOopXod1QIDAQAB
Private key:
MIICXQIBAAKBgQCE3pA746UfpC8sFk8ZJp0yupyJqj5jy6cjdxUYoP7mCm7c0mqQDeCcDNBYW2eSozCioPrH/9L+CDQEPLYakoem+jFnUKDH5+pru/0PJTJJF8Xh/ZT9eJlvsYBr1/qSfICf6RTs7kzwq9IuSZBw7/tfNEF9i0A8FVox6HOopXod1QIDAQABAoGANOFrYBqK5lvu1koOswDWQZFZqcSSzh8IZyoGwGWa7S0r0EECXlDXmuPSq8e9IfRG8ALHrH+ZlrbnFOSgyVSWHfpj3aH+qknoSX5TW2rMQHih8865xuqheMQ+RTZ7+BRDqNsYkzxB/Z8mqzpoJQSYf+H7nWxdDCgAJVYZzxl3DmUCQQD32iEjnwiwUjii8slcmvCEZl+z84DWNdvJOg6Z38sI4AvrfpKc1WAcDg1rNZCKrRgokh54wpLt08cpFcrD04c3AkEAiTzDmc0bdgfg5wj6xHFZpYlBwiGm/bjOR2PS57P0GNU5PsDllRbFqIuzArITutO5lvZZImzuYz7Lf+cQ73pxUwJBAOdEwmdaneDo17A0m2+to3/nhqWDMVSwLMU3RyiNigZeCMFU+bkd4PBMrHi9IoJDwacZsRU9eZwxYEUV8H2Jg0ECQEEkOqRSm2pXKwX/WSjNtQPCNxhy6NUeV6vDUmTxIjh3XYjP/ynZeVEbnoj1BjB0N2/U11Jj6nPpZqb7gyppMEkCQQCoGdVYDipU+hMMnvxa0zOIyQc/a+HE0lESqn+2ZPafYi9Z1RldRMvUXhP8U7s+OuhRwprdw2ivvOFrnWyz9lL2
The code for the program is bellow . Any help is wellcomed :)
package com;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Enumeration;
import java.util.Scanner;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import javax.crypto.Cipher;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
public class Main {
public final static int BUFFER_SIZE = 117;
public static void decrypt(String originalZipFileName, String newZipFileName, String privateKeyFileName) throws Exception {
byte[] buffer = new byte[128];
ZipFile originalZipFile = new ZipFile(originalZipFileName);
ZipOutputStream newZipFile = new ZipOutputStream(new FileOutputStream(newZipFileName));
Enumeration<? extends ZipEntry> zipEntries = originalZipFile.entries();
String privateKey = getKeyString(privateKeyFileName);
PrivateKey key = makePrivateKey(privateKey);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
while(zipEntries.hasMoreElements()){
ZipEntry entry = zipEntries.nextElement();
ZipEntry copy = new ZipEntry(entry.getName());
newZipFile.putNextEntry(copy);
InputStream inputEntry = originalZipFile.getInputStream(entry);
while(inputEntry.read(buffer) != -1){
newZipFile.write(cipher.doFinal(buffer));
}
newZipFile.closeEntry();
inputEntry.close();
}
newZipFile.close();
originalZipFile.close();
}
public static void encrypt(String originalZipFileName, String newZipFileName, String publicKeyFileName) throws Exception{
byte[] buffer = new byte[BUFFER_SIZE];
ZipFile originalZipFile = new ZipFile(originalZipFileName);
ZipOutputStream newZipFile = new ZipOutputStream(new FileOutputStream(newZipFileName));
Enumeration<? extends ZipEntry> zipEntries = originalZipFile.entries();
String publicKey = getKeyString(publicKeyFileName);
PublicKey key = makePublicKey(publicKey);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, key);
while(zipEntries.hasMoreElements()){
ZipEntry entry = zipEntries.nextElement();
ZipEntry copy = new ZipEntry(entry.getName());
newZipFile.putNextEntry(copy);
InputStream inputEntry = originalZipFile.getInputStream(entry);
while(inputEntry.read(buffer) != -1){
newZipFile.write(cipher.doFinal(buffer));
}
newZipFile.closeEntry();
inputEntry.close();
}
newZipFile.close();
originalZipFile.close();
}
public static String getKeyString(String fileName){
String key = new String();
try {
BufferedReader buf = new BufferedReader(new FileReader(fileName));
key = buf.readLine();
} catch ( IOException e) {
e.printStackTrace();
}
return key.trim();
}
public static PublicKey makePublicKey(String stored) throws GeneralSecurityException {
byte[] data = Base64.getDecoder().decode(stored);
X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance("RSA");
return fact.generatePublic(spec);
}
public static PrivateKey makePrivateKey(String stored) throws GeneralSecurityException, Exception {
/*byte[] data = Base64.getDecoder().decode(stored);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance("RSA");
return fact.generatePrivate(spec);*/
byte[] data = Base64.getDecoder().decode(stored);
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(0));
ASN1EncodableVector v2 = new ASN1EncodableVector();
v2.add(new ASN1ObjectIdentifier(PKCSObjectIdentifiers.rsaEncryption.getId()));
v2.add(DERNull.INSTANCE);
v.add(new DERSequence(v2));
v.add(new DEROctetString(data));
ASN1Sequence seq = new DERSequence(v);
byte[] privKey = seq.getEncoded("DER");
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privKey);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey key = fact.generatePrivate(spec);
return key;
}
public static void main(String[] args) throws Exception {
Scanner scan = new Scanner(System.in);
System.out.println("Enter type of operation:");
String line = scan.nextLine();
if(line.equals("encrypt")){
System.out.println("Enter name of original ZIP file:");
String originalZipFileName = scan.nextLine();
System.out.println("Enter name of new ZIP file:");
String newZipFileName = scan.nextLine();
System.out.println("Enter name of file containg public key:");
String publicKeyFileName = scan.nextLine();
encrypt(originalZipFileName, newZipFileName, publicKeyFileName);
}
if(line.equals("decrypt")){
System.out.println("Enter name of original ZIP file:");
String originalZipFileName = scan.nextLine();
System.out.println("Enter name of new ZIP file:");
String newZipFileName = scan.nextLine();
System.out.println("Enter name of file containg private key:");
String privateKeyFileName = scan.nextLine();
decrypt(originalZipFileName, newZipFileName, privateKeyFileName);
}
}
}
PS: Updated decrypt method. Still gives same error.
public static void decrypt(String originalZipFileName, String newZipFileName, String privateKeyFileName) throws Exception {
byte[] buffer = new byte[128];
ZipFile originalZipFile = new ZipFile(originalZipFileName);
ZipOutputStream newZipFile = new ZipOutputStream(new FileOutputStream(newZipFileName));
Enumeration<? extends ZipEntry> zipEntries = originalZipFile.entries();
String privateKey = getKeyString(privateKeyFileName);
PrivateKey key = makePrivateKey(privateKey);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
while(zipEntries.hasMoreElements()){
ZipEntry entry = zipEntries.nextElement();
ZipEntry copy = new ZipEntry(entry.getName());
newZipFile.putNextEntry(copy);
InputStream inputEntry = originalZipFile.getInputStream(entry);
while(inputEntry.read(buffer) != -1){
newZipFile.write(cipher.doFinal(buffer));
}
newZipFile.closeEntry();
inputEntry.close();
}
newZipFile.close();
originalZipFile.close();
}
Jozef is right.
When you create cipher with default parameters, it defaults to "RSA/ECB/PKCS1Padding". You should specify padding explicitly, if you don't like nasty surprises. Because other security providers might have different default parameters. And you never know in advance which security settings each specific JRE has.
So PKCS1 padding adds 11 bytes to your original data increasing it from 117 bytes to 128 bytes. You should take into account that these numbers are specific to 1024 bit RSA keys (which are marginally secure) and will be different for longer keys. Since you are loading the key from a file consider checking its length.
#Test
public void testPadding() throws Exception {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024, random);
KeyPair keyPair = keyGen.generateKeyPair();
/* constant 117 is a public key size - 11 */
byte[] plaintext = new byte[117];
random.nextBytes(plaintext);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] ciphertext = cipher.doFinal(plaintext);
System.out.println(plaintext.length + " becomes " + ciphertext.length);
}
This prints
117 becomes 128
And finally, consider using AES instead of RSA for file encryption.
So to fix the problem you need to use buffer of size public key length - 11 (117) for encryption and public key size (128) for decryption.
Change
outputFile.write(cipher.doFinal(buffer), 0, read);
to
outputFile.write(cipher.doFinal(buffer));
because buffer read is 117 bytes and size of doFinal result is 128 bytes.
Also you need to buffer input streams. When you are reading from file, it can be slow sometimes and then InputStream will read less data than buffer may contain. By using BufferedInputStream one ensures that there is enough data before read call returns. However, for decryption it's crucial to have the full block of data
InputStream inputEntry = new BufferedInputStream(originalZipFile.getInputStream(entry));
while((read = inputEntry.read(buffer)) != -1){
outputFile.write(cipher.doFinal(buffer), 0, read);
}
You have a problem here. read is the size of the plaintext that was read, not the ciphertext. You should remove the 2nd and 3rd parameters altogether.
It is also a waste of time and space to write the ciphertext to an intermediate file. Just write it straight to the zip stream.
The decrypt method's byte array should be 256 bytes in length as it is the default output size of the algorithm (The extra bytes result in this length). Change byte[] buffer = new byte[128]; to byte[] buffer = new byte[256];.
PHP Encrypt Function
$privateKey = "1234567812345678";
$iv = "1234567812345678";
$data = "Test string";
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $privateKey, $data, MCRYPT_MODE_CBC, $iv);
echo(base64_encode($encrypted));
Result: iz1qFlQJfs6Ycp+gcc2z4w==
When I try to decrypt this result in Java using the function below, all I get back is ì�š#ÔBKxnfÈ~¯Ô'M while I am expecting "Test string". Any ideas where I am wrong? Thanks
public static String decrypt() throws Exception{
try{
String Base64EncodedText = "iz1qFlQJfs6Ycp+gcc2z4w==";
String decodedText = com.sun.xml.internal.messaging.saaj.util.Base64.base64Decode(Base64EncodedText);
String key = "1234567812345678";
String iv = "1234567812345678";
javax.crypto.spec.SecretKeySpec keyspec = new javax.crypto.spec.SecretKeySpec(key.getBytes(), "AES");
javax.crypto.spec.IvParameterSpec ivspec = new javax.crypto.spec.IvParameterSpec(iv.getBytes());
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] decrypted = cipher.doFinal(decodedText.getBytes());
String str = new String(decrypted);
return str;
}catch(Exception e){
return null;
}
}
EDIT: As of Java 8 Java now includes an acceptable Base64 class, java.util.Base64.
This line
String decodedText = com.sun.xml.internal.messaging.saaj.util.Base64.base64Decode(Base64EncodedText);
looks wrong. Instead, use the apache commons codec classes or the Harder base64 class. Also the default padding used by mcrypt, zero padding, is arguably wrong and makes it difficult to use the results in other languages. The users comments section for the mcrypt_encrypt web pages has examples of how to do this.
Here is small example that uses the apache commons classes to decrypt your string.
import java.nio.charset.Charset;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
public class AESToy3 {
private static final Charset ASCII = Charset.forName("US-ASCII");
public static void main(String[] args) throws Exception {
String base64Cipher = "iz1qFlQJfs6Ycp+gcc2z4w==";
byte [] cipherBytes = Base64.decodeBase64(base64Cipher);
byte [] iv = "1234567812345678".getBytes(ASCII);
byte [] keyBytes = "1234567812345678".getBytes(ASCII);
SecretKey aesKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NOPADDING");
cipher.init(Cipher.DECRYPT_MODE, aesKey, new IvParameterSpec(iv));
byte[] result = cipher.doFinal(cipherBytes);
System.out.println(Hex.encodeHexString(result));
}
}
this produces the following output:
5465737420737472696e670000000000
which when decoded as ASCII and removing the trailing zeros gives you Test string