Java RSA Decryption - java

I'm having trouble decrypting some text
public class mainClass {
private static Cipher cipher;
private static KeyPairGenerator keyGen;
private static KeyPair pair;
private static PrivateKey privateKey;
private static PublicKey publicKey;
public static void createKeys() {
pair = keyGen.generateKeyPair();
privateKey = pair.getPrivate();
publicKey = pair.getPublic();
}
public static String encryptText(String msg)
throws NoSuchAlgorithmException, NoSuchPaddingException,
UnsupportedEncodingException, IllegalBlockSizeException,
BadPaddingException, InvalidKeyException {
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return Base64.getEncoder().encodeToString((cipher.doFinal(msg.getBytes("UTF-8"))));
}
public static String decryptText(String msg)
throws InvalidKeyException, UnsupportedEncodingException,
IllegalBlockSizeException, BadPaddingException {
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(msg)), "UTF-8");
}
public static void main(String args[]) throws Exception {
int keylength = 1024;
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(keylength, random);
cipher = Cipher.getInstance("RSA");
createKeys();
FileInputStream f = new FileInputStream("C:\\Users\\caleb.baker\\Downloads\\test100k.db");
byte[] b = new byte[f.available()];
int offset = 0;
String asf = "";
f.read(b);
String test = new String(b);
for (int x = 0; x < b.length; x += 117) {
try {
asf += encryptText(test.substring(x, x + 117));
} catch (StringIndexOutOfBoundsException e) {
asf += encryptText(test.substring(x));
}
}
String t = asf;
asf = "";
for (int x = 0; x < t.length(); x += 117) {
System.out.println("run");
try {
asf += decryptText(test.substring(x, x + 117));
} catch (StringIndexOutOfBoundsException e) {
asf += decryptText(test.substring(x));
}
}
}
}
The error i get is
Exception in thread "main"
java.lang.IllegalArgumentException: Last unit does not have enough valid bits
at java.util.Base64$Decoder.decode0(Unknown Source)
at java.util.Base64$Decoder.decode(Unknown Source)
at java.util.Base64$Decoder.decode(Unknown Source)
at mainClass.decryptText(mainClass.java:60)
at mainClass.main(mainClass.java:162)
I'm pretty new to encryption. the symmetric AES worked great, and I know its faster but for my own curiosity I want to time RSA. Another error I will get is a bad padding exception. I have been working on it some but I think that may be laying around in my code somewhere waiting to get me.
Also the Encryption works fine. I did not check to see if it was correct but I did print out the length to make sure the string was long enough. the loop for decryption never makes it past the first loop

there are multiple issues with your program
as already commented - wrong keys are used (private key is to be used to decrypt, public key is to be used to encrypt)
RSA is to be used to encrypt only limited amount of data, not text of any length. It can be used that way, but under some circumstances it may be not secure
Last unit does not have enough valid bits means the base64 encoded value is not valid. Decoding base64 means transforming 4 bytes of 6 bit encoding to 3 bytes of 8 bits. so the length of the decoded part must be multiplier of 4 (which 1177 is not) the same goes with the encryption loop, just joining multiple base64 string doesn't necessary result in valid b64 value.
I did not get purpose of the loops in the program., imho it is overcomplicated, you ought to keep it simple. what were you trying to achieve?
Another error I will get is a bad padding exception. RSA (default cipher RSA/ECB/pkcs1padding) adds padding to the value to be encrypted. So you cannot just take any value and decrypt it otherwise the padding won't be valid (which happens in the loops with some substring)
Some time ago I wrote a blog about encryption in Java you may get some inspiration from

Related

Using decrypted DES key obtained from RSA decryption

I am working on a hybrid encryption mechanism in java that involves encrypting message using 3DES encryption algorithm and then encrypt its key using RSA encryption mechanism at the sender side. Once delivered to the receiver side, the encrypted 3DES key is decrypted using RSA decryption mechanism and then is used to decrypt the cipher text.
Once I obtain the decrypted 3DES key its string value is the same but byte [] is not the same instead returns a 2's complement of the original key.
How can I get the decrypted 3DES to be the same as the originally generated 3DES in byte [] form at the receiver side?
Below is the code I am using for my hybrid encryption mechanism:
package hybrid_implementation;
import java.security.Key;
import java.security.InvalidKeyException;
import java.security.spec.InvalidKeySpecException;
import java.security.NoSuchAlgorithmException;
import java.util.Scanner;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.IllegalBlockSizeException;
public class Hybrid_Implementation {
//RSA_Encryption Algorithm Required Variables
private static final BigInteger one = new BigInteger("1");
private static final SecureRandom random = new SecureRandom();
private BigInteger privatekey;
private BigInteger publickey;
private BigInteger modulus;
//3DES_Encryption Algorithm Required Variables
private byte[] DES_Key;
private SecretKeyFactory keyfactory;
private DESedeKeySpec spec;
private Key deskey;
private int DES_Key_Length;
private byte[] data;
private Cipher cipher;
private String CipherText;
private byte [] CIPHERText;
Hybrid_Implementation() throws InvalidKeyException,
NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException
{
DES_Key_Generator();
RSA_Key_Generator(999);
}
//3DES Encryption-Decryption Algorithm with 2 differnt keys
private String DES_Encryption(String plaintext) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException
{
data = plaintext.getBytes();
cipher.init(Cipher.ENCRYPT_MODE, deskey);
CIPHERText = cipher.doFinal(data);
StringBuilder hexCiphertext = new StringBuilder();
for(int i=0; i<CIPHERText.length; i++)
{
int v = CIPHERText[i] & 0xff;
v+=0x100;
String temp = Integer.toString(v,16);
hexCiphertext.append(temp).substring(1);
}
return hexCiphertext.toString();
}
private String DES_Decryption(byte [] key, byte [] encrypted_text) throws
InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
InvalidKeySpecException
{
spec = new DESedeKeySpec(key);
deskey = keyfactory.generateSecret(spec);
byte[] plaintext = cipher.doFinal(encrypted_text);
StringBuilder decrypttext= new StringBuilder();
for (int i = 0; i < plaintext.length; i++)
decrypttext.append((char) plaintext[i]);
String decrypted_plaintext = decrypttext.toString();
return decrypted_plaintext;
}
private void DES_Key_Generator() throws InvalidKeyException,
NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException
{
Random rnd = new Random();
String key = rnd.toString();
DES_Key = key.getBytes();
spec = new DESedeKeySpec(DES_Key);
keyfactory = SecretKeyFactory.getInstance("desede");
deskey = keyfactory.generateSecret(spec);
cipher = Cipher.getInstance("desede");
}
//RSA Encryption-Decryption Algorithm
private BigInteger RSA_Encryption(BigInteger des_Key ) //RSA Encryption of
3DES Key
{
BigInteger encrypted_DES_Key = des_Key.modPow(publickey, modulus);
return encrypted_DES_Key;
}
private BigInteger RSA_Decryption(BigInteger encrypted_DES_Key) //RSA
Decryption of 3DES Key
{
BigInteger des_Key = encrypted_DES_Key.modPow(privatekey, modulus);
return des_Key;
}
private void RSA_Key_Generator(int number) //RSA Public - Private Key
Generation
{
BigInteger p = BigInteger.probablePrime(number/2,random);
BigInteger q = BigInteger.probablePrime(number/2, random);
BigInteger phi = (p.subtract(one)).multiply(q.subtract(one));
modulus = p.multiply(q);
publickey = new BigInteger("65537");
privatekey = publickey.modInverse(phi);
}
private String encryption(String plaintext) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException
{
String cipher_text = DES_Encryption(plaintext);
BigInteger RSA_DESKey = RSA_Encryption(new BigInteger(DES_Key));
String temp_key = RSA_DESKey.toString();
DES_Key_Length = temp_key.length();
CipherText ="";
CipherText = new
StringBuilder().append(temp_key).append(cipher_text).toString();
return CipherText;
}
private String decryption(String encrypted_text) throws InvalidKeyException,
InvalidKeySpecException, IllegalBlockSizeException, BadPaddingException
{
StringBuilder encryptedkey = new StringBuilder();
for(int i = 0 ; i < DES_Key_Length; i++)
encryptedkey.append (encrypted_text.charAt(i));
StringBuilder cipheredtext = new StringBuilder();
for(int j = DES_Key_Length ; j< encrypted_text.length() ; j++)
cipheredtext.append (encrypted_text.charAt(j));
BigInteger DES_Encrypted_Key = new BigInteger(encryptedkey.toString());
BigInteger DES_KEY = RSA_Decryption(DES_Encrypted_Key);
byte[] decrypt_key = DES_KEY.toByteArray();
String plaintext =
DES_Decryption(decrypt_key,cipheredtext.toString().getBytes());
return plaintext;
}
/**
*
* #param args
* #throws InvalidKeyException
* #throws IllegalBlockSizeException
* #throws BadPaddingException
* #throws java.security.NoSuchAlgorithmException
* #throws java.security.spec.InvalidKeySpecException
* #throws javax.crypto.NoSuchPaddingException
*/
public static void main(String[] args) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException,
InvalidKeySpecException, NoSuchPaddingException {
String plaintext;
Hybrid_Implementation hi = new Hybrid_Implementation ();
Scanner sc = new Scanner(System.in);
System.out.print("Enter Text = ");
plaintext = sc.nextLine();
String encrypted_text = hi.encryption(plaintext);
String decrypted_text = hi.decryption(encrypted_text);
System.out.println("Plain Text Entered = "+plaintext);
System.out.println("Encrypted Text = "+encrypted_text);
System.out.println("Decrypted Text = "+decrypted_text);
}
}
The output I receive is:
enter image description here
Whereas the decrypted text is not the same as the entered plain text
There are multiple (many) issues with your code
The main issue (why you get incorrect decrypted text) is in the ciphertext encoding. The ciphertext you are decrypting with 3DES is different than the one you got from the encryption (your "hex encoding" is simply buggy). Just debug you program (and print the values after encryption and before decryption) and you should find it. I'd advice using something standard, such as working hexadecimal or base64 encoding.
Other issues:
you are using "textbook RSA" which is not really secure, so I hope you are doing that for learning / assignment purposes, not real life encryption application. As already commented, using RSA should be always with padding (e.g. RSA/ECB/PKCS1Padding or OAEP)
Generating random keys - you should use SecureRandom instead of Random (which is not really random) or - much better - a KeyGenerator
DESede without providing any IV is using ECB mode, which has its weaknesses. So using symmetric encryption you should provide random IV and specify the encryption mode explicitly (e.g. DESede/CBC/PKCS5Padding)
for each operation after doFinal() you should use a new Cipher instance with initialized decrpytion mode
I have a blog about encryption with a few examples, you may take inspiration from.

javax.crypto.BadPaddingException during RSA Decryption

In my Java code, I'm trying to encrypt a String using RSA, with a public key. The String is a Base64 encoded String that represents an Image (Image was converted to String). It will be decrypted using a private key.
During the Encryption, I first got an exception "javax.crypto.IllegalBlockSizeException: Data must not be longer than 190 bytes". So, I processed the String (plaintext) in blocks of 189 which then resolved it.
During the Decryption, I got another exception "javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes". So, I processed the byte[] (ciphertext), by converting it to a String first, in blocks of 256 which then resolved it as well.
Again, during my decryption process, I end up getting a "javax.crypto.BadPaddingException: Decryption error" Exception, which I have been unable to resolve.
Upon the recommendation of experts on this site, I used "OAEPWithSHA-256AndMGF1Padding". I even tried using No Padding, after other padding methods, to see if the Exception would go away, but it did not work. What have I done wrong?
I was able to identify that the Exception was thrown at the line - decryptedImagePartial = t.rsaDecrypt(cipherTextTrimmed.getBytes(), privateKey);
- which is in the decryption portion of the main method.
Please bear with me if my coding practices are poor. I'd really prefer to just find out the error behind the exception for now.
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
public class Tester
{
public KeyPair buildKeyPair() throws NoSuchAlgorithmException
{
final int keySize = 2048;
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(keySize);
return keyPairGenerator.genKeyPair();
}
public byte[] encrypt(PublicKey publicKey, String message) throws Exception
{
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(message.getBytes());
}
public String decrypt(PrivateKey privateKey, byte [] encrypted) throws Exception
{
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(encrypted));
}
public byte[] rsaEncrypt(String watermarkMsg, PublicKey publicKey) throws Exception
{
byte[] cipherText = encrypt(publicKey, watermarkMsg);
return cipherText;
}
public String rsaDecrypt(byte[] cipherText, PrivateKey privateKey) throws Exception
{
String plainText = decrypt(privateKey, cipherText);
return plainText;
}
public static void main(String args[]) throws NoSuchAlgorithmException
{
Tester t = new Tester();
String inputImageFilePath = "<file_path_here";
String stringOfImage = null;
byte[] encryptedImage = null;
byte[] encryptedImagePartial = null;
KeyPair keyPair = t.buildKeyPair();
PublicKey pubKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate()
//-----------IMAGE TO STRING CONVERSION----------------
//The imagetostring() function retrieves the image at the file path and converts it into a Base64 encoded String
try
{
stringOfImage = t.imagetostring(inputImageFilePath);
}
catch(Exception e)
{
System.out.println(e.toString());
}
//-----------ENCRYPTION OF STRING----------------
//The encryption is done in blocks of 189, because earlier I got an exception - "javax.crypto.IllegalBlockSizeException: Data must not be longer than 190 bytes"
try
{
String plaintext = stringOfImage;
String plaintextTrimmed = "";
System.out.println(stringOfImage);
encryptedImage = new byte[15512]; //The size is given as 15512 because the length of the particular string was found to be 15512
while(plaintext!="")
{
if(plaintext.length()>189)
{
plaintextTrimmed = plaintext.substring(0, 189);
plaintext = plaintext.substring(189);
}
else
{
plaintextTrimmed = plaintext;
plaintext = "";
}
encryptedImagePartial = t.rsaEncrypt(plaintextTrimmed, pubKey);
encryptedImage = t.concatenate(encryptedImage, encryptedImagePartial);
System.out.println(encryptedImage.length);
}
}
catch(Exception e)
{
System.out.println(e.toString());
}
t.byteDigest(encryptedImage);
//-----------DECRYPTION OF STRING--------------
//The decryption is done in blocks of 189, because earlier I got an exception - "javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes"
try
{
// The ciphertext is located in the variable encryptedImage which is a byte[]
String stringRepOfCipherText = new String(encryptedImage); String cipherTextTrimmed = "";
String decryptedImagePartial;
String decryptedImage = "";
while(stringRepOfCipherText!="")
{
if(stringRepOfCipherText.length()>189)
{
cipherTextTrimmed = stringRepOfCipherText.substring(0, 189);
stringRepOfCipherText = stringRepOfCipherText.substring(189);
}
else
{
cipherTextTrimmed = stringRepOfCipherText;
stringRepOfCipherText = "";
}
decryptedImagePartial = t.rsaDecrypt(cipherTextTrimmed.getBytes(), privateKey);
decryptedImage = decryptedImage + decryptedImagePartial;
}
}
catch(BadPaddingException e)
{
System.out.println(e.toString());
}
catch(Exception e)
{
System.out.println(e.toString());
}
}
}
Also, I noticed a few other examples where KeyFactory was used to generate the keys. Could anyone also tell me the difference between using KeyFactory and what I have used?
You can not cut the ciphertext into arbitrary chunks!
Since you specifically asked for plain RSA without symmetric algorithms involved (which I strongly recommend against!), this is what you need to do:
Find out the maximum payload size for your RSA configuration.
Split your plaintext into chunks of this size
Encrypt each chunk individually and do not simply concatenate them and discard chunk boundaries!
During decryption:
Pass each ciphertext chunk to the decrypt function using the original size it has after encryption. Do not append any data and do not create "substrings".
Concatenate the resulting plaintexts.
Ideally you should use a hybrid encryption scheme:
generate an encryption key (encKey)
encrypt your image using a symmetric algorithm with encKey
encrypt encKey using pubKey with RSA
Symmetric ciphers can be used in different modes of operation, that avoid such length limitations.
First of all, it makes absolutely no sense to first encode the image to base 64. The input of modern ciphers consist of bytes, and images are already bytes. You may want to base 64 encode the ciphertext if you want to store that a string.
The input block size is indeed 190 bytes. You can see a table for RSA / OAEP here (don't forget to upvote!). I'm not sure why you would want to use 189 in that case; my code is however generalized. The output block size is simply the key size for RSA as it is explicitly converted to the key size in bytes (even if it could be smaller).
During decryption you convert the ciphertext to a string. However, string decoding in Java is lossy; if the decoder finds a byte that doesn't represent a character then it is dropped silently. So this won't (always work), resulting for instance in a BadPaddingException. That's OK though, we can keep to binary ciphertext.
So without further ado, some code for you to look at. Note the expansion of the ciphertext with the 66 bytes per block and the poor performance of - mainly - the decryption. Using AES with RSA in a hybrid cryptosystem is highly recommended (and not for the first time for this question).
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import javax.crypto.Cipher;
public class Tester {
private static final int KEY_SIZE = 2048;
private static final int OAEP_MGF1_SHA256_OVERHEAD = 66;
public static KeyPair buildKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(KEY_SIZE);
return keyPairGenerator.generateKeyPair();
}
public static void main(String args[]) throws Exception {
KeyPair keyPair = Tester.buildKeyPair();
RSAPublicKey pubKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// assumes the bitLength is a multiple of 8 (check first!)
int keySizeBytes = pubKey.getModulus().bitLength() / Byte.SIZE;
byte[] image = new byte[1000];
Arrays.fill(image, (byte) 'm');
// --- encryption
final Cipher enc;
try {
enc = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("OAEP with MGF-1 using SHA-256 not available in this runtime", e);
}
enc.init(Cipher.ENCRYPT_MODE, pubKey);
int fragmentsize = keySizeBytes - OAEP_MGF1_SHA256_OVERHEAD;
ByteArrayOutputStream ctStream = new ByteArrayOutputStream();
int off = 0;
while (off < image.length) {
int toCrypt = Math.min(fragmentsize, image.length - off);
byte[] partialCT = enc.doFinal(image, off, toCrypt);
ctStream.write(partialCT);
off += toCrypt;
}
byte[] ct = ctStream.toByteArray();
// --- decryption
Cipher dec = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
dec.init(Cipher.DECRYPT_MODE, privateKey);
ByteArrayOutputStream ptStream = new ByteArrayOutputStream();
off = 0;
while (off < ct.length) {
int toCrypt = Math.min(keySizeBytes, ct.length - off);
byte[] partialPT = dec.doFinal(ct, off, toCrypt);
ptStream.write(partialPT);
off += toCrypt;
}
byte[] pt = ptStream.toByteArray();
// mmmm...
System.out.println(new String(pt, StandardCharsets.US_ASCII));
}
}

Blowfish example where automatically pad and unpad the key to size

Blowfish is capable of strong encryption and can use key sizes up to 56 bytes (a 448 bit key).
The key must be a multiple of 8 bytes (up to a maximum of 56).
I want to write example will automatically pad and unpad the key to size. Because Blowfish creates blocks of 8 byte encrypted output, the output is also padded and unpadded to multiples of 8 bytes.
actually want to write java code to simulate-
http://webnet77.com/cgi-bin/helpers/blowfish.pl
I am using info for tool-
ALGORITM = "Blowfish";
HEX KEY = "92514c2df6e22f079acabedce08f8ac3";
PLAIN_TEXT = "sangasong#song.com"
Tool returns-
CD3A08381467823D4013960E75E465F0B00C5E3BAEFBECBB
Please suggest.
Tried the java code:
public class TestBlowfish
{
final String KEY = "92514c2df6e22f079acabedce08f8ac3";
final String PLAIN_TEXT = "sangasong#song.com";
byte[] keyBytes = DatatypeConverter.parseHexBinary(KEY);
}
public static void main(String[] args) throws Exception
{
try
{
byte[] encrypted = encrypt(keyBytes, PLAIN_TEXT);
System.out.println( "Encrypted hex: " + Hex.encodeHexString(encrypted));
}catch (GeneralSecurityException e)
{
e.printStackTrace();
}
}
private static byte[] encrypt(byte[] key, String plainText) throws GeneralSecurityException
{
SecretKey secret_key = new SecretKeySpec(key, "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish");
cipher.init(Cipher.ENCRYPT_MODE, secret_key);
return cipher.doFinal(plainText.getBytes());
}
Result -
Encrypted hex: 525bd4bd786a545fe7786b0076b3bbc2127425f0ea58c29d
So the script uses an incorrect version of PKCS#7 padding that does not pad when the size of the input is already dividable by the block size - both for the key and the plaintext. Furthermore it uses ECB mode encryption. Neither of which should be used in real life scenarios.
The following code requires the Bouncy Castle provider to be added to the JCE (Service.addProvider(new BouncyCastleProvider())) and that the Hex class of Bouncy Castle libraries is in the class path.
Warning: only tested with limited input, does not cut the key size if the size of the key is larger than the maximum.
WARNING: THE FOLLOWING CODE IS NOT CRYPTOGRAPHICALLY SOUND
import org.bouncycastle.util.encoders.Hex;
public class BadBlowfish {
private static SecretKey createKey(String theKey) {
final byte[] keyData = theKey.getBytes(StandardCharsets.US_ASCII);
final byte[] paddedKeyData = halfPadPKCS7(keyData, 8);
SecretKey secret = new SecretKeySpec(paddedKeyData, "Blowfish");
return secret;
}
private static byte[] halfUnpadPKCS7(final byte[] paddedPlaintext, int blocksize) {
int b = paddedPlaintext[paddedPlaintext.length - 1] & 0xFF;
if (b > 0x07) {
return paddedPlaintext.clone();
}
return Arrays.copyOf(paddedPlaintext, paddedPlaintext.length - b);
}
private static byte[] halfPadPKCS7(final byte[] plaintext, int blocksize) {
if (plaintext.length % blocksize == 0) {
return plaintext.clone();
}
int newLength = (plaintext.length / blocksize + 1) * blocksize;
int paddingLength = newLength - plaintext.length;
final byte[] paddedPlaintext = Arrays.copyOf(plaintext, newLength);
for (int offset = plaintext.length; offset < newLength; offset++) {
paddedPlaintext[offset] = (byte) paddingLength;
}
return paddedPlaintext;
}
public static void main(String[] args) throws Exception {
Cipher cipher = Cipher.getInstance("Blowfish/ECB/NoPadding");
SecretKey key = createKey("123456781234567");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] plaintextData = cipher.doFinal(Hex.decode("085585C60B3D23257763E6D8BB0A0891"));
byte[] unpaddedPlaintextData = halfUnpadPKCS7(plaintextData, cipher.getBlockSize());
String plaintextHex = Hex.toHexString(unpaddedPlaintextData);
System.out.println(plaintextHex);
String plaintext = new String(unpaddedPlaintextData, StandardCharsets.UTF_8);
System.out.println(plaintext);
}
}
I am not sure about the relevance of this question: IMHO there is no point of getting the same output as this script: you have no guaranty about how secure/efficient it is...
What raises my eyebrow is the part about the padding: there are several solution to pad a block, some of then are simple but very unsecured, and maybe this script is using one of these "bad" solution.
Did you check that your program is able to retrieve the correct plain text ? (you will need to code the matching decrypt function).
If so, it means that it works correctly and it can be used for whatever your original purpose was, regardless what the ouput of this script is...

Cryptography in Java

I'm making an app that encrypts some files. I want to use gnu's cryptix library. It says it is no longer developed since 2005, but I guess it has everything I need... should I use something else?
And I have a question about encrypting a single file. Right now I do it with a loop like this:
for(int i=0; i+block_size < bdata.length; i += block_size)
cipher.encryptBlock(bdata, i, cdata, i);
So my question is how to encrypt the last block that may not have the same size as the block_size. I was thinking maybe a should add some extra data to the last block, but than I don't know how to decrypt that...
I would strongly suggest using AES encryption and it too comes with the JAVA SDK. Have a look at: Using AES with Java Technology which will give you some great example. To read up more on AES see: Advanced Encryption Standard - Wikipedia.
Never use your own encryption scheme or an older form of an encryption scheme. AES has been tried and tested by people with far greater knowledge in that field then us, so you know it will work. Where as with your own or an old encryption scheme we might miss a fatal loop hole that will leave our data open to attacks.
See this question here to see the difference in the encryption schemes: Comparison of DES, Triple DES, AES, blowfish encryption for data
Addendum:
AES in java will work flawlessly for 192 and 256bit keys but you will have to install the newer JCE Policy Files. See here and here. You should also place the files in your JDK or else it wont work when executed from your IDE.
Note: Make sure you download the correct JCE policy files, depending on your Java version i.e 1.4, 1.5 1.6 or 7.
However if you use 128bit keys no need to install the newer JCE files.
Here is a template of some secure AES usage in java it use CBC/AES/PKCS5Padding and a random IV using RandomSecure.
Note you need both the key and IV for decrypting:
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* This program generates a AES key, retrieves its raw bytes, and then
* reinstantiates a AES key from the key bytes. The reinstantiated key is used
* to initialize a AES cipher for encryption and decryption.
*/
public class AES {
/**
* Encrypt a sample message using AES in CBC mode with a random IV genrated
* using SecyreRandom.
*
*/
public static void main(String[] args) {
try {
String message = "This string contains a secret message.";
System.out.println("Plaintext: " + message + "\n");
// generate a key
KeyGenerator keygen = KeyGenerator.getInstance("AES");
keygen.init(128); // To use 256 bit keys, you need the "unlimited strength" encryption policy files from Sun.
byte[] key = keygen.generateKey().getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
// build the initialization vector (randomly).
SecureRandom random = new SecureRandom();
byte iv[] = new byte[16];//generate random 16 byte IV AES is always 16bytes
random.nextBytes(iv);
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);
System.out.println("Key: " + new String(key, "utf-8") + " This is important when decrypting");
System.out.println("IV: " + new String(iv, "utf-8") + " This is important when decrypting");
System.out.println();
// encrypt the message
byte[] encrypted = cipher.doFinal(message.getBytes());
System.out.println("Ciphertext: " + asHex(encrypted) + "\n");
// reinitialize the cipher for decryption
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivspec);
// decrypt the message
byte[] decrypted = cipher.doFinal(encrypted);
System.out.println("Plaintext: " + new String(decrypted) + "\n");
} catch (IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException | InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | NoSuchAlgorithmException ex) {
ex.printStackTrace();
}
}
/**
* Turns array of bytes into string
*
* #param buf Array of bytes to convert to hex string
* #return Generated hex string
*/
public static String asHex(byte buf[]) {
StringBuilder strbuf = new StringBuilder(buf.length * 2);
int i;
for (i = 0; i < buf.length; i++) {
if (((int) buf[i] & 0xff) < 0x10) {
strbuf.append("0");
}
strbuf.append(Long.toString((int) buf[i] & 0xff, 16));
}
return strbuf.toString();
}
}
I always use BouncyCastle
I also use the streaming framework instead of the for loop you were describing: it deals with the issue raised. Mostly I use that because when it comes to cryptography (and threading) I rarely trust my own code, I trust the people that live eat and breath it. Here is the code I use when I want "gash" cryptography. i.e. I have no particular threat model, and just want something "a little secure".
The hex encoding of the keys makes them much easier to manipulate / store and so on. I use "makeKey" to ... well ... make a key, then I can use the key in the encrypt and decrypt methods. You can obviously go back to using byte[] instead of hex strings for the keys.
private static boolean initialised;
private static void init() {
if (initialised)
return;
Security.addProvider(new BouncyCastleProvider());
initialised = true;
}
public static String makeKey() {
init();
KeyGenerator generator = KeyGenerator.getInstance(algorithm, provider);
generator.init(keySize);
Key key = generator.generateKey();
byte[] encoded = key.getEncoded();
return Strings.toHex(encoded);
}
public static String aesDecrypt(String hexKey, String hexCoded) {
init();
SecretKeySpec key = new SecretKeySpec(Strings.fromHex(hexKey), algorithm);
Cipher cipher = Cipher.getInstance(algorithm + "/ECB/PKCS5Padding", provider);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] codedBytes = Strings.fromHex(hexCoded);
CipherInputStream inputStream = new CipherInputStream(new ByteArrayInputStream(codedBytes), cipher);
byte[] bytes = getBytes(inputStream, 256);
String result = new String(bytes, "UTF-8");
return result;
}
public static String aesEncrypt(String hexKey, String input) {
init();
SecretKeySpec key = new SecretKeySpec(Strings.fromHex(hexKey), algorithm);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, key);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(input.length());
CipherOutputStream outputStream = new CipherOutputStream(byteArrayOutputStream, cipher);
setText(outputStream, input);
byte[] outputBytes = byteArrayOutputStream.toByteArray();
String output = new String(Strings.toHex(outputBytes));
return output;
}
public static void setText(OutputStream outputStream, String text, String encoding) {
try {
outputStream.write(text.getBytes(encoding));
outputStream.flush();
} finally {
outputStream.close();
}
}
public static byte[] getBytes(InputStream inputStream, int bufferSize) {
try {
List<ByteArrayAndLength> list = Lists.newList();
while (true) {
byte[] buffer = new byte[bufferSize];
int count = inputStream.read(buffer);
if (count == -1) {
byte[] result = new byte[ByteArrayAndLength.length(list)];
int index = 0;
for (ByteArrayAndLength byteArrayAndLength : list) {
System.arraycopy(byteArrayAndLength.bytes, 0, result, index, byteArrayAndLength.length);
index += byteArrayAndLength.length;
}
assert index == result.length;
return result;
}
list.add(new ByteArrayAndLength(buffer, count));
}
} finally {
inputStream.close();
}
}
static class ByteArrayAndLength {
byte[] bytes;
int length;
public ByteArrayAndLength(byte[] bytes, int length) {
super();
this.bytes = bytes;
this.length = length;
}
static int length(List<ByteArrayAndLength> list) {
int result = 0;
for (ByteArrayAndLength byteArrayAndLength : list) {
result += byteArrayAndLength.length;
}
return result;
}
}
I've taken out some of the exception catching to reduce the size of the code, and Strings.fromHex turns the string back into a byte[]
Maybe you should consider using a javax.crypto package.
Here is an example of how to use Ciphers:
DES encryption
Hope this helps
I would seriously think twice before going this route. The development of the software was halted because standard alternatives exist, and have a look at the mailing list, there's been no significant activity since 2009. In my book that means that the software is abandoned, and abandoned software means you're more or less on your own.
Have a look here on SO, there are several questions and answers that may help you like this one. An at first sight interesting package that could simplify things for you (but still using the standard JCE infrastructure) is jasypt

AES BadPaddingException

if I use a wrong key or wrong salt for decryption an BadPaddingException is thrown.
I would expect an incorrect string to be returned.
The doFinal() causes the exception in the decrypt-method
Message : This is just an example
Unfug : 'ΩÙΩ„SåF?V®ßs.k˚·ºç€èÀHfif∫ÙÉÕ
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.AESCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at casino.AES.decryptString(AES.java:130)
at casino.AES.main(AES.java:172)
public static void main(String[] args) throws Exception {
//Encryption
AES encr = new AES();
encr.setKey("KEY");
encr.setSalt("SALT");
encr.setup();
String message = "This is just an example";
System.out.println("Message : " + message);
byte[] code = encr.encrypt(message);
System.out.println("Encrypted Strinng : "+ new String(code, "UTF-8"));
//Decryption
AES dec = new AES();
dec.setKey("INCORRECT"); //<--- incorrect
dec.setSalt("SALT");
dec.setup();
System.out.println(dec.decryptString(code));
}
public synchronized void setKey(String key) throws UnsupportedEncodingException {
this.key = key.getBytes("UTF-8");
isPasswordAlreadySet = true;
}
public synchronized void setSalt(String salt) throws UnsupportedEncodingException {
this.salt = salt.getBytes("UTF-8");
}
public synchronized void setup() throws Exception {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(key);
digest.update(salt);
byte[] raw = digest.digest();
skeySpec = new SecretKeySpec(raw, "AES");
cipher = Cipher.getInstance("AES");
}
public synchronized byte[] encrypt(byte[] klartext) throws Exception {
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(klartext);
return encrypted;
}
public synchronized byte[] encrypt(String klartext) throws Exception{
return encrypt(klartext.getBytes("UTF-8"));
}
public synchronized byte[] decrypt(byte[] code) throws Exception {
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] original = cipher.doFinal(code);
return original;
}
public synchronized double decryptDouble(byte[] code) throws Exception {
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] original = cipher.doFinal(code);
return doubleFromBytes( original);
}
Thank you!
Frederik
The padding is a fine sanity check. Assuming the incorrectly decrypted data is uniformly distributed, it will only appear to be correctly PKCS5/PKCS7 padded about 1 time for every 255 incorrect passwords. (1/256 + 1/256^2 + 1/256^3 ...)
So it's helpful, but it's not something you should rely on --- what is effectively an almost-8-bit message digest is not a sufficient test of data integrity.
One more thing: If an attacker can repeatedly alter the ciphertext and get you to decrypt it, (an example might be encrypted data stored in a cookie) and if they can distinguish between your behavior when the decrypted data throws an exception for bad padding and when it is simply garbage, then they can determine the plaintext via a "padding oracle attack".
By the way, if you actually want the behavior you expected, you can use "AES/CTR/NoPadding", which will not require an exact block size and will always return a decrypted byte[], whether or not the keys match.
You should either use AES with an implicit padding declaration (see the available modes) or force the length (in bytes) of data encrypted/decrypted to be a multiple of 16.
Also, by default, java uses ECB mode, which can be really unsecured depending on which type of data you are using, you should probably use a CBC mode.
SecretKeySpec specifies a secret key in a provider-independent fashion
http://themasterofmagik.wordpress.com/2014/03/19/simple-aes-encryption-and-decryption-in-java/

Categories