I have function that crypts string:
BASE64Decoder decoder = new BASE64Decoder();
BASE64Encoder encoder = new BASE64Encoder();
public String encryptValueWithBlowfish(String data, String secretKey) {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
try {
SecretKeySpec key = new SecretKeySpec(decoder.decodeBuffer(secretKey), "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish/CBC/NoPadding", "BC");
String iv = "\0\0\0\0\0\0\0\0";
IvParameterSpec ivs = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, key, ivs);
MessageDigest sha = MessageDigest.getInstance("SHA-1");
return encoder.encode(sha.digest(cipher.doFinal(decoder.decodeBuffer(data))));
} catch (Exception e) {
lg.info("Failed to encryptValueWithBlowfish: " + e.getMessage());
return "";
}
}
Line cipher.init(Cipher.ENCRYPT_MODE, key, ivs); rises exception "Unsupported keysize or algorithm parameters". This code forks fine in another Linux machine. Parameters passed in both cases are the same. I'm not strong in crypto things. What might be wrong?
What might be wrong?
Well, you're using the default character encoding here: iv.getBytes() - that's never a good start. Perhaps the two different machines have different default encodings.
If you want to create a byte array of all-zeroes and a particular size, why not just use:
IvParameterSpec ivs = new IvParameterSpec(new byte[8]);
Or use 16 if you wanted a 16 byte IV.
It's not clear what decoder is here - but you use it twice and again, if that's using the default character encoding it could vary by machine.
Related
Please excuse the hackjob! Still new at coding and was utilizing a lot of sys outs to troubleshoot, and this is my first post at StackOverflow. Thank you for the help!
So I've been working on trying to encrypt String objects using the javax.crypto.Cipher API, and I have found some success, but only when it is using the same instance of the Cipher object. However, for the purposes of my project, I am encrypting text (String) to and decrypting text from a text file, and will not be accessing the same Cipher object every time.
I believe the issue is not from converting between the byte arrays and Strings, since the Base64 encoder seems to have taken care of that problem. The output of the byte arrays are identical pre-encoding and post-decoding, so that should isolate it as an issue during the decryption phase. What can be done so that my decryptPW method can use a different Cipher instance (with the same arguments passed) and not trigger a BadPaddingException?
private static String encryptPW(String pw){
byte[] pwBytes = pw.getBytes();
byte[] keyBytes = "0123456789abcdef".getBytes();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
try {
Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encryptedBytes = ciph.doFinal(pwBytes);
pw = Base64.getEncoder().encodeToString(ciph.doFinal(pwBytes));
for (byte b : encryptedBytes){
System.out.print(b);
}
System.out.println();
} catch (Exception e){
e.printStackTrace();
}
return pw;
}
private static String decryptPW(String pw){
byte[] pwBytes = Base64.getDecoder().decode(pw.getBytes());
for (byte b : pwBytes){
System.out.print(b);
}
System.out.println();
byte[] keyBytes = "0123456789abcdef".getBytes();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
try {
Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.DECRYPT_MODE, keySpec, ciph.getParameters());
pw = new String(ciph.doFinal(pwBytes));
} catch (Exception e){
e.printStackTrace();
}
return pw;
}
Again, thank you!
As you use CBC mode, you need to save the random Initialization Vector (IV) from the encryption cipher and provide it to the decryption cipher.
You can get it from the encryption cipher after init like:
ciph.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] iv = ciph.getIV();
And provide it to the decrypt cipher with something like:
ciph.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));
You need to come up with a method of saving the IV. A common way is to prefix the encrypted data with the IV, so it's available when you need to initialize your decrypt cipher. It don't need to be secret.
I am aware of a question very similar to this (How do I encrypt in Python and decrypt in Java?) but I have a different problem.
My problem is, I am not able to decrypt in Java correctly. Despite using the correct key and IV, I still get garbage characters after decryption. I don't have any compile/run-time errors or exceptions in Java so I believe I am using the right parameters for decryption.
Python Encryption Code -
from Crypto.Cipher import AES
import base64
key = '0123456789012345'
iv = 'RandomInitVector'
raw = 'samplePlainText'
cipher = AES.new(key,AES.MODE_CFB,iv)
encrypted = base64.b64encode(iv + cipher.encrypt(raw))
Java Decryption Code -
private static String KEY = "0123456789012345";
public static String decrypt(String encrypted_encoded_string) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
String plain_text = "";
try{
byte[] encrypted_decoded_bytes = Base64.getDecoder().decode(encrypted_encoded_string);
String encrypted_decoded_string = new String(encrypted_decoded_bytes);
String iv_string = encrypted_decoded_string.substring(0,16); //IV is retrieved correctly.
IvParameterSpec iv = new IvParameterSpec(iv_string.getBytes());
SecretKeySpec skeySpec = new SecretKeySpec(KEY.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
plain_text = new String(cipher.doFinal(encrypted_decoded_bytes));//Returns garbage characters
return plain_text;
} catch (Exception e) {
System.err.println("Caught Exception: " + e.getMessage());
}
return plain_text;
}
Is there anything obvious that I am missing?
The Cipher Feedback (CFB) mode of operation is a family of modes. It is parametrized by the segment size (or register size). PyCrypto has a default segment size of 8 bit and Java (actually OpenJDK) has a default segment size the same as the block size (128 bit for AES).
If you want CFB-128 in pycrypto, you can use AES.new(key, AES.MODE_CFB, iv, segment_size=128). If you want CFB-8 in Java, you can use Cipher.getInstance("AES/CFB8/NoPadding");.
Now that we have that out the way, you have other problems:
Always specify the character set you're using, because it can change between different JVMs: new String(someBytes, "UTF-8") and someString.getBytes("UTF-8"). When you do, be consistent.
Never use a String to store binary data (new String(encrypted_decoded_bytes);). You can copy the bytes directly: IvParameterSpec iv = new IvParameterSpec(Arrays.copyOf(encrypted_decoded_bytes, 16)); and cipher.doFinal(Arrays.copyOfRange(encrypted_decoded_bytes, 16, encrypted_decoded_bytes.length)).
In Java, you're assuming that the IV is written in front of the ciphertext and then encoded together, but in Python, you're never doing anything with the IV. I guess you posted incomplete code.
It is crucial for CFB mode to use a different IV every time if the key stays the same. If you don't change the IV for every encryption, you will create a multi-time pad which enables an attacker to deduce the plaintext even without knowing the key.
I'm currently running into problems decrypting my data. The base64 of the encoded string is being stored in the database. So, I'm printing out the encoded string and then trying to run it back through with "DECRYPT" instead of "ENCRYPT". However, I never get a value that the Decrypter method likes, it always gives me an error about parameters or the value not being 16 bytes.
public class crypto {
public static void main(String [] args) {
String s = args[0];
String s1 = args[1];
String ivkey = "thisisasecretkey";
byte[] ivraw = ivkey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(ivraw, "AES");
if (s.equalsIgnoreCase("ENCRYPT")) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(s1.getBytes());
System.out.println(new String(Base64.encodeBase64(encrypted)));
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(s1.getBytes());
System.out.println(new String(Base64.decodeBase64(encrypted)));
} catch (Exception e) {
e.printStackTrace();
}
}
return;
};
}
command:crypto "ENCRYPT" "password"
output: 5eQvSzPG1TE2AybgCmeV6A==
command:crytpo "DECRYPT" "5eQvSzPG1TE2AybgCmeV6A=="
output: java.security.InvalidKeyException: Parameters missing
I'm aware of the security flaws, that's not what I'm asking about and I would prefer answers/comments not get cluttered with best practices.
You should do base 64 decoding, and you should do that before decrypting.
You are not including the initialization vector (IV).
AES in CBC mode has both a 16 byte IV and the 16 byte symmetric key.
String IV = "AAAAAAAAAAAAAAAA"; // generate this randomly
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(IV.getBytes()));
byte[] encrypted = cipher.doFinal(s.getBytes());
Edit: as it turns out, encryption does not require a IV to be provided (as owlstead pointed out), but decryption does. The best bet would be to be explicit and use IV in both encryption and decryption. Change your decryption function to include the IV, and you will run into the other error in your code that owlstead pointed out.
I am in the process of implementing a Java library in Ruby. I have come across the following road block. Is it possible to implement the following code in ruby? Are there any ruby equivalents for byte[], IvParameterSpec, SecretKeySpec ?
private String decrypt(String token)
{
//parse token into its IV and token components
byte[] ivAndToken = Base64.decodeBase64(token);
byte[] iv = new byte[ivLength];
System.arraycopy(ivAndToken, 0, iv, 0, ivLength);
int length = ivAndToken.length - ivLength;
byte[] tokenBytes = new byte[length];
System.arraycopy(ivAndToken, ivLength, tokenBytes, 0, length);
//prepare initialization vector specification
IvParameterSpec spec = new IvParameterSpec(iv);
//create cipher instance based on transformer params
Cipher cipher = Cipher.getInstance(algorithm + mode + padding, CRYPTO_PROVIDER);
//convert key bytes into valid key format
Key key = new SecretKeySpec(Base64.decodeBase64(symkey), algorithm);
//initialize cipher for decryption
cipher.init(Cipher.DECRYPT_MODE, key, spec);
//decrypt the payload
String plaintext = new String(cipher.doFinal(tokenBytes));
return plaintext;
}
You'll probably have to implement both IvParameterSpec and SecretKeySpec on Ruby if you want the algorithm to behave exactly like it does in Java. byte[] is of course just a byte array. You'll probably want to at the docs for them (links above) and also hopefully you understand block cipher operation modes work.
If you don't, SecretKey refers to the symmetric key (eg: the passphrase), and IV is the initialization vector, a cryptographic nonce used to make different encryptions of the same plaintext generate different ciphertext. IV's are needed for all operation modes except ECB. See this wikipedia page for more details.
I got an exception in the following code for AES algorithm in java.
Code decryptes an encrypted string and returns the original string.
Plz help me to fix this.
Code:
public class AES
{
public byte[] encrypted;
public byte[] original;
public String originalString;
public static String asHex (byte buf[])
{
StringBuffer strbuf = new StringBuffer(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();
}
public String AESencryptalgo(byte[] text)
{
String newtext="";
// Get the KeyGenerator
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec); encrypted = cipher.doFinal(text);
System.out.println("encrypted string: " + asHex(encrypted));
cipher.init(Cipher.DECRYPT_MODE, skeySpec); original = cipher.doFinal(encrypted);
originalString = new String(original); System.out.println("Original string: " + originalString + " " + asHex(original));
}
catch(Exception e)
{ }
finally
{
newtext=new String(encrypted);
System.out.println("ENCRYPTED "+newtext);
//AESdecryptalgo(newtext.getBytes());
return newtext;
}
}
public String AESdecryptalgo(byte[] text)
{
// Get the KeyGenerator
try
{
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); // 192 and 256 bits may not be available
// Generate the secret key specs.
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
// Instantiate the cipher
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
original = cipher.doFinal(text); //Exception occurs here
originalString = new String(original);
System.out.println("Original string: " + originalString + " " + asHex(original));
}
catch(Exception e)
{
System.out.println("exception");
}
finally
{
System.out.println("DECRYPTED "+originalString);
return originalString;
}
}
public static void main(String[] args)
{
AES a=new AES();
a.AESencryptalgo("hello".getBytes());
System.out.println();
}}
`
exception:
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*..)
According to Java™ Cryptography Architecture (JCA) Reference Guide (emphasis mine):
Cipher objects are obtained by using
one of the Cipher getInstance() static
factory methods. Here, the algorithm
name is slightly different than with
other engine classes, in that it
specifies not just an algorithm name,
but a "transformation". A
transformation is a string that
describes the operation (or set of
operations) to be performed on the
given input to produce some output. A
transformation always includes the
name of a cryptographic algorithm
(e.g., DES), and may be followed by a
mode and padding scheme.
A transformation is of the form:
"algorithm/mode/padding" or
"algorithm"
For example, the following are valid transformations:
"DES/CBC/PKCS5Padding"
"DES"
If just a transformation name is specified, the system will
determine if there is an
implementation of the requested
transformation available in the
environment, and if there is more than
one, returns there is a preferred one.
If both a transformation name and a
package provider are specified, the
system will determine if there is an
implementation of the requested
transformation in the package
requested, and throw an exception if
there is not.
If no mode or padding is specified,
provider-specific default values for
the mode and padding scheme are used.
For example, the SunJCE provider uses
ECB as the default mode, and
PKCS5Padding as the default padding
scheme for DES, DES-EDE and Blowfish
ciphers. This means that in the case
of the SunJCE provider:
Cipher c1 = Cipher.getInstance("DES/ECB/PKCS5Padding");
and
Cipher c1 = Cipher.getInstance("DES");
are equivalent statements.
Using modes
such as CFB and OFB, block ciphers can
encrypt data in units smaller than the
cipher's actual block size. When
requesting such a mode, you may
optionally specify the number of bits
to be processed at a time by appending
this number to the mode name as shown
in the "DES/CFB8/NoPadding" and
"DES/OFB32/PKCS5Padding"
transformations. If no such number is
specified, a provider-specific default
is used. (For example, the SunJCE
provider uses a default of 64 bits for
DES.) Thus, block ciphers can be
turned into byte-oriented stream
ciphers by using an 8 bit mode such as
CFB8 or OFB8.
Appendix A of this document contains a
list of standard names that can be
used to specify the algorithm name,
mode, and padding scheme components of
a transformation.
The objects returned by factory
methods are uninitialized, and must be
initialized before they become usable.
Because your code does not specify mode or padding, provider-specific default values are being used. It appears that your provider is SunJCE and that it's default padding is probably "NoPadding". With this padding, you are responsible for ensuring that the size of the byte array being encrypted is a multiple of the number of bytes in the secret key. You can make you're life easier by specifying the mode and padding in your transformation:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
WARNING: You should not use ECB mode in real code. Try CBC instead.
Update: I didn't think it was fair to recommend CBC mode without offering a little sample of how it works:
public static void main(String... args) throws Exception {
byte[] data = "hello".getBytes();
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // 192 and 256 bits may not be available
SecretKey secretKey = keyGenerator.generateKey();
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// By initializing the cipher in CBC mode, an "initialization vector" has been randomly
// generated. This initialization vector will be necessary to decrypt the encrypted data.
// It is safe to store the initialization vector in plain text for later use. You can obtain
// it's bytes by calling iv.getIV().
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
IvParameterSpec iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
byte[] encryptedData = cipher.doFinal(data);
// When decrypting the encrypted data, you must provide the initialization vector used
// during the encryption phase.
cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
byte[] decryptedData = cipher.doFinal(encryptedData);
if (!Arrays.equals(data, decryptedData)) {
throw new Exception("Data was not decrypted successfully");
}
}
Well if this is the error Input length must be multiple of 16 when decrypting with padded cipher. Than the answear is obvious, the length of your buffer must be a multiple of 16.
Have you checked the length of buf[]?
Your code manages to get almost everything wrong. Just for a start, your mistakes include:
generating a new random symmetric key before encryption and decryption. You have to use the same key to decrypt that was used to encrypt.
Using String as a container for binary data. cipher output is cannot be reliably made into a String unless you use an encoding, such as base64.
Your exception handling is incorrect.
Furthermore, your code runs without exceptions for me.