2

I try to generate a sha256 HMAC using a base64-decoded secret key on a message. I would like to use the dart language. In python, I could do it with the following code:

# PYTHON CODE
import hmac, hashlib, base64
...
message = 'blabla'
secret = 'DfeRt[...]=='
secret_b64 = base64.b64decode(secret)
signature = hmac.new(secret_b64, message, hashlib.sha256)
signature_b64 = signature.digest().encode('base64').rstrip('\n')

Here is what I tried with dart:

// DART CODE
import 'package:crypto/crypto.dart';
import 'dart:convert';
...
String message = 'blabla';
String secret = 'DfeRt[...]=='
var secret_b64 = BASE64.decode(secret);
var hmac = new Hmac(sha256, secret_b64);
// what now?

But then I don't know how to go on. I found some old example code which looks like the following

var message_byte = UTF8.encode(message);
hmac.add(message_byte);

However, the method "add" does not exist any more in the Hmac class. I also tried this, but I am not sure if this is correct

var message_byte = UTF8.encode(message);    
var signature = hmac.convert(message_byte);
var signature_b64 = BASE64.encode(signature.bytes);

Can someone help me out?

thyme
  • 388
  • 5
  • 18
  • Read the documentation, finding old example code is not the same. – zaph Apr 14 '18 at 13:52
  • That is true, but reading the documentation only helps if you know what you are looking for. I think I also have some trouble with the concept of hmac and sha256. This is why I somehow got lost. For example, I dont really understand which of the new methods of the Hmac class replaced the old "add" method. This is why I wrote this question. – thyme Apr 14 '18 at 14:27
  • The best coding advice I ever received was from Rick one night in the computer center. I asked Rick about how to use a system command and he told me to read the manual, I did that that changed my developer life. Sometimes is is necessary to spend days (at 8 hours per day) studying, in those things you do not understand, you case such as `HAMC` and `SHA-256` and `Dart`. As you study more you will learn what you are looking for. – zaph Apr 14 '18 at 14:40
  • 1
    Yes, but studying with examples and guidance from experts is definitly more efficient than reading theory. In particular, if you are just playing a bit. Of course, it is extremely nice to read concepts and study things deeply, if you already know how the wind blows. And then, when you are an expert, one uses to say: rtfm. But thanks for your wisdom. – thyme Apr 14 '18 at 14:48

3 Answers3

11

If you have the whole 'message' available then just call convert(). If the message is large or in pieces then deal with it in chunks.

Your example is simple, when spelled out step by step.

  String base64Key = 'DfeRt...';
  String message = 'blabla';

  List<int> messageBytes = utf8.encode(message);
  List<int> key = base64.decode(base64Key);
  Hmac hmac = new Hmac(sha256, key);
  Digest digest = hmac.convert(messageBytes);

  String base64Mac = base64.encode(digest.bytes);

Please read the Effective Dart guide. Note how constants are now lower case, variables in Dart use camel case, etc

Richard Heap
  • 48,344
  • 9
  • 130
  • 112
  • Thanks a lot for your answer. Why does the method 'convert' needs an argument of type List instead of String? To make it more general, such that any kind of data can be signed? – thyme Apr 16 '18 at 06:39
  • 1
    Yes, hashes and MACs work on arrays of bytes, so Strings need to be converted to arrays of bytes first using an encoding. UTF-8 is probably the most popular but there are many others like UTF-16 (with both endians) or ISO 8859-1. Obviously the receiver of your message has to be using the same encoding for your MACs to match! – Richard Heap Apr 16 '18 at 13:49
1

I had to sign a request with hmac for calling an API in my recent project. Here is what I had done. Hope this helps you too.

import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:crypto/crypto.dart';

String getHmacAuthHeader({
  @required final String inputUrl,
  @required final dynamic inputJsonContent,
  @required final String appId,
  @required final String appSecrets,
  final String method = "POST",
}) {
  final url = _encodeUrl(inputUrl);
  final seconds =
      (DateTime.now().millisecondsSinceEpoch / 1000).round().toString();
  final nonce = "N${DateTime.now().millisecondsSinceEpoch}";

  final contentHash = _getMd5HashInBase64FromJson(inputJsonContent);

  final signature = "$appId$method$url$seconds$nonce$contentHash";

  final signatureHmacHashBase64 = _getHmacHashInBase64FromString(appSecrets, signature);

  final token = "$appId:$signatureHmacHashBase64:$nonce:$seconds";

  return "hmacauth $token";
}

String _encodeUrl(String url) {
  if (!url.startsWith("/")) {
    url = "/$url";
  }
  return Uri.encodeComponent(url).toLowerCase();
}

String _getMd5HashInBase64FromJson(dynamic json) {
  final jsonString = jsonEncode(json);
  final jsonStringBytes = Utf8Encoder().convert(jsonString);

  final hashBytes = md5.convert(jsonStringBytes).bytes;
  final hashBase64 = base64Encode(hashBytes);
  return hashBase64;
}

String _getHmacHashInBase64FromString(String key, String data){
  final keyBytes = Utf8Encoder().convert(key);
  final dataBytes = Utf8Encoder().convert(data);

  final hmacBytes = Hmac(sha256, keyBytes)
      .convert(dataBytes)
      .bytes;

  final hmacBase64 = base64Encode(hmacBytes);
  return hmacBase64;
}
Kamran Bashir
  • 632
  • 6
  • 12
0

Hey I'm late to Answer this question. But I think anyone can use this Answer. I use https://pub.dev/packages/crypto package

For that you can use

import 'dart:convert';
import 'package:crypto/crypto.dart';


message = 'blabla'
secret = 'DfeRt[...]=='

void main() {
  var key = utf8.encode(secret);
  var bytes = utf8.encode(message);

  var hmacSha256 = Hmac(sha256, key); // HMAC-SHA256
  var digest = hmacSha256.convert(bytes);

  print("HMAC digest as bytes: ${digest.bytes}");
  print("HMAC digest as hex string: $digest");
}

I think this will save codes and clean.

Kasun Hasanga
  • 1,626
  • 4
  • 15
  • 35