Получение MD5-хеша строки или файла в Dart

MD5 (Message Digest 5) — 128-битный алгоритм хеширования, позволяющий вычислять уникальные "отпечатки" фиксированной длины для данных произвольного объема, и обладающий свойством "лавинного эффекта" (незначительные изменения исходных данных приводят к существенным и непредсказуемым изменениям в хеше).

Алгоритм MD5 - создан профессором Рональдом Л. Ривестом из Массачусетского технологического института в 1991 году, и долгое время являлся одним из самых распространенных методов для проверки целостности файлов, хранения цифровых подписей и паролей. Но в конце 2008 года отдел US-CERT, входящий в одну из структур министерства внутренней безопасности США, призвал разработчиков программного обеспечения, владельцев веб-сайтов и пользователей прекратить использовать MD5 в любых целях, так как исследования продемонстрировали ненадёжность этого алгоритма.

Чтобы вычислить MD5-хеш для собственного набора данных, можно просто воспользоваться классом MD5, входящим в состав библиотеки Dart API - "dart:crypto". Алгоритм действий достаточно простой - сначала, необходимо получить экземпляр класса с помощью стандартного конструктора:

MD5 md5 = new MD5();

потом задать данные, для которых нужно вычислить хеш, с помощью метода MD5.add()

md5.add([1, 2, 3, 4, 5]);

и на конец, получить хеш с помощью метода MD5.close()

md5.close();

Пример вычисления MD5-хеша для строки

Получить MD5-хеш для произвольной строки достаточно просто необходимо передать строку в виде массива байт (с помощью свойства String.charCodes в объект класса MD5 и рассчитать хеш.

// подключаем криптографическую библиотеку
import "dart:crypto";

// определяем функцию для получения md5-хеша строки
String md5(String string) {
  // получим экземпляр класса MD5
  MD5 hash = new MD5();
  
  // добавляем данные строки
  hash.add(source.codeUnits);

  // вернем MD5-хеш, в качестве строки с шестнадцатеричными символами, 
  // с помощью вспомогательной функции CryptoUtils.bytesToHex
  return CryptoUtils.bytesToHex(hash.close());
}

void main() {
  // протестируем использование нашей функции
  print( md5("") );
}

Пример вычисления MD5-хеша для файла

Получить MD5-хеш для файла не на много сложнее чем для простой строки, единственно, что дополнительно необходимо - это прочитать данные из файла. Сделать это можно множеством разных способов, здесь же мы остановимся на двух - последовательное чтение данных из файла с помощью метода File.readAsBytesSync и асинхронное чтение с помощью потоков File.openInputStream.

Для простоты здесь мы так же пропустим шаги по обработке ошибок ввода-вывода, не существования файла и т.д.

Более подробно про способы работы с файлами можно посмотреть в документации класса File, а так же в статье "Как прочитать текстовый файл в Dart?"

Получение сигнатуры MD5 для файла при синхронном чтении

// подключаем криптографическую библиотеку
import "dart:crypto";
// подключаем библиотеку ввода-вывода
import "dart:io";

// объявим функцию для последовательного получения MD5-хеша из файла
String md5_sync(File source) {
  // получим экземпляр класса MD5
  MD5 hash = new MD5();

  // синхронно прочитаем все содержимое файла в массив, с помощью метода
  // readAsBytesSync
  List<int> fileContent = source.readAsBytesSync();
	
  // добавим наши данные к буферу для расчета сигнатуры MD5
  hash.add(fileContent);
	
  // получим MD5 "отпечаток"
  List<int> result = hash.close();
	
  // и выведем на печать хеш в качестве строки с шестнадцатеричными символами, 
  // с помощью вспомогательной функции CryptoUtils.bytesToHex
  print (CryptoUtils.bytesToHex(result));
}

void main() {
  // протестируем использование нашей функции
  md5_sync(new File('/test1'));
}

Получение сигнатуры MD5 для файла при асинхронном чтении

// подключаем криптографическую библиотеку
import "dart:crypto";
// подключаем библиотеку ввода-вывода
import "dart:io";

void md5_async(File source) {
  // получим экземпляр класса MD5
  MD5 hash = new MD5();

  // открываем поток для чтения
  InputStream inputStream = source.openInputStream();

  // определяем callback-функцию обработчик, которая будет вызываться при
  // получении новой порции данных из файла
  inputStream.onData = () {
    // при получении новых данных из файла будем добавлять их к буферу расчета MD5
    hash.add(inputStream.read());
  };

  // определяем callback-функцию обработчик, которая будет вызываться при
  // завершении чтения из файла
  inputStream.onClosed = () {
    // когда все данные прочитаны из файла, получим MD5 "отпечаток"
    List<int> result = hash.close();
		
    // и выведем на печать хеш в качестве строки с шестнадцатеричными символами, 
    // с помощью вспомогательной функции CryptoUtils.bytesToHex
    print (CryptoUtils.bytesToHex(result));
  };
}

void main() {
  // протестируем использование нашей функции
  md5_async(new File('/test2'));
}