如何使用 BearSSL 创建 sha256 has (SAS Token)

问题描述

我正在尝试使用 Tasmota 框架 https://tasmota.github.io/ 中的 PubSub 客户端生成 SAS 令牌以对 Azure IoT 中心进行身份验证。

以下示例:https://github.com/Azure/azure-sdk-for-c/blob/master/sdk/samples/iot/aziot_esp8266/aziot_esp8266.ino,与已知的优秀 Python 示例相比,我只能为 4 个字符的秘密创建签名。当我增加或减少秘密的长度时,散列不匹配。任何关于我做错了什么的建议都会很棒。

看起来它只是对前 4 个字符进行了散列和编码(注意 C 代码如何使用 'mark' = 'markmark')。

C 代码(在 ESP8266 上运行):

#include <beaRSSl\beaRSSl.h>  // from https://github.com/arendst/Tasmota/tree/development/lib/lib_ssl
#include <base64.hpp>         // from https://github.com/arendst/Tasmota/tree/development/lib/lib_ssl

const char *PSK = "MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4=";
const char *dataToSign = "mark";

br_sha256_context sha256_context;
br_hmac_key_context hmac_key_context;
br_hmac_context hmac_context;

unsigned char decodedPSK[32];
unsigned char encryptedSignature[100];
unsigned char encodedSignature[100];

// need to base64 decode the Preshared key and the length
int base64_decoded_device_length = decode_base64((unsigned char*)PSK,decodedPSK);

// create the sha256 hmac and hash the data
br_sha256_init(&sha256_context);
br_hmac_key_init(&hmac_key_context,sha256_context.vtable,decodedPSK,base64_decoded_device_length);
br_hmac_init(&hmac_context,&hmac_key_context,32);
br_hmac_update(&hmac_context,dataToSign,sizeof(dataToSign));
br_hmac_out(&hmac_context,encryptedSignature);

// base64 decode the HMAC to a char
encode_base64(encryptedSignature,br_hmac_size(&hmac_context),encodedSignature);

printf("PSK is %s \n",PSK);
printf("decodedPSK is %s \n",decodedPSK);
printf("dataToSign is %s \n",dataToSign);
printf("encryptedSignature is %s \n",encryptedSignature);
printf("encodedSignature is %s \n",encodedSignature);

Python 代码

from base64 import b64encode,b64decode
from hashlib import sha256
from urllib.parse import quote_plus,urlencode
from hmac import HMAC
PSK="MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4="
sign_key = "mark".encode('utf-8')
signature = b64encode(HMAC(b64decode(PSK),sign_key,sha256).digest())
print('PSK = ' + PSK)
print('sign_key = ' + str(sign_key))
print('signature = ' + str(signature))

如果将 dataToSign 设置为标记,则哈希匹配:

C 输出

PSK is MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4=
ecodedPSK is 0␕��f�3D␙�Ӿ�␄��Gm␓�ST!^␌Q��␆��
dataToSign is mark
encryptedSignature is ␝�8)����jLQ��␃b!��x␞Ǿ2��1�W␗S4���?���?�␕�? 
encodedSignature is Hak4Kaic0vtqTFGSlQNiIeeD6ngex74yoOExgVcXUzQ=

Python 输出

C:/python38-64/python.exe d:/Git/createsastoken/generate/test.py
PSK = MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4=
sign_key = b'mark'
signature = b'Hak4Kaic0vtqTFGSlQNiIeeD6ngex74yoOExgVcXUzQ=

如果我将 dataToSign 更改为 'markmark',它们不匹配:

C 输出

PSK is MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4=
ecodedPSK is 0␕��f�3D␙�Ӿ�␄��Gm␓�ST!^␌Q��␆��
dataToSign is markmark
encryptedSignature is ␝�8)����jLQ��␃b!��x␞Ǿ2��1�W␗S4���?���?�␕�? 
encodedSignature is Hak4Kaic0vtqTFGSlQNiIeeD6ngex74yoOExgVcXUzQ=

Python 输出

C:/python38-64/python.exe d:/Git/createsastoken/generate/test.py
PSK = MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4=
sign_key = b'markmark'
signature = b'XSf//iSJx9hB6lOQe5lcYkxLGWY+g5oXM8AX0XVGIh4='

如果我将 dataToSign 缩短,例如“joe”,也会出现此问题。

C 输出

PSK is MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4=
ecodedPSK is 0␕��f�3D␙�Ӿ�␄��Gm␓�ST!^␌Q��␆��
dataToSign is joe
encryptedSignature is ~����␕�␡
encodedSignature is fp21m4IVnH8Arfnpb1SNY3HifxunD5se9QwF/KA3z2E=

蟒蛇:

& C:/python38-64/python.exe d:/Git/createsastoken/generate/test.py
PSK = MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4=
sign_key = b'joe'
signature = b'69FWap6f/xD4JauDwia5y9fqYdq8qnSH5F7Yo5aa4FI='

解决方法

尺寸

const char *dataToSign = "mark";

将始终为 4,因为它是一个指针(至少在 32 位系统上)。因此它对“mark”有效,但对“joe”和“markmark”无效。 声明为

const char dataToSign[] = "whatever...";

然后 sizeof 将为您提供数组大小,但这将包括终止零,因此您需要减去一。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...