How to decrypt AES 256 CBC with Dart

1,370

The generateBase64Key or generateKey methods of the PBKDF2 package expect a string for the salt. In the implementation of these methods, the salt is UTF8 encoded.
Typically, a salt is randomly generated and therefore contains byte sequences that are incompatible with the UTF8 encoding. It is not known how the salt used here was generated. However, it contains (like a randomly generated salt) byte sequences that are incompatible with the UTF8 encoding. For this reason the PBKDF2 package applied is unsuitable for key derivation in this case. In my opinion, the choice of the string type for a salt is a poor design.

The cryptography package, on the other hand, provides a Pbkdf2 implementation that processes the salt as a Uint8List and is thus suitable. This library also supports AES/CBC, so it makes sense to use this library for the decryption as well.

The following implementation decrypts the posted ciphertext:

var encryptedResopnse = 'dBluiiVaHxhRcWJPaEip9kCGXDwufk3mFp8Xe9ioh9UKu6xL+CHUZrKvuf3xI7P1vFpvyyJ2Vz2Q3ieLEuRHk7NOinZU82FNdE3SOc9D2JTUFkif5ye3rVfQ7O39DpBnV41CduEP0OsASA8cr/RChqhulVHsaw6oUP0mg79M3Jlnpbab0EqlWRQx3k85rcajmov4cYLmsja++p2Lyw/BgOTKDf/yw3NWiK73Ot4P3C6urUiFNUCQTaOHCas1Sa8Wl0udQo1viyApuCE9+Ll1SGnUu26uNy5RR55IFLVnAHuIOBDePjdAw3DapAtLFnSd+FrVjYcUuevMMliSy3PHiZU66qdyx8YSn13tYH6KGFxC/kvPsi5dLGorQ1TdNR5fxZGRPNQXEEIwWYSiF8LA0AJzVqpRoXs9PkEseCUnH1Sj5sBQgXQc0RA8vHWf3n2X/cABLEWaRHHlBlZjqjJXl0uKSgAWC3JoelABGSuSCvL3GJhn9SuSV6+jCOftb6UCmw7LzalKB7UNIQPJ1vMtKl3+38RKDwp7a4xpdlln+IPp+R2aGuobuhk9ySSJYN3GCn7MoC/uaCAR0aEYsIHP1BQ+UgOPOsQFZEVdKMrFLJsJ3HtQ1fQxqpPQ13TClWCOyZu+w+1q4W+8CBJuI4l4Em+91aMT4xm7FWB/RhmUN8hfsHk7EATW8CkRGF4zFGKKdeN9zzGM0ViZYv30PARg8W2SJRKoZkaMOgZXtE/8D9fWzrmNDdHBCbMt0yrGycBbn8/b3JLQkcqxzY6VnWrBR1VJ66OB1mH5i6ejDrkxLx5VvkdKf3fcoKEZ/FptZK4zUwXgHJIF/YLChsYUj2mX9Ox18ZZi9vBG9L5vONc0GuQ31FzjwG77yGrJrS4mVi76uaifu7Thd6TiYXuu7OaFBl9+lPMvfHf+wWRqLQbgDoVtOXvND5e4LncWPHZbEjHGwO9I/MnVjMnH6nSbKER63Na8XBUIwsSlJwrswa3fLNInJA1/qGBb9nrVNzKLRfvku1UPvavDP1WxsTEzg0gH8Ui6KzBoBOd9IK/7ZtmSmSug5Ig8GAZ0R/kR7DnSs4ekxKxmcCJ95YVyf9fx0Vlw2oB9iOoUaHM3OITeldfMtoM=';
var secret = 'abcd123';
var decrypt = extractPayload(encryptedResopnse, secret);
print(decrypt);

with

import 'dart:convert' as ConvertPack;
import 'package:cryptography/cryptography.dart' as CryptographyPack;
...
String extractPayload(String encryptedResopnse, String secret)  {
    if (encryptedResopnse == null) {
      return '';
    }

    // Separate data
    // Note: Authentication not considered (separated: size = 1; see ciphertext and PHP code (hmac derived but unused))
    var separated = encryptedResopnse.split(':');
    var data = ConvertPack.base64Decode(separated[0].trim());
    var iv = data.sublist(0, 16);
    var salt = data.sublist(16, 32);
    var cipherText = data.sublist(32);

    // Derive key
    var generator = CryptographyPack.Pbkdf2(
        macAlgorithm: CryptographyPack.Hmac(CryptographyPack.sha1),
        iterations: 2048,
        bits: 256,
    );
    var hash = generator.deriveBitsSync(
        ConvertPack.utf8.encode(secret),
        nonce: CryptographyPack.Nonce(salt)
    );

    // Decrypt
    var decrypted = CryptographyPack.aesCbc.decryptSync(
        cipherText,
        secretKey: CryptographyPack.SecretKey(hash),
        nonce: CryptographyPack.Nonce(iv));
    var plaintext = ConvertPack.utf8.decode(decrypted);

    return plaintext;
}

and returns as result:

{"nofollow":false,"id":"2226521","title":"When You Say Nothing At All","album":"Ronan","albumID":"237798","artist":"Ronan Keating","artistID":"52715","track":"6","year":"1999","duration":"258.00","coverArt":"323816","ArtistArt":1002340723,"allowoffline":1,"genre":"Pop","AlbumArt":"323816","keywords":["When You Say Nothing At All","Ronan Keating","Ronan"],"languageid":2,"bitrates":"24,256","hexcolor":"#b43931","cleardetails":1,"bitrate":64,"size":"2108905","releasedate":"1999-01-01","explicit":"0","extras":"eyJyZXF1ZXN0dHlwZWlkIjo...","saveprogress":0,"lyrics":"true","is_podcast":0,"is_original":1,"location":"https:\\\/\\\/some audio file\u2019s URL","debugurl":"http:\\\/\\\/some URL","debugurldata":"http:\\\/\\\/some URL","hash":"b1229af8b0078e0b9ec9e203e3b32b7c","plays":"593963","likes":"13705"}

The cryptography package must be referenced in the pubspec.yaml in the dependencies section, here:

cryptography: ^1.4.1
Share:
1,370
Yasin ilhan
Author by

Yasin ilhan

Updated on December 26, 2022

Comments

  • Yasin ilhan
    Yasin ilhan over 1 year

    I want to convert below PHP script to dart i tried a lot case but nothing help me.

    I have tried following code; But throw an exception here encrypter.decrypt method.

    import 'package:encrypt/encrypt.dart' as EncryptPack;
     import 'package:crypto/crypto.dart' as CryptoPack;
     import 'dart:convert' as ConvertPack;
    void main(List<String> arguments) {
      var decrypt = extractPayload('$encryptedResopnse');
      print(decrypt);
    }
    String extractPayload(String encryptedResopnse) {
      if (encryptedResopnse == null) {
        return '';
      }
      var separated = encryptedResopnse.split(':');
      var secret = 'abcd123';
      var data = ConvertPack.base64Decode(separated[0].trim());
      var iv = CryptoPack.sha256.convert(data).toString().substring(0, 16);
      var salt = CryptoPack.sha256.convert(data).toString().substring(16, 32);
      var cipherText = CryptoPack.sha256.convert(data).toString().substring(64);
      print('cipherText : ${cipherText}');
      var ivObj = EncryptPack.IV.fromBase64(iv);
      var generator = PBKDF2(hashAlgorithm: CryptoPack.sha1);
      var hash = generator.generateBase64Key(secret, salt, 2048, 32);
      print('hash : $hash');
      var keyObj = EncryptPack.Key.fromBase64(hash);
      final encrypter = EncryptPack.Encrypter(
          EncryptPack.AES(keyObj, mode: EncryptPack.AESMode.cbc)); // Apply CBC mode
      print(cipherText);
      var firstBase64Decoding = cipherText; // First Base64 decoding
      print(firstBase64Decoding);
      final decrypted = encrypter.decrypt(
          EncryptPack.Encrypted.fromBase64(firstBase64Decoding),
          iv: ivObj);
      return decrypted;
    }
    

    for demo content ;

    Initialize the aes_secret

    $aes_secret = '123456ac';

    Demo content;

    $encryptedResopnse = "dBluiiVaHxhRcWJPaEip9kCGXDwufk3mFp8Xe9ioh9UKu6xL+CHUZrKvuf3xI7P1vFpvyyJ2Vz2Q3ieLEuRHk7NOinZU82FNdE3SOc9D2JTUFkif5ye3rVfQ7O39DpBnV41CduEP0OsASA8cr/RChqhulVHsaw6oUP0mg79M3Jlnpbab0EqlWRQx3k85rcajmov4cYLmsja++p2Lyw/BgOTKDf/yw3NWiK73Ot4P3C6urUiFNUCQTaOHCas1Sa8Wl0udQo1viyApuCE9+Ll1SGnUu26uNy5RR55IFLVnAHuIOBDePjdAw3DapAtLFnSd+FrVjYcUuevMMliSy3PHiZU66qdyx8YSn13tYH6KGFxC/kvPsi5dLGorQ1TdNR5fxZGRPNQXEEIwWYSiF8LA0AJzVqpRoXs9PkEseCUnH1Sj5sBQgXQc0RA8vHWf3n2X/cABLEWaRHHlBlZjqjJXl0uKSgAWC3JoelABGSuSCvL3GJhn9SuSV6+jCOftb6UCmw7LzalKB7UNIQPJ1vMtKl3+38RKDwp7a4xpdlln+IPp+R2aGuobuhk9ySSJYN3GCn7MoC/uaCAR0aEYsIHP1BQ+UgOPOsQFZEVdKMrFLJsJ3HtQ1fQxqpPQ13TClWCOyZu+w+1q4W+8CBJuI4l4Em+91";

    class AesEncryption {
      private static $encryptionMethod = 'aes-256-cbc';
      private static $blockSize = 16;
      private static $keySize = 32; // in bytes - so 256 bit for aes-256
      private static $iterations = 2048;
    
      public static function sign($data, $key) {
        return hash_hmac('sha256', $data, $key);
      }
    
      /**
       * @param string $encryptedContent
       * @param string $secret
       * @return string
       */
      public static function decrypt(string $encryptedContent, string $secret) {
        if (!$encryptedContent) {
          return "";
        }
    
        // Separate payload from potential hmac
        $separated = explode(":", trim($encryptedContent));
      
        // Extract HMAC if signed
        $hmac = (isset($separated[1])) ? $separated[1] : null;
    
        // Convert data-string to array
        $data = base64_decode($separated[0]);
    
        // Then we remove the iv and salt to fetch the original text
        $iv = substr($data, 0, self::$blockSize);
    
        //echo($iv);
    
    
        $salt = substr($data, self::$blockSize, self::$blockSize);
    
        // We finally extract  the ciphertext
        $cipherText = substr($data, self::$blockSize * 2);
    
        // Generate Key
        $key = hash_pbkdf2('sha1', $secret, $salt, self::$iterations, self::$keySize, true);
        
        
    
        
        // Check https://www.php.net/manual/en/function.openssl-decrypt.php
        return openssl_decrypt($cipherText, self::$encryptionMethod, $key, OPENSSL_RAW_DATA, $iv);
      }
    
    }
    
    • Topaco
      Topaco over 3 years
      SO is not a code writing service. Please post your most recent dart code and describe your problems.
    • Yasin ilhan
      Yasin ilhan over 3 years
      @Topaco i shared the code.