I'm very new to programming and I've been trying to encrypt a message using Arduino and decrypting with Python.
On the Arduino, I managed to encrypt and decrypt correctly, but when I try to decrypt with Python it doesn't show an error but the result isn't right.
I've used the library AESlib with the latest version (2.2.1) on Arduino with MEGA2560.
On the Arduino part I encrypted and decrypted the message correctly, I used the simple example that the AESlib offer but changed a bit to be able to do what I need it, encrypting with AES and encoding with base64, and then decoding with base64 to be able to decrypt with AES again. When that worked I printed the base64 encoded message and then copied it into a function on the python program and tried to decrypt it without working.
On the Python part, I've used the CBC mode for the decryption. Copied the key, the IV, and the encoded message for then decoded and decrypted.
Here is the message with the key and IV that I've used:
#define INPUT_BUFFER_LIMIT (400 + 1) //Maximum message caracters
unsigned char cleartext[INPUT_BUFFER_LIMIT] = {0}; // THIS IS INPUT BUFFER (FOR TEXT)
unsigned char ciphertext[2*INPUT_BUFFER_LIMIT] = {0}; // THIS IS OUTPUT BUFFER (FOR BASE64-ENCODED ENCRYPTED DATA)
unsigned char decryptedtext[INPUT_BUFFER_LIMIT] = {0}; // THIS IS OUTPUT BUFFER (FOR DECRYPTED TEXT)
unsigned char readBuffer[399] = "0013;0013;0013;15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3;NULL";//THIS IS THE VARIABLE THAT CONTAINS THE MESSAGE TO ENCRYPT
byte aes_key[N_BLOCK] = "06a9214036b8a15b512e03d534120006"; // THIS IS THE VARIABLE THAT CONTAINS THE KEY
byte aes_iv[N_BLOCK] = "6543210987654"; // THIS IS THE VARIABLE THAT CONTAINS THE IV
Arduino code:
#include "AESLib.h"
#define BAUD 9600
AESLib aesLib;
#define INPUT_BUFFER_LIMIT (400 + 1)
unsigned char cleartext[INPUT_BUFFER_LIMIT] = {0}; // THIS IS INPUT BUFFER (FOR TEXT)
unsigned char ciphertext[2*INPUT_BUFFER_LIMIT] = {0}; // THIS IS OUTPUT BUFFER (FOR BASE64-ENCODED ENCRYPTED DATA)
unsigned char decryptedtext[INPUT_BUFFER_LIMIT] = {0}; // THIS IS OUTPUT BUFFER (FOR DECRYPTED TEXT)
unsigned char readBuffer[399] = "0013;0013;0013;15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3;NULL";//THIS IS THE VARIABLE THAT CONTAINS THE MESSAGE TO ENCRYPT
byte aes_key[N_BLOCK] = "06a9214036b8a15b512e03d534120006"; // THIS IS THE VARIABLE THAT CONTAINS THE KEY
byte aes_iv[N_BLOCK] = "6543210987654"; // THIS IS THE VARIABLE THAT CONTAINS THE IV
// Generate IV
void aes_init() {
aesLib.gen_iv(aes_iv);
aesLib.set_paddingmode((paddingMode)0);
}
uint16_t encrypt_to_ciphertext(char * msg, uint16_t msgLen, byte iv[]) {
int i = 0;
Serial.println("Calling encrypt (string)...");
int cipherlength = aesLib.encrypt((byte*)msg, msgLen, (char*)ciphertext, aes_key, sizeof(aes_key), iv);
// uint16_t encrypt(byte input[], uint16_t input_length, char * output, byte key[],int bits, byte my_iv[]);
return cipherlength;
}
uint16_t decrypt_to_cleartext(byte msg[], uint16_t msgLen, byte iv[]) {
int i = 0;
Serial.print("Calling decrypt...; ");
uint16_t dec_bytes = aesLib.decrypt(msg, msgLen, (char*)decryptedtext, aes_key, sizeof(aes_key), iv);
Serial.print("Decrypted bytes: "); Serial.println(dec_bytes);
return dec_bytes;
}
void setup() {
Serial.begin(BAUD);
Serial.setTimeout(60000);
delay(2000);
aes_init(); // generate random IV, should be called only once? causes crash if repeated...
Serial.println(readBuffer[2]);
}
/* non-blocking wait function */
void wait(unsigned long milliseconds) {
unsigned long timeout = millis() + milliseconds;
while (millis() < timeout) {
yield();
}
}
byte enc_iv_to[N_BLOCK] = "6543210987654"; //A COPY OF THE IV TO DECRYPT WITH THE SAME IV
void loop() {
int i = 0;
Serial.print("readBuffer length: "); Serial.println(sizeof(readBuffer));
// must not exceed INPUT_BUFFER_LIMIT bytes; may contain a newline
sprintf((char*)cleartext, "%s", readBuffer);
// Encrypt
// iv_block gets written to, provide own fresh copy... so each iteration of encryption will be the same.
uint16_t msgLen = sizeof(readBuffer);
memcpy(aes_iv, enc_iv_to, sizeof(enc_iv_to));
uint16_t encLen = encrypt_to_ciphertext((char*)cleartext, msgLen, aes_iv); //CALL THE FUNCTION TO ENCRYPT THE MESSAGE
unsigned char base64encoded[2*INPUT_BUFFER_LIMIT] = {0};
base64_encode((char*)base64encoded, (char*)ciphertext, sizeof(ciphertext)); //CALL THE FUNCTION TO ENCODE THE ENCRYPTED MESSAGE
Serial.println("ciphertext_base64_encoded");
Serial.println((char*)base64encoded);
delay(5000);
Serial.print("Encrypted length = "); Serial.println(encLen);
Serial.print("Encrypted base64 length = "); Serial.println(sizeof(base64encoded));
Serial.println("Encrypted. Decrypting..."); Serial.println(sizeof(base64encoded)); Serial.flush();
unsigned char base64decoded[2*INPUT_BUFFER_LIMIT] = {0};
base64_decode((char*)base64decoded, (char*)base64encoded, sizeof(base64encoded));
Serial.println((char*)base64decoded);
delay(3000);
memcpy(aes_iv, enc_iv_to, sizeof(enc_iv_to));
uint16_t decLen = decrypt_to_cleartext((char*)base64decoded, encLen, aes_iv);
Serial.print("Decrypted cleartext of length: "); Serial.println(decLen);
Serial.print("Decrypted cleartext:\n"); Serial.println((char*)decryptedtext);
if (strcmp((char*)readBuffer, (char*)decryptedtext) == 0) {
Serial.println("Decrypted correctly.");
} else {
Serial.println("Decryption test failed.");
}
delay(3000);
Serial.println("---");
exit(0);
}
Python code:
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad, pad
from Crypto.Random import get_random_bytes
from Crypto.Util.strxor import strxor
import random, base64, hashlib, getpass, argparse, re
def decrypt_CBC_base64(key, ciphertext_base64, iv):
ciphertext_base64 += "=" * ((4 - len(ciphertext_base64) % 4) % 4)
ciphertext = base64.b64decode(ciphertext_base64)
ciphertext = pad (ciphertext, 16)
py_bytes = decrypt_CBC(key, ciphertext, iv)
return py_bytes
def decrypt_CBC(key, ciphertext, iv):
cipher = AES.new(key, AES.MODE_CBC, iv)
pt_bytes = cipher.decrypt(ciphertext)
return pt_bytes
try:
key = b'06a9214036b8a15b512e03d534120006'
iv = b'6543210987654321'
plaintext = b'0013;0013;0013;15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3,15.3;NULL'
ciphertext_base64 = 'aS/80Cy1jEqiclhtrYmqkYmLsxlKERuSeTfSGHfmN86N/VJHizQEmLAUJ8T7qlIgNZVr1EZzNCG2/8qzwRwHoLdMmmZKjIOKPSpbqWfiMRbvZNoREnOBv8EtGIDM4GElUvGCdJ1FYYT+S2ThZB71cRjM+oOn53w6tIQwgNuUHuZ9UMzOVBtcNKVJRUs93CXZs76qZdy7amNTyYEFkuqRjBEvA+dM8ucaxrJUUhrKfbXn2bIqHDeRJr7nldnNSqn3yT98uzJzz/YHZm4I0ZKtP3P3G7LfCmm2tGlKFiUBVsIKF+Z7WD9PHqZ55x7cHZrk3Y1T1j0l9LOIj0BHj5pjkH5ilAW0+kMU9+iyPD1AVO600U41EcPD9Iohw0pU13ooFn4Q+HDgXBHzV9Aufx8ORkw/5U/okjNxujhp07Xg/chbBXQBR1tzHgoj//XZoD5l/G+zrJVmOXCffRipV08PjbN9dDYjaFMmTsAcCBCNWd8yUGsEwOuv/cePuYSrALg3hh/tfaMZ0/H7C2gvS4XMHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
print(ciphertext_base64)
decrypted = decrypt_CBC_base64(key, ciphertext_base64, iv)
print(decrypted)
except Exception as err:
print("error: {0}".format(err))
except KeyboardInterrupt:
print("\n\n[*] (Ctrl-C) Detected, shutting down...")
exit()
Any solutions? Hope there's enough information.
The AESLib iterates over data in 16 byte chunks, encrypting one chunk of 16 bytes at a time. To decrypt the data in Python using the pycryptodome library, you would need to decrypt 16 bytes at a time and then concatenate all the decrypted data together.
I use this Python code to upload to PageBlob by manually chunking the file. It works. I translated that logic to C#. I am wondering is there a simpler solution built into Azure SKD? less hacky solution.
byte[] bytes = (encoding ?? Encoding.UTF8).GetBytes(content);
int fileSize = bytes.Length;
int blockSize = fileSize;
int boundary = blockSize % 512;
if (boundary != 0)
{
byte[] padding = Enumerable.Repeat((byte)0x0, 512 - boundary).ToArray();
bytes = bytes.Concat(padding).ToArray();
blockSize = blockSize + 512 - boundary;
}
// Allocate pages
cloudPageBlob.Create(
blockSize,
accessCondition,
options,
operationContext);
var CHUNK_MAX_SIZE = 4 * 1024 * 1024;
var count = (int)Math.Ceiling(1.0 * bytes.Length / CHUNK_MAX_SIZE);
int remaining = bytes.Length;
for (var i = 0; i < count; i++)
{
int chunkSize = Math.Min(CHUNK_MAX_SIZE, remaining);
cloudPageBlob.UploadFromByteArray(
bytes,
i,
chunkSize,
accessCondition,
options,
operationContext);
remaining -= chunkSize;
}
I tried in my system able to upload chunks and slightly modify your code , taken dummy bytes to testing purpose
using System;
using System.Linq;
using System.Text;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Auth;
using Microsoft.Azure.Storage.Blob;
namespace UploadPageChunck
{
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("blob uri");
Console.WriteLine("Hello World!");
AccessCondition data =null;
byte[] bytes= { 0x32, 0x00, 0x1E, 0x00 , 0x00 , 0x00 , 0x00 , 0x00 };
//byte[] bytes= { 0x32, 0x00, 0x1E, 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00, 0x00, 0x00, };
var storage = new StorageCredentials("testsnapshotex", "Storage key ”)
int fileSize = bytes.Length;
int blockSize = fileSize;
int boundary = blockSize % 512;
if (boundary != 0)
{
byte[] padding = Enumerable.Repeat((byte)0x0, 512 - boundary).ToArray();
bytes = bytes.Concat(padding).ToArray();
blockSize = blockSize + 512 - boundary;
}
// Allocate pages
CloudPageBlob cloudPageBlob = new CloudPageBlob(uri,storage);
//cloudPageBlob.Create(
// blockSize,
// null,
// null,
// null);
var CHUNK_MAX_SIZE = 4 * 1024 * 1024;
var count = (int)Math.Ceiling(1.0 * bytes.Length / CHUNK_MAX_SIZE);
int remaining = bytes.Length;
for (var i = 0; i < count; i++)
{
int chunkSize = Math.Min(CHUNK_MAX_SIZE, remaining);
cloudPageBlob.UploadFromByteArray(
bytes,
i,
chunkSize,
null,
data,
null);
remaining -= chunkSize;
}
}
}
}
OUTPUT
I have been working on the Azure Device Twins for the past month. The main goal is to subscribe to three device twin paths so that any change of the desired property results in a notification shown on the serial monitor and the desired property is parsed and given as a reported property. I am running the code on a NodeMCU and the issue I am facing is that the device connects to the IoTHub and publishes data but any changes in the device twin are not shown or parsed. The Device Twin works when api-version=2016-11-14 is added to the MQTT Username, as soon as I add the api-version in the username it gives the error failed rc=-1. The code I am using is given below
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <WiFiClientSecure.h>
const char mqtt_client[] = "test-device1";
const char mqtt_server[] = "HyperNet-IoTHub-Azure-Sphere.azure-devices.net";
const int mqtt_port = 8883;
const char mqtt_user[] = "HyperNet-IoTHub-Azure-Sphere.azure-devices.net/test-device1/api-version=2016-11-14";
void callback(char* topic, byte* payload, unsigned int length);
WiFiClientSecure WiFiclient;
PubSubClient client(WiFiclient);
void callback(char* topic, byte* payload, unsigned int length);
void parseDesiredProperties(byte* payload);
unsigned long previousMillis = 0;
long interval = 5000;
void reconnect();
void setup(){
Serial.begin(9600);
delay(250);
client.setServer(mqtt_server, 8883);
client.setCallback(callback);
WiFi.disconnect();
delay(100);
WiFi.mode(WIFI_STA);
Serial.println("Connecting To Wi-Fi: " + String(ssid));
WiFi.begin(ssid,pass);
while(WiFi.status() !=WL_CONNECTED){
delay(500);
Serial.print("..");
}
Serial.println(" ");
Serial.println("Wi-Fi Connected");
}
void loop(){
if(!client.connected()){
reconnect();
}
unsigned long currentMillis = millis();
if(currentMillis - previousMillis >= interval){
previousMillis = currentMillis;
float value1 = 12.3;
float value2 = 13.6;
String postData = "{\"testvalue1\":" + String(value1) + ",\"testvalue2\":" + String(value2) +"}";
char postBuffer[postData.length()+1];
postData.toCharArray(postBuffer, postData.length()+1);
Serial.println(postBuffer);
client.publish("devices/test-device1/messages/events/", postBuffer);
}
client.loop();
}
void reconnect(){
WiFiclient.setInsecure();
while(!client.connected()){
Serial.println("Attempting MQTT Connection");
if (client.connect(mqtt_client, mqtt_user, mqtt_pass)) {
Serial.println("connected");
client.subscribe("devices/test-device1/messages/devicebound/#");
// subscribe to operation responses
client.subscribe("$iothub/twin/res/#");
// subscribe to desired property updates
client.subscribe("$iothub/twin/PATCH/properties/desired/#");
}
else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("MQTT message arrived on topic: ");
Serial.println(topic);
if (String(topic).startsWith("$iothub/twin/PATCH/properties/desired")) {
parseDesiredProperties(payload);
}
}
void parseDesiredProperties(byte* payload) {
JsonObject& root = jsonDesiredProperties.parseObject(payload);
if(root.success()) {
Serial.println("Parsed desired properties");
int newMillis=root["reportInterval"];
if(newMillis > 2999 && newMillis < 120001) {
interval = newMillis;
String postProperty = "{\"reportInterval\":" + String(newMillis) + "}";
char postBuffer[postProperty.length()+1];
postProperty.toCharArray(postBuffer, postProperty.length()+1);
client.publish("$iothub/twin/PATCH/properties/reported/?$rid=1", postBuffer);
Serial.print("Set new interval to: ");
Serial.println(newMillis);
}
}
else {
Serial.println("Could not parse desired properties");
}
}
the code snippet you posted seems to be incomplete. It was missing the jsonDesiredProperties for instance. You might have left some other things out, but one important thing missing is the certificate fingerprint. IoT Hub only accepts secure MQTT connections, and to do that, you need to include the SHA1 fingerprint of Microsoft's CA cert.
You can download the certificate and put it in a file and retrieve the fingerprint with openssl:
openssl x509 -noout -fingerprint -sha1 -inform pem -in [certificate-file.crt]
Your setup method can then include the fingerprint:
static const char *fingerprint PROGMEM = "D4 DE 20 D0 5E 66 FC 53 FE 1A 50 88 2C 78 DB 28 52 CA E4 74";
void setup(){
Serial.begin(9600);
delay(250);
WiFiclient.setFingerprint(fingerprint);
client.setServer(mqtt_server, 8883);
client.setCallback(callback);
WiFi.disconnect();
delay(100);
WiFi.mode(WIFI_STA);
Serial.println("Connecting To Wi-Fi");
WiFi.begin("<REDACTED>","<REDACTED>");
while(WiFi.status() !=WL_CONNECTED){
delay(500);
Serial.print("..");
}
Serial.println(" ");
Serial.println("Wi-Fi Connected");
}
Another change I made is the API version you included in your MQTT username. You can use ?api-version=2018-06-30 rather than api-version=2016-11-14.
I tested these changes on my NodeMCU, and updating the reported twin properties is working just fine! Let me know if this works for you!
DynamicJsonBuffer jsonDesiredProperties;
char ssid[] = "Imran's Phone";
char pass[] = "1234567890";
int status = WL_IDLE_STATUS;
const char mqtt_client[] = "test-device1";
const char mqtt_server[] = "Hypernet-IOF.azure-devices.net";
const int mqtt_port = 8883;
const char mqtt_user[] = "HyperNet-IoF.azure-devices.net/test-device1/2018-06-30";
static const char *fingerprint PROGMEM = "D4 DE 20 D0 5E 66 FC 53 FE 1A 50 88 2C 78 DB
28 52 CA E4 74";
void callback(char* topic, byte* payload, unsigned int length);
WiFiClientSecure WiFiclient;
PubSubClient client(WiFiclient);
void callback(char* topic, byte* payload, unsigned int length);
void parseDesiredProperties(byte* payload);
unsigned long previousMillis = 0;
long interval = 5000;
void reconnect();
void setup(){
Serial.begin(9600);
delay(250);
WiFiclient.setFingerprint(fingerprint);
client.setServer(mqtt_server, 8883);
client.setCallback(callback);
WiFi.disconnect();
delay(100);
WiFi.mode(WIFI_STA);
Serial.println("Connecting To Wi-Fi: " + String(ssid));
WiFi.begin(ssid,pass);
while(WiFi.status() !=WL_CONNECTED){
delay(500);
Serial.print("..");
}
Serial.println(" ");
Serial.println("Wi-Fi Connected");
}
void loop(){
if(!client.connected()){
reconnect();
}
unsigned long currentMillis = millis();
if(currentMillis - previousMillis >= interval){
previousMillis = currentMillis;
float value1 = 12.3;
float value2 = 13.6;
String postData = "{\"testvalue1\":" + String(value1) + ",\"testvalue2\":" +
String(value2) +"}";
char postBuffer[postData.length()+1];
postData.toCharArray(postBuffer, postData.length()+1);
Serial.println(postBuffer);
client.publish("devices/test-device1/messages/events/", postBuffer);
}
client.loop();
}
void reconnect(){
WiFiclient.setInsecure();
while(!client.connected()){
Serial.println("Attempting MQTT Connection");
if (client.connect(mqtt_client, mqtt_user, mqtt_pass)) {
Serial.println("connected");
client.subscribe("devices/test-device1/messages/devicebound/#");
// subscribe to operation responses
client.subscribe("$iothub/twin/res/#");
// subscribe to desired property updates
client.subscribe("$iothub/twin/PATCH/properties/desired/#");
}
else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}
void callback(char* topic, byte* payload, unsigned int length) {
Serial.print("MQTT message arrived on topic: ");
Serial.println(topic);
if (String(topic).startsWith("$iothub/twin/PATCH/properties/desired")) {
parseDesiredProperties(payload);
}
}
void parseDesiredProperties(byte* payload) {
JsonObject& root = jsonDesiredProperties.parseObject(payload);
if(root.success()) {
Serial.println("Parsed desired properties");
int newMillis=root["reportInterval"];
if(newMillis > 2999 && newMillis < 120001) {
interval = newMillis;
String postProperty = "{\"reportInterval\":" + String(newMillis) + "}";
char postBuffer[postProperty.length()+1];
postProperty.toCharArray(postBuffer, postProperty.length()+1);
client.publish("$iothub/twin/PATCH/properties/reported/?$rid=1", postBuffer);
Serial.print("Set new interval to: ");
Serial.println(newMillis);
}
}
else {
Serial.println("Could not parse desired properties");
}
}
I'm trying to interact with an API which takes GET and POST requests which include a signature derived from a message and my private key, hashed using HMAC SHA512. The docs give an example:
key (in base-64):
bEDtDJnW0y/Ll4YZitxb+D5sTNnEpQKH67EJRCmQCqN9cvGiB8+IHzB7HjsOs3mSlxLmu4aiPDRpe9anuWzylw==
message:
/account/balance1515058794242
should produce the following (base-64) signature:
NjqZ8Mgdkj6hrtY/xdKBy1S0kLjU2tA7G+pR2TdOBF45b7+evfpzGH/C/PiNHEDvuiRChRBlRo3AGJ7Gcvlwqw==
The docs also state that the key needs to be decoded from base-64 before using in the hmac, but is unclear about what format it should be in.
I've been playing around with online hmac generators and can replicate the example signature with no issue. For example, at https://www.liavaag.org/English/SHA-Generator/HMAC/ - input the above key as input type = Base-64, the above message string as input type = TEXT, and output type = base-64, and the output signature is the same as above. It also works fine when I give the key as HEX-type and use the hex equivalent:
6C40ED0C99D6D32FCB9786198ADC5BF83E6C4CD9C4A50287EBB1094429900AA37D72F1A207CF881F307B1E3B0EB379929712E6BB86A23C34697BD6A7B96CF297
BUT I cannot replicate the example signature using my own program using BCrypt. It seems that the BCrypt hmac is interpreting my key as 'TEXT'-type input in the same way as the online generator. That is, when I give the key as the hex string:
CONST BYTE key[] = { "6C40ED0C99D6D32FCB9786198ADC5BF83E6C4CD9C4A50287EBB1094429900AA37D72F1A207CF881F307B1E3B0EB379929712E6BB86A23C34697BD6A7B96CF297" };
I get an output signature (in hex) of:
16ab16ed3874fab51dbda66155edf269883d128de6067d77762dcee4129f1612b36fc556df10beb358c81262d034efe4c50d68d89ac43606df4318a8af56b
On the online generator I get the same output when I use that hex string (6C40...) as TEXT type key and ouput as HEX.
Is there any way I can force BCrypt to interpret my key as hex? I've even tried declaring the key as hex literals, i.e:
CONST BYTE key[] = { 0x6C, 0x40, 0xed, 0x0c, 0x99, 0xd6, 0xd3, 0x2f,
0xCB, 0x97, 0x86, 0x19, 0x8a, 0xdc, 0x5b, 0xf8,
0x3E, 0x6c, 0x4c, 0xd9, 0xc4, 0xa5, 0x02, 0x87,
0xeb, 0xb1, 0x09, 0x44, 0x29, 0x90, 0x0a, 0xa3,
0x7d, 0x72, 0xf1, 0xa2, 0x07, 0xcf, 0x88, 0x1f,
0x30, 0x7b, 0x1e, 0x3b, 0x0e, 0xb3, 0x79, 0x92,
0x97, 0x12, 0xe6, 0xbb, 0x86, 0xa2, 0x3c, 0x34,
0x69, 0x7b, 0xd6, 0xa7, 0xb9, 0x6c, 0xf2, 0x97 };
But this gives another, different signature. At least the hex string key is getting something sort of like replicating the online converter. Sorry for any confusion around why I'm using hex instead of base-64, which is what I'll eventually need to use - it's just an extra complicating step at the moment, so for now I'm just trying to get the hex equivalent working, and then I can concentrate on encoding it as base-64. The hex equivalent of the base-64 signature I'm trying to get is:
363a99f0c81d923ea1aed63fc5d281cb54b490b8d4dad03b1bea51d9374e045e396fbf9ebdfa73187fc2fcf88d1c40efba2442851065468dc0189ec672f970ab
Full code I'm using is below:
#include <windows.h>
#include <stdio.h>
#include <bcrypt.h>
#pragma comment(lib, "bcrypt.lib")
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
#include <fstream>
int hexToInt(char ch)
{
return 0;
}
void __cdecl wmain(
int argc,
__in_ecount(argc) LPWSTR *wargv)
{
std::wofstream fout;
fout.open("signature.txt");
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_HASH_HANDLE hHash = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
DWORD cbData = 0,
cbHash = 0,
cbHashObject = 0;
PBYTE pbHashObject = NULL;
PBYTE pbHash = NULL;
CONST BYTE message[] = { "/account/balance1515058794242" };
CONST BYTE key[] = { "6C40ED0C99D6D32FCB9786198ADC5BF83E6C4CD9C4A50287EBB1094429900AA37D72F1A207CF881F307B1E3B0EB379929712E6BB86A23C34697BD6A7B96CF297" };
//open an algorithm handle
if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(
&hAlg,
BCRYPT_SHA512_ALGORITHM,
NULL,
BCRYPT_ALG_HANDLE_HMAC_FLAG)))
{
fout << "**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n" << status;
goto Cleanup;
}
//calculate the size of the buffer to hold the hash object
if (!NT_SUCCESS(status = BCryptGetProperty(
hAlg,
BCRYPT_OBJECT_LENGTH,
(PBYTE)&cbHashObject,
sizeof(DWORD),
&cbData,
0)))
{
fout << "**** Error 0x%x returned by BCryptGetProperty\n" << status;
goto Cleanup;
}
//allocate the hash object on the heap
pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHashObject);
if (NULL == pbHashObject)
{
fout << "**** memory allocation failed\n";
goto Cleanup;
}
//calculate the length of the hash
if (!NT_SUCCESS(status = BCryptGetProperty(
hAlg,
BCRYPT_HASH_LENGTH,
(PBYTE)&cbHash,
sizeof(DWORD),
&cbData,
0)))
{
fout << "**** Error 0x%x returned by BCryptGetProperty\n" << status;
goto Cleanup;
}
//allocate the hash buffer on the heap
pbHash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHash);
if (NULL == pbHash)
{
fout << "**** memory allocation failed\n";
goto Cleanup;
}
//create a hash
if (!NT_SUCCESS(status = BCryptCreateHash(
hAlg,
&hHash,
pbHashObject,
cbHashObject,
(PBYTE)key,
sizeof(key) - 1,
0)))
{
fout << "**** Error 0x%x returned by BCryptCreateHash\n" << status;
goto Cleanup;
}
//hash some data
if (!NT_SUCCESS(status = BCryptHashData(
hHash,
(PBYTE)message,
sizeof(message) - 1,
0)))
{
fout << "**** Error 0x%x returned by BCryptHashData\n" << status;
goto Cleanup;
}
//close the hash
if (!NT_SUCCESS(status = BCryptFinishHash(
hHash,
pbHash,
cbHash,
0)))
{
fout << "**** Error 0x%x returned by BCryptFinishHash\n" << status;
goto Cleanup;
}
fout << "\nThe hash is: \n";
for (DWORD i = 0; i < cbHash; i++)
{
fout << std::hex << pbHash[i];
}
Cleanup:
if (hAlg)
{
BCryptCloseAlgorithmProvider(hAlg, 0);
}
if (hHash)
{
BCryptDestroyHash(hHash);
}
if (pbHashObject)
{
HeapFree(GetProcessHeap(), 0, pbHashObject);
}
if (pbHash)
{
HeapFree(GetProcessHeap(), 0, pbHash);
}
fout.close();
};
You need to use the CONST BYTE key[] = { 0x6C, ... }; method of specifying your key. Keys and input data are binary data, represented as byte arrays in most programming languages. Hexadecimals and base 64 are two ways of representing or encoding binary values so that they can be used as printable text.
However, if you use a byte array then sizeof will return the number of actual bytes. Arrays aren't null terminated, so there is no reason to remove the final null byte. So sizeof(key) - 1 will remove the last byte of the key as input parameter, rather than remove the non-existent null byte.
The message string is null terminated, so it should work well. You may however explicitly want to mention that the message needs to be US-ASCII or explicitly encode the string (e.g. to UTF-8).
Hi i am writing a script to personalize java card using smartcardio API.
Following is the java code
TerminalFactory tf = TerminalFactory.getDefault();
List<CardTerminal> terminals = tf.terminals().list();
CardTerminal cardTerminal1 = (CardTerminal) terminals.get(0);
Card connection1 = cardTerminal1.connect("T=0");
CardChannel channel = connection1.getBasicChannel();
byte[] apdu = new byte[] { (byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, (byte) 0x07, (byte) 0xA0,(byte) 0x00, (byte) 0x00, (byte) 0x01,(byte) 0x51,(byte) 0x00,(byte) 0x00};
String hex1 = DatatypeConverter.printHexBinary(apdu);
System.out.println("Select Request : " + hex1 + "\n");
ResponseAPDU respApdu = channel.transmit(new CommandAPDU(apdu));
byte[] resp1 = respApdu.getBytes();
hex1 = DatatypeConverter.printHexBinary(resp1);
System.out.println("Select Response : " + hex1 + "\n");
apdu = new byte[] { (byte) 0x80, (byte) 0xCA, (byte) 0x9F, (byte) 0x7F, (byte) 0x2A};
hex1 = DatatypeConverter.printHexBinary(apdu);
System.out.println("Get Request with CLA 80 : " + hex1 + "\n");
respApdu = channel.transmit(new CommandAPDU(apdu));
resp1 = respApdu.getBytes();
hex1 = DatatypeConverter.printHexBinary(resp1);
System.out.println("Get Response with CLA: " + hex1 + "\n");
apdu = new byte[] { (byte) 0x00, (byte) 0xCA, (byte) 0x9F, (byte) 0x7F, (byte) 0x2A};
hex1 = DatatypeConverter.printHexBinary(apdu);
System.out.println("Get Request with CLA 00 : " + hex1 + "\n");
respApdu = channel.transmit(new CommandAPDU(apdu));
resp1 = respApdu.getBytes();
hex1 = DatatypeConverter.printHexBinary(resp1);
System.out.println("Get Response with CLA 00: " + hex1 + "\n");
apdu = new byte[] { (byte) 0x80, (byte) 0x50, (byte) 0x00, (byte) 0x00, (byte) 0x08,(byte) 0xEC, (byte) 0xB9, (byte) 0x27, (byte) 0x11, (byte) 0xDF, (byte) 0x0F, (byte) 0x61, (byte) 0x79};
hex1 = DatatypeConverter.printHexBinary(apdu);
System.out.println("Initialize Request Command : " + hex1 + "\n");
respApdu = channel.transmit(new CommandAPDU(apdu));
resp1 = respApdu.getBytes();
hex1 = DatatypeConverter.printHexBinary(resp1);
System.out.println("Initialize Response : " + hex1 + "\n");
Following is output
Select Request : 00A4040007A0000001510000
Select Response : 6F5B8407A0000001510000A550734A06072A864886FC6B01600C060A2A864886FC6B02020101630906072A864886FC6B03640B06092A864886FC6B040215650B06092B8510864864020103660C060A2B060104012A026E01029F6501FF9000
Get Request with CLA 80 : 80CA9F7F2A
Get Response with CLA: 9F7F2A53430019492170676001E7702B05718231581144729111447291114472912A0D000000000000006D00
Get Request with CLA 00 : 00CA9F7F2A
Get Response with CLA 00: 53430019492170676001E7702B05718231581144729111447291114472912A0D000000000000000000009000
Initialize Request Command : 8050000008ECB92711DF0F6179
Initialize Response : 6D00
If i send a command with CLA 00 i get proper response 90 00 otherwise card is returning 6D 00.
If any one has idea just let me know how can i resolve this issue using smartcardio API?
This has nothing to do with smartcardio, 6D00 means that the command you are sending is not recognized by the card. Check your card/applet manual for correct coding of the command APDU.