Last week we started to work with a new API of a provider. For authentication they request a JWE in the header of an https request.
We have no experience with JWE, then we started to look information about it, to devolope it with PHP.
After many tested and thanks to DinoChiesa online JWT Decoder (https://dinochiesa.github.io/jwt/), we developed a function to make it, but there're something was wrong yet. At the end Dino Chiesa helped us to fix issues and the function below now is working.
<?php
require_once("auth/key.php");
include 'vendor/autoload.php';
use phpseclib3\Crypt\PublicKeyLoader;
function jwe (){
global $payloadAuth; //put here payload that you have to use
/**************************************************/
/********************HEADER************************/
/**************************************************/
// base64 encodes the header json
$arr = array('enc' => 'A256GCM', 'alg' => 'RSA-OAEP');
$arr2 = json_encode($arr);
$encoded_header=base64url_encode($arr2);
/**************************************************/
/********************JWE KEY***********************/
/**************************************************/
$CEK=openssl_random_pseudo_bytes(32);
$encoded_EncCEK = base64url_encode(rsaEncryptionOaepSha256($publicKey, $CEK));
/**************************************************/
/********************VECTOR************************/
/**************************************************/
$iv = openssl_random_pseudo_bytes(12);
$encoded_iv = base64url_encode($iv);
/**************************************************/
/********************CYPHERTEXT********************/
$cipher = "aes-256-gcm";
$option=1;
$aad=$encoded_header;
$ciphertext=openssl_encrypt($payloadAuth, $cipher, $CEK, $option, $iv, $tag, $aad);
$encoded_ciphertext=base64url_encode($ciphertext);
/**************************************************/
/********************TAG***************************/
/**************************************************/
$encoded_tag=base64url_encode($tag);
/**************************************************/
/********************FIN***************************/
/**************************************************/
return $encoded_header . '.' . $encoded_EncCEK . '.' . $encoded_iv . '.' . $encoded_ciphertext . '.' . $encoded_tag;
}
function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
function rsaEncryptionOaepSha256($publicKey, $plaintext) {
global $publicKey;
$rsa = PublicKeyLoader::load($publicKey)->withHash('sha1')->withMGFHash('sha1');
return $rsa->encrypt($plaintext);
}
?>
Could someone explain how would change the code if "enc"="A256CBC-HS512" and "alg"="RSA-OAEP-256"?
Thanks and regards, Luis
I expect that people who work with PHP can find year a completee solution for JWE authentication.
I'd suggest using web-token/jwt-framework instead of trying to create your own builder/parser functions, as these things can get very tricky!
Related
I have the following decrypted message, which has previously been encrypted using AES-256-CBC
240dcbefc0f82fadc00ef8494488aaa81400000c2def01e79fec6c4d9a822358dd8a910cac606e8afcb607793cb442093a56b7b40b0b0b0b0b0b0b0b0b0b0b0b
I derive the following 20 BYTE HMAC from this message:
dd8a910cac606e8afcb607793cb442093a56b7b4
My goal is to re-create this HMAC using PHP, I attempt with the following code:
$iv = hex2bin('240dcbefc0f82fadc00ef8494488aaa8'); // random iv - first 16 bytes of the message
$message = hex2bin('1400000c2def01e79fec6c4d9a822358'); // the actual message being decrypted - next 16 bytes
$key = hex2bin('b109124b62e2c8b8248e9865990325fddcc61143'); // encryption key
$hmac = hash_hmac('sha1', $iv.$message, $key);
print($hmac); // 03634ba3f4a0c854a0b791d27f331ecdfad1e87e
$attempt2 = hash('sha256', $iv.$message, true);
$hmac = hash_hmac('sha1', $attempt2, $key);
print($hmac); // 39ad1fb94ab251cdaf3f21cf8673e070733f4e16
I know I'm missing something but I'm struggling to understand the HMAC process as it's very confusing to me. Any help or advise is appreciated, thanks.
I found a solution on a random blog post online :D
here is what worked for me:
function check_mac($seq, $type, $msg, $key) {
$SequenceNumber = pack("NN", 0, $seq);
$Type = pack("Cnn", $type, 0x0303, strlen($msg));
$data = $SequenceNumber . $Type . $msg;
$calculated_mac = hash_hmac("sha1", $data, $key, true);
print(bin2hex($calculated_mac) . "\n");
}
Also, the IV does not need to be included, just the message by itself as the $msg variable
Blog Post:
https://adayinthelifeof.nl/2013/12/30/decoding-tls-with-php/
I have this code in page A:
<?php
$token = // 64 chars alphanumerical token;
$method = "aes128";
$key = "12345678";
$crypto_code = openssl_encrypt($token, $method, $key);
$encoded_crypto_code = base64_encode($crypto_code);
$target_url = "https://mysamplesite.com/use_qrcode.php?code=" . $encoded_crypto_code;
// now the code is put inside a qrcode
QRcode::png($target_url, QR_ECLEVEL_H, 3, 10);
?>
Then I read the code with a smartphone that takes me to page B (use_qrcode.php) where I have the following code:
<?php
$code_flag = 0;
if (isset($_GET["code"])){
$encoded_crypto_code = $_GET["code"];
$crypto_code = base64_decode($encoded_crypto_code);
$method = "aes128";
$key = "12345678";
$code = openssl_decrypt($crypto_code, $method, $key);
if (false === $code) {
echo sprintf("OpenSSL error: %s", openssl_error_string() . "<br>");
} else {
echo "received code: " . $code . "<br>";
$code_flag = 1;
}
}
?>
My if(false === $code) catch an error, which is "OpenSSL error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length".
If I check both the base64 encoded string and the crypto string before and after sending them by QRcode, they are ok, so no problems in QR encoding and decoding nor in base64 encoding and decoding. The problem is somewhere in the OpenSSL. I am surprised since I use openssl_ encrypt and openssl_decrypt in many places all around the site, and this is the first time I face an error. If I do not use base64 encoding and decoding, the OpenSSL error is still there.
Is there any problem with the token I use?
Same result if I use some other similar tokens.
Where am I wrong?
I have to do a native JWT Key Method, and i have to use RSA256 method, but when i try this :
$getPrivate = openssl_pkey_get_private(".../rsa_private.pem", $passphrase);
openssl_sign($data, $partialSignature, $getPrivate, sha256WithRSAEncryption);
$signature .= $partialSignature;
$function_rsa = base64_encode($signature);
or that
$encrypt_rsa = openssl_private_encrypt($data, $partialEncrypted, $privateKey, OPENSSL_PKCS1_PADDING);
$encrypted .= $partialEncrypted;
$function_rsa = base64_encode($encrypted);
it won't work correctly, it return me that for the case 1 :
F/BXtAkMLK7W/2wGw2Gi/s1XT+2UphCafVug/oa/OQ62IXwblaS7vVyx5jyB1HTNMz/hlTGmr0ukj3J9lyDAunbP/2nmgWOGMjv2o52WGGaoVsLfJHPyrp7uLuYsq3VrLnnMRyaIrfSw/au2kiVX4HUhvBFOd5GHiRqXSfMiRqd4sj+DLSLlMMSw+LtyWWhlX1Sr4pcTFqa9EDrwdDuhpTWoagTVQP/4FTLl2pncd/zZso5MB+tjX72T/YjUnBuQyPqtjQPDiNMgm+M4lz6d3GZZvOHV05gWXr+aTgCAnTKPje8DBwNFLs5XgBIascUqRVz1k1v8n7B8lRxg1yOlqg==
or that for the case 2 :
e0Gm+MnmGvpFXhNDoj0Vsdl+1v+xxqW7uFFLBeGTjrcAr+ON7CadR0KFQ6lffEiZ17PAA584gDdLOtvpOH3q8V1UIqhbLQhJwEPstkBZRMNsplJse+eBsHNPnev2JYBDABbnAhE7I+xM9mI8iTCjW0DTcAecXahN+UctzBlKveZBMOcnJsRhgfYGZ7SKPySysIc6QoTkiNEJCgb68S8mfsiX9KSDgCwfc50Rql2nKEc15nO83OtkFvuLAjGxsQdSytzQ3hUuLXKqvEqeZsIgYYwQwbKWpLlJuFFQWL3VyjhupnBIO0tDRbZ/Lhqr62DhAoWkKjFtQiHdGdDv407xPQ==
I have correct privateKey.
Can you guys help me ? Thank you :)
I'm currently attempting to generate a signature to make API calls to quickbooks online, however I keep getting authentication errors. I'm sure the signature portion is where I'm going wrong. Is this incorrect:
//method to generate signature
//$this->method = "GET"
//QBO_SANDBOX_URL = 'https://some_url.com/'
//$this->_query = 'something=something'
public function generate_signature()
{
$base = $this->_method.'&'.rawurlencode($this->_url.QBO_SANDBOX_URL.'v3/company/'.$this->_realm_id).'&'
.rawurlencode("oauth_consumer_key=".rawurlencode($this->_consumer_key).'&'
.'&oauth_nonce='.rawurlencode('34604g54654y456546')
.'&oauth_signature_method='.rawurlencode('HMAC-SHA1')
.'&oauth_timestamp='.rawurlencode(time())
.'&oauth_token='.rawurlencode($this->_auth_token)
.'&oauth_version='.rawurlencode('1.0')
.'&'.rawurlencode($this->_query));
$key = rawurlencode($this->_consumer_secret.'&'.$this->_token_secret);
$this->_signature = base64_encode(hash_hmac("sha1", $base, $key, true));
}
Now when I go to send my request, here are the headers:
$this->_headers = array(
'Authorization: '.urlencode('OAuth oauth_token="'.$this->_auth_token.'",oauth_nonce="ea9ec8429b68d6b77cd5600adbbb0456",oauth_consumer_key="'.$this->_consumer_key.'",oauth_signature_method="HMAC-SHA1", oauth_timestamp="'.time().'", oauth_version ="1.0"oauth_signature="'.$this->_signature.'"').''
);
I get a 401 Authorization response. Am I signing incorrectly?
EDIT: All fields not included here (i.e $this->_auth_token) are set.
For anyone that might use this as a basis for their own integration, there is one other issue with the code originally posted:
$key = rawurlencode($this->_consumer_secret.'&'.$this->_token_secret);
should be
$key = rawurlencode($this->_consumer_secret).'&'.rawurlencode($this->_token_secret);
This issue was in the base string:
.rawurlencode("oauth_consumer_key=".rawurlencode($this->_consumer_key).'&'
.'&oauth_nonce='.rawurlencode('34604g54654y456546')
The & after the consumer key and once again before the oauth_nonce.
In the signature creation I think it lacks the call to rawurlencode():
$this->_signature = rawurlencode(base64_encode(hash_hmac("sha1", $base, $key, true)));
instead :
$this->_signature = base64_encode(hash_hmac("sha1", $base, $key, true));
I have the PHP application integrated with Dropbox API. It worked good but since some time ago it stopped to work with non-latin symbols.
For example, if i try to create a dropbox folder with the API and use Cyrillic letters in folder name then API requests fail with error {"error": "Unauthorized"}
I have the PHP function
function createOAuthSignature($apiurl, $params, $oauth_secret, $oauth_token_secret, $method='GET'){
$urlencoded_apiurl = urlencode($apiurl);
$urlencoded_params = urlencode($params);
$basestring = $method .'&'.$urlencoded_apiurl.'&'.$urlencoded_params;
$oauth_signature = base64_encode(hash_hmac('sha1', $basestring, $oauth_secret.'&'.$oauth_token_secret, true));
$oauth_signature = urlencode($oauth_signature);
return $oauth_signature;
}
And the calling code is
$fullname = $parent . $foldername;
$fullnameEnc = rawurlencode($fullname);
$apiurl = $this->api_url .'fileops/create_folder/';
$params = 'oauth_consumer_key='.$this->oauth_consumer_key
.'&oauth_nonce='. $this->createNonce()
.'&oauth_signature_method=HMAC-SHA1'
.'&oauth_timestamp='. time()
.'&oauth_token='.$oauth_token
.'&oauth_version=1.0'
.'&path='. $fullnameEnc
.'&root=dropbox';
$oauth_signature = $this->createOAuthSignature($apiurl, $params, $this->oauth_secret, $oauth_token_secret);
$params .= '&oauth_signature='.$oauth_signature;
$action = $apiurl .'?'. $params;
$s = $this->curl->get($action);
What can be the problem there? I presume it is related to signature generating. But what is wrong there? This code worked fine just couple weeks ago.
Thanks
As Greg mentioned in a comment above, we're investigating, but if you want to switch to PLAINTEXT signing, it's quite easy. Change createOAuthSignature to just do this:
function createOAuthSignature($apiurl, $params, $oauth_secret, $oauth_token_secret, $method='GET'){
return urlencode($oauth_secret.'&'.$oauth_token_secret);
}
And in the calling code, change the signature method to PLAINTEXT:
$params = ...
.'&oauth_signature_method=PLAINTEXT'
...