I have a PHP script that I have been using do decrypt a session key that I encrypted from iOS. The encryption is done on the client using a 1024-bit public key. Decryption on the server side is done with the corresponding private key. Now I'm trying to write an encryption method for Android. Unfortunately, the decryption continues to fail, and I can't see what is wrong.
Here is the Android code:
public String encryptSessionKeyWithPublicKey(String pemString, byte[] sessionKey) {
try {
PublicKey publicKey = getPublicKeyFromPemFormat(pemString);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherData = cipher.doFinal(sessionKey);
return Base64.encodeToString(cipherData, Base64.DEFAULT);
} catch (IOException ioException) {
Log.e(TAG, "ioException");
} catch (NoSuchAlgorithmException exNoSuchAlg) {
Log.e(TAG, "NoSuchAlgorithmException");
} catch (InvalidKeySpecException exInvalidKeySpec) {
Log.e(TAG, "InvalidKeySpecException");
} catch (NoSuchPaddingException exNoSuchPadding) {
Log.e(TAG, "NoSuchPaddingException");
} catch (InvalidKeyException exInvalidKey) {
Log.e(TAG, "InvalidKeyException");
} catch (IllegalBlockSizeException exIllBlockSize) {
Log.e(TAG, "IllegalBlockSizeException");
} catch (BadPaddingException exBadPadding) {
Log.e(TAG, "BadPaddingException");
}
return null;
}
private PublicKey getPublicKeyFromPemFormat(String PEMString)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException
{
AssetManager assetManager = context.getAssets();
InputStream inputStream = assetManager.open(PEMString);
BufferedReader pemReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer content = new StringBuffer();
String line = null;
while ((line = pemReader.readLine()) != null) {
if (line.indexOf("-----BEGIN PUBLIC KEY-----") != -1) {
while ((line = pemReader.readLine()) != null) {
if (line.indexOf("-----END PUBLIC KEY") != -1) {
break;
}
content.append(line.trim());
}
break;
}
}
if (line == null) {
throw new IOException("PUBLIC KEY not found");
}
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(new X509EncodedKeySpec(Base64.decode(content.toString(), Base64.DEFAULT)));
}
The PHP script is fairly simple:
<?php
$passphrase = 'xxxxxxxx'; // just for testing - load from file later
$decrypted_session_key = 'unavailable';
$encrypted_session_key = base64_decode($_POST['encrypted_session_key']);
$fp = fopen('private128.pem', 'r');
if ($fp == null) {
$arr = array('response' => 'failure', 'message' => 'private key not readable');
echo json_encode($arr);
die();
}
$priv_key = fread($fp, 8192);
fclose($fp);
$res = openssl_get_privatekey($priv_key, $passphrase);
$decr_result = openssl_private_decrypt($encrypted_session_key, $decrypted_session_key, $res, OPENSSL_PKCS1_OAEP_PADDING);
if (!$decr_result) {
$arr = array('response' => 'failure', 'message' => $decr_result);
echo json_encode($arr);
die();
}
// write the decrypted string to a file
$session_key_file = fopen("session_key", "w") or die("Unable to open file!");
fwrite($session_key_file, $decrypted_session_key);
fclose($session_key_file);
$arr = array('response' => 'success', 'message' => 'server confirms receipt of session key');
echo json_encode($arr);
?>
All I am trying to encrypt are 16 randomly generated bytes.
The PHP output is:
{"response":"failure","message":false} which means that openssl_private_decrypt line isn't getting a correct decryption result.
Since my PHP script works with my iOS code, I do not want to change it unless absolutely necessary. Can anyone see what I should do to my Java code to align it with what is happening on the PHP side?
Your PHP function has OPENSSL_PKCS1_OAEP_PADDING but your java function is using RSA/ECB/PKCS1PADDING
Change your PHP decryption to OPENSSL_PKCS1_PADDING which seems to match your java encryption.
OR
switch your java encryption to RSA/ECB/OAEPWithSHA-1AndMGF1Padding
Related
I'm new to cryptography and this here is the code that I wrote filling in the skeleton code provided by my assigner(please don't mind the structure). I describe the problem below this code.
public class FileEncryptor {
private static final String progName = "FileEncryptor";
private static final int bufSize = 128;
public static void main(String[] args) {
BufferedInputStream in = null; // A buffered input stream to read from
BufferedOutputStream out = null; // And a buffered output stream to write to
SecretKeyFactory kf = null; // Something to create a key for us
KeySpec ks = null; // This is how we specify what kind of key we want it to generate
byte[] salt = new byte[20]; // Some salt for use with PBKDF2, only not very salty
SecretKey key = null; // The key that it generates
Cipher cipher = null; // The cipher that will do the real work
SecretKeySpec keyspec = null; // How we pass the key to the Cipher
int bytesRead = 0; // Number of bytes read into the input file buffer
// First, check the user has provided all the required arguments, and if they haven't, tell them then exit
if(args.length != 4) {
printUsageMessage(); System.exit(1);
}
// Open the input file
try {
in = new BufferedInputStream(new FileInputStream(args[1]));
} catch (FileNotFoundException e) {
printErrorMessage("Unable to open input file: " + args[1], null);
System.exit(1);
}
// And then the output file
try {
out = new BufferedOutputStream(new FileOutputStream(args[2]));
} catch (FileNotFoundException e) {
printErrorMessage("Unable to open output file: " + args[2], e);
System.exit(1);
}
// Create a PBKDF2 secret key factory
String algorithm = "PBKDF2WithHmacSHA256";
try {
kf = SecretKeyFactory.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
printErrorMessage("Specified algorithm could not be found: " + algorithm , e);
System.exit(1);
}
// Set up a KeySpec for password-based key generation of a 128-bit key
ks = new PBEKeySpec(args[3].toCharArray(),salt,4096, 128);
// Now run the passphrase through PBKDF2 to get the key
try {
key = kf.generateSecret(ks);
} catch (InvalidKeySpecException e) {
printErrorMessage("The key spec provided is invalid: " + keyspec, e);
System.exit(1);
}
// Get the byte encoded key value as a byte array
byte[] aeskey = key.getEncoded();
// Now generate a Cipher object for AES encryption in ECBC mode with PKCS #5 padding
// Use ECB for the first task, then switch to CBC for versions 2 and 3
try {
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
} catch (NoSuchAlgorithmException e) {
printErrorMessage("No Such Algorithm Exception when creating main cipher", e);
System.exit(2);
} catch (NoSuchPaddingException e) {
printErrorMessage("No Such Padding Exception when creating main cipher",e);
System.exit(2);
}
// Set a variable to indicate whether we're in encrypt or decrypt mode, based upon args[0]
int cipherMode = -1;
char mode = Character.toLowerCase(args[0].charAt(0));
switch (mode) {
case 'e' : cipherMode = Cipher.ENCRYPT_MODE; break;
case 'd' : cipherMode = Cipher.DECRYPT_MODE; break;
default: printUsageMessage(); System.exit(1);
}
// Set up a secret key specification, based on the 16-byte (128-bit) AES key array previously generated
keyspec = new SecretKeySpec(aeskey, "AES");
// Now initialize the cipher in the right mode, with the keyspec and the ivspec
try {
cipher.init(cipherMode, keyspec);
} catch (InvalidKeyException e) {
printErrorMessage("Invalid Key Spec",e); System.exit(2);
}
// Set up some input and output byte array buffers
byte[] inputBuffer = new byte[bufSize];
byte[] outputBuffer = null;
// "Prime the pump" - we've got to read something before we can encrypt it
// and not encrypt anything if we read nothing.
try {
bytesRead = in.read(inputBuffer);
} catch (IOException e1) {
printErrorMessage("Error reading input file " + args[1],e1); System.exit(1);
}
// As long as we've read something, loop around encrypting, writing and reading
// bytesRead will be zero if nothing was read, or -1 on EOF - treat them both the same
while (bytesRead > 0) {
byte[] input = inputBuffer;
// Now encrypt this block
outputBuffer = cipher.update(input);
// Write the generated block to file
try {
out.write(outputBuffer);
} catch (IOException e1) {
printErrorMessage("Error writing to output file " + args[2],e1); System.exit(1);
}
// And read in the next block of the file
try {
bytesRead = in.read(inputBuffer);
} catch (IOException e1) {
printErrorMessage("Error reading input file " + args[1],e1); System.exit(1);
}
}
// Now do the final processing
try {
outputBuffer = cipher.doFinal(outputBuffer);
} catch (IllegalBlockSizeException e2) {
printErrorMessage("Illegal block size encountered while doing final processing: " + outputBuffer.length , e2);
System.exit(1);
} catch (BadPaddingException e2) {
printErrorMessage("Bad Padding found", e2);
System.exit(1);
}
// Write the final block of output
try {
out.write(outputBuffer);
} catch (IOException e1) {
printErrorMessage("Error on final write to output file " + args[2],e1); System.exit(1);
}
// Close the output files
try {
in.close();
out.close();
} catch (IOException e1) {
printErrorMessage("Error closing file", e1);
}
}
}
For convenience I omitted the printErrorMessage() and printUsageMessage() methods.
This here seems to be the problem; I am getting a BadPaddingException and though the code catches that exception I do not know how to handle that exception or what to do with it. I tried removing the System.exit(1) line from my code in final processing but then the output files comes out twice the size of my encrypted file which is 256 bytes regardless of the size of my input files(having size less than 256 bytes). So, if my input file contains:
hello there
And I encrypt it using key as the passphrase I get my output file containing something like this:
9`ÃLå7}
syXÀ¢Èì´^¾Õn¾AXëC~9‹`Èì´^¾Õn¾AXëC~9‹`Èì´^¾Õn¾AXëC~9‹`Èì´^¾Õn¾AXëC~9‹`Èì´^¾Õn¾AXëC~9‹`Èì´^¾Õn¾AXëC~9‹`Èì´^¾Õn¾AXëC~9‹`0‹ó‚±G¸Y·IñåsòQœ ÙÞç„ò;uîðpoîœ ÙÞç„ò;uîðpoîœ ÙÞç„ò;uîðpoîœ ÙÞç„ò;uîðpoîœ ÙÞç„ò;uîðpoîœ ÙÞç„ò;uîðpoîœ ÙÞç„ò;uîðpoÄQé±uTö/ژͤ
And now when I decrypt this file using the same passphrase key I get something like this:
hello there 9`Ì巽
syXÀ¢Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`Ȭ´^¾ծ¾AX냾9`
How can I get rid of this shit-storm that's trailing my decrypted text?
All helps and suggestions will be appreciated. Thanks.
I am trying to encrypt the otp in php. i have the java code that encrypt the otp and when i send that encrypted otp it decrypt at client end as expected. but when i encrypt it using php, decryption does not work.
Actual Java Encryption:
public static final String appKey = "wbx+mGnapzZMietP0gK6muJb/vUU7jnAk9Fe5gTHh4w=";
public static String encryptEK(byte[] plainText, byte[] secret){
try{
SecretKeySpec sk = new SecretKeySpec(secret, AES_ALGORITHM);
ENCRYPT_CIPHER.init(Cipher.ENCRYPT_MODE, sk);
return Base64.encodeBase64String(ENCRYPT_CIPHER.doFinal(plainText));
}catch(Exception e){
e.printStackTrace();
return "";
}
}
public static String encryptOTP(String otp)
{
String encryptedOtp = null;
try {
encryptedOtp = encryptEK(otp.getBytes(),decodeBase64StringTOByte(appKey));
} catch (Exception e) {
e.printStackTrace();
}
return encryptedOtp;
}
encryptOTP("251826")
Current PhP encryption.
class AtomAES {
public function encrypt_aps_secret($data = '', $key = NULL) {
if($key != NULL && $data != ""){
$method = "AES-256-ECB";
$encrypted = openssl_encrypt($data, $method, $key, OPENSSL_RAW_DATA);
$result = base64_encode($encrypted);
return $result;
}else{
return "String to encrypt, Key is required.";
}
}
}
$appKey = mb_convert_encoding("wbx+mGnapzZMietP0gK6muJb/vUU7jnAk9Fe5gTHh4w=", "UTF-8");
$enc_otp = $atomAES->encrypt_aps_secret("251826", base64_decode(base64_encode($appKey)));
print_r(json_encode(array("enc_otp"=>mb_convert_encoding($enc_otp, "UTF-8"))));
I need the exact encryption as java does using php. how to achieve this
base64_decode(base64_encode($appKey))
I believe you use the key in php as string bytes, not as a decoded byte array, try following
base64_decode($appKey)
It is at least what meets the eye. Still there are other assumptions, such as we can only assume ENCRYPT_CIPHER is AES/ECB/PKCS5Padding, as well you should create a new Cipher instance every time, as the ENCRYPT_CIPHER may not be thread-safe
i'm having a problem during my project at school. what im trying to do is read plain text file and encrypt it using AES 128 which will give me a cipher text file, then i want to take the cipher file and convert it string to embed it into and image.
the AES class dealing with inputstream and the steganography dealing with string. If i extract the cipher text from image and save it to file to read it again using AES i get problem because there are some symbol which make a different between the file.
AES encryption method:
public static void encrypt(int keyLength, char[] password, InputStream input, OutputStream output){
if (keyLength != 128 && keyLength != 192 && keyLength != 256) {
throw new InvalidKeyLengthException(keyLength);
}
byte[] salt = generateSalt(SALT_LENGTH);
Keys keys = keygen(keyLength, password, salt);
Cipher encrypt = null;
try {
encrypt = Cipher.getInstance(CIPHER_SPEC);
encrypt.init(Cipher.ENCRYPT_MODE, keys.encryption);
} catch (NoSuchAlgorithmException | NoSuchPaddingException impossible) {
} catch (InvalidKeyException e) {
throw new StrongEncryptionNotAvailableException(keyLength);
}
byte[] iv = null;
try {
iv = encrypt.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
} catch (InvalidParameterSpecException impossible) {
}
output.write(keyLength / 8);
output.write(salt);
output.write(keys.authentication.getEncoded());
output.write(iv);
byte[] buffer = new byte[BUFFER_SIZE];
int numRead;
byte[] encrypted = null;
while ((numRead = input.read(buffer)) > 0) {
encrypted = encrypt.update(buffer, 0, numRead);
if (encrypted != null) {
output.write(encrypted);
}
}
try {
encrypted = encrypt.doFinal();
} catch (IllegalBlockSizeException | BadPaddingException impossible) {
}
if (encrypted != null) {
output.write(encrypted);
}
}
Steganography encode method:
public boolean encode(BufferedImage img, String message) {
BufferedImage image = user_space(img);
image = add_text(image, message);
return (setImage(image));
}
If i can make steganography to return text file i think that would solve the problem.
what do you think?
I've been banging my head against the wall trying to figure out exactly how to format everything to decrypt this string in PHP that's been encrypted in a custom Java class.
Here's the relevent functions from the Java class. The "salt" variable is a class variable byte array set earlier:
public DesEncrypter(String passPhrase) {
try {
// Create the key
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt,
iterationCount);
SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES")
.generateSecret(keySpec);
ecipher = Cipher.getInstance(key.getAlgorithm());
dcipher = Cipher.getInstance(key.getAlgorithm());
// Prepare the parameter to the ciphers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt,
iterationCount);
// Create the ciphers
ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (java.security.InvalidAlgorithmParameterException e) {
} catch (java.security.spec.InvalidKeySpecException e) {
} catch (javax.crypto.NoSuchPaddingException e) {
} catch (java.security.NoSuchAlgorithmException e) {
} catch (java.security.InvalidKeyException e) {
}
}
public String encrypt(String str) {
try {
// 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 sun.misc.BASE64Encoder().encode(enc);
} catch (javax.crypto.BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
} catch (UnsupportedEncodingException e) {
}
return null;
}
public String decrypt(String str) {
try {
// Decode base64 to get bytes
byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);
// Decrypt
byte[] utf8 = dcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch( Exception ex) {
ex.printStackTrace(System.err);
}
return null;
}
And here's what I have so far in PHP (FYI, I'm using this encryption library in PHP https://github.com/phpseclib/phpseclib):
$app->get('/decrypt', function () use ($app) {
$data = '3aCRLRd3srA/QF4MQb0D+P==';
$salt = pack('nvc*', 0xB7, 0x9A, 0xC1, 0x34, 0x26, 0x89, 0xW3, 0x30);
$secret = "secret";
$keyLength = 16;
$cipher = new Crypt_DES(CRYPT_DES_MODE_CBC);
$cipher->setPassword($secret, 'pbkdf2', 'md5', $salt, $keyLength);
var_dump($cipher->decrypt($data));
});
Right now it's dumping out a bunch of binary, which I've tried base64_decoding, but that doesn't do anything either.
If key.getAlgorithm() is "DES" then you need to provide a fully specified Cipher name like "DES/CBC/PKCS5Padding".
You will also need to provide the IV if it is non-null. Usually the IV is prepended to the ciphertext.
You can get the IV with cipher.getIV() and set with $cipher->setIV('...');.
I searched over the internet for a Triple Des Algorithm implementation for Java.
I found a lot of solutions and choosed one (The one with better documentation for me).
I tested and works Ok.
Then, I searched for an AES Algorithm implementation for Java. And found a good ones. Really similar to Triple Des Algorithm implementation, but not exactly the same.
So I think, what appends if I use the AES Algorithm implementation but changing the Cipher instance parameter from "AES" to "DESede"?
I made the change, tested the code and worked ok. But, the String returned it is different from the String returned on my previous Triple Des Algorithm implementation.
So, like the title say, how do I know if I'm using the right version of Triple Des Algorithm implementation?
This is the first implementation:
public String encrypt(SecretKey key, String stringIn){
String outString = "";
if (stringIn.isEmpty() || stringIn.toUpperCase().equals("NULL")){
return "";
}
try {
if (key == null)
key = this.key;
InputStream in = new ByteArrayInputStream(stringIn.getBytes("UTF-8"));
ByteArrayOutputStream out = new ByteArrayOutputStream();
// Create and initialize the encryption engine
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.ENCRYPT_MODE, key);
// Create a special output stream to do the work for us
CipherOutputStream cos = new CipherOutputStream(out, cipher);
// Read from the input and write to the encrypting output stream
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
cos.write(buffer, 0, bytesRead);
}
cos.close();
// For extra security, don't leave any plaintext hanging around memory.
java.util.Arrays.fill(buffer, (byte) 0);
outString = out.toString();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
return outString;
}
}
This is the second one:
public String encrypt(SecretKey key, String stringIn){
String outString = "";
if (stringIn.isEmpty() || stringIn.toUpperCase().equals("NULL")){
return "";
}
try {
if (key == null)
key = this.key;
Cipher ecipher = Cipher.getInstance("DESede");
ecipher.init(Cipher.ENCRYPT_MODE, key);
byte[] bytes = stringIn.getBytes("UTF8");
byte[] encVal = ecipher.doFinal(bytes);
outString = new sun.misc.BASE64Encoder().encode(encVal);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
return outString;
}
}
This is the Test case:
String In: 6985475896580019
String Returned when I Encripted with First code: Kœ¼i …€‡ä«‘]<žéù âpU
String Returned when I Encripted with Second code: w1ujopasjH6ccFKgUtOgansFNBtxbWe8YwDhso2pZN8=
Sorry for my poor english.
Thanks for your help
cipher.init(mode,key) generates a random IV. This is actually the most secure way of using it; you should use .getIV() and return that with the encrypted text (which is also automatic; Java tacks it onto the first few bytes of the cryptostream, which is how they decrypt OK). Different IV changes the result just as much as a different key, but it doesn't need to be secret, it's just to make sure identical things don't encrypt identically.
To force an IV for comparing algorithms, or for decrypting with a known one not included, use cipher.init(mode,key,new IvParameterSpec(iv))