I need to access some data that used PHP encryption. The PHP encryption is like this.
base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($cipher), $text, MCRYPT_MODE_ECB));
As value of $text they pass the time() function value which will be different each time that the method is called in. I have implemented this in Java. Like this,
public static String md5(String string) {
byte[] hash;
try {
hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Huh, MD5 should be supported?", e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Huh, UTF-8 should be supported?", e);
}
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
int i = (b & 0xFF);
if (i < 0x10) hex.append('0');
hex.append(Integer.toHexString(i));
}
return hex.toString();
}
public static byte[] rijndael_256(String text, byte[] givenKey) throws DataLengthException, IllegalStateException, InvalidCipherTextException, IOException{
final int keysize;
if (givenKey.length <= 192 / Byte.SIZE) {
keysize = 192;
} else {
keysize = 256;
}
byte[] keyData = new byte[keysize / Byte.SIZE];
System.arraycopy(givenKey, 0, keyData, 0, Math.min(givenKey.length, keyData.length));
KeyParameter key = new KeyParameter(keyData);
BlockCipher rijndael = new RijndaelEngine(256);
ZeroBytePadding c = new ZeroBytePadding();
PaddedBufferedBlockCipher pbbc = new PaddedBufferedBlockCipher(rijndael, c);
pbbc.init(true, key);
byte[] plaintext = text.getBytes(Charset.forName("UTF8"));
byte[] ciphertext = new byte[pbbc.getOutputSize(plaintext.length)];
int offset = 0;
offset += pbbc.processBytes(plaintext, 0, plaintext.length, ciphertext, offset);
offset += pbbc.doFinal(ciphertext, offset);
return ciphertext;
}
public static String encrypt(String text, String secretKey) throws Exception {
byte[] givenKey = String.valueOf(md5(secretKey)).getBytes(Charset.forName("ASCII"));
byte[] encrypted = rijndael_256(text,givenKey);
return new String(Base64.encodeBase64(encrypted));
}
I have referred this answer when creating MCRYPT_RIJNDAEL_256 method."
Encryption in Android equivalent to php's MCRYPT_RIJNDAEL_256
"I have used apache codec for Base64.Here's how I call the encryption function,
long time= System.currentTimeMillis()/1000;
String encryptedTime = EncryptionUtils.encrypt(String.valueOf(time), secretkey);
The problem is sometimes the output is not similar to PHP but sometimes it works fine.
I think that my MCRYPT_RIJNDAEL_256 method is unreliable.
I want to know where I went wrong and find a reliable method so that I can always get similar encrypted string as to PHP.
The problem is likely to be the ZeroBytePadding. The one of Bouncy always adds/removes at least one byte with value zero (a la PKCS5Padding, 1 to 16 bytes of padding) but the one of PHP only pads until the first block boundary is encountered (0 to 15 bytes of padding). I've discussed this with David of the legion of Bouncy Castle, but the PHP zero byte padding is an extremely ill fit for the way Bouncy does padding, so currently you'll have to do this yourself, and use the cipher without padding.
Of course, as a real solution, rewrite the PHP part to use AES (MCRYPT_RIJNDAEL_128), CBC mode encryption, HMAC authentication, a real Password Based Key Derivation Function (PBKDF, e.g. PBKDF2 or bcrypt) and PKCS#7 compatible padding instead of this insecure, incompatible code. Alternatively, go for OpenSSL compatibility or a known secure container format.
Related
So here is a question.
I have an old code on a Windows system that just takes a short string and makes CryptEncrypt on it with a public RSA key.
The minimum working example is here (avoiding any checks to make it shorter. avoiding freeing as well)
std::string testData = "12345678";
HCRYPTPROV context;
CryptAcquireContext(&context, nullptr, nullptr, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT);
auto pubK = FromBase64(PublicKey);
CERT_PUBLIC_KEY_INFO *pubKeyInfo = nullptr;
DWORD keyLength = 0;
CryptDecodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
pubK.data(), pubK.size(),
CRYPT_ENCODE_ALLOC_FLAG, nullptr,
&pubKeyInfo, &keyLength);
HCRYPTKEY key;
CryptImportPublicKeyInfo(context, X509_ASN_ENCODING, pubKeyInfo, &key);
std::vector<std::uint8_t> result(testData.begin(), testData.end());
DWORD size = testData.size();
CryptEncrypt(key, NULL, true, 0, result.data(), &size, result.size());
result.resize(size);
size = testData.size();
CryptEncrypt(key, NULL, true, 0, result.data(), &size, result.size());
std::cout << ToBase64(result) << "\n";
The code works and returns a base64 encoded string. like lTq01sOcgDcgwtDaFFoHH/Qb6xLw0KU+/n/+3t0eEb8l4N69QGcaEWf1qso3a4qn7Y8StlXcfMe8uspNF/KDj6qQOMvCuM+uUl+tkLd5NXiESsjycgjyxAqdCIO71iTSmsYVcsS3fY/gtIbO4UAFnCRPOXoSyqWqpXW7IRtFzL2N3MxgIBlIMErNvNWs5HPA7xAY/XO6UpSMWsQO4ppccdeNLSZDPwOxohKD/BX5oDin81nFn7fvIZgghfH5knF1nezK8IGKl+vtbgrwlUUULp/wJ4POceyIn0HaZoVsaCu6xFJcUJGfBqSvm4GZqkp2MlGxBODku0OSgEfIDEGMTg==.
Then I have to decrypt this string with the private key on another side running java.
I use bouncycastle:
try {
Security.addProvider(new BouncyCastleProvider());
String value = "";
AsymmetricKeyParameter privateKey =
(AsymmetricKeyParameter) PrivateKeyFactory.createKey(Base64.getDecoder().decode(privateKeyValue));
AsymmetricBlockCipher e = new RSAEngine();
e = new org.bouncycastle.crypto.encodings.PKCS1Encoding(e);
e.init(false, privateKey);
byte[] messageBytes = Base64.getDecoder().decode(inputdata);
byte[] hexEncodedCipher = e.processBlock(messageBytes, 0, messageBytes.length);
value = new String(hexEncodedCipher);
System.out.println(value);
return value;
}
catch (Exception e) {
System.out.println(e);
}
And this code shows me the next error:
org.bouncycastle.crypto.DataLengthException: input too large for RSA cipher.
And I believe I'm missing something on the windows side, because if I use the same keys on the Java side and crypt the same data with the same public key, the decryption works as expected.
Here the keys, I generated with openssl for this question (RSA 2048):
Private:
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCeL7K++3+vl52Q
WFC6ThgvNPlhHLlP5ylIMi/7Gy60wcCHtx8R5Hzi2j7Kx/uBXyr7SCbePS7NtqHx
meVK3VhEvYHz2uYaUNf6GqJgNNjfRymnL5ll8K8lq1wym6A7KZ+L3qLHH1oI6GfW
+uf32nUfy1PQvsatPN7aiJ4ymiaJj2LcI6Bp78CINsu56Cx9QBN9uoXqKO/7NjOz
y2GRdYdckiqCkcmGDzA4/5tJROxj21gUbxwoUR+yz2sVFGlcJycsDDJiIXiPjbVN
XgM/YfmRjdAPQQwXXj9AK5w/dp9TPq621WqYES74rIY05cba4v1kihmFbs0DHLrV
fQZB0Pu9AgMBAAECggEAGuD//nO9vpiErYJUNVQPx/W4akf3NRySZzIf9QspZI2H
qYf0P5YTonhzMwHIOrNxGkGoWRsMWOgvnF4KGC6EUSniaw1HDDGwgU8FSFOyhj4R
VddAuZGsMTps8CyBjYwFED9EaZFqOxlCi8UWpYb5X+2s0EuadtVhCMEuIGsRIU53
mfW11182YtbAI7Zqi7wg/w0yRz5rVj4ph8nbSFPgi6qtVWA9bxVxNeRTXyQDWjUU
NFQzGcRG6D/SYTnRzTndVM5TmTwEVt0hvJNA2/pMWF5XMu6B2exOpJIVVVdQ40l/
XHh33SU8+gQYY56rHzFebCa0Nuxwdk0paluv+x3CAQKBgQDKwe8QXfNlMDqrM06n
iq/NwKE7tB+1gs1nKr4qZdbI71IzgQJgIJcxbJwbuE6z6Yk8PoancEELH9mSY7EN
YGeSO3QsO1LF1vyJWgWQU1G3pfC9sh8sY6f9WYg8+JogIeJsgjwf7PXrGaBq8++Q
GBMUATzPAh/ERIqUip0nEQ2IJwKBgQDHuYgPvv77qhLc+ZNOdhaZtibeVsFx50V+
a+qR+INSTJ/CChNkoVtMg803ZQWckJBOYL/TS2Mw+ctNomUg+ImjULGT9GzqMeme
HkPpAj5pNyNVpRfVNZfmS2cwwuRyE1QbFGs3C4p7D5MKKCfl4/8MBQXtJGWrQZFr
owh2NNyHewKBgHsMmSYorlcBnwlZOOnK7AiFWBRgq0G/4SI0OXaHmYMWYp+pMqTe
AoPXMyJLh0/+ce/izlt9b6vtp2AFKmVA1XpUpJtXYVN5tocw3+GH/zbh+SlWmT6a
OFAz7s953CeWCNDrdMu3RkNoqQdfhUrAoYtpeNr0ogy9wBCH0vnrinfPAoGBALNy
U/hpz+lH1qjqKFsPqKC001ljM21msL60sU4zrbHNHKEXsnLwsvodVc3Wm2MfVDjH
nrJ2gomnde2r4hbsl6W/w70+mHkXHWKuqK97D54zJzE1IyOygmctCmr6QIzqJuAp
yWbsnKCSzrcKe0aHQkmHXdrCoAJt5/2AvwKN3jJvAoGAIaLts1F7shIZ365FXyWD
poOFtXsNRXESkcYpJ2Pn+K0fUNtdH4BtR/5Z5bsEWXBf2LbL7s15UZFZbck0KH3K
22BRgPQze7wEhMVFlIMNhF17WrOh2NTGUAVz2CYSthbYh0QSI+5XJk0AEfiQcqYk
+E4bZw/RUTY94V+ITEK6F8g=
public
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAni+yvvt/r5edkFhQuk4Y
LzT5YRy5T+cpSDIv+xsutMHAh7cfEeR84to+ysf7gV8q+0gm3j0uzbah8ZnlSt1Y
RL2B89rmGlDX+hqiYDTY30cppy+ZZfCvJatcMpugOymfi96ixx9aCOhn1vrn99p1
H8tT0L7GrTze2oieMpomiY9i3COgae/AiDbLuegsfUATfbqF6ijv+zYzs8thkXWH
XJIqgpHJhg8wOP+bSUTsY9tYFG8cKFEfss9rFRRpXCcnLAwyYiF4j421TV4DP2H5
kY3QD0EMF14/QCucP3afUz6uttVqmBEu+KyGNOXG2uL9ZIoZhW7NAxy61X0GQdD7
vQIDAQAB
What do I wrong?
CryptEncrypt returns the ciphertext in little-endian format, see CryptEncrypt( Remarks, last section). For the decryption in Java the byte array messageBytes must therefore be inverted, e.g. here.
I am trying to do AES Encryption using JAVA, I have made multiple attempts, tried a lot of codes and did many changes to finally reach to a place where my encrypted text matches with the encrypted text generated using C# code BUT PARTIALLY. The last block of 32 bits is different. I do not have access to the C# code since it is a 3rd Party Service. Can anyone guide what am I missing?
Conditions Mentioned are to use:
Use 256-bit AES encryption in CBC mode and with PKCS5 padding to encrypt the entire query string using your primary key and initialization vector. (Do not include a message digest in the query string.) The primary key is a 64-digit hexadecimal string and the initialization vector is a 32-digit hexadecimal string.
The sample values I used are:
Aes_IV = 50B666AADBAEDC14C3401E82CD6696D4
Aes_Key = D4612601EDAF9B0852FC0641DC2F273E0F2B9D6E85EBF3833764BF80E09DD89F (my KeyMaterial)
Plain_Text = ss=brock&pw=123456&ts=20190304234431 (input)
Encrypted_Text = 7643C7B400B9A6A2AD0FCFC40AC1B11E51A038A32C84E5560D92C0C49B3B7E0 A072AF44AADB62FA66F047EACA5C6A018 (output)
My Output =
7643C7B400B9A6A2AD0FCFC40AC1B11E51A038A32C84E5560D92C0C49B3B7E0 A38E71E5C846BAA6C31F996AB05AFD089
public static String encrypt( String keyMaterial, String unencryptedString, String ivString ) {
String encryptedString = "";
Cipher cipher;
try {
byte[] secretKey = hexStrToByteArray( keyMaterial );
SecretKey key = new SecretKeySpec( secretKey, "AES" );
cipher = Cipher.getInstance( "AES/CBC/PKCS5Padding" );
IvParameterSpec iv;
iv = new IvParameterSpec( hexStrToByteArray( ivString ) );
cipher.init( Cipher.ENCRYPT_MODE, key, iv );
byte[] plainText = unencryptedString.getBytes( "UTF-8") ;
byte[] encryptedText = cipher.doFinal( plainText );
encryptedString = URLEncoder.encode(byteArrayToHexString( encryptedText ),"UTF-8");
}
catch( InvalidKeyException | InvalidAlgorithmParameterException | UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException e ) {
System.out.println( "Exception=" +e.toString() );
}
return encryptedString;
}
I have used this for conversions.
public static byte[] hexStrToByteArray ( String input) {
if (input == null) return null;
if (input.length() == 0) return new byte[0];
if ((input.length() % 2) != 0)
input = input + "0";
byte[] result = new byte[input.length() / 2];
for (int i = 0; i < result.length; i++) {
String byteStr = input.substring(2*i, 2*i+2);
result[i] = (byte) Integer.parseInt("0" + byteStr, 16);
}
return result;
}
public static String byteArrayToHexString(byte[] ba) {
String build = "";
for (int i = 0; i < ba.length; i++) {
build += bytesToHexString(ba[i]);
}
return build;
}
public static String bytesToHexString ( byte bt) {
String hexStr ="0123456789ABCDEF";
char ch[] = new char[2];
int value = (int) bt;
ch[0] = hexStr.charAt((value >> 4) & 0x000F);
ch[1] = hexStr.charAt(value & 0x000F);
String str = new String(ch);
return str;
}
Any Suggestions, what should I do to match the outputs?
If only the last block of ECB / CBC padding is different then you can be pretty sure that a different block cipher padding is used. To validate which padding is used you can try (as Topaco did in the comments below the question) or you can decrypt the ciphertext without padding. For Java that would be "AES/CBC/NoPadding".
So if you do that given the key (and IV) then you will get the following output in hexadecimals:
73733D62726F636B2670773D3132333435362674733D3230313930333034323334343331000000000000000000000000
Clearly this is zero padding.
Zero padding has one big disadvantage: if your ciphertext ends with a byte valued zero then this byte may be seen as padding and stripped from the result. Generally this is not a problem for plaintext consisting of an ASCII or UTF-8 string, but it may be trickier for binary output. Of course, we'll assume here that the string doesn't use a null terminator that is expected to be present in the encrypted plaintext.
There is another, smaller disadvantage: if your plaintext is exactly the block size then zero padding is non-standard enough that there are two scenarios:
the padding is always applied and required to be removed, which means that if the plaintext size is exactly a number of times the block size that still a full block of padding is added (so for AES you'd have 1..16 zero valued bytes as padding);
the padding is only applied if strictly required, which means that no padding is applied if the plaintext size is exactly a number of times the block size (so for AES you'd have 0..15 zero valued bytes as padding).
So currently, for encryption, you might have to test which one is expected / accepted. E.g. Bouncy Castle - which is available for C# and Java - always (un)pads, while the horrid PHP / mcrypt library only pads where required.
You can always perform your own padding of course, and then use "NoPadding" for Java. Remember though that you never unpad more than 16 bytes.
General warning: encryption without authentication is unfit for transport mode security.
I'm trying to reproduce an old encryption/decryption done in Java to a new one in Ruby one because I'm rebuilding the whole app. All this to change this encryption asap, obviously.
Here is the Java code:
public class MyClass {
private static String algo;
private static SecretKey key;
static {
algo = "AES";
String keyString = "someString";
byte[] decodedKey = Base64.getDecoder().decode(keyString);
key = new SecretKeySpec(decodedKey, 0, decodedKey.length, algo);
}
private static String decrypt(String encrypted) {
try {
Cipher cipher = Cipher.getInstance(algo);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decodedBytes = Base64.getDecoder().decode(encrypted.getBytes());
byte[] original = cipher.doFinal(decodedBytes);
return new String(original);
}
catch (Exception e) {
// Some error
return "bad";
}
}
private static String encrypt(String toEncrypt) {
try {
Cipher cipher = Cipher.getInstance(algo);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());
byte[] encryptedValue = Base64.getEncoder().encode(encrypted);
return new String(encryptedValue);
}
catch (Exception e) {
// Some error
return "bad";
}
}
}
Java code comes from here
I have a problem with the decryption. Here is my Ruby code:
key = Digest::SHA256.digest(key)
aes = OpenSSL::Cipher.new('AES-256-CBC')
aes.decrypt
aes.key = Digest::SHA256.digest(key)
aes.update(secretdata) + aes.final
# => OpenSSL::Cipher::CipherError: bad decrypt
What am I doing wrong?
The "AES" algorithm description isn't complete; it will use a default mode of operation and padding scheme. In the provider included in the JRE this will default to ECB and PKCS#7 padding ("AES/ECB/PKCS5Padding"). This is obviously insecure as ECB is insecure, but due to applications relying on this default it cannot be changed (which is one of the reasons why having defaults was a mistake in the first place).
Furthermore, in the code you've provided there is no hashing involved. This is a good thing as a single secure hash over a key is not enough to provide a good amount of security. Storing a key in a string is almost as bad, but not quite. Instead however the key is base 64 encoded.
So you have to switch to 'AES-256-ECB' and remove the double hashing of the key, replacing it with base 64 decoding instead.
It was not that easy but here is my way to do it.
class ManualEncryption
class << self
attr_accessor :configuration
def config
#configuration ||= Configuration.new
end
def aes
return #aes if #aes
#aes = OpenSSL::Cipher.new(config.algo) # "AES-256-ECB"
#aes
end
def decodedKey
return #decodedKey if #decodedKey
#decodedKey = Base64.decode64(config.key_string) # "mySecretString"
end
def configure
yield(config)
raise 'Algo not specified' unless config.algo
raise 'key not specified' unless config.key_string
end
def encrypt(value)
raise 'No configuration done' unless config.algo && config.key_string
aes_perform('encrypt', value)
end
def decrypt(value)
raise 'No configuration done' unless config.algo && config.key_string
return value unless value
aes_perform('decrypt', value)
end
def aes_perform(status, value)
aes.reset
if status.eql?('encrypt')
aes.encrypt
aes.key = decodedKey
aes_val = aes.update(value) + aes.final
Base64::encode64(aes_val)
else
aes.decrypt
aes.key = decodedKey
decoded_value = Base64::decode64(value)
aes.update(decoded_value) + aes.final
end
end
end
class Configuration
attr_accessor :algo, :key_string
attr_reader :aes
end
end
Note: I still have a problem with the encryption. It creates \n inside my encrypted value and I don't know why. I'm working on it.
I have made a https api request from android to server. The API request contains one parameter that needs to be encrypted before it is send (i.e. it's a password). RSA/ECB/PKCS1Padding is the encryption used in both end.
Encryption in android side does the following things:
/*Encrypt the password using public key.public key is obtained from generateRsaPublicKey(BigInteger modulus, BigInteger publicExponent) function)*/
public static String rsaEncrypt(String originalString, PublicKey key) {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherByte = cipher.doFinal(original);
return bytesToHex(cipherByte);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//generate public key with given module and exponent value
public static PublicKey generateRsaPublicKey(BigInteger modulus, BigInteger publicExponent) {
PublicKey key = null;
try {
key = KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(modulus, publicExponent));
return key;
} catch (Exception e) {
Log.e("error", e.toString());
// return null;
}
return key;
}
// Helper methods
final protected static char[] hexArray = "0123456789abcdef".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
// Log.d("byte array representaion","value in integrer"+v);
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
below is the source for decrypting the password on the server
// *** setup private key
RSAPrivateKeySpec privateRPKS
= new RSAPrivateKeySpec(new BigInteger(gModulusPlainS, 16), new BigInteger(privateExponentPlainS, 16));
KeyFactory keyFactoryKF = KeyFactory.getInstance("RSA");
RSAPrivateKey gPrivateKeyRPK = (RSAPrivateKey) keyFactoryKF.generatePrivate(privateRPKS);
// *** setup cipher
Cipher cipherC = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipherC.init(Cipher.DECRYPT_MODE, gPrivateKeyRPK);
// *** decrypt hex-encoded cipherTxS
byte[] baCipherText = hexToBin(cipherTxS.getBytes());
byte[] baPlainText2 = cipherC.doFinal(baCipherText);
String decryptedTextS = new String(baPlainText2);
But I got the following error log
javax.crypto.IllegalBlockSizeException: Data size too large
at com.ibm.crypto.provider.RSASSL.a(Unknown Source)
at com.ibm.crypto.provider.RSASSL.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Unknown Source)
javax.crypto.BadPaddingException: Not PKCS#1 block type 2 or Zero padding
But it is working on websight part. Why it isn't working in android?
Thank you for your kindness to looking my code.
You are sending the ciphertext as string, if cipherTxS.getBytes() is indeed a string to byte array conversion. Ciphertext should be either kept in binary or possibly encoded using base 64 encoding.
Thanks to Alex Klyubin, Android Security Engineer. I have got answer from Here
Developers who use JCA for key generation, signing or random number generation should update their applications to explicitly initialize the PRNG with entropy from /dev/urandom or /dev/random.Also, developers should evaluate whether to regenerate cryptographic keys or other random values previously generated using JCA APIs such as SecureRandom, KeyGenerator, KeyPairGenerator, KeyAgreement, and Signature.
Code comment: Install a Linux PRNG-based SecureRandom implementation as the default, if not yet installed.This is missing from jellybean onwards.
Sample code implementation
I am new to blackberry development and got to complete a task of encryption and decryption with AES/ECB/NoPadding. I used below code, from internet.
Encryption method:
public static byte[] encrypt( byte[] keyData,String message )
throws Exception
{
byte[] data = message.getBytes("UTF-8");
// Create the AES key to use for encrypting the data.
// This will create an AES key using as much of the keyData
// as possible.
if ((data.length % 16) != 0 ) {
StringBuffer buffer = new StringBuffer(message);
int moduleOut = data.length % 16;
int padding = 16 - moduleOut;
for(int i = 0 ; i < padding; i++){
buffer.append(" ");
}
data = buffer.toString().getBytes("UTF-8");
}
AESKey key = new AESKey( keyData);
NoCopyByteArrayOutputStream out = new NoCopyByteArrayOutputStream(data.length);
AESEncryptorEngine engine = new AESEncryptorEngine(key);
BlockEncryptor encryptor = new BlockEncryptor(engine, out);
encryptor.write(data,0,data.length);
int finalLength = out.size();
byte[] cbytes = new byte[finalLength];
System.arraycopy(out.toByteArray(), 0, cbytes, 0, finalLength);
// encryptor.close();
// out.close();
return cbytes;
}
Decryption method:
public static byte[] decrypt(byte[] keyData, byte[] base64EncodedData)
throws CryptoException, IOException
{
// String base64EncodedData=new String(base64EncodedData);
byte[] cipherText =Base64ToBytes(new String(base64EncodedData));
// First, create the AESKey again.
AESKey key = new AESKey(keyData);
// Now, create the decryptor engine.
AESDecryptorEngine engine = new AESDecryptorEngine(key);
// Create the BlockDecryptor to hide the decryption details away.
ByteArrayInputStream input = new ByteArrayInputStream(cipherText);
BlockDecryptor decryptor = new BlockDecryptor(engine, input);
// Now, read in the data.
byte[] temp = new byte[100];
DataBuffer buffer = new DataBuffer();
for (;;)
{
int bytesRead = decryptor.read(temp);
buffer.write(temp, 0, bytesRead);
if (bytesRead < 100)
{
// We ran out of data.
break;
}
}
byte[] plaintext = buffer.getArray();
return plaintext;
}
Base64 to Bytes convert method:
private static byte[] Base64ToBytes(String code) {
byte[] aesString = null;
try
{
aesString = Base64InputStream.decode(code);
}
catch (IOException ioe)
{
}
return aesString;
}
Now the problem is when i encrypt my string with above method i get padded unicodes at the end of the string, which is not tolerable at Server side.
I know it is due to PKCS5FormatterEngine and its normal to have characters appended at end of the string while using this class.
But what if i want to encrypt and decrypt string with AES/ECB method and that too with NoPadding. I know ECB is not a secure mode and all that but server is with PHP and ready, works great in Android, and J2ME.
Please guide. How to bypass PKCS5FormatterEngine or encrypt without any padding.
Update:
I tried to use Cipher class in Blackberry as i used in Android and J2ME but it seems unavailable in net_rim_api.jar, even i tried downloading bouncy castle jar file and dependent class NoSuchAlogrithmException so java.security jar (org.osgi.foundation-1.0.0.jar), compiles but when i try to run it stops saying duplicate classes found. It has a problem with few duplicate classes in jar i have kept for java.security.
If you have solution towards this, please let me know.
Update for Answer:
I have update my code with full encryption and decryption code and also check the answer for better understanding.
Not sure this is really an answer in general, but it might help in this specific case, so I am adding it as such.
You can't really use AES without some padding, because the AES processing does not wish to assume the data you supply will be a multiple of 16 bytes. However if you actually always supply a buffer that is a multiple of 16 bytes, then you can just encrypt your data with code like this:
AESEncryptorEngine engine = new AESEncryptorEngine( key );
for ( int j = 0; j < ciphertext.length - 15; ) {
engine.encrypt(plainText, j, ciphertext, j);
j = j+16;
}
So how do you make sure this works OK at the other end? It may or may not be possible to do this - it actually depends on what is being transferred.
But if, for example, you were passing XML data, then you can append spaces to make the data up to a 16 byte boundary and these will be decrypted as spaces by the Server, and then ignored by the parsing. You can add redundant padding bytes to a variety of file formats and the padding will be ignored, it all depends on the file format being processed.
Update
Given that the actual data is JSON and that I believe that JSON will ignore trailing spaces, the approach I would take is append spaces to the JSON data before encrypting it.
So convert the JSON string to bytes:
byte [] jsonBytes = jsonString.getBytes("UTF-8");
pad this if you need too:
if ( (jsonBytes.length % 16) != 0 ) {
// Now pad this with spaces
}
and you can encrypt the result with no worries about padding bytes.