I need to replicate functionality in java, NOT IMPROVE PHP code.
Php 7 code:
openssl_encrypt("123::secret", "aes-256-cbc", "1234567899999999", 0, "0123456789999999");
// VZq5nLyJaAzajWQqMMLgmQ==
openssl_decrypt("VZq5nLyJaAzajWQqMMLgmQ==", "aes-256-cbc", "1234567899999999", 0, "0123456789999999");
// 123::secret
In Java, I want to make the same, so PHP would be able to decrypt it
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec("1234567899999999".getBytes(), "AES"), new IvParameterSpec("0123456789999999".getBytes()));
byte[] res = cipher.doFinal("123::secret".getBytes());
java.util.Base64.getEncoder().encodeToString(res);
// xIwXN6R+X/FMMqfmsYhEQQ==
But, this key is different and PHP returns false on decrypt attempt. I find many solutions, but they around generating random 256 key lengths, but in my situation PHP code is using hardcoded secret
Topaca's answer is correct (php code can not be changed). To fully emulate described PHP function with provided arguments, need to update the password with "\0" to meet the length requirement
Related
As a bit of context to this I am converting a java file to python and am on the last operation. I'm at about 200 LOC so it makes it that much more edge of the seat...
Anyways, in java the operation is:
Cipher cipher = Cipher.getInstance("AES/ecb/nopadding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(keys[i], "AES"));
//doFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)
cipher.doFinal(save, saveOffset, 16, save, saveOffset);
In python I have this:
from Crypto.Cipher import AES
cipher = AES.new(bytes(keys[i]), AES.MODE_ECB)
cipher.decrypt(?????)
This is taken from the package under decrypt():
:Parameters:
ciphertext : bytes/bytearray/memoryview
The piece of data to decrypt.
The length must be multiple of the cipher block length.
:Keywords:
output : bytearray/memoryview
The location where the plaintext must be written to.
If ``None``, the plaintext is returned.
:Return:
If ``output`` is ``None``, the plaintext is returned as ``bytes``.
Otherwise, ``None``.
As you can see .decrypt() doesn't really have an input for offsets, but I was wondering if there was some way around this?
This is why I decided to post on SO, would I be able to send:
temp_bytes = save[offset]
temp_decrypt = cipher.decrypt(temp_bytes)
save[offset] = temp_decrypt
Or when decrypting does it use the whole file as context and i will get the wrong output? I would love to just do it and test it but the output is just gibberish that i will have to write another program to parse (another java to python project).
What ended up working was:
save[saveOffset:saveOffset+16] = cipher.decrypt(save[saveOffset:saveOffset+16])
Did not expect it to work so easily
Assume I have the following Java code to generate a Public-private keypair:
KeyPairGenerator generator = KeyPairGenerator.getInstance ("RSA");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
generator.initialize (1024, random);
KeyPair pair = generator.generateKeyPair();
RSAPrivateKey priv = (RSAPrivateKey)pair.getPrivate();
RSAPublicKey pub = (RSAPublicKey)pair.getPublic();
// Sign a message
Signature dsa = Signature.getInstance("SHA1withRSA");
dsa.initSign (priv);
dsa.update ("Hello, World".getBytes(), 0, "Hello, World".length());
byte[] out = dsa.sign();
/* save the signature in a file */
FileOutputStream sigfos = new FileOutputStream("sig");
sigfos.write(out);
sigfos.close();
How would one go about and decrypt the file "sig" in PHP? I've read the post: https://stackoverflow.com/a/1662887/414414 which supplies a function to convert a DER file to PEM (Assume I also save the public key from Java).
I have tried something like:
$key = openssl_pkey_get_public ("file://pub_key.pem");
$data = null;
openssl_public_decrypt ( file_get_contents ("sig"), $data, $key);
echo $data, "\n";
It successfully decrypts the message, but it is many weird characters.
Our scenario is a Java client that is sending messages to a PHP server, but encrypts the data with a private key. PHP knows about the public key, which it should use to decrypt and validate the message.
I've read a lot of posts regarding this issue here on SO, but I've come to realize that this is a bit specific issue, especially if there's different algorithms in use, etc. So sorry if this may be a duplicate.
Any feedbacks are greatly appreciated!
an "RSA signature" is usually more than just "encrypt with private key, decrypt with public key", since Public key protocols like PKCS#1 also specify padding schemes, and all signature schemes will encrypt a digest of the message, instead of the full message. I cannot find any documentation if java's signature scheme uses the signature padding scheme specified in PKCS#1, but my suspicion is that it is.
If it is, you will instead want to use the openssl_verify method in PHP, documented here. This will return a 0 or 1 if the signature is invalid or valid, respectively.
In the event that Java does not use a padding scheme, your issue is that the data encrypted in the signature is a hash of the message, instead of the message itself (you can see in the Java code that it uses the SHA-1 hash algorithm). So on the PHP side, you will need to take the sha1 hash of your message using the sha1 method with $raw_output set to true, and compare those strings to ensure your message is valid.
From the snippet
$key = openssl_pkey_get_public ("file://pub_key.pem");
It looks like you're referencing the public key, which would be the wrong one to decrypt. Double check ?
So basically I have these snippets of code and would like them to produce the same output:
require 'openssl'
aes = OpenSSL::Cipher::Cipher.new("AES-128-CBC")
aes.key = "aaaaaaaaaaaaaaaa"
aes.iv = "aaaaaaaaaaaaaaaa"
aes.encrypt
encrypted = aes.update("1234567890123456") << aes.final
puts encrypted.unpack('H*').join
This prints:
8d3bbffade308f8e4e80cb77ecb8df19ee933f75438cec1315c4a491bd1b83f4
And this Java code:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
String key = "aaaaaaaaaaaaaaaa";
String textToEncryptpt = "1234567890123456";
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(key.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(textToEncryptpt.getBytes());
System.out.println(Crypto.bytesToHex(encrypted));
Prints:
2d3760f53b8b3dee722aed83224f418f9dd70e089ecfe9dc689147cfe0927ddb
Annoying thing is that it was working a couple of days ago... so I am not sure what happened. What's wrong with this code? Do you see anything unusual?
Ruby script is wrong. You have to first call the encrypt method, and then set the key and iv:
require 'openssl'
aes = OpenSSL::Cipher::Cipher.new("AES-128-CBC")
aes.encrypt
aes.key = "aaaaaaaaaaaaaaaa"
aes.iv = "aaaaaaaaaaaaaaaa"
encrypted = aes.update("1234567890123456") << aes.final
puts encrypted.unpack('H*').join
I figured out because when trying to decode an encrypted string I got:
aescrypt.rb:13:in `final': bad decrypt (OpenSSL::Cipher::CipherError)
from aescrypt.rb:13:in `<main>'
Seems you found already the reason that your script does give different results.
Some more things to consider:
Don't ever hardcode the key in the program - that way you can't easily change it, and if someone gets access to your program code, she also gets to see the key.
Don't ever use a constant initialization vector. Instead, generate a random one and send it together with the ciphertext. Alternatively, if you generate the key from a password and some salt, you can also generate the IV from the same ... but don't use the key directly as IV.
Your key/IV values are strings, not bytes. String.getBytes() (in Java) converts the string to bytes using some encoding. The encoding used is system-dependent, and none of the usual String encodings (UTF-8, Latin-1, ...) can represent all bytes as printable (and typeable) characters. Preferably use something like Base64 or hex-encoding, if you have to store your key as string.
And whenever you transform a string to bytes, specify an encoding (and use the same encoding later for retrieving it).
#Cristian, For key and initial vector, you can create a function by using today's date plus the secure fixed keyword.
Eg: key = January 8, 2012 + Key
And for initial vector,
Eg: iv = January 8, 2012 + IV
Then enter that data(key and iv) to MD5 it will produce the output 16 bytes that you can use for the Key and IV. Every day, key and iv will change randomly.
Make sure both systems use the same date format and setup on the same date.
I have been tearing my hair out all day trying to solve this...
I have an objective-c client running on the iPhone, connecting to a Java server. The iPhone is encrypting data using AES but I cannot decrypt it on the server. I am using a known passphrase and message (single string) and am generating the byte array on the iPhone, generating a comparison byte array on the Java server using the same key and message but the byte arrays are completely different (and hence can't be decoded on the Java side).
The client is using the CommonCrypto library with the following settings...
Data is an NSData holding the word "message" using dataUsingEncoding:NSASCIIStringEncoding
Key is an NSData holding the phrase "1234567891123456" again using the encoding as above.
Algorithm is kCCAlgorithmAES128
Options is kCCOptionsPKCS7Padding (which I believe equates to ECB on the server?!)
The server is using the following code...
byte[] key = "1234567891123456".getBytes();
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKeySpec k = new SecretKeySpec(key, "AES");
c.init(Cipher.ENCRYPT_MODE, k);
byte[] encryptedData = c.doFinal("message".getBytes());
BUT the data in encryptedData does not match that which is being generated in the objective-c code, the byte arrays are completely different.
Can anyone see anything obvious I am doing wrong? I think the settings are all the same... :(
UPDATE - As requested....
Ok so here goes....
iPhone client is encrypting the following string "message"
It uses the key "1234567891123456"
It uses an initialisation vector of "1010101010101010"
It is using AES128, with CBC mode (as far as I can tell) and options of kCCOptionsPKCS7Padding.
The result of the encryption (with base64 encoding) is UHIYllDFAXl81ZM7OZPAuA==
The server is encrypting the same string, with the same key and initialisation vector.
It is using the following Cipher.getInstance("AES/CBC/PKCS5Padding");
The result of the encryption (with base64 encoding) is ALBnFIHysLbvAxjvtNo9vQ==
Thanks.
UPDATE 2 - As requested...
Here is the iPhone code....
NSData *toencrypt = [#"message" dataUsingEncoding:NSASCIIStringEncoding];
NSData *pass = [#"1234567891123456" dataUsingEncoding:NSASCIIStringEncoding];
NSData *iv = [#"1010101010101010" dataUsingEncoding:NSASCIIStringEncoding];
CCCryptorStatus status = kCCSuccess;
NSData *encrypted = [toencrypt dataEncryptedUsingAlgorithm:kCCAlgorithmAES128 key:pass initializationVector:iv options:kCCOptionPKCS7Padding error:&status];
NSString *text = [NSString base64StringFromData:encrypted length:[encrypted length]];
The NSData category for encrypting comes from here...
http://github.com/AlanQuatermain/aqtoolkit/tree/master/CommonCrypto/
Incidentally, I have checked the byte arrays that are in toencrypt, pass and iv and they match those that are on the server.
This is not my area but it looks like on the client you have PKCS7 but on the server you have PKCS5.
I just ran across the exact same problem. I am using CommonCrypto on the iOS client, using settings:
NSData * encrypted = [data dataEncryptedUsingAlgorithm:kCCAlgorithmAES128 key:pass initializationVector:iv options:kCCOptionPKCS7Padding error:&status];
The server uses Cipher.getInstance("AES/CBC/PKCS5Padding"); with the same key and initialization vector as the client.
After banging my head against the wall for the last few hours, I finally followed Jason's advice and checked the dataEncryptedUsingAlgorithm routine and printed out keyData right after FixKeyLengths. It turns out my 128bit key was extended to 192bit with 0s added to the end. After fixing this, everything works properly. :)
Update: My answer was posted almost 2 years ago, and this issue seems to be fixed in the latest NSData+CommonCrypto code. Specifically, this was the part that caused the issue:
static void FixKeyLengths( CCAlgorithm algorithm, NSMutableData * keyData, NSMutableData * ivData )
{
NSUInteger keyLength = [keyData length];
switch ( algorithm )
{
case kCCAlgorithmAES128:
{
if ( keyLength <= 16 )
{
[keyData setLength: 16];
}
else if ( keyLength <= 24 )
{
[keyData setLength: 24];
}
else
{
[keyData setLength: 32];
}
break;
}
The first check keyLength <= 16 wasn't there before.
If you are still experiencing problems now, it's probably something else.
What mode is the iPhone using with AES? You don't list anything, so perhaps that means it's using no chaining (ECB).
However, on the Java side, you are using CBC, but not specifying an initialization vector. That is definitely wrong. If you really are using CBC, you have to have the IV that was used during encryption. The IV is not secret; it can be sent along with the ciphertext.
If you are really using ECB, there is no IV, but your Java specifies the wrong mode.
Based on your samples, the server is doing it right, and the client is not.
Looking at the data, I would guess that the key is wrong. Please show us the iPhone code, especially the code to go from "1234567891123456" to your key.
I recently ran across this in another project. The problem was that the key was one byte too long to fit into the char buffer inside of the dataEncryptedUsingAlgorithm method.
The problem was that the getBytes method on the NSString was soft-failing. It would copy most of the string into the buffer, but then since the key was one byte too long, it would "mark" the operation as failed by setting the first char to NUL (char 0).
Step into that method in Xcode and see what your key char[16] buffer looks like. It may have this same problem, and have the contents { 0, '2', '3', '4', ... }.
I recently asked a question about Oracle Encryption. Along the way to finding a solution for myself I decided to move the encryption (well, obfuscation) to the application side for certain tasks.
My problem is that the database is already encrypting data a certain way and I need Java code to duplicate that functionality, so that text encrypted by one system can be decrypted by the other and vice versa.
I want the encryption to be compatible with what the DB was already doing but couldn't find the documentation that describes exactly what Oracle is doing. How do I replicate this in Java?
dbms_obfuscation_toolkit.DESEncrypt(
input_string => v_string,
key_string => key_string,
encrypted_string => encrypted_string );
RETURN UTL_RAW.CAST_TO_RAW(encrypted_string);
No matter what I try, it seems as if the Java DES encryption is different than Oracle's.
I found this works:
KeySpec ks = new DESKeySpec(new byte[] {'s','e','c','r','e','t','!','!'});
SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
SecretKey sk = skf.generateSecret(ks);
Cipher c = Cipher.getInstance("DES/CBC/NoPadding");
IvParameterSpec ips = new IvParameterSpec(new byte[] {0,0,0,0,0,0,0,0});
c.init(Cipher.ENCRYPT, sk, ips);
// or
c.init(Cipher.DECRYPT, sk, ips);
The missing piece was the Initialization Vector (ips) which must be 8 zeros. When you use null in Java you get something different.
Using Java in the database would have been another approach that would (should!) have guarenteed that the code (and hence results) would be identical.