Need help with openssl, What would be the openssl equivalent of the below java method?
private static String decryptString(String value, String myKey) {
MessageDigest sha = null;
SecretKeySpec secretKey = null;
try {
byte[] key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
secretKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return (new String(cipher.doFinal(Base64.decodeBase64(value))));
} catch (Exception e) {
System.out.println("Error while decrypting: " + e.toString());
}
return null;
}
The function is digesting a string (myKey) using SHA-1 and then using the first 16 bytes of the resulting digest as the key for an AES128-ECB decryption operation. Before decryption, the ciphertext is first base64 decoded.
To digest a string using SHA-1 use the code here:
https://wiki.openssl.org/index.php/EVP_Message_Digests
That code actually uses SHA-256. To make it do SHA-1 instead replace the instance of EVP_sha256() with EVP_sha1(). The first 16 bytes of the result is your "key".
Next the input "value" is base64 decoded. There is some sample code to do that with OpenSSL here:
http://www.ioncannon.net/programming/122/howto-base64-decode-with-cc-and-openssl/
Finally, to do the AES-ECB decryption operation use the code here:
https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption#Decrypting_the_Message
That code is using AES256-CBC. To make it do AES128-ECB instead replace the instance of EVP_aes_256_cbc() with EVP_aes_128_ecb(). The key argument is as you calculated above. The iv argument is just NULL for ECB. The ciphertext is the base64 decoded data that you calculated above. Note that the "PKCS5PADDING" part of the Java code is the default in OpenSSL anyway, so nothing special needs to be done for that.
The plaintext that has been output from the decryption operation is the return value from your Java function.
Related
Hello everyone i have string encrypted in PHP by openssl_encrypt with algorithm 'aes-256-cbc'
Key: C4E30455853D4949A8E91B2C366BE9DE
Vector: 5686044872102713
Encrypted string: ak9YSTd6RXU5TENocUxQUGxieVhpZ3VqSlFiQUdndGZrbVJvbEliTGZjZz0=
And here is my Java function for decrypt:
public static String Decrypt_AES_FromBase64(String AEncryptedText, String AKey32Bytes, String AVectorNum16Bytes) {
try {
byte[] vEncryptedBytes = Base64.getDecoder().decode(AEncryptedText);
Key SecretKey = new SecretKeySpec(AKey32Bytes.getBytes(), "AES");
IvParameterSpec vSpec = new IvParameterSpec(AVectorNum16Bytes.getBytes());
Cipher vCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
vCipher.init(Cipher.DECRYPT_MODE, SecretKey, vSpec);
return new String(vCipher.doFinal(vEncryptedBytes));
} catch (Exception e) {
Common.mContext.getLogger().log(e.toString());
return "";
}
}
When i try to decrypt i have error:
javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
Can somebody tell what the wrong?
The encrypted string AKey32Bytes is double Base64 encoded.
Instead of AKey32Bytes.getBytes() you need to double Base64 decode the encrypted data to binary.
Encrypted string:
ak9YSTd6RXU5TENocUxQUGxieVhpZ3VqSlFiQUdndGZrbVJvbEliTGZjZz0=
After one Base64 decode:
jOXI7zEu9LChqLPPlbyXigujJQbAGgtfkmRolIbLfcg=
After a second Base64 decode (displayed in hex because it is not binary):
8CE5C8EF312EF4B0A1A8B3CF95BC978A0BA32506C01A0B5F9264689486CB7DC8
That is what needs to be provided to the decryption function.
The decrypted result is:
(in hex) 257531362A2179704B40577255516272
(in ASCII): "%u16*!ypK#WrUQbr" (all valid ASCII characters)
Note: there is a full block of PKCS#7 padding (in hex): 10101010101010101010101010101010
As much as it pains me to say this, from the correct padding I can assume the decryption was successful.
See Cryptomathic AES CALCULATOR
I have done Encryption with ,
public static String encrypt(String plainText) {
try {
byte[] keyData = secret_key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(keyData, "AES/ECB/PKCS7Padding");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] cipherText = cipher.doFinal(plainText.getBytes("UTF-8"));
String encryptedString = Base64.encodeToString(cipherText, Base64.NO_WRAP);
return encryptedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
it's working well.
but part of Decryption gives Error like,
W/System.err: javax.crypto.BadPaddingException: pad block corrupted
W/System.err: at com.android.org.bouncycastle.jce.provider.JCEBlockCipher.engineDoFinal(JCEBlockCipher.java:701)
W/System.err: at javax.crypto.Cipher.doFinal(Cipher.java:1111)
decrypt Code like,
public static String decrypt(String encryptedText) {
try {
byte[] keyData = secret_key.getBytes();
SecretKeySpec secretKey = new SecretKeySpec(keyData, "AES/ECB/PKCS7Padding");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] cipherText = Base64.decode(encryptedText,Base64.NO_WRAP);
String decryptedString = new String(cipher.doFinal(cipherText),"UTF-8");
return decryptedString;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
here what is the problem? How can i solve this Issue?
It is likely that your secret_key value contains bytes which are not well represented in the ambiguous encoding you're using. When you call String#getBytes() without specifying an encoding, you get the system default, which can vary.
You should use hexadecimal encoding whenever you represent your key as a String. This will be consistent across serialization/deserialization on every platform. There are many standard implementations of this encoding/decoding process available (i.e. org.bouncycastle.util.encoders.Hex.decode("0123456789ABCDEFFEDCBA9876543210"); or org.apache.commons.codec.binary.Hex.decodeHex("0123456789ABCDEFFEDCBA9876543210".toCharArray()); which both return the raw byte[]).
Some side notes:
You are using ECB mode of operation, which is extremely susceptible to frequency analysis for cryptanalysis and is effectively deprecated aside from toy crypto demonstrations. I suggest you use CBC, CTR, or GCM.
You do not provide an initialization vector (IV), so the same message encrypted with the same key will always yield identical cipher text. Use a unique and non-predictable IV for every encryption operation by generating 16 bytes from SecureRandom and populating it into an IvParameterSpec. You can prepend the IV bytes to the cipher text and transport/store it in the clear.
Your cipher text is not authenticated, allowing for malicious users to both manipulate encrypted data and to attempt decryption via padding oracle/CCA attacks. Use an authenticated encryption with associated data (AEAD) mode like GCM, or use an HMAC/SHA-256 message authentication code (MAC) over the cipher text, and verify it using a constant-time equals method before attempting any decryption.
You do not need to provide the mode of operation or padding scheme when instantiating a key. SecretKey key = new SecretKeySpec(keyData, "AES"); is sufficient.
I would like to encrypt a String with RSA encryption. My public/private keys were generated and stored in DB. In android, I use this code:
public static String encryptRSAToString(String text, String strPublicKey) {
byte[] cipherText = null;
String strEncryInfoData="";
try {
KeyFactory keyFac = KeyFactory.getInstance("RSA");
KeySpec keySpec = new X509EncodedKeySpec(Base64.decode(strPublicKey.trim().getBytes(), Base64.DEFAULT));
Key publicKey = keyFac.generatePublic(keySpec);
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance("RSA");
// encrypt the plain text using the public key
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipherText = cipher.doFinal(text.getBytes());
strEncryInfoData = new String(Base64.encode(cipherText,Base64.DEFAULT));
} catch (Exception e) {
e.printStackTrace();
}
return strEncryInfoData.replaceAll("(\\r|\\n)", "");
}
For debug purpose, I try to call 2 times this method with the same parameters and String result were similar (as expected).
I want to generate the same encrypted String in java. However, "android.util.Base64" class is not available in Java, so I've tried with the default Base64 class:
public static String encryptRSAToString(String text, String strPublicKey) {
byte[] cipherText = null;
String strEncryInfoData="";
try {
KeyFactory keyFac = KeyFactory.getInstance("RSA");
KeySpec keySpec = new X509EncodedKeySpec(Base64.decodeBase64(strPublicKey.trim().getBytes()));
Key publicKey = keyFac.generatePublic(keySpec);
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance("RSA");
// encrypt the plain text using the public key
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipherText = cipher.doFinal(text.getBytes());
strEncryInfoData = new String(Base64.encodeBase64(cipherText));
} catch (Exception e) {
e.printStackTrace();
}
return strEncryInfoData.replaceAll("(\\r|\\n)", "");
}
But the String generated in Android and the one in java are different.
Generated in Android side :
Ky2T4j1JdI081ZESVJgxZXEf/xmtpehfv/EwpVvKQxUu1JI8lwXP2Rc66jHZRc0P846ZYuF3C9YEmWoKbXGXk2MBuT5KVxa2yoTbwZlMmhVOX3X3Efq0VyaO5zZ4qavIq036cA3MzvQbUAb678UdbALW/CjRCsOdeH+hSCzNQ+0=
Generated in JAVA side :
XhSLxfiJUUdZW5kWh0MEPSrqoROBBhNC/krfTx+sdnXML3WegYbMzSvNnPgB8+8Z9joEUBMmoeBI1OhTF6qPFL1EEixkFYAkGaryEFxvN/aFI75kEUj71OHNzAHAuvS+h+9Nssx9psSZ5gc2OoLQH0QtbGDyXB4p+qUGFCde4tY=
Does someone know how to solve my issue ?
thank you
It looks like you've been undone by relying on defaults. Never do that if you hope for interoperability.
Here are the two examples of mistakenly relying on defaults in your code that I've found.
final Cipher cipher = Cipher.getInstance("RSA");
The tranformation string is supposed to be of the form "algorithm/mode/padding" but you've left off the mode and padding specifications. As a result you got default values for those. The defaults are evidently different on Android and Oracle Java. You should always fully specify the transformation, for example:
final Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");
Another bad example is
cipherText = cipher.doFinal(text.getBytes());
In text.getBytes() you are relying on the no-args getBytes() method which uses the default charset for the platform. But this default charset differs on different platforms, and thus this is not portable. In almost all cases I've run across you should specify the UTF-8 charset. So the correct line would thus be
cipherText = cipher.doFinal(text.getBytes("UTF-8"));
and the correct string constructor to use to recreate the original string in the decrypt method is the String(byte [] data, String charsetName).
I canĀ“t comment yet so I answer.
It is possible that different default configurations are being used. Check this question: Is there any difference between Apache's Base64.encodeBase64 and Android's Base64.encode with Base64.Default flag?
There are deviations of different cipher and hash implementations. I would suggest using OpenSSL as a common implementation.
This question already has answers here:
How to decrypt a SHA-256 encrypted string?
(4 answers)
Closed 9 years ago.
I have encrypted a string using the above code.
public String encrypt(String generatedKey)
{
try {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(generatedKey.getBytes("UTF-8"));
byte digest[] = md.digest();
return (new BASE64Encoder()).encode(digest);
}
catch (Exception e) {
return null;
}
}
Similarly i need a code to decrypt the above generated code. How can i do this?
SHA is a digest algorithm, not an encryption algorithm. Digest values are not decryptable. That's why they are secure. Two different inputs may give same digest values. But it is a very little possibility. For sha256 it is 1/(2^256).
Output of digest algorithms have constant length. For SHA256 it is always 256 bit, regardless of your input length, 1 bit or 100 Gbs. If we could decrypt 256 bit digest value and have the original 1Gb input back, we would never need compression algorithms :)
Message digests produce a small "fingerprint" of a larger set of data. It's a one way procedure.
What you probably is looking for is encryption.
Key key = new SecretKeySpec(secret.getBytes(), ALGORITHM);
// Encrypt
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedData = cipher.doFinal(plainText);
// Decrypt
cipher.init(Cipher.DECRYPT_MODE, key)
byte[] decryptedData = cipher.doFinal(encryptedData);
Where ALGORITHM can be one of
http://docs.oracle.com/javase/6/docs/technotes/guides/security/StandardNames.html#Cipher
I am pretty sure that we cannot decode a SHA encrypted string directly.
See this for a clear explanation: How to decrypt SHA-256 encrypted String?
I am trying to encrypt in client and decrypt in sever using AES,
so using cryptojs to encrypt in client side with CBC mode and nopadding
in server side also using Cipher class with same mode and nopadding
function call()
{
var key = CryptoJS.enc.Hex.parse('roshanmathew1989');
var iv = CryptoJS.enc.Hex.parse('roshanmathew1989');
var encrypted = CryptoJS.AES.encrypt("roshanmathew1989",key,{ iv: iv},
{padding:CryptoJS.pad.NoPadding});
alert(encrypted.ciphertext.toString(CryptoJS.enc.Base64));
alert(encrypted.iv.toString());
}
Server side code
public class Crypto
{
private static byte[] key = null;
public void setKey(String key){this.key=key.getBytes();}
public String encrypt(String strToEncrypt)
{
String encryptedString =null;
try
{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
final SecretKeySpec secretKey = new SecretKeySpec(key,"AES");
System.out.println("sdfsdf = "+key.toString());
IvParameterSpec ips = new IvParameterSpec(key);
cipher.init(Cipher.ENCRYPT_MODE, secretKey,ips);
encryptedString = Base64.encodeBase64String(cipher.doFinal(strToEncrypt.getBytes()));
}
catch(Exception e)
{
System.out.println(" ERROR : "+e.getMessage());
}
return encryptedString;
} other method omitted ....
implementation
Crypto cry=new Crypto();
cry.setKey("roshanmathew1989");
String s=cry.encrypt("roshanmathew1989");
Results
Browser side value = O64X/bKNBu7R2Tuq2lUbXeFlQ7wD2YnFasyyhsVUryw=
Server side value of s = RrNcVIER/75fzdjHr884sw==
Can anybody point out the mistake?
There are a few things wrong with the code:
you are using hexadecimal decoding of the key in JavaScript, and String.getBytes() - character encoding without specifying the character set - in Java
your key is 16 characters (it should be 16, 24 or 32 randomized bytes), but it is not in hexadecimals
you are encrypting instead of decrypting on the "server side", although that one is probably on purpose
Take another good look on how to perform encoding and character-encoding, they are essential for good crypto and often performed incorrectly (it's probably the most common issue on Stackoverflow regarding encryption)