Cannot change the cipher key in Base64 Decoder - java

I have a pre-written code which is used to cipher the given plain text or vice-versa .
The class has 3 methods, where in 2 methods can be used for encrypting and decrypting respectively.
public class SqlCipherUtil {
private Cipher ecipher;
private Cipher dcipher;
public String encryptString(String pStrPlainText) {
try {
generateKey();
byte[] utf8 = pStrPlainText.getBytes("UTF8");
byte[] enc = this.ecipher.doFinal(utf8);
return new BASE64Encoder().encode(enc);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public String decryptString(String pStrCipherText){
try {
generateKey();
byte[] dec = new BASE64Decoder().decodeBuffer(pStrCipherText);
byte[] utf8 = this.dcipher.doFinal(dec);
return new String(utf8, "UTF8");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* This method is used to generate the encrypted key.
*/
private void generateKey() {
try {
byte[] decodedStr = new BASE64Decoder().decodeBuffer("rA/LUdBA/hA=");
SecretKey key = new SecretKeySpec(decodedStr, "DES");
this.ecipher = Cipher.getInstance("DES");
this.dcipher = Cipher.getInstance("DES");
this.ecipher.init(1, key);
this.dcipher.init(2, key);
} catch (Exception e) {
e.printStackTrace();
}
}
}
The key present in the class cannot be changed to any other key
in line byte[] decodedStr = new BASE64Decoder().decodeBuffer("rA/LUdBA/hA=");,
and it is giving an exception.
java.security.InvalidKeyException: Invalid key length: 9 bytes
at com.sun.crypto.provider.DESCipher.engineGetKeySize(DashoA13*..)
at javax.crypto.Cipher.b(DashoA13*..)
at javax.crypto.Cipher.a(DashoA13*..)
at javax.crypto.Cipher.a(DashoA13*..)
at javax.crypto.Cipher.a(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..)
I tried out the below code, and I am getting exactly 8 bytes in the array.
public static void main(String[] args) throws IOException {
byte[] decodedStr = new BASE64Decoder().decodeBuffer("rA/LUdBA/hA=");
for(byte b : decodedStr){
System.out.print(b);
System.out.print(" ");
}
}
}
Any other combination of the key will make the byte array size more than 8 or less than 7.
What is the concept behind getting the byte array size 8 ?
What should be done to use custom key combination or our custom generated keys ?
Please answer Both the questions.

Any other combination of the key will make the byte array size more
than 8 or less than 7.
I doubt that. You're probably adding or removing the wrong characters; or at the wrong position. See: http://en.wikipedia.org/wiki/Base64
And yes 9 bytes is not a valid key length for DES. You could simply shorten it to the proper length. You do get 9 bytes because your base64 string is 3x4 characters long which will be decoded to 3x3 = 9 characters. Trim the output.
What is the concept behind getting the byte array size 8 ?
DES uses 56 Bit Keys. 8 Bytes = 64 Bit, so sufficient bits for the key.
What should be done to use custom key combination or our custom generated keys ?
Let the user enter a key that has at least 7 characters (56 Bit).
I really don't see why you use base64 in this sample at all - probably just because you copied it from somewhere? You just need a few random bytes. The common way to get those are to build a hash from any input the user gives and use bytes from that hash.

if your target is encode and decode the string, Use Base64.
public class PasswordCodecHandler {
Base64 codec = null;
public PasswordCodecHandler() {
codec = new Base64();
}
public String encode(String password) {
byte[] temp;
String encodedPassword = null;
temp = codec.encode(password.getBytes());
encodedPassword = new String(temp);
return encodedPassword;
}
public String decode(byte[] encodedPassword) {
byte[] temp;
String decodedPassword;
temp = codec.decode(encodedPassword);
decodedPassword = new String(temp);
return decodedPassword;
}
public static void main(String[] args) {
PasswordCodecHandler passwordCodecHandler = new PasswordCodecHandler();
String s1 = passwordCodecHandler.encode("password");
System.out.println(s1);
String s2 = passwordCodecHandler.encode("admin");
System.out.println(s2);
String s3 = passwordCodecHandler.encode("administrator");
System.out.println(s3);
String s4 = passwordCodecHandler.encode("123456");
System.out.println(s4);
}
}
For other data type : it can be java.lang.OutOfMemoryError based on the your memory allocation size
/* Download apache common-io.xxx. jar*/
public class CodecHandler {
Base64 codec = null;
public CodecHandler() {
codec = new Base64();
}
public byte[] encode(byte[] decoded) {
return codec.encode(decoded);
}
public byte[] decode(byte[] encoded) {
return codec.decode(encoded);
}
public static void main(String[] args) throws IOException {
File file = new File("D:/Test.mp4");
byte[] fileByteArray = FileUtils.readFileToByteArray(file);
CodecHandler codecHandler = new CodecHandler();
byte[] encoded = codecHandler.encode(fileByteArray);
System.out.println("Byte Size : " + encoded.length);
byte[] decode = codecHandler.decode(encoded);
FileUtils.writeByteArrayToFile(new File("C:/Test.mp4"), decode);
}
}

What is the concept behind getting the byte array size 8?
Your new, based-64–encoded key must be 12 characters long, ending with one, and only one, = character.
In base-64, the character = is a padding character, and it can only appear at the end of the encoded string. Base-64 encoding outputs a block of 4 characters from each input block of 3 bytes. If the input length is not a multiple of 3, the last block is padded.
In the encoded key, "rA/LUdBA/hA=", there are 12 characters, which can encode 9 bytes. But the last character is padding, which means the last byte should be ignored, leaving 8 bytes.
What should be done to use custom key combination or our custom generated keys?
First, you shouldn't use DES. It's too weak and insecure. But in general, the correct process for generating a secure key in Java is to use the KeyGenerator class. For (insecure) DES, you can generate a key and encode it with base-64 like this:
import java.util.Base64;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
…
KeyGenerator gen = KeyGenerator.getInstance("DES");
gen.init(56);
SecretKey secret = gen.generateKey();
String b64 = Base64.getEncoder().encodeToString(secret.getEncoded());
System.out.println(b64);
To use the key, decode it like this:
import java.util.Base64;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeySpec;
…
SecretKey key = new SecretKeySpec(Base64.getDecoder().decode(b64), "DES");

Related

Encrypt string using given modulus and exponent

I need to replicate the functionality of the following JAVA code that receives a string with the exponent and modulus of a public key to generate a public key with said parameters and encrypt a string:
package snippet;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.Security;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import javax.crypto.Cipher;
public class Snippet {
public static void main(String ... strings) {
try {
// Needed if you don't have this provider
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
//String and received public key example
String ReceivedString = "1234";
String publicRSA = "010001|0097152d7034a8b48383d3dba20c43d049";
EncryptFunc(ReceivedString, publicRSA);
//The result obtained from the ReceivedString and the publicRSA is as follows:
//Result in hex [1234] -> [777786fe162598689a8dc172ed9418cb]
} catch (Exception ex) {
System.out.println("Error: " );
ex.printStackTrace();
}
}
public static String EncryptFunc(String ReceivedString, String clavePublica) throws Exception {
String result = "";
//We separate the received public string into exponent and modulus
//We receive it as "exponent|modulus"
String[] SplitKey = clavePublica.split("\\|");
KeyFactory keyFactory = KeyFactory.getInstance("RSA","BC");
RSAPublicKeySpec ks = new RSAPublicKeySpec(new BigInteger(hex2byte(SplitKey[1])), new BigInteger(hex2byte(SplitKey[0])));
//With these specs, we generate the public key
RSAPublicKey pubKey = (RSAPublicKey)keyFactory.generatePublic(ks);
//We instantiate the cypher, with the EncryptFunc and the obtained public key
Cipher cipher= Cipher.getInstance("RSA/None/NoPadding","BC");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
//We reverse the ReceivedString and encrypt it
String ReceivedStringReverse = reverse(ReceivedString);
byte[] cipherText2 = cipher.doFinal(ReceivedStringReverse.getBytes("UTF8"));
result = byte2hex(cipherText2);
System.out.println("result in hex ["+ReceivedString+"] -> ["+result+"]");
return result;
}
public static byte[] hex2byte(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
public static String byte2hex(byte[] bytes) {
StringBuilder result = new StringBuilder();
for (byte aByte : bytes) {
result.append(String.format("%02x", aByte));
// upper case
// result.append(String.format("%02X", aByte));
}
return result.toString();
}
public static String reverse(String source) {
int i, len = source.length();
StringBuilder dest = new StringBuilder(len);
for (i = (len - 1); i >= 0; i--){
dest.append(source.charAt(i));
}
return dest.toString();
}
}
I've tried several approaches with this one, And I have done some searching here, here, here, here and here.
I Managed to create the public key with the given parameters, but the results are always different when I encrypt the string:
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace RSACypherTest
{
public class Program
{
public static RSACryptoServiceProvider rsa;
static void Main(string[] args)
{
string str = "1234";
string publicRSA = "010001|0097152d7034a8b48383d3dba20c43d049";
string encrypted = "";
Console.WriteLine("Original text: " + str);
encrypted = Encrypt(str, publicRSA);
Console.WriteLine("Encrypted text: " + encrypted);
Console.ReadLine();
}
public static string Encrypt(string str, string PublicRSA)
{
string[] Separated = PublicRSA.Split('|');
RsaKeyParameters pubParameters = MakeKey(Separated[1], Separated[0], false);
IAsymmetricBlockCipher eng = new Pkcs1Encoding(new RsaEngine());
eng.Init(true, pubParameters);
byte[] plaintext = Encoding.UTF8.GetBytes(Reverse(str));
byte[] encdata = eng.ProcessBlock(plaintext, 0, plaintext.Length);
return ByteArrayToString(encdata);
}
public static string Reverse(string s)
{
char[] charArray = s.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
public static string ByteArrayToString(byte[] ba)
{
return BitConverter.ToString(ba).Replace("-", "");
}
public static byte[] StringToByteArray(string hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
private static RsaKeyParameters MakeKey(string modulusHexString, string exponentHexString, bool isPrivateKey)
{
var modulus = new BigInteger(modulusHexString, 16);
var exponent = new BigInteger(exponentHexString, 16);
return new RsaKeyParameters(isPrivateKey, modulus, exponent);
}
}
}
I'm trying to use BouncyCastle because it seems to be the most effcient way of dealing with the key generation and everything. Any help concerning this would be very much appreciated.
Thanks in advance.
This is not the answer to your question but may help you in understanding RSA encryption.
I setup a sample encryption program in C# and used your given public key (converted the BigInteger modulus & exponent to Base64 values and further just wrote the XML-String representation of the public to use this key for encryption. The keylength is good for a length of maximum 5 byte data.
When running the encryption 5 times you will receive different encodedData (here in Base64 encoding) each run. So it's the expected behavior of the RSA encryption.
As C# allows me to "build" a short key it is not possible to generate a fresh keypair of such length and I doubt that Bouncy Castle would do (but here on SO there are many colleagues with a much better understanding of BC :-).
If you would like the program you can use the following external link to the program: https://jdoodle.com/ia/40.
Result:
load a pre created public key
publicKeyXML2: lxUtcDSotIOD09uiDEPQSQ==AQAB
encryptedData in Base64: JIFfO7HXCvdi0nSxKb0eLA==
encryptedData in Base64: dvtRw0U0KtT/pDJZW2X0FA==
encryptedData in Base64: CqJJKZevO6jWH6DQ1dnkhQ==
encryptedData in Base64: G7cL6BBwxysItvD/Rg0PuA==
encryptedData in Base64: HcfZJITu/PzN84WgI8yc6g==
code:
using System;
using System.Security.Cryptography;
using System.Text;
class RSACSPSample
{
static void Main()
{
try
{
//Create byte arrays to hold original, encrypted, and decrypted data.
byte[] dataToEncrypt = System.Text.Encoding.UTF8.GetBytes("1234");
byte[] encryptedData;
//Create a new instance of RSACryptoServiceProvider to generate
//public and private key data.
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
Console.WriteLine("load a pre created public key");
string publicKeyXML = "<RSAKeyValue><Modulus>AJcVLXA0qLSDg9PbogxD0Ek=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
RSA.FromXmlString(publicKeyXML);
string publicKeyXML2 = RSA.ToXmlString(false);
Console.WriteLine("publicKeyXML2: " + publicKeyXML2);
Console.WriteLine();
//Pass the data to ENCRYPT, the public key information
//(using RSACryptoServiceProvider.ExportParameters(false),
//and a boolean flag specifying no OAEP padding.
for (int i = 0; i < 5; i++)
{
encryptedData = RSAEncrypt(dataToEncrypt, RSA.ExportParameters(false), false);
string encryptedDataBase64 = Convert.ToBase64String(encryptedData);
Console.WriteLine("encryptedData in Base64: " + encryptedDataBase64);
}
}
}
catch (ArgumentNullException)
{
//Catch this exception in case the encryption did
//not succeed.
Console.WriteLine("Encryption failed.");
}
}
public static byte[] RSAEncrypt(byte[] DataToEncrypt, RSAParameters RSAKeyInfo, bool DoOAEPPadding)
{
try
{
byte[] encryptedData;
//Create a new instance of RSACryptoServiceProvider.
using (RSACryptoServiceProvider RSA = new RSACryptoServiceProvider())
{
//Import the RSA Key information. This only needs
//toinclude the public key information.
RSA.ImportParameters(RSAKeyInfo);
//Encrypt the passed byte array and specify OAEP padding.
//OAEP padding is only available on Microsoft Windows XP or
//later.
encryptedData = RSA.Encrypt(DataToEncrypt, DoOAEPPadding);
}
return encryptedData;
}
//Catch and display a CryptographicException
//to the console.
catch (CryptographicException e)
{
Console.WriteLine(e.Message);
return null;
}
}
}
While I won't mark my own answer as the correct one, I've found that there's the possibility to recreate the entire functionality of the java code mentioned in my question.
As Michael Fehr mentions in his answer, Its absolutely logical that any encryption method will try to avoid creating repeating or predictable patterns, as this answer perfectly describes.
Since in this particular situation the aim is to replicate the java code functionality, and said functionality revolves around getting the same results when encrypting a string with a given public key, we can use the answer in this post to generate a pice of code like the following:
private static string EncryptMessage(string str, string publicRSA)
{
string[] Separated = publicRSA.Split('|');
RsaKeyParameters pubParameters = MakeKey(Separated[1], Separated[0], false);
var eng = new RsaEngine();
eng.Init(true, pubParameters);
string x = Reverse(str);
byte[] plaintext = Encoding.UTF8.GetBytes(x);
var encdata = ByteArrayToString(eng.ProcessBlock(plaintext, 0, plaintext.Length));
return encdata;
}
private static RsaKeyParameters MakeKey(string modulusHexString, string exponentHexString, bool isPrivateKey)
{
byte[] mod = StringToByteArray(modulusHexString);
byte[] exp = StringToByteArray(exponentHexString);
var modulus = new BigInteger(mod);
var exponent = new BigInteger(exp);
return new RsaKeyParameters(isPrivateKey, modulus, exponent);
}
To recap:
As Michael Fehr says, it is not only normal but expected of a crypyography engine to NOT generate repeatable/predictable patterns
To deliver on the previous point, they add random "padding" to the messages
It's possible (but not recommended) to use BouncyCastle to generate a No-padding engine, emulating the functionality of Java code such as this Cipher rsa = Cipher.getInstance("RSA/ECB/nopadding");

Decryption does not work on Production(Red Hat Enterprise Linux 7) and works on UAT(SUSE based linux distro)

I'm using DES for encryption/decryption as its not recommended but its an old code so i could'nt move to AES, now my code working fine on local environment(i.e mac ) with production db, also its working fine on UAT which is SUSE based linux distro but decryption not working on Production which is redhat based enironment. on Production it throws "Input length (with padding) not multiple of 8 bytes" Illegal Block size exception
#Service
public class EncryptionUtil {
private static final Logger log = LogManager.getLogger(EncryptionUtil.class);
#Autowired
GpsCacheManager gpsCacheManager;
private Cipher ecipher;
private Cipher dcipher;
#Autowired
private StringUtils stringUtils;
public EncryptionUtil() throws Exception {
ecipher = Cipher.getInstance("DES");
dcipher = Cipher.getInstance("DES");
initCipher();
}
private void initCipher() {
try {
String response = “[-3232, -34, -98, 111, -222, 33, -22, 55]”;
String[] byteValues = response.substring(1, response.length() - 1).split(",");
byte[] bytes = new byte[byteValues.length];
for (int i = 0, len = bytes.length; i < len; i++) {
bytes[i] = Byte.parseByte(byteValues[i].trim());
}
SecretKey key = new SecretKeySpec(bytes, "DES");
ecipher.init(Cipher.ENCRYPT_MODE, key);
dcipher.init(Cipher.DECRYPT_MODE, key);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
public String encryptUTF8(String str) throws Exception {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new String(Base64.encodeBase64(enc));
}
public String decryptUTF8(String str) throws Exception {
if (stringUtils == null) {
stringUtils = new StringUtils();
}
//do not decrypt if a valid email.
if (stringUtils.isValidEmail(str)) {
return str;
}
// Decode base64 to get bytes
byte[] dec = Base64.decodeBase64(str.getBytes());
byte[] utf8 = null;
try {
utf8 = dcipher.doFinal(dec);
} catch (IllegalBlockSizeException e) {
return str;
}
// Decode using utf-8
return new String(utf8, "UTF8");
}
}
There is an issue with String.getBytes() and new String(byte[]) which are platform dependent and should not be used here. At the same time I replaced that Base64 class with the standard java's Base64, that was intended to replace several Base64 implementations some ten years ago.
public String encryptUTF8(String str) throws Exception {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes(StandardCharsets.UTF_8);
// Encrypt
byte[] enc = ecipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return Base64.getEncoder().encodeToString(enc));
//Old class: return new String(Base64.encodeBase64(enc), StandardCharsets.US_ASCII);
}
public String decryptUTF8(String str) throws Exception {
if (stringUtils == null) {
stringUtils = new StringUtils();
}
//do not decrypt if a valid email.
if (stringUtils.isValidEmail(str)) {
return str;
}
// Decode base64 to get bytes
//byte[] dec = Base64.getDecoder().decode(str.getBytes(StandardCharsets.US_ASCII));
byte[] dec = Base64.getDecoder().decode(str);
try {
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, StandardCharsets.UTF_8);
} catch (IllegalBlockSizeException e) {
return str;
}
}
There is one problem: String is for Unicode text, with two-byte chars (UTF-16).
This means any byte[] value must be text in some encoding, and with that encoding converted to a String. Any arbitrary byte[] value will not be a valid String. Especially on Linux with the preeminent UTF-8 encoding that will corrupt data.
The problem probably resides in decryptUTF8. If in the original code the default encoding is a single byte encoding, everything is swallowed as-is. For Linux, UTF-8, erroneous UTF-8 multi-byte sequences might be encountered. Or the encoding is a 7-bits ASCII.
In general keep String and byte[] apart; use byte[] for non-text binary data.

Issue with key and iv on AES 256-CBC

I get a encrypted base64 string from Python.
The format is AES 256 CBC, but when I try to decrypt using Android it return decrypted string as nil.
Python
# coding=utf-8
import base64
from random import choice
from string import letters
try:
from Crypto import Random
from Crypto.Cipher import AES
except ImportError:
import crypto
import sys
sys.modules['Crypto'] = crypto
from crypto.Cipher import AES
from crypto import Random
class AESCipher(object):
def __init__(self, key):
self.bs = 32
self.key = key
def encrypt(self, raw):
_raw = raw
raw = self._pad(raw)
print raw, ';'
print _raw, ';'
iv = "".join([choice(letters[:26]) for i in xrange(16)])
print " iv :", iv
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw))
def decrypt(self, enc):
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
cipher = AES.new(self.key, AES.MODE_CBC, iv)
return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')
def _pad(self, s):
a = (self.bs - len(s) % self.bs)
b = chr(self.bs - len(s) % self.bs)
return s + a * b
#staticmethod
def _unpad(s):
return s[:-ord(s[len(s) - 1:])]
def encrypt(k, t):
o = AESCipher(k)
return o.encrypt(t)
def decrypt(k, t):
o = AESCipher(k)
return o.decrypt(t)
def main():
k = "qwertyuiopasdfghjklzxcvbnmqwerty"
s1 = "Hello World!"
d2 = encrypt(k, s1)
print " Password :", k
print "Encrypted :", d2
print " Plain :", decrypt(k, d2)
if __name__ == '__main__':
main()
Java
Here I use https://github.com/fukata/AES-256-CBC-Example
final String aEcodedSting = "aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk";
String decrypted = AESUtil.decrypt(aEcodedSting);
When I try to decrypt I got this
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.vinu.aessamble/com.example.vinu.aessamble.MainActivity}:
java.lang.RuntimeException: javax.crypto.BadPaddingException: error:1e06b065:Cipher functions:EVP_DecryptFinal_ex:BAD_DECRYPT
This is the Python encryption output:
Password : qwertyuiopasdfghjklzxcvbnmqwerty
Encrypted : aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk
iv : iegejanpeybezgmy
plainText : ser456&*(
Please notify me when anyone can solve this using another library.
There are 4 problems:
Difference between python output and java input
Different IV and key
Different key creation
Padding
1) Currently your python code output is a base64 encoding of iv + encrypted_data
return base64.b64encode(iv + cipher.encrypt(raw))
But in java you're directly decrypting raw data.
You should fix this way
// Decode base64
byte[] array = Base64.decode(src);
// Get only encrypted data (removing first 16 byte, namely the IV)
byte[] encrypted = Arrays.copyOfRange(array, 16, array.length);
// Decrypt data
decrypted = new String(cipher.doFinal(encrypted));
2) You must use same IV and key for input and output, so you should copy them from python console output:
iv : qbmocwtttkttpqvv
Password : qwertyuiopasdfghjklzxcvbnmqwerty
Encrypted : anZxZHVpaWJpb2FhaWdqaCK0Un7H9J4UlXRizOJ7s8lchAWAPdH4GRf5tLAkCmm6
Plain : Hello World!
and paste in java code:
private static final String ENCRYPTION_KEY = "qwertyuiopasdfghjklzxcvbnmqwerty";
private static final String ENCRYPTION_IV = "qbmocwtttkttpqvv";
3) In python you're using the key as string, but in java library it is hashed before being used for decrypting, so you should change your makeKey() method:
static Key makeKey() {
try {
byte[] key = ENCRYPTION_KEY.getBytes("UTF-8");
return new SecretKeySpec(key, "AES");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
4) Finally, you don't need to specify a padding in java with "AES/CBC/PKCS5Padding", because this way you force Cipher to pad automatically.
You can simply use "AES/CBC/NoPadding" in your decrypt() method, so it should look like this:
public static String decrypt(String src) {
String decrypted = "";
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, makeKey(), makeIv());
byte[] array = Base64.decode(src);
byte[] encrypted = Arrays.copyOfRange(array, 16, array.length);
decrypted = new String(cipher.doFinal(encrypted));
} catch (Exception e) {
throw new RuntimeException(e);
}
return decrypted;
}
Java output with your base64 and IV:
encrypted: aWVnZWphbnBleWJlemdteeAal+cw04QPYRuuIC3J1/zbkZZSCqxGLo/a26ZiieOk
decrypted: ser456&*(
Edit:
As suggested by Artjom B. (thank you), it would be better to read IV directly from ciphertext instead of hardcoding in AESUtil.
Your input consists of the IV in first 16 bytes and encrypted text in last 16 bytes, so you could take advantage of this.
public static String decrypt(String src) {
String decrypted = "";
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
// Decode input
byte[] array = Base64.decode(src);
// Read first 16 bytes (IV data)
byte[] ivData = Arrays.copyOfRange(array, 0, 16);
// Read last 16 bytes (encrypted text)
byte[] encrypted = Arrays.copyOfRange(array, 16, array.length);
// Init the cipher with decrypt mode, key, and IV bytes array (no more hardcoded)
cipher.init(Cipher.DECRYPT_MODE, makeKey(), new IvParameterSpec(ivData));
// Decrypt same old way
decrypted = new String(cipher.doFinal(encrypted));
} catch (Exception e) {
throw new RuntimeException(e);
}
return decrypted;
}
Moreover, as said here
Python code uses a 32 byte block size for padding which means that Java will still not be able to decrypt half of all possible ciphertexts. AES block size is 16 bytes and this should be changed in the Python implementation
You could change your Python class as below (AES.block_size is equal to 16):
class AESCipher(object):
def __init__(self, key):
self.bs = AES.block_size
self.key = key

How to turn 64 character string into key for 256 AES encryption

public static byte[] decryptByte(byte[] blahh, byte[] keyExample) throws Exception
{
Cipher cipher = null;
try
{
cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return cipher.doFinal(Base64.decodeBase64(blah));
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
String keyExample = "99112277445566778899AABBCCDDEEFF0123456789ABCDEF0123456789ABCDEF";
byte[] key = keyExample.getBytes();
byte[] barrayMessage = {123,45,55,23,64,21,65};
byte[] result = decryptByte(barrayMessage, key);
Exception thrown: java.security.InvalidKeyException: Invalid AES key length: 64 bytes
When you call String.getBytes() (JDK documentation) you encodes characters of the given string into a sequence of bytes using the platform's default charset.
What you are actually need to do is to convert each hexadecimal (also base 16) number (represented by two characters from 0 to 9 and A to F e.g. 1A, 99, etc.) into its corresponding numerical (byte) value e.g. "FF" -> -1 byte.
Sample code is as follows:
import static java.lang.Character.digit;
...
private static byte[] stringToBytes(String input) {
int length = input.length();
byte[] output = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
output[i / 2] = (byte) ((digit(input.charAt(i), 16) << 4) | digit(input.charAt(i+1), 16));
}
return output;
}
...
String keyExample = "99112277445566778899AABBCCDDEEFF0123456789ABCDEF0123456789ABCDEF";
byte[] key = stringToBytes(keyExample);
byte[] barrayMessage = {123,45,55,23,64,21,65};
byte[] result = decryptByte(barrayMessage, key);
Please bear in mind that because we convert each two characters into a single byte, the proposed method assumes your input will have even number of characters (also the input is not null and empty).
If that method is going to be used internally that form is acceptable but if you make it as a part of library visible to others, it would be good to put some checks and throw exception on invalid input.
You should try and decode your key using a hexadecimal decoder instead of calling getBytes().

How do I encrypt/decrypt a string of text using 3DES in java? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How do I use 3des encryption/decryption in Java?
How do I encrypt/decrypt a string of text using 3DES in java?
I found my answer. Duplicate question that didn't show up when I asked this one.
How do I use 3des encryption/decryption in Java?
From an old code:
public void testSymCypher(SecretKey k, String str)
throws BadPaddingException, IllegalBlockSizeException,
InvalidAlgorithmParameterException, InvalidKeyException,
NoSuchAlgorithmException, NoSuchPaddingException
{
Cipher cip = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cip.init(Cipher.ENCRYPT_MODE,k);
byte[] ciphered = cip.doFinal(str.getBytes());
byte iv[] = cip.getIV();
// printing the ciphered string
printHexadecimal(ciphered);
IvParameterSpec dps = new IvParameterSpec(iv);
cip.init(Cipher.DECRYPT_MODE,k,dps);
byte[] deciphered = cip.doFinal(ciphered);
// printing the deciphered string
printHexadecimal(deciphered);
}
Notice than other usage of DESede are available in Java JDK 6:
DESede/CBC/NoPadding (168)
DESede/CBC/PKCS5Padding (168)
There is also ECB mode available (but be carreful to not use it twice !!), you don't need to use iv part in this case:
DESede/ECB/NoPadding (168)
DESede/ECB/PKCS5Padding (168)
To generate key for DESede:
KeyGenerator generatorDes = KeyGenerator.getInstance("DESede");
SecretKey skaes = generatorDes.generateKey();
Finally I recommand reading this document from SUN if you need to work on Java and Cryptography
We use this little helper class for password-based DES encryption from String to Hex String and back - not sure how to get this working with 3DES though:
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
public class DesHelper {
private static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(DesHelper.class);
static final byte[] SALT = { (byte) 0x09, /* snip - randomly chosen but static salt*/ };
static final int ITERATIONS = 11;
private Cipher _ecipher;
private Cipher _dcipher;
public DesHelper(final String passphrase) {
try {
final PBEParameterSpec params = new PBEParameterSpec(SALT, ITERATIONS);
final KeySpec keySpec = new PBEKeySpec(passphrase.toCharArray());
final SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES")
.generateSecret(keySpec);
_ecipher = Cipher.getInstance(key.getAlgorithm());
_dcipher = Cipher.getInstance(key.getAlgorithm());
_ecipher.init(Cipher.ENCRYPT_MODE, key, params);
_dcipher.init(Cipher.DECRYPT_MODE, key, params);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
public String encrypt(final String string) {
try {
// Encode the string into bytes using utf-8
final byte[] bytes = string.getBytes("UTF-8");
// Encrypt
final byte[] enc = _ecipher.doFinal(bytes);
// Encode bytes to base64 to get a string
return bytesToHex(enc);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
public String decrypt(final String str) {
try {
// Decode base64 to get bytes
final byte[] dec = hexToBytes(str);
// Decrypt
final byte[] utf8 = _dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (final Exception e) {
log.info("decrypting string failed: " + str + " (" + e.getMessage() + ")");
return null;
}
}
private static String bytesToHex(final byte[] bytes) {
final StringBuilder buf = new StringBuilder(bytes.length * 2);
for (final byte b : bytes) {
final String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
buf.append("0");
}
buf.append(hex);
}
return buf.toString();
}
private static byte[] hexToBytes(final String hex) {
final byte[] bytes = new byte[hex.length() / 2];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16);
}
return bytes;
}
}
You would use this class like this:
public static void main(final String[] args) {
final DesHelper h = new DesHelper("blabla");
System.out.println(h.decrypt(h.encrypt("foobar")));
}
I wrote an article on this sometimes back. Please visit the following link in my blog that has a working, completed code with explanations and diagram.
View My Triple DES Encryption Article, Code Here
Hopefully you will find it helpful.
You may also consider using a stream cipher (e.g., OFB or CTR mode on top of a 3DES block encryption), so that you don't have to deal with padding the string to a multiple of the cipher blocksize.

Categories

Resources