Sorry, JAVA beginner here. I was trying out some encryption decryption examples. My methods were supposed to be returning an InputStream and were also supposed to take in an Inputstream as a parameter.
The signature of the method looked like this,
public static InputStream encriptFile(InputStream inputFile).
I researched a bit, and wrote some code confidently, but i don't think the code is properly encrypting a sample file because when I decrypt it and convert into string, it still shows me gibberish. I really don't know what's going wrong with encrypting and decrypting the InputStreams. The Java class looks like this,
private static final String key = "aesEncryptionKey";
private static final String initVector = "encryptionIntVec";
/*
* Getting a 128 bit key and iv for encryption
*/
public static InputStream encriptFile(InputStream inputFile) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
byte[] nonEncryptedByteArray = IOUtils.toByteArray(inputFile);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec secretkey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); //Cipher instance using AES encryption algorithm
cipher.init(Cipher.ENCRYPT_MODE, secretkey, iv);
byte[] encryptedByteArray = cipher.doFinal(nonEncryptedByteArray);
/*
* Used the cipher library to encrypt the stream to a byte array
*/
InputStream encryptedInputStream = new ByteArrayInputStream(encryptedByteArray);
/*
* Back to streams, but this time encrypted
*/
return encryptedInputStream;
}
public static InputStream decriptFile(InputStream inputFile) throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
byte[] encrytToDecryptByteArray = IOUtils.toByteArray(inputFile);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec secretkey = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretkey, iv);
byte[] decryptedByteArray = cipher.doFinal(encrytToDecryptByteArray);
/*
* dencrypted the encrypted data
*/
InputStream decryptedInputStream = new ByteArrayInputStream(decryptedByteArray);
return decryptedInputStream;
}
The main method looks like this,
File file = new File("test.txt");
InputStream is = new FileInputStream(file);
InputStream eis = encriptFile(is);
StringWriter writer = new StringWriter();
IOUtils.copy(eis, writer, "UTF-8");
String theString = writer.toString();
System.out.print(theString);
The contents of the text file are "Hello, file to be encrypted. Let's see if this works.".
The output which should have printed out an encrypted output looks like this.
��T��� ���N�?]�7!2. When I go ahead and decrypt it, it still shows me gibberish. Sorry for the really long question, any help is appreciated.
You should not return input streams at all. And the way you are using the streams, you're not actually streaming. If you have to use a stream, use CipherInputStream. Personally I'd always use CipherOutputStream for encryption and CipherInputStream for decryption (you are not likely to do anything with the encrypted data, after all, other than exporting it from your application).
A cipher furthermore returns binary data. That's not the same as UTF-8, and no encoding should be necessary for files either, as they accept binary data directly. This is likely the current problem. Just use FileOutputStream / FileInputStream instead of writers or readers.
I tested your code and I think you are printing the encrypted value (so, gibberish) and not the decrypted.
If you update the main to:
public static void main(String[] args) throws Exception {
InputStream is = new FileInputStream(new File("test.txt"));
InputStream eis = encriptFile(is);
InputStream result = decriptFile(eis); // <-- Decryption here
StringWriter writer = new StringWriter();
IOUtils.copy(result, writer, "UTF-8");
String theString = writer.toString();
System.out.print(theString);
}
You should be fine.
I tested just by changing the decryptFile() method to:
public static InputStream decriptFile(InputStream inputFile) throws Exception {
byte[] encrytToDecryptByteArray = new byte[inputFile.available()];
inputFile.read(encrytToDecryptByteArray);
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes(StandardCharsets.UTF_8));
SecretKeySpec secretkey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES);
Cipher cipher = Cipher.getInstance(AES_CBC_PKCS_5_PADDING);
cipher.init(Cipher.DECRYPT_MODE, secretkey, iv);
byte[] decryptedByteArray = cipher.doFinal(encrytToDecryptByteArray);
System.out.println(new String(decryptedByteArray));
return new ByteArrayInputStream(decryptedByteArray);
}
And calling it with the result from the encriptFile() and it worked properly.
Related
I wrote a simple Encryption and Decryption helper class for my android app to encrypt and store Strings securely.
It consists of a single static public method to encrypt, then it calls a private static method to decrypt the encrypted message and returns it. I wrote the method this way to check if the message is intact after encryption/decryption.
I wrote a simple JUnit test with a String and called AssertEquals on the String before and after sending it to the Crypto encryption method.
I get this following errors from running the test:
javax.crypto.AEADBadTagException: Tag mismatch!
The error stack:
at com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:571)
at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1046)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:983)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:845)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at util.Crypto.decrypt(Crypto.java:94)
at util.Crypto.encrypt(Crypto.java:64)
at com.example.ali.meappley.CryptoTest.encryptAndDecryptTest(CryptoTest.java:29)
I'm new to cryptography, but I read different stackoverflow replies and couldn't find anything of help. Some users suggested calling cipher.update(someByteArray) before calling cipher.doFinal(someByteArray) but I couldnt manage to get it working. Any suggestions?
This is my helper class
public class Crypto {
//public methods
//public static encrypt method
public static String encrypt(String messageToEncrypt, #Nullable byte[] associatedData) throws NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidAlgorithmParameterException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException {
byte[] plainBytes = messageToEncrypt.getBytes();
/////////////////////////////////////////////////////////////////
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[16];
secureRandom.nextBytes(key);
SecretKey secretKey = new SecretKeySpec(key, "AES");
byte[] iv = new byte[12]; //NEVER REUSE THIS IV WITH SAME KEY
secureRandom.nextBytes(iv);
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //128 bit auth tag length
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
if (associatedData != null) {
cipher.updateAAD(associatedData);
}
byte[] cipherText = cipher.doFinal(plainBytes);
ByteBuffer byteBuffer = ByteBuffer.allocate(4 + iv.length + cipherText.length);
byteBuffer.putInt(iv.length);
byteBuffer.put(iv);
byteBuffer.put(cipherText);
byte[] cipherMessage = byteBuffer.array();
Arrays.fill(key,(byte) 0); //overwrite the content of key with zeros
///////////////////////////////////////////////////////////////////
byte[] decrypted = decrypt(cipherMessage, null, key);
return decrypted.toString();
}
//public static decrypt method
private static byte[] decrypt(byte[] cipherMessage, #Nullable byte[] associatedData, byte[] key) throws NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidAlgorithmParameterException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException {
ByteBuffer byteBuffer = ByteBuffer.wrap(cipherMessage);
int ivLength = byteBuffer.getInt();
if(ivLength < 12 || ivLength >= 16) { // check input parameter
throw new IllegalArgumentException("invalid iv length");
}
byte[] iv = new byte[ivLength];
byteBuffer.get(iv);
byte[] cipherText = new byte[byteBuffer.remaining()];
byteBuffer.get(cipherText);
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv));
if (associatedData != null) {
cipher.updateAAD(associatedData);
}
cipher.update(cipherText);
byte[] plainText= cipher.doFinal(cipherText);
return plainText;
}
There are a few issues with your code:
1) In your encrypt-method remove the following line (or shift it behind the decrypt-call).
Arrays.fill(key, (byte) 0); // overwrite the content of key with zeros
Otherwise the key for encryption and decryption differ.
2) In your encrypt-method also pass the associatedData in your decrypt-call i.e. replace
byte[] decrypted = decrypt(cipherMessage, null, key);
with
byte[] decrypted = decrypt(cipherMessage, associatedData, key);
The associatedData passed for encryption and decryption have to match for validity. For the purpose of the associatedData see e.g. https://crypto.stackexchange.com/questions/6711/how-to-use-gcm-mode-and-associated-data-properly
3) In your decrypt-method remove the line
cipher.update(cipherText);
For the purpose of the update-method see e.g. What does cipher.update do in java?
All three issues give rise to an AEADBadTagException.
4) I suspect for testing purposes your encrypt-method returns decrypted.toString() which however only gives you the object's class and hashcode. It would make more sense to return e.g. new String(decrypted).
I'm having some issues with my code whereby I am not using the same IV for encryption and decryption. I know in order to do this correctly I have to write the IV to my output file before the data however I am struggling implementing this. Could anyone help me with this issue?
Edited code code again to show full scope
public class TestFileEncryption {
private static void mainCrypto(int cipherMode, File inputFile, File outputFile) throws Exception{
//Let the user enter the key they wish to use
Key secretKey = new SecretKeySpec(UITest.getStoreKey().getBytes(), UITest.getSendAlg()); //Generates a key based on the default keysize for the specified algorithm
//Generate an Initialization Vector (IV)
final int ALG_KEYLENGTH = UITest.getStoreKey().length(); //Change this as desired for the security level you want
byte[] iv = new byte[ALG_KEYLENGTH]; //Save the IV bytes or send it in plaintext with the encrypted data so you can decrypt the data later
SecureRandom prng = new SecureRandom(); //Use SecureRandom to generate random bits. The size of the IV matches the blocksize of the cipher
prng.nextBytes(iv); //Construct the appropriate IvParameterSpec object for the data to pass to Cipher's init() method
//Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode
//Initialize the Cipher for Encryption
cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));
//Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length() - ALG_KEYLENGTH];
inputStream.read(iv);
inputStream.read(inputBytes);
byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(iv);
outputStream.write(outputBytes);
inputStream.close();
outputStream.close();
}
public static void encrypt(File inputFile, File outputFile) throws Exception {
mainCrypto(Cipher.ENCRYPT_MODE, inputFile, outputFile); //ENC_MODE = Constant used to initialize cipher to encryption mode.
}
public static void decrypt(File inputFile, File outputFile) throws Exception {
mainCrypto(Cipher.DECRYPT_MODE, inputFile, outputFile); //ENC_MODE = Constant used to initialize cipher to encryption mode.
}
public static void main(String[] args) {}
}
You just have to write the IV before the ciphertext:
outputStream.write(iv);
outputStream.write(outputBytes);
Then, when decrypting, read the IV and the ciphertext:
byte[] iv = new byte[ALG_BLOCKSIZE];
byte[] inputBytes = new byte[(int) inputFile.length() - ALG_BLOCKSIZE];
inputStream.read(iv);
inputStream.read(inputBytes);
Here ALG_BLOCKSIZE needs to be 16 for AES-CBC.
Just extending answer of #Javier.
it looks like you'd like to use the same method for encryption and decrpytion (depending on the mode) however there's a difference in handling the IV.
You generated a random IV, then you overwrote it with the input of the (plain) input and at the end you wrote it to the output (regardless it's decryption).
So you have to distinguish if the mode is
encryption - the IV is generated and written to the output before ciphertext
decryption - the IV is read from the input and used for decryption, but not written to the output
something like that:
private void encrypt(File inputFile, File outputFile) {
//Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length()];
byte[] iv = new byte[16]; // 16 for AES-CBC
SecureRandom prng = new SecureRandom(); //Use SecureRandom to generate random bits. The size of the IV matches the blocksize of the cipher
prng.nextBytes(iv); //Construct the appropriate IvParameterSpec object for the data to pass to Cipher's init() method
//Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode
//Initialize the Cipher for Encryption
cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));
inputStream.read(inputBytes);
byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(iv);
outputStream.write(outputBytes);
outputStream.flush();
inputStream.close();
outputStream.close();
}
}
private void decrypt(File inputFile, File outputFile) {
//Declare / Initialize the Data, Convert the Input to Bytes and encrypt or decrypt using doFinal.
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length()-16];
byte[] iv = new byte[16]; // 16 for AES-CBC
//Create a Cipher by specifying the following parameters: Alg name, Mode (CBC), Padding (PKC7/PKCS5)
Cipher cipherForEncryption = Cipher.getInstance(UITest.getSendAlg() + "/CBC/PKCS5PADDING"); // Must specify the mode explicitly as most JCE providers default to ECB mode
//Initialize the Cipher for Encryption
cipherForEncryption.init(cipherMode, secretKey, new IvParameterSpec(iv));
inputStream.read(iv);
inputStream.read(inputBytes);
byte[] outputBytes = cipherForEncryption.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(outputBytes);
outputStream.flush();
inputStream.close();
outputStream.close();
}
To leave out some detail, maybe you could directly use Java CipherOutputStream and CiptherInputStream and the implementation will handle these details for you (if you don't care about exact format).
Next what are you missing is an authentication tag, at least hash of the plaintext assuring integrity of the ciphertext. (it's called authenticated encryption)
I am trying to do a simple AES encryption in Java, using Java Cryto, that can then be decrypted in ObjectiveC, using OpenSSL.
as I am not doing the ObjectiveC side, I want to make sure it works, using the openSSL command line, but I always get "bad magic number"
Here is my Java code
public class EncryptionUtils {
private static final String AES_CIPHER_METHOD = "AES";
private static final int AES_KEY_SIZE = 128;
public static byte[] generateAesKey() throws NoSuchAlgorithmException {
KeyGenerator keyGenerator = KeyGenerator.getInstance(AES_CIPHER_METHOD);
keyGenerator.init(AES_KEY_SIZE);
SecretKey key = keyGenerator.generateKey();
return key.getEncoded();
}
public static SecretKeySpec createAesKeySpec(byte[] aesKey) {
return new SecretKeySpec(aesKey, AES_CIPHER_METHOD);
}
public static void aesEncryptFile(File in, File out, SecretKeySpec aesKeySpec) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IOException {
Cipher aesCipher = Cipher.getInstance(AES_CIPHER_METHOD);
aesCipher.init(Cipher.ENCRYPT_MODE, aesKeySpec);
InputStream inputStream = new FileInputStream(in);
try {
OutputStream outputStream = new CipherOutputStream(new FileOutputStream(out), aesCipher);
try {
IOUtils.copy(inputStream , outputStream);
} finally {
outputStream.close();
}
} finally {
inputStream.close();
}
}
}
//testcode
#Test
public void testAesEncryptFile() throws IOException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
byte[] aesKey = EncryptionUtils.generateAesKey();
SecretKeySpec aesKeySpec = EncryptionUtils.createAesKeySpec(aesKey);
EncryptionUtils.aesEncryptFile(new File("C:\\test\\test.txt"), new File("C:\\test\\test-encrypted.txt"), aesKeySpec);
FileOutputStream outputStream = new FileOutputStream("C:\\test\\aes.key");
outputStream.write(aesKey);
outputStream.close();
}
#Test
public void testAesDecryptFile() throws IOException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
FileInputStream keyFis = new FileInputStream("C:\\test\\aes.key");
ByteArrayOutputStream keyBaos = new ByteArrayOutputStream();
IOUtils.copy(keyFis, keyBaos);
SecretKeySpec keySpec = new SecretKeySpec(keyBaos.toByteArray(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
FileInputStream fileInputStream = new FileInputStream("C:\\test\\test-encrypted.txt");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(fileInputStream, baos);
byte[] decrypted = cipher.doFinal(baos.toByteArray());
FileOutputStream outputStream = new FileOutputStream("C:\\test\\test-decrypted.txt");
outputStream.write(decrypted);
outputStream.close();
}
Now that runs as expected, file "test-encrypted.txt" is indeed encrypted, and "test-decrypted.txt" == "test.txt"
I then tried to run a decryption on the command line using OpenSSL
openssl enc -d -aes-128-ecb -in test-encrypted.txt -k aes.key
however, this always give me
bad magic number
From what I can see, the using algorithm "AES" in Java uses "ECB" mode by default, so the above should work. What am I doing wrong.
The problem is indeed due to the key that is computed from the password by OpenSSL.
Most likely the reason is that OpenSSL has its own algorithm to derive a key, EVP_BytesToKey, from the password, and that is not the same as Java's.
The only solution I found was to use a Java reimplementation of that algorithm:
private static final int KEY_LENGTH = 32;
private byte[] deriveKey(String encryptionPassword, byte[] salt) throws NoSuchAlgorithmException {
final byte[] passAndSalt = ArrayUtils.addAll(encryptionPassword.getBytes(), salt);
byte[] hash = new byte[0];
byte[] keyAndIv = new byte[0];
for (int i = 0; i < 3 && keyAndIv.length < KEY_LENGTH; i++) {
final byte[] dataToHash = ArrayUtils.addAll(hash, passAndSalt);
final MessageDigest md = MessageDigest.getInstance("SHA-256");
hash = md.digest(dataToHash);
keyAndIv = ArrayUtils.addAll(keyAndIv, hash);
}
return Arrays.copyOfRange(keyAndIv, 0, KEY_LENGTH);
}
ArrayUtils is part of Apache Commons library.
Full usage:
IvParameterSpec initializationVectorSpec = new IvParameterSpec(
Hex.decodeHex(encryptionInitializationVector.toCharArray()));
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] salt = new SecureRandom().generateSeed(8);
byte[] key = deriveKey(encryptionPassword, salt);
Key keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, initializationVectorSpec);
byte[] rawEncryptedInput = cipher.doFinal(input.getBytes());
byte[] encryptedInputWithPrependedSalt = ArrayUtils.addAll(ArrayUtils.addAll(
"Salted__".getBytes(), salt), rawEncryptedInput);
return Base64.getEncoder()
.encodeToString(encryptedInputWithPrependedSalt);
Credit to this answer for showing me the way.
The problem is with the key. The -k argument expects a passphrase, not a file. In turn, when a passphrase is used by the openssl encryption routine, a magic and salt is put in front of the encrypted result. That's the magic that cannot be found.
To use the openssl command line, print out the key in hex and use the -K option instead of the lowercase -k option.
You could also use:
`cat aes.key`
as argument after -K, given that aes.key contains the key in hexadecimals.
I am doing a simple program to encrypt/decrypt using RSA algorithm in Java. I create a cipher object as follows:
//Create a Cipher object
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/NoPadding");
I do the encryption by calling the encrypt function:
String cipher=encrypt(textByte, pair, rsaCipher);
System.out.println("The Encryption using RSA Algorithm : "+cipher);
And the decryption as:
//Decryption
String plain=decrypt(Base64.decodeBase64(cipher),pair, rsaCipher);
System.out.println("The Decryption using RSA Algorithm : "+plain);
When I display the output, the decryption output returns a long space before the original text:
However, when I edit the code for creating the Cipher object to be:
//Create a Cipher object
Cipher rsaCipher = Cipher.getInstance("RSA");
i.e, removed the operation mode and padding arguments, the problem get resolved and the output becomes:
Where is the problem. In the first case (when the space appears), I specified NoPadding? Why the spaces appears in the decrypted message ? Even if I used padding, I expect this should not happen.
EDIT:
This is the encrypt and decrypt methods:
public static String encrypt(byte[] textBytes, KeyPair pair, Cipher rsaCipher) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
//get the public key
PublicKey pk=pair.getPublic();
//Initialize the cipher for encryption. Use the public key.
rsaCipher.init(Cipher.ENCRYPT_MODE, pk);
//Perform the encryption using doFinal
byte[] encByte = rsaCipher.doFinal(textBytes);
// converts to base64 for easier display.
byte[] base64Cipher = Base64.encodeBase64(encByte);
return new String(base64Cipher);
}//end encrypt
public static String decrypt(byte[] cipherBytes, KeyPair pair, Cipher rsaCipher) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException
{
//get the public key
PrivateKey pvk=pair.getPrivate();
//Create a Cipher object
//Cipher rsaCipher = Cipher.getInstance("RSA/ECB/NoPadding");
//Initialize the cipher for encryption. Use the public key.
rsaCipher.init(Cipher.DECRYPT_MODE, pvk);
//Perform the encryption using doFinal
byte[] decByte = rsaCipher.doFinal(cipherBytes);
return new String(decByte);
}//end decrypt
Your problem is indeed with the padding. Some kind of padding, either PKCS#1 1.5 or OAEP padding in practice, is required for secure RSA functionality. Furthermore, it is required to find the start and end of the encrypted plain text.
The modular exponentiation of RSA is performed using large integers. The results of these operations are then represented as octet strings. These octet strings are basically big endian, unsigned, fixed length representation of an integer. These integers are left padded with 00 valued bytes (this is called the I2OS primitive in the RSA standard). So what you are seeing is the result of the modular exponentiation, with the 00 padding still in place.
Long story short, always use a padding scheme. Nowadays, OAEP would be preferable. Use it together with hybrid encryption scheme, or use a higher level container format such as CMS or PGP.
//This is a complete encryption and decryption module using
//Algorithm: JWEAlgorithm.RSA_OAEP_256
//Encryption Method: A128CBC_HS256
public static String encrypt(String text) throws Exception {
// Set the plain text
Payload payload = new Payload(text);
// Create the header
JWEHeader header = new JWEHeader(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A128CBC_HS256);
// Create the JWE object and encrypt it
JWEObject jweObject = new JWEObject(header, payload);
jweObject.encrypt(new RSAEncrypter(getPublicKey()));
// Serialise to compact JOSE form...
String jweString = jweObject.serialize();
LOG.info("Generated Encrypted Key : {}", jweString);
return jweString;
}
public static String decrypt(String text) throws Exception {
// Parse into JWE object...
JWEObject jweObject = JWEObject.parse(text);
jweObject.decrypt(new RSADecrypter(getPrivateKey()));
// Get the plain text
Payload payload = jweObject.getPayload();
System.out.println(payload.toString());
return payload.toString();
}
private static RSAPublicKey getPublicKey() throws Exception {
String filename = "/home/vaibhav/Setups/cert/pub.der";
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)f.length()];
dis.readFully(keyBytes);
dis.close();
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return (RSAPublicKey) kf.generatePublic(spec);
}
private static RSAPrivateKey getPrivateKey() throws Exception {
String filename = "/home/vaibhav/Setups/cert/private.pkcs8";
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)f.length()];
dis.readFully(keyBytes);
dis.close();
PKCS8EncodedKeySpec spec1 = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) kf.generatePrivate(spec1);
}
I'm facing a rather strange behavior about decrypting in Java. Using the code below
public void decrypt(File file, String output_file_path) throws FileNotFoundException, IOException, GeneralSecurityException {
String hex_enc_key = "346a23652a46392b4d73257c67317e352e3372482177652c";
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(HexParser.fromHexString(hex_enc_key), "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
CipherOutputStream cos = new CipherOutputStream(new FileOutputStream(new File(output_file_path)), cipher);
FileInputStream fis = new FileInputStream(file);
doCopy(fis, cos);
}
I get random exceptions
java.security.InvalidKeyException: Illegal key size or default parameters
I googled the issue and found out about JCE unlimited strength, but I can't understand why I get those random exceptions even if I'm always using the same key (sometimes it works and sometimes not, based on the input file I need to decrypt).
For the sake of clarity I'm using
Cipher.getMaxAllowedKeyLength("AES")
to check for JCE limitations, and have no issue whatsoever encrypting with the same settings:
public void encrypt(File file, String output_file_path) throws FileNotFoundException, IOException, GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(HexParser.fromHexString(db_enc_key), "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
CipherInputStream cis = new CipherInputStream(new FileInputStream(file), cipher);
FileOutputStream os = new FileOutputStream(new File(output_file_path));
doCopy(cis, os);
cis.close();
os.close();
}
Can anyone point me in the right direction?
Thanks a lot, nicola