I need to create a sign of a string. I have a working code in PHP:
function buildSign($toSign, $key) {
$signature = null;
$pkeyid = openssl_get_privatekey($key);
openssl_sign($toSign, $signature, $pkeyid);
openssl_free_key($pkeyid);
return base64_encode($signature);
}
How can I create an equivalent of this function in objective c?
I tried this code
- (NSString*) signString:(NSString*)string key:(NSString *)key {
const char *cKey = [key cStringUsingEncoding:NSUTF8StringEncoding];
const char *cData = [string cStringUsingEncoding:NSUTF8StringEncoding];
unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *hmac = [[NSData alloc] initWithBytes:cHMAC
length:sizeof(cHMAC)];
return [hmac base64EncodedString];
}
But it returns a very short string like this: zM5GPdSiDooWSm78fLCdTnw1LHQ=.
I need a long sign like this:
ECkpqi5euq0lL66biLA3A92eeKaQhEuwg4IJ7IC4fmJjF5LwV9VNuxaJNpI4z7xqxlUg+kMi+u0pgMP7b5cH141EWdFGHa3SSR9PvyMTszRRLJq3ykaJIX/yKvW7wEfFxVCosiX8ufSiR1o3mAnm8jArDhJoQTjproMS3pmeSsF5anhh5TUn+5wgYgrgjS+3WEnN6X95GTRcpnDE/F/W2ON7ydhd1iGtNoT/1MPn9nbCI3CMubI7RjnompVXB3eVcv7ZQII3jLfMEdSQ93IDCS6bFGUVNF37or/Bhr4B4DARO5u4TvPtq0vZQNmUSLdkaLl593FgA8Det/NevYprmodL6hzgTlws8OIYVoeyxBpoHquuoqPMvkkFzFUlKRAUeGwBMhW83EmKU4DU5+n7aRrdxotBt+RxAfcqoJJNpSmzeeeWcLu5XAizvVmm59EB0Qeuuy8EdqMoCVTee4dHJf63alrvV8NYu+PoMuF3GohVldYoYYuqdGfhI4tH0dqv83F8OXpFADQU+CPgIm4bRoS5P8jR6X94SmSVCHWgjuXGDVt//tUCzAG8xsmEBuy8i75MlFmYZ8SFzyXzBtRJ0xUVhc8WToQYcjB8SmwAIyUICwyyuSt5flUF30x2b6GxBt8yCmgrzXlIRIzA1TfFGada1dJKTiikOlZb02eA+xs=
SHA-1 creates a 20 byte hash of the input. HMAC-SHA1 returns the SHA-1 output and in this case a BASE64 encoded string representation. But HSA-1 should no longer be used, instead use SHA-256 or SHA-512, these return 32 or 64 bytes.
But that is not what you need inorder to match the PHP code which is using the private key or a key pair to sign with the OPENSSL_ALGO_SHA1 algorithm.
While Apple does not supply a pre-compiled version of OpenSSL you can get the source and compile it yourself.
Related
I'd like to encrypt a string in PHP and then decrypt it in C. I'm stuck on the decryption part.
(PHP) I first encrypt the string:
function encrypt($plaintext, $key) {
$iv = 'aaaaaaaaaaaaaaaa';
$ciphertext = openssl_encrypt($plaintext, "AES-256-CBC", $key, OPENSSL_RAW_DATA, $iv);
return $ciphertext;
}
echo encrypt('This is a test', 'test');
// output: 7q�7h_��8� ��L
(C) Then I want to decrypt it, I use tiny-AES-c library for the functions:
int test_decrypt_cbc(void) {
uint8_t key[] = "test";
uint8_t iv[] = "aaaaaaaaaaaaaaaa";
uint8_t str[] = "7q�7h_��8� ��L";
printf("%s", str);
printf("\n Decrypted buffer\n");
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, key, iv);
AES_CBC_decrypt_buffer(&ctx, str, sizeof(str));
printf("%s", str);
printf("\n");
return 1;
}
This outputs:
7q�7h_��8� ��L
Decrypted buffer
?L??Ɵ??m??Dˍ?'?&??c?W
It should instead output "This is a test".
How can I fix this?
In the PHP code, AES-256 is used. tiny-AES-c only supports AES-128 by default. In order for AES-256 to be supported, the corresponding constant must be defined in aes.h, i.e. the line //#define AES256 1 must be commented in, here.
PHP uses PKCS7 padding by default. The padding should be removed in the C code.
PHP implicitly pads too short keys with zero values to the specified length. Since AES-256-CBC was specified in the PHP code, the key test is extended as follows:
test\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0
In the C code this extended key must be used (see also the comment of #r3mainer).
For the transfer of the ciphertext between the two codes a suitable encoding must be used, e.g. Base64 or hexadecimal (see also the comment of #Ôrel). For the latter, bin2hex can be applied to the ciphertext in the PHP code. An example of a hex decoding in C can be found here.
A possible C-implementation is:
// Pad the key with zero values
uint8_t key[] = "test\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
uint8_t iv[] = "aaaaaaaaaaaaaaaa";
uint8_t ciphertextHex[] = "3771e837685ff5d4173801900de6e14c";
// Hex decode (e.g. https://stackoverflow.com/a/3409211/9014097)
uint8_t ciphertext[sizeof(ciphertextHex) / 2], * pos = ciphertextHex;
for (size_t count = 0; count < sizeof ciphertext / sizeof * ciphertext; count++) {
sscanf((const char*)pos, "%2hhx", &ciphertext[count]);
pos += 2;
}
// Decrypt
struct AES_ctx ctx;
AES_init_ctx_iv(&ctx, key, iv);
AES_CBC_decrypt_buffer(&ctx, ciphertext, sizeof(ciphertext));
// Remove the PKCS7 padding
uint8_t ciphertextLength = sizeof(ciphertext);
uint8_t numberOfPaddingBytes = ciphertext[ciphertextLength - 1];
ciphertext[ciphertextLength - numberOfPaddingBytes] = 0;
printf("%s", ciphertext);
Here, I have c++ program which encodes the string and I have to decrypt in php. I have verified that the key and the iv are same in both programs, still getting false in openssl_decrypt() command.
int main(int argc, char** args)
{
unsigned char *salt = (unsigned char*)"12345678";
unsigned char *data = (unsigned char*)"123456789123450";
unsigned int count = 5;
int dlen = strlen((char*)data);
unsigned int ksize = 16;
unsigned int vsize = 12;
unsigned char *key = new unsigned char[ksize];
unsigned char *iv = new unsigned char[vsize];
int ret = EVP_BytesToKey( EVP_aes_128_gcm() , EVP_sha1(), salt, data, dlen, count, key, iv);
const EVP_CIPHER* m_cipher = EVP_aes_128_gcm();
EVP_CIPHER_CTX* m_encode;
EVP_CIPHER_CTX* m_decode;
if (!(m_encode = EVP_CIPHER_CTX_new()))
cout << "ERROR :: In encode Initiallization"<< endl;
EVP_EncryptInit_ex(m_encode, m_cipher, NULL, key, iv);
if (!(m_decode = EVP_CIPHER_CTX_new()))
cout << "ERROR :: In decode Initiallization"<< endl;
EVP_DecryptInit_ex(m_decode, m_cipher, NULL, key, iv);
unsigned char* plain = (unsigned char*)"My Name IS DON !!!";
int len = strlen((char*)plain);
unsigned char* encData = new unsigned char[len];
int c_len = len;
int f_len = 0;
EVP_EncryptInit_ex(m_encode, NULL, NULL, NULL, NULL);
EVP_EncryptUpdate(m_encode, encData, &c_len, plain, len);
EVP_EncryptFinal_ex(m_encode, encData + c_len, &f_len);
len = c_len + f_len;
cout << string( encData, encData + len)<< endl;
}
And the following is decryption code in php. "./abc_enc.txt" contains encryption string of c++ code. As I mentioned above I am getting same key and iv for both programs but openssl_decrypt function returns false. Can someone figure out what is the mistake?
<?
function EVP_BytesToKey($salt, $password) {
$ivlen = 12;
$keylen = 16;
$iterations = 5;
$hash = "";
$hdata = "";
while(strlen($hash)<$ivlen+$keylen)
{
$hdata .= $password.$salt;
$md_buf = openssl_digest($hdata, 'sha1');
for ($i = 1; $i < $iterations; $i++) {
$md_buf = openssl_digest ( hex2bin($md_buf),'sha1');
}
$hdata = hex2bin($md_buf);
$hash.= $hdata;
}
return $hash;
}
function decrypt($ivHashCiphertext, $password) {
$method = "aes-128-gcm";
$salt = "12345678";
$iterations = 5;
$ivlen = openssl_cipher_iv_length($method);
$ciphertext = $ivHashCiphertext;
$genKeyData = EVP_BytesToKey($salt, $password);
$keylen = 16;
$key = substr($genKeyData,0,$keylen);
$iv = substr($genKeyData,$keylen,$ivlen);
//var_dump($key);
//var_dump($iv);
$ret = openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
var_dump($ret);
return $ret;
}
$file = './abc_enc.txt';
$fileData = (file_get_contents($file));
$encrypted = $fileData;
$decrypted = decrypt($encrypted, '123456789123450');
?>
The GCM-mode provides both, confidentiality and authenticity. To verify authenticity the GCM-mode uses an authentication tag and defines a length between incl. 12 and 16 Byte for the tag. The authentication strength depends on the length of the tag, i.e. the longer the tag, the more secure the proof of authenticity.
However, in the current C++-code the authentication tag is not determined! This means that one of the main functionalities of the GCM-mode, authentication, is not used.
While the decryption in C++ using EVP is independent from the authentication (this means that the decryption is also performed even if the authentication tags differ), the decryption in PHP using openssl_decrypt is only done if the authentication is successful, i.e. in PHP the authentication tag is mandatory for decryption. Therefore, the authentication tag must be determined in the C++-code. For this purpose, the following code must be added after the EVP_EncryptFinal_ex-call:
unsigned int tsize = 16;
unsigned char *tag = new unsigned char[tsize];
EVP_CIPHER_CTX_ctrl(m_encode, EVP_CTRL_GCM_GET_TAG, tsize, tag);
Here a tagsize of 16 Byte is used. In addition, the authentication tag must be used in the PHP-code for decryption. This is done by passing the authentication tag as the 6th parameter of the openssl_decrypt-method:
$ret = openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv, $tag);
Decryption can only be performed if the tag used for decryption matches the tag used for encryption.
For the data in the posted example the C++-code generates the following authentication tag (as hexadecimal string):
f7c18e8b99587f3063383d68230c0e35
Finally, a more detailed explanation for AES-GCM with OpenSSL can be found here for encryption and decryption (including the consideration of the authentication tag).
In short
I'm neither an openSSL nor a PHP specialist. But at first sight, it appears that you might experience problems because you read and write binary data using files in text mode.
More infos about the potential problem
The encrypted data resulting from your C++ code is binary data. Your code does not show how you write the file. But unless you explicitly request the file to be in binary mode, you will get a file in text mode.
This might cause multiple issues when writing the data, since text mode allows for OS-dependent transformations to happen. Typical examples are characters with value 0x0A (new lines), skipping trailing 0x20 (space character), adding a 0x0A at the end of the file if there isn't and similar undesired transformation.
Your PHP code might open the file in an incompatible default mode, which might add further transformation if it's text mode, or avoid reverting transformations if it's binary.
This means that in the end, the string you try to decode might not be the original encrypted string !
How to solve it ?
First inspect with a binary editor the content of the file to see if it matches the expectations. Or check the expected length of the encrypted source data, the length of the file, and the length of the loaded content. If they all match, my answer is not relevant.
If it is relevant, or if you intend sooner or later to allow cross platform exchanges (e.g. windows client communicating with a linux server), then you could:
either add the necessary statements to use binary mode on both sides.
or add a Base64 encoding to transform binary into a robust ascii string on the writing side, and retransforming Base64 into binary on the reading side (openssl provides for base64 encoding and PHP has everything needed as well)
I have a Python application and PHP website that communicate over some specific network layer sending messages. My task is to send all messages AES-encrypted and base64-encoded using that channel. Encryption key is pre-shared for both parties manually.
In my PHP, i used this code to create final message text called $payload:
$key = substr('abdsbfuibewuiuizasbfeuiwhfashgfhj56urfgh56rt7856rh', 0, 32);
$magic = 'THISISANENCRYPTEDMESSAGE';
function crypted($data) {
global $key, $magic;
// serialize
$payload = json_encode($data);
// encrypt and get base64 string with padding (==):
$payload = #openssl_encrypt($payload, 'AES-192-CBC', $key);
// prepend with magic
$payload = $magic.$payload;
return $payload;
}
And i receive such message in my Python application, stripping the magic, getting base64 byte data. The problem that i cannot find a sample to make compatible AES cipher to decode this message.
Key and "Magic" are only values pre-shared and known on both sides, is this correct? Do i need an IV?
Here is Python solution from SO that does not work for my crypted messages.
from base64 import b64encode, b64decode
from Crypto.Cipher import AES
class AESCipher:
class InvalidBlockSizeError(Exception):
"""Raised for invalid block sizes"""
pass
def __init__(self, key):
self.key = key
self.iv = bytes(key[0:16], 'utf-8')
def __pad(self, text):
text_length = len(text)
amount_to_pad = AES.block_size - (text_length % AES.block_size)
if amount_to_pad == 0:
amount_to_pad = AES.block_size
pad = chr(amount_to_pad)
return text + pad * amount_to_pad
def __unpad(self, text):
pad = ord(text[-1])
return text[:-pad]
def encrypt( self, raw ):
raw = self.__pad(raw)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv)
return b64encode(cipher.encrypt(raw))
def decrypt( self, enc ):
enc = b64decode(enc)
cipher = AES.new(self.key, AES.MODE_CBC, self.iv )
r = cipher.decrypt(enc) # type: bytes
return self.__unpad(r.decode("utf-8", errors='strict'))
It fails on last line with decode problem. "ignore" decoding mode returns empty string.
# with magic: "THISISANENCRYPTEDMESSAGE8wZVLZpm7UNyUf26Kds9Gwl2TBsPRo3zYDFQ59405wI="
# contains: {'test': 'hello world'}
payload = '8wZVLZpm7UNyUf26Kds9Gwl2TBsPRo3zYDFQ59405wI='
aes = AESCipher('abdsbfuibewuiuizasbfeuiwhfashgfh')
print(aes.decrypt(payload))
Raises:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "../test.py", line 36, in decrypt
return self.__unpad(cipher.decrypt(enc).decode("utf-8"))
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x9e in position 0: invalid start byte
What am i missing?
You are using Cipher Block Chaining, but didn't pass in an IV to openssl_encrypt(); this means the IV is then 16 times the NUL byte. But your Python code uses the key as the IV instead, so that'll produce a different decryption result altogether.
Next, you picked AES-192-CBC, not AES-256-CBC, so only 192 bits are used for the key. 192 bits == 24 bytes, and not 32 as you thought.
You also need to drop the __unpad() call entirely, there is no padding in your encrypted data, removing data from the end before decrypting will only lead to the decryption failing.
So to decrypt on the Python side, use 24 characters for the key, give an IV that is 16 times \x00, and pass in all data you decoded from Base64:
>>> from Crypto.Cipher import AES
>>> from base64 import b64decode
>>> key = 'abdsbfuibewuiuizasbfeuiwhfashgfh'[:24]
>>> key
'abdsbfuibewuiuizasbfeuiw'
>>> payload = '8wZVLZpm7UNyUf26Kds9Gwl2TBsPRo3zYDFQ59405wI='
>>> enc = b64decode(payload)
>>> cipher = AES.new(key, AES.MODE_CBC, '\x00' * 16)
>>> cipher.decrypt(enc)
b'{"test":"hello world"}\n\n\n\n\n\n\n\n\n\n'
If you wanted to use the full 32 characters of the key, use AES-256-CBC instead.
You really want to produce a random IV, so that someone snooping on the traffic can't determine patterns (where the same payload produces the same encrypted message each time). Generate the IV, include it in the data you send, and extract it on the Python side to pass to the AES.new() function.
Hi is there anyone with a working SHA512 iOS implementation? The code doesn't seem to generate the same one I have on php.
<?php
$code = hash("SHA512", '123' . '123' );
echo $code;
?>
Output:
263fec58861449aacc1c328a4aff64aff4c62df4a2d50b3f207fa89b6e242c9aa778e7a8baeffef85b6ca6d2e7dc16ff0a760d59c13c238f6bcdc32f8ce9cc62
- (NSString *) sha512:(NSString *) input withSalt: (NSString *) salt {
const char *cKey = [salt cStringUsingEncoding:NSUTF8StringEncoding];
const char *data = [input cStringUsingEncoding:NSUTF8StringEncoding];
unsigned char digest[CC_SHA512_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA512, cKey, strlen(cKey), data, strlen(data), digest);
NSString *hash;
NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA512_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_SHA512_DIGEST_LENGTH; i++)
[output appendFormat:#"%02x", digest[i]];
hash = output;
return hash;
}
[self sha512:#"123" withSalt:#"123"];
Output:
3cafe40f92be6ac77d2792b4b267c2da11e3f3087b93bb19c6c5133786984b44
You are using HMAC on iOS, you need to use SHA and concatenate "123" and "123" the same as you are doing for php. HMAC does not just concatenate the key and data.
Use:
#import <CommonCrypto/CommonDigest.h>
extern unsigned char *CC_SHA512(const void *data, CC_LONG len, unsigned char *md)
Your code has two problems:
Your ObjC code appears to be calculating a SHA512 HMAC and your PHP code is calculating a SHA512 hash which are 2 different beasts. As far as I know [which is not far] the corresponding PHP code should be something like:
hash_hmac('SHA512', '123', '123' );
The output for the above is still much longer that the ObjC code, ie:
0634fd04380bbaf5069c8c46a74c7d21df7414888d980c27a16d5e262cb8c9059139c212d0926000faf026e483904cefae2f5e9d9bd5f51fbc2ac4c4de518115
Which is 128 characters [512 bits] long and ostensibly the expected length from SHA512 function.
Edited the php code:
hash_hmac('SHA512', '123', '123' );
This solved the problem. Thanks!
I am using the following function to encrypt my data via the OpenSSL Library in Qt:
QByteArray Crypto::Encrypt(QByteArray source, QString password)
{
EVP_CIPHER_CTX en;
unsigned char *key_data;
int key_data_len;
QByteArray ba = password.toLatin1();
key_data = (unsigned char*)ba.data();
key_data_len = strlen((char*)key_data);
int nrounds = 28;
unsigned char key[32], iv[32];
EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), NULL, key_data, key_data_len, nrounds, key, iv);
QByteArray bkey = reinterpret_cast<const char*>(key) //EDIT: Contains the key afterwards
QByteArray biv = reinterpret_cast<const char*>(iv) //EDIT: Is Null afterwards
EVP_CIPHER_CTX_init(&en);
EVP_EncryptInit_ex(&en, EVP_aes_256_cbc(), NULL, key, iv);
char *input = source.data();
char *out;
int len = source.size();
int c_len = len + 16, f_len = 0;
unsigned char *ciphertext = (unsigned char *)malloc(c_len);
EVP_EncryptInit_ex(&en, NULL, NULL, NULL, NULL);
EVP_EncryptUpdate(&en, ciphertext, &c_len, (unsigned char *)input, len);
EVP_EncryptFinal_ex(&en, ciphertext+c_len, &f_len);
len = c_len + f_len;
out = (char*)ciphertext;
EVP_CIPHER_CTX_cleanup(&en);
return QByteArray(out, len);
}
"source" is in that case "12345678901234567890123456789012abc".
"password" is "1hA!dh==sJAh48S8Ak!?skiitFi120xX".
So....if I got that right, then EVP_BytesToKey() should generate a key out of the password and supplied data to decrypt the string with later.
To Base64-Encoded that key would be: "aQkrZD/zwMFU0VAqjYSWsrkfJfS28pQJXym20UEYNnE="
I don't use a salt, so no IV (should be null).
So QByteArray bkey in Base64 leaves me with "aQkrZD/zwMFU0VAqjYSWsrkfJfS28pQJXym20UEYNnE="
QByteArray bvi is giving me Null
The encryptet text is "CiUqILbZo+WJBr19IiovRVc1dqGvrastwo0k67TTrs51HB8AbJe8S4uxvB2D7Dkr".
Now I am using the following PHP function to decrypt the ciphertext with the generated key again:
<?php
function decrypt_data($data, $iv, $key) {
$cypher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
//if(is_null($iv)) {
// $ivlen = mcrypt_enc_get_iv_size($cypher);
// $iv = substr($data, 0, $ivlen);
// $data = substr($data, $ivlen);
//}
// initialize encryption handle
if (mcrypt_generic_init($cypher, $key, $iv) != -1) {
// decrypt
$decrypted = mdecrypt_generic($cypher, $data);
// clean up
mcrypt_generic_deinit($cypher);
mcrypt_module_close($cypher);
return $decrypted;
}
return false;
}
$ctext = "CiUqILbZo+WJBr19IiovRVc1dqGvrastwo0k67TTrs51HB8AbJe8S4uxvB2D7Dkr";
$key = "aQkrZD/zwMFU0VAqjYSWsrkfJfS28pQJXym20UEYNnE=";
$res = decrypt_data(base64_decode($ctext), null, base64_decode($key));
echo $res;
?>
Now I'd expect a response like "12345678901234567890123456789012abc".
What I get is "7890123456789012abc".
My string seems to be decrypted in the right way, but it's cut in half and only the last 19 characters are displayed.
Can someone please help me with that?
I'm new to encryption and can't really figure out where exactly I went wrong.
This is probably because of a misinterpretation from your part. You say:
I don't use a salt, so no IV (should be null).
But there is no reason at all why that would be the case. The EVP_BytesToKey method provided both a key and an IV. The key is obviously correct, but the IV is not. This will result in random characters in your plain text (the IV only changes the first block). As this block will likely contain control characters and what more, it may not display well.
Remember that a salt and IV may have a few things in common (should not be repeated, can be public etc.) but that they are entirely different concepts in cryptography.
Please try again with your Qt code, and this time print out the IV as well as the key...
I solved the problem with the empty initialisation vector by trial and error now, though I have no clue why the following was a problem at all.
Maybe someone can explain that to me.
Changing the line: int nrounds = 28; did the trick.
If i put any other number than 28 in there, an IV is generated and when I use it afterwards in mcrypt the ciphertext is decrypted in the correct way.
Why was it a problem to generate the key with 28 rounds with the openssl-function EVP_BytesToKey()?
I reduced it to 5 rounds now, but I'm curious whether this problem might happen again with a password-rounds-combination that has the possibility to generate such a Null-IV.
I don't realy know how the process of the IV generation is handled in this function.