ColdFusion or Java equivalent to PHP encryption/decryption - java

I have an API spec that reads:
Encryption Algorithm
The API utilizes the AES-128 (also known as Rijndael-128) algorithm with a 192-bit key in
CBC mode for encryption and decryption of sensitive pieces of information – the password parameter in the user/signin and user/signup methods, the authentication token, etc. The steps of the algorithm are listed below:
Encryption
Pad the input data so that its size is a multiple of the encryption algorithm block size – 16 bytes. In case the length of input data is a multiple of 16, a block of additional 16 bytes needs to be appended. The value of each pad byte is the number of pad bytes as an 'unsigned char'. That is, the last byte of the padded data should always be between 0x01 and 0x10.
Generate a 16-byte long initialization vector (IV) for the encryption algorithm.
Encrypt the padded data using AES-128 with the EK and the generated IV.
Combine the IV with the encrypted data.
Encode the result with urlsafe Base64. The urlsafe Base46 alphabet uses '–' instead of '+' and '_' instead of '/'.
Decryption
Base64-decode the input data.
Extract the first 16 bytes – these are the IV for the AES algorithm.
Decrypt the data using AES-128 with the EK and IV.
Read the value of the last byte of the decrypted data and remove that many bytes off its tail.
The only example provided by the supplier of this API is in PHP, using mcrypt. I know absolutely nothing about PHP, and am not an encryption expert. I need to be able to represent the above algorithm using ColdFusion 10.
I started by trying to take the example PHP files and find equivalents in either the ColdFusion tag or function library, and then by looking for a Java library with the same interface. I just don't know enough to make this happen.
Is there someone here that can point me in the right direction, or work with me offline to assist?
EDIT:
Here's the example given, for the basic task of doing a "check" on the keys (partner key and encryption key) provided to me for use with the API.
Object Client.php, has this constructor:
public function __construct($hostApiUrl, $partnerKey, $encryptionKey, $token = null)
{
$this->_pk = $partnerKey;
$this->_ek = $encryptionKey;
$this->_crypt = new Crypt($encryptionKey);
$this->_url = rtrim($hostApiUrl, '/') . self::BASE_URL;
if ($token) {
$this->setUserSession($token);
}
}
and this is the function I'm attempting to use:
public function checkKeys()
{
$secret = $this->_encodeParam($this->_ek);
$result = $this->call('partner/checkkeys', array(
'secret' => $secret
));
if (!$result || !$this->_isCodeOk($result->code)) {
return false;
}
return true;
}
So the client object already has the partner key and encryption key when this method is called, obviously.
so the "secret" is created by "encoding" the encryption key provided, using _encodeParam() method. that looks like this:
protected function _encodeParam($secret)
{
$secret = "{$secret}:{$this->_pk}";
return $this->_crypt->encrypt($secret);
}
so the secret is appended with the partner key. and then encrypted using this method in the crypt object (AES_BLOCK_SIZE is set as 16):
public function encrypt($data)
{
$pad = self::AES_BLOCK_SIZE - strlen($data) % self::AES_BLOCK_SIZE;
$data .= str_repeat(chr($pad), $pad);
if (stristr(PHP_OS, 'win') !== false) {
$random_source = MCRYPT_RAND;
} else {
$random_source = MCRYPT_DEV_URANDOM;
}
$iv = mcrypt_create_iv(self::AES_BLOCK_SIZE, $random_source);
mcrypt_generic_init($this->_td, $this->_key, $iv);
$data = $iv . mcrypt_generic($this->_td, $data);
mcrypt_generic_deinit($this->_td);
return self::urlsafe_b64encode($data);
}
this is returned back to the above checkKeys() function which sends the request to the API, which then returns a response. That actual API call is a POST which is easy enough to generate of course, but all those encryption hoops, including the MCRYPT library calls, are where I get stuck trying to determine the equivalent in CF10 or Java.
If I were to get an example thus far, I think I'd stand a chance of replicating the other functions in the crypt object (the ones that are even necessary, which may not be, since some may be built right into the CF encrypt() and decrypt() functions). This seems like a reasonable starting point, however.

Related

How to decrypt a JWT, in java, which is encrypted with JWE when the encrypted token is present in form of String?

I have a token in String format such as:
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.K52jFwAQJH-DxMhtaq7sg5tMuot_mT5dm1DR_01wj6ZUQQhJFO02vPI44W5nDjC5C_v4pW1UiJa3cwb5y2Rd9kSvb0ZxAqGX9c4Z4zouRU57729ML3V05UArUhck9ZvssfkDW1VclingL8LfagRUs2z95UkwhiZyaKpmrgqpKX8azQFGNLBvEjXnxxoDFZIYwHOno290HOpig3aUsDxhsioweiXbeLXxLeRsivaLwUWRUZfHRC_HGAo8KSF4gQZmeJtRgai5mz6qgbVkg7jPQyZFtM5_ul0UKHE2y0AtWm8IzDE_rbAV14OCRZJ6n38X5urVFFE5sdphdGsNlA.gjI_RIFWZXJwaO9R.oaE5a-z0N1MW9FBkhKeKeFa5e7hxVXOuANZsNmBYYT8G_xlXkMD0nz4fIaGtuWd3t9Xp-kufvvfD-xOnAs2SBX_Y1kYGPto4mibBjIrXQEjDsKyKwndxzrutN9csmFwqWhx1sLHMpJkgsnfLTi9yWBPKH5Krx23IhoDGoSfqOquuhxn0y0WkuqH1R3z-fluUs6sxx9qx6NFVS1NRQ-LVn9sWT5yx8m9AQ_ng8MBWz2BfBTV0tjliV74ogNDikNXTAkD9rsWFV0IX4IpA.sOLijuVySaKI-FYUaBywpg
Now I want to decrypt this String through some java library and ultimately want to access the payload. Anyone done this so far ?
The assertion you shared with us is a JWE serialized in its compact representation. It has 5 parts separated by a dot (.) which are:
Part 1: the header (eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ)
Part 2: the encrypted key - depending on the algorithm, it may be an empty string - (K52jFwAQJH-DxMhtaq7sg5tMuot_mT5dm1DR_01wj6ZUQQhJFO02vPI44W5nDjC5C_v4pW1UiJa3cwb5y2Rd9kSvb0ZxAqGX9c4Z4zouRU57729ML3V05UArUhck9ZvssfkDW1VclingL8LfagRUs2z95UkwhiZyaKpmrgqpKX8azQFGNLBvEjXnxxoDFZIYwHOno290HOpig3aUsDxhsioweiXbeLXxLeRsivaLwUWRUZfHRC_HGAo8KSF4gQZmeJtRgai5mz6qgbVkg7jPQyZFtM5_ul0UKHE2y0AtWm8IzDE_rbAV14OCRZJ6n38X5urVFFE5sdphdGsNlA)
Part 3: the initialisation vector - IV depending on the algorithm, it may be an empty string - (gjI_RIFWZXJwaO9R)
Part 4: the cyphertext (oaE5a-z0N1MW9FBkhKeKeFa5e7hxVXOuANZsNmBYYT8G_xlXkMD0nz4fIaGtuWd3t9Xp-kufvvfD-xOnAs2SBX_Y1kYGPto4mibBjIrXQEjDsKyKwndxzrutN9csmFwqWhx1sLHMpJkgsnfLTi9yWBPKH5Krx23IhoDGoSfqOquuhxn0y0WkuqH1R3z-fluUs6sxx9qx6NFVS1NRQ-LVn9sWT5yx8m9AQ_ng8MBWz2BfBTV0tjliV74ogNDikNXTAkD9rsWFV0IX4IpA)
Part 5: the Additional Authentication Data - AAD - (sOLijuVySaKI-FYUaBywpg)
The header indicates at least the
Key Encryption Algorithm (alg) used to encrypt the content encryption key (CEK)
Content Encryption Algorithm (enc) used in conjunction with the CEK to encrypt the content.
In your case, The alg is RSA-OAEP and the enc is A128GCM.
According to these algorithm, you should have a RSA private key to decrypt the CEK and then use it to decrypt the cyphertext.
https://jwt.io mentioned in the accepted answer will not be useful as it does not support JWE, but JWS. The result shown with a JWE input will not give you the decrypted payload.
However, the https://connect2id.com/products/nimbus-jose-jwt resource will help you as it seems that the algorithms used for your token are supported.
You probably don't necessarily need a library:
String token = eyJ....;
String[] splitToken = JWTEncoded.split("\\.");
// splitToken[0] is the header, splitToken[1] is the payload and
// splitToken[2] is the signature
byte[] decodedBytes = Base64.decode(splitToken[1], Base64.URL_SAFE);
// You don't have to convert it to string but it really depends on what type
// data you expect
String payload = new String(decodedBytes, "UTF-8");
https://jwt.io has a nice little decrypter for you to test data
In regards to JWE the only library I could find is this and an example to unencrypted JWE tokens can be found at the bottom of this page.
Note: I haven't tested this library so I won't be of much use beyond this, but it seems fairly straight forward.

RSA encryption in Java, decrypt in PHP

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 ?

MessageDigest MD5 Algorithm not returning what I expect

Something in the back of my head is telling me I'm missing something obvious here.
I'm integrating an existing java project with a third-party api that uses an md5 hash of an api key for authentication. It's not working for me, and during debugging I realized that the hashes I'm generating don't match the examples that they've supplied. I've found some websites that create MD5 hashes from strings to check their examples, and as far as I can tell I'm wrong and they're right.
for example, according to this website, the string "hello" generates a hash of "5d41402abc4b2a76b9719d911017c592". (FWIW I don't know anything about this website except that it seems to correctly hash the examples that I have). When I run it through my code I get:
XUFAKrxLKna5cZ2REBfFkg==
Here is the simple method I'm using to generate the md5 hash/string.:
private String md5(String md5Me) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
md.reset();
md.update(md5Me.getBytes("UTF-8"));
return Base64.encodeBase64String(md.digest());
}
I used a very similar method to successfully authenticate a different API using the SHA1 algorithm last week. I'm wondering if the problem is related to the org.apache.commons.net.util.Base64.encodeBase64String... Any help is greatly appreciated, if only some tests to see if the byteArray is correct but the converted string is wrong.
for example, according to this website, the string "hello" generates a hash of "5d41402abc4b2a76b9719d911017c592". (FWIW I don't know anything about this website except that it seems to correctly hash the examples that I have). When I run it through my code I get:
XUFAKrxLKna5cZ2REBfFkg==
Both are correct ways of representing the same sixteen-byte hash. 5d41402abc4b2a76b9719d911017c592 represents each byte of the hash as two hexadecimal digits, whereas XUFAKrxLKna5cZ2REBfFkg== uses Base-64 to represent every three bytes of the hash as four characters.
To generate the hexadecimal-version that this third-party API is expecting, you can change this:
Base64.encodeBase64String(md.digest());
to this:
String.format("%032x", new BigInteger(1, md.digest()));
(mostly taken from this StackOverflow answer).
However, you might want to consider using an external library for this. Perception, in a comment above, mentions Apache Commons DigestUtils. If you use that, you'll want the md5hex method.
The md5 Hash algorithm is part of the core java API so there is no need for any external libraries. Here is the method I used to encrypt a password with MD5.
import java.security.MessageDigest;
/**
* Use to encrypt passwords using MD5 algorithm
* #param password should be a plain text password.
* #return a hex String that results from encrypting the given password.
*/
public static String encryptPassword(String password) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(password.getBytes());
byte byteData[] = md.digest();
StringBuffer hexString = new StringBuffer();
for (int i=0;i<byteData.length;i++) {
String hex=Integer.toHexString(0xff & byteData[i]);
if(hex.length()==1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
catch(java.security.NoSuchAlgorithmException missing) {
return "Error.";
}
}

Encrypt in java, decrypt in node.js

I need to encrypt in java and decrypt with node.js. The decryption result is corrupted.
Here is the java code:
public String encrypt(SecretKey key, String message){
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] stringBytes = message.getBytes("UTF8");
byte[] raw = cipher.doFinal(stringBytes);
// converts to base64 for easier display.
BASE64Encoder encoder = new BASE64Encoder();
String base64 = encoder.encode(raw);
return base64;
}
Here is the node.js code:
AEse3SCrypt.decrypt = function(cryptkey, encryptdata) {
encryptdata = new Buffer(encryptdata, 'base64').toString('binary');
var decipher = crypto.createDecipher('aes-128-cbc', cryptkey);
decipher.setAutoPadding(false);
var decoded = decipher.update(encryptdata);
decoded += decipher.final();
return decoded;
}
As a key I use: "[B#4ec6948c"
The jave encrypted result is: "dfGiiHZi8wYBnDetNhneBw=="<br>
The node.js result is garbich....
In java I use "PKCS5Padding". What should be done in node.js regarding padding? I made setAutoPadding(false). If I don't do it I get error decipher fail. (only from node.js version 0.8).
I tried to remove utf8 encoding from the java in order to be complementary with the node.js but it didn't work.
Any idea what is wrong?
As a key I use: "[B#4ec6948c"
That sounds very much like you're just calling toString() on a byte array. That's not giving you the data within the byte array - it's just the default implementation of Object.toString(), called on a byte array.
Try using Arrays.toString(key) to print out the key.
If you were using that broken key value in the node.js code, it's no wonder it's giving you garbage back.
I tried to remove utf8 encoding from the java in order to be complementary with the node.js
That's absolutely the wrong approach. Instead, you should work out how to make the node.js code interpret the plaintext data as UTF-8 encoded text. Fundamentally, strings are character data and encryption acts on binary data - you need a way to bridge the gap, and UTF-8 is an entirely reasonable way of doing so. The initial result of the decryption in node.js should be binary data, which you then "decode" to text via UTF-8.
I'm afraid I don't know enough about the padding side to comment on that.

AES difference between iPhone (Objective-c) and Java

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', ... }.

Categories

Resources