In the frontend I'm using PHP (Symfony). In a PHP form the password is stored in the database, there I want to encrypt it. That same password is used in the backend for an API that in programmed in Go, there I need to decrypt it.
I like to use AES encryption, but if something better is proposed than that is also fine.
In backend I'm using the following GO function to decrypt it:
func DecryptAES(key []byte, ct string) string {
ciphertext, _ := hex.DecodeString(ct)
c, _ := aes.NewCipher(key)
pt := make([]byte, len(ciphertext))
c.Decrypt(pt, ciphertext)
s := string(pt[:])
return s
}
I use for example the following input:
// cipher key
key := "1234567890123456"
// password
pt := "-This is secret-" // needs to be 16 chars
To decrypt with the key above I need to following Ciphertext: 55c6ca0c634fb5cc7d7a287d30285f09
How can I generate this Ciphertext with PHP?
I did try the following, but the outcome if different:
$ct = openssl_encrypt('-This is secret-', 'AES-128-ECB', "1234567890123456");
Related
I am trying to send a data to an API. They wants it as encrypted before send it over.
The documentation says:
Generate a private key using AES algorithm(with SHA-1 HMac function by PKCS 5 V2.0 Scheme - I DON'T know what this means.
Encrypt the data with this key (data is json_string) (PADDING_PKCS1 = 11 and key length is 2048bit)
Encrypt the private_key using RSA algorithm with the public key(they gave us a pem file)
Then send this encrypted datas in a json format.
They also gives a dll file. So I decompiled it, and it uses a library called BouncyCastle.
Their function is basicly does this;
this.aesService.GenerateKey(password_variable, salt_file_contents_as_bytes); (they gives that both)
byte[] data1 = this.aesService.Encode(json_string);
byte[] data2 = this.rsaService.Encode(this.aesService.GetAesKey(), reads_public_keys_data_as_byte);
// and some http requests nothing special
What I tried is;
1- generated a key using openssl_pbkdf2 function with their password and salt
$key = openssl_pbkdf2($password, $salt, 32, 20000, 'sha1');
2- encrypted the data with this key with openssl_encrypt function
$encryptedJson = openssl_encrypt($json_string, "AES256", $key);
3- encrypted the key that I generate with their public key with openssl_public_encrypt function
openssl_public_encrypt($key, $encryptedAes, $public_key, OPENSSL_PKCS1_PADDING);
4- convert this results to hex with bin2hex function and send it to them
$jsonData = bin2hex($encryptedJson);
$keyData = bin2hex($encryptedAes);
But it returns error (says invalid object -I don't know what this means). I asked them about it but they didn't reply yet.
What I want to ask you is:
Am I doing it right? Am I using the right functions for this operations?
EDIT:
They still didn't reply back. But I just added the IV to the the start of the encrypted data returned by the openssl_encrypt function and convert it to hex. Voilà! It worked.
I try to make the app with PHP that creates ECDSA signature for some document and that signature is verified with Golang app.
I use private keys generated with openssl tool. It is prime256v1 curve key. Created with the command:
openssl ecparam -name prime256v1 -genkey -noout -out prime256v1-key.pem
In PHP i create signature using openssl_sign function.
And all my attempts to verify the signature with Golang fail. In Golang use the crypto/ecdsa, crypto/elliptic packages.
There is my code.
PHP
<?php
$stringtosign = "my test string to sign";
// Privcate key was geerated with openssl tool with the command
// openssl ecparam -name prime256v1 -genkey -noout -out prime256v1-key.pem
$cert = file_get_contents('prime256v1-key.pem');
$prkey = openssl_pkey_get_private($cert);
// we sign only hashes, because Golang lib can wok with hashes only
$stringtosign = md5($stringtosign);
// we generate 64 length signature (r and s 32 bytes length)
while(1) {
openssl_sign($stringtosign, $signature, $prkey, OPENSSL_ALGO_SHA256);
$rlen = ord(substr($signature,3,1));
$slen = ord(substr($signature,5+$rlen,1));
if ($slen != 32 || $rlen != 32) {
// try other signature if length is not 32 for both parts
continue;
}
$r = substr($signature,4,$rlen);
$s = substr($signature,6+$rlen,$slen);
$signature = $r.$s;
break;
}
openssl_free_key($prkey);
$signature = bin2hex($signature);
echo $signature."\n";
Golang
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/md5"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"math/big"
"crypto/x509"
"encoding/pem"
)
func main() {
stringtosign := "my test string to sign"
// This is outpur of PHP app. Signature generated by PHP openssl_sign
signature := "18d5c1d044a4a752ad91bc06499c72a590b2842b3d3b4c4b1086bfd0eea3e7eb5c06b77e15542e5ba944f3a1a613c24eabaefa4e2b2251bd8c9355bba4d14640"
// NOTE . Error verificaion is skipped here
// Privcate key was geerated with openssl tool with the command
// openssl ecparam -name prime256v1 -genkey -noout -out prime256v1-key.pem
prikeybytes, _ := ioutil.ReadFile("prime256v1-key.pem")
p, _ := pem.Decode(prikeybytes)
prikey, _ := x509.ParseECPrivateKey(p.Bytes)
signatureBytes, _ := hex.DecodeString(signature)
// make MD5 hash
h := md5.New()
io.WriteString(h, stringtosign)
data := h.Sum(nil)
// build key and verify data
r := big.Int{}
s := big.Int{}
// make signature numbers
sigLen := len(signatureBytes)
r.SetBytes(signatureBytes[:(sigLen / 2)])
s.SetBytes(signatureBytes[(sigLen / 2):])
curve := elliptic.P256()
// make public key from private key
x := big.Int{}
y := big.Int{}
x.SetBytes(prikey.PublicKey.X.Bytes())
y.SetBytes(prikey.PublicKey.Y.Bytes())
rawPubKey := ecdsa.PublicKey{Curve: curve, X: &x, Y: &y}
v := ecdsa.Verify(&rawPubKey, data, &r, &s)
if v {
fmt.Println("Success verify!")
return
}
fmt.Println(fmt.Sprintf("Signatire doed not match"))
}
What do i do wrong? Can anyone show me working example where Golang verifies signatre created with PHP?
I tried to use different versions in openssl_sign instead of OPENSSL_ALGO_SHA256 . Tried OPENSSL_ALGO_SHA1, OPENSSL_ALGO_SHA512
The problem with your code seems to be, that you hash the string in PHP using MD5 before signing it using OPENSSL_ALGO_SHA256, which hashes what you sign (the MD5 hash) again, while in your Go program, you only have the first of these 2 hashes. To fix this, I would remove the MD5 step in the PHP code and replace the h := md5.New() line in your code with the hash used by your signature algorithm (h := sha256.New() in your example).
To elaborate a bit more on what theses signing functions do, I would first like to break signing and verifying down into the following steps:
Signing:
Hash the message
Encrypt the message's hash using the private key (this encrypted hash is the signature)
Verifying:
Hash the message
Decrypt the signature using the public key (this yields the hash which was encrypted while signing).
Compare the calculated and decrypted hashes. If they match, then the signature is correct.
Now the call to openssl_sign in your PHP code, does all the signing steps, while the call to ecdsa.Verify in Go, only does the second and third step of the verification process. And this is why it takes a hash as the second argument. So to verify a signature, you must implement the first verification step yourself, namely generating the hash.
You must use the same hashing algorithm while signing and verifying, therefore you must use SHA256, not MD5, in your Go code (as you sign using OPENSSL_ALGO_SHA256), otherwise the hashes will (generally) not match.
Also, I would recommend to not use MD5 for signatures, as it is no longer considered collision resistant (a hash collision is, when you have 2 different strings/files/... with the same hash). For more details about that, you can check the Wikipedia article on MD5, specifically the section "Collision vulnerabilities". This is a problem, as 2 messages with the same MD5 hash, will also have the same signature and an attacker could use the signature generated for one of the strings to trick you into thinking the other was signed (and therefore trust it).
Additionally, ecdsa.PrivateKey can give you the corresponding public key, and you can call ecdsa.Verify like this:
ecdsa.Verify(&prikey.PublicKey, data, &r, &s)
This saves you the trouble of copying all the data from the private key to a new object.
I am trying to do the equivalent of the php seal function.
What I have is a string to encrypt, a public key and a randomly generated secret key, and I have to encode the string using the 'rs4' algorithm.
So far I managed to encode the string with the crypto functions:
var password = crypto.randomBytes(128);
var cipher = crypto.createCipher('rc4', password);
var crypted = cipher.update(text,'utf8','base64');
crypted += cipher.final('base64');
But somehow, I need to include in the encryption the public x509 certificate.
Can someone point me in the right direction?
RC4 is not a public key encryption system. You're looking for (in order of preference):
node-sodium
RSAES-OAEP with MGF1+SHA256 and e = 65537
Also, for secret-key cryptography, don't use RC4.
In the end I didn't find any way of achieving the same encryption in node, so I just called a php cli script from node that sent the data to be encrypted, and read the stdout for the encrypted base64 result :(
I'm using Zend2 Crypt module to encrypt a data. Here's my code.
$cipher = BlockCipher::factory('mcrypt', array(
'algorithm' => 'aes',
));
$cipher->setKey('mypassphrase');
$encrypted = $cipher->encrypt('Hey, I am the secret data');
Cool, it works well! Now, I need to decrypt that $encrypted data (Hey, I am the secret data) in Python.
I am using pycrypto to do that. What the steps to decrypt data outside my PHP environment?
from Crypto.Cipher import AES
import base64
import hashlib
password = 'mypassphrase'
key = hashlib.sha256(password).digest()
decoded = base64.standard_b64decode(encrypted)
cipher = AES.new(key, AES.MODE_CBC)
data = cipher.decrypt(decoded)
I need to specify an IV because Zend uses MODE_CBC by default. How can I specify it in my Python code?
Here's the Zend2 documentation:
The output of the encryption is a string, encoded in Base64 (default), that contains the HMAC value, the IV vector, and the encrypted text. The encryption mode used is the CBC (with a random IV by default) and SHA256 as default hash algorithm of the HMAC. The Mcrypt adapter encrypts using the PKCS#7 padding mechanism by default. You can specify a different padding method using a special adapter for that (Zend\Crypt\Symmetric\Padding). The encryption and authentication keys used by the BlockCipher are generated with the PBKDF2 algorithm, used as key derivation function from the user’s key specified using the setKey() method.
Can someone help me to adapt my Python code to decrypt the data?
Thanks
I found a way to decrypt the data encrypted by Zend2. Here's my code:
from base64 import b64decode
from Crypto import Random
from Crypto.Cipher import AES
from Crypto.Hash import SHA256, HMAC
from Crypto.Protocol.KDF import PBKDF2
# The hmac starts from 0 to 64 (length).
hmac_size = 64
hmac = data[:hmac_size]
# The cipher text starts after the hmac to the end.
# The cipher text is base64 encoded, so I decoded it.
ciphertext = data[hmac_size:]
ciphertext = b64decode(ciphertext)
# The IV starts from 0 to 16 (length) of the ciphertext.
iv = ciphertext[:16]
# The key size is 256 bits -> 32 bytes.
key_size = 32
# The passphrase of the key.
password = 'mypassphrase'
# The key is generated using PBKDF2 Key Derivation Function.
# In the case of Zend2 Crypt module, the iteration number is 5000,
# the result length is the key_size * 2 (64) and the HMAC is computed
# using the SHA256 algorithm
the_hash = PBKDF2(password, iv, count=5000, dkLen=64, prf=lambda p, s:
HMAC.new(p, s, SHA256).digest())
# The key starts from 0 to key_size (32).
key = the_hash[:key_size]
# The hmac key starts after the key to the end.
key_hmac = the_hash[key_size:]
# HMAC verification
hmac_new = HMAC.new(key_hmac, 'aes%s' % ciphertext, SHA256).hexdigest()
if hmac_new != hmac:
raise Exception('HMAC verification failed.')
# Instanciate the cipher (AES CBC).
cipher = AES.new(key, AES.MODE_CBC, iv)
# It's time to decrypt the data! The ciphertext starts after the IV (so, 16 after).
data = cipher.decrypt(ciphertext[16:])
Mission succeeded!
I am trying to use the DEC 3.0 library (Delphi Encryption Compedium Part I) to encrypt data in Delphi 7 and send it to a PHP script through POST, where I am decrypting it with mcrypt (RIJNDAEL_256, ECB mode).
Delphi part:
uses Windows, DECUtil, Cipher, Cipher1;
function EncryptMsgData(MsgData, Key: string): string;
var RCipher: TCipher_Rijndael;
begin
RCipher:= TCipher_Rijndael.Create(KeyStr, nil);
RCipher.Mode:= cmECB;
Result:= RCipher.CodeString(MsgData, paEncode, fmtMIME64);
RCipher.Free;
end;
PHP part:
function decryptMsgContent($msgContent, $sKey) {
return mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sKey, base64_decode($msgContent), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND));
}
The problem is that the decryption from PHP doesn't work and the output is gibberish, differing from the actual data.
Of course, Delphi Key and PHP $Key is the same 24 characters string.
Now I know DEC 3.0 is old and outdated, and I'm not an expert in encryption and can't tell if the inplementation is actually Rijndael 256. Maybe someone can tell me how this implementation differs from PHP's mcrypt w/ RIJNDAEL_256. Maybe the keysize is different, or the block size, but can't tell this from the code. Here's an excerpt from Cipher1.pas:
const
{ don’t change this }
Rijndael_Blocks = 4;
Rijndael_Rounds = 14;
class procedure TCipher_Rijndael.GetContext(var ABufSize, AKeySize, AUserSize: Integer);
begin
ABufSize := Rijndael_Blocks * 4;
AKeySize := 32;
AUserSize := (Rijndael_Rounds + 1) * Rijndael_Blocks * SizeOf(Integer) * 2;
end;
Side question:
I know ECB mode isn't recommended and I'll use CBC as soon as I get ECB working. The question is, do I have to transmit the generated IV in Delphi to the PHP script also? Or knowing the key is sufficient, like for ECB?
You are calling the TCipher.Create(const Password: String; AProtection: TProtection); constructor, which will compute a hash of the password before passing it to the Init method, which performs the standard key schedule of the implemented algorithm. To override this key derivation, use:
function EncryptMsgData(MsgData, Key: string): string;
var RCipher: TCipher_Rijndael;
begin
RCipher:= TCipher_Rijndael.Create('', nil);
RCipher.Init(Pointer(Key)^,Length(Key),nil);
RCipher.Mode:= cmECB;
Result:= RCipher.CodeString(MsgData, paEncode, fmtMIME64);
RCipher.Free;
end;
OK, so to sum this up, there were 3 problems with my code:
Due to my poor understanding of mcrypt and ciphers in general, MCRYPT_RIJNDAEL_256 refers to 128 bits block and doesn't refer to the keysize. My correct choice should have been MCRYPT_RIJNDAEL_128, which is the AES standard and is also supported by DEC 3.0.
DEC has it's own default key derivation, so I needed to bypass it so I wouldn't have to implement it in PHP also. In actuality, I am using my own key derivation algorithm that was easy to reproduce in PHP (first 32 characters of sha1(key)).
DEC doesn't pad plaintext to a multiple of the block size of the cipher, as mcrypt expects, so I had to do it manually.
Providing working code below:
Delphi:
uses Windows, DECUtil, Cipher, Cipher1, CryptoAPI;
function EncryptMsgData(MsgData, Key: string): string;
var RCipher: TCipher_Rijndael;
KeyStr: string;
begin
Result:= '';
try
// key derivation; just making sure to feed the cipher a 24 chars key
HashStr(HASH_SHA1, Key, KeyStr);
KeyStr:= Copy(KeyStr, 1, 24);
RCipher:= TCipher_Rijndael.Create('', nil);
RCipher.Init(Pointer(KeyStr)^, Length(KeyStr), nil);
RCipher.Mode:= cmECB;
Result:= RCipher.CodeString(MsgData + StringOfChar(#0,16-(Length(MsgData) mod 16)), paEncode, fmtMIME64);
RCipher.Free;
except
end;
end;
PHP:
function decryptMsgContent($msgContent, $sKey) {
$sKey = substr(sha1(sKey), 0, 24);
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $sKey, base64_decode($msgContent), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND)));
}
A 256 bit key I found is 32 charachters, or 32 bytes. Not 24. This may be the issue.
[EDIT]
I combined everyone's ideas (ansistring, etc) into one single idea with a fix.
Also, you are using codestring( -- it should be Encodestring(
I pasted working Encrypt and Decrypt source below:
function EncryptMsgData(MsgData, Key: AnsiString): AnsiString;
var RCipher: TCipher_Rijndael;
begin
RCipher:= TCipher_Rijndael.Create('', nil);
RCipher.Init(Pointer(Key)^,Length(Key),nil);
RCipher.Mode:= cmCBC;
Result:= RCipher.EncodeString(MsgData);
RCipher.Free;
end;
function DecryptMsgData(MsgData, Key: AnsiString): AnsiString;
var RCipher: TCipher_Rijndael;
begin
RCipher:= TCipher_Rijndael.Create('',nil);
RCipher.Init(Pointer(Key)^,Length(Key),nil);
RCipher.Mode:= cmCBC;
Result:= RCipher.DecodeString(MsgData);
RCipher.Free;
end;
Use that with a 32 charachter key and you get proper encryption and decryption.
In order to store and use the encrypted data as a string you may want to use Base64Encode(
But do not forget to Base64Decode prior to decrypting.
This is the same technique needed for Blowfish. Sometimes the charachters actually are like a backspace, and perform the function rather than showing on screen. Base64Encode basically converts the charachters to something you can display in text.
Prior to transferring the encoded data across the internet or to another application in the same or another language, you MUST base64encode and decode in order to not loose data. Don't forget it in PHP too!