Escaping From "/" Character in Dart Encryption - flutter

I have an encryption class in Dart, which will encrypt User ID for posting it to MariaDB table with REST (Post Request, ? and * is placeholder, these are different in my real code.):
import 'package:encrypt/encrypt.dart';
//import 'dart:convert' as convert;
class Security {
///Key
static final _key =
Key.fromUtf8('********************************'); //32 chars
static final iv = IV.fromUtf8('????????????????'); //16 chars
///encrypt, [text] is data.
static String encryptMyData(String text) {
final e = Encrypter(AES(_key, mode: AESMode.cbc));
final encryptedData = e.encrypt(text, iv: iv);
return encryptedData.base64;
}
///decrypt, [text] is data.
static String decryptMyData(String text) {
final e = Encrypter(AES(_key, mode: AESMode.cbc));
final decryptedData = e.decrypt(Encrypted.fromBase64(text), iv: iv);
return decryptedData;
}
}
Sometimes, "/" char coming in encrypted ID, which causes 404 and 405 errors on POST request. Because "/" is HTML routing character.
My POST code is:
///Adds an user item to database via decoding / encoding the JSON value.
///[name] is name, [surname] is surname, [uid] is user id, [type] is user role, [debt] is user debt, [paucode] is pay code.
static Future<bool> addUserToDatabase(String name, String surname, String uid,
double debt, String paycode, int type) async {
try {
if (name != null &&
surname != null &&
uid != null &&
debt != null &&
paycode != null &&
type != null) {
var addingUser = User(name.toLowerCase(), surname.toLowerCase(),
Security.encryptMyData(uid), type, debt, paycode);
print('This user will be added:\n------\n$addingUser\n-----------\n');
var uri = Uri.https('localhost:5001',
'User/username=${addingUser.userName}&surname=${addingUser.userSurname}&id=${addingUser.personID}&debt=${addingUser.userDebt}&type=${addingUser.type}&paycode=${addingUser.payCode}');
print('The URI: $uri');
var request = await http.post(uri);
print(
'The response:\n----\n- ${request.body}\n Status Code: ${request.statusCode}\n----\n');
if (request.statusCode == 201 || request.statusCode == 200) {
// If the server did return a 201 Created response,
// then return value.
return true;
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception(
'Failed to add user. Status Code: ${request.statusCode}');
}
}
return false;
} catch (e) {
print('An error occurred - $e');
return false;
}
}
How I escaping from "/" character in Dart encryption ? I don't want this character in the encrypted string. Thanks.

encryptMyData() Base64 encodes the data. The Base64 alphabet contains alphanumeric characters and the characters /, + and = (padding), where the last three have a special meaning and are therefore reserved in the context of URIs.
A simple way to mask these characters would be to URL encode the Base64 encoded strings with Uri.encodeComponent(). Accordingly, URL decoding with Uri.decodeComponent() must be performed before Base64 decoding:
var base64Data = 'EBES/+7dzA==';
var urlEnc = Uri.encodeComponent(base64Data);
print(urlEnc); // EBES%2F%2B7dzA%3D%3D
// Apply in URL
var urlDec = Uri.decodeComponent(urlEnc);
print(urlDec); // EBES/+7dzA==
Alternatively, Base64url can be applied instead of Base64. Base64url uses the characters _ and - instead of / and + as well as optional padding. Unlike / and +, _ and - are not reserved for URIs.
Base64url is supported in Dart by base64Url.encode(). The padding is applied, but can be removed explicitly (e.g. with replaceAll('=','')). Decoding is possible with base64Url.decode() (after the padding has been added). Alternatively, base64Url.normalize() can be used to convert to Base64 (which automatically adds the padding) and base64.decode() can be applied for Base64 decoding:
var b64urlEnc = base64Url.encode([16, 17, 18, 255, 238, 221, 204]);// [0x10, 0x11, 0x12, 0xff, 0xee, 0xdd, 0xcc]
print(b64urlEnc); // EBES_-7dzA==
var b64urlEncNoPadding = b64urlEnc.replaceAll('=','');
print(b64urlEncNoPadding); // EBES_-7dzA
// Apply in URL
var base64Enc = base64Url.normalize(b64urlEncNoPadding);
print(base64Enc); // EBES/+7dzA==
var base64Dec = base64.decode(base64Enc);
print(base64Dec); // [16, 17, 18, 255, 238, 221, 204]
Optionally, for already Base64 encoded data, the characters / and + can be replaced by _ and - and the padding (=) can be removed.

Related

Getting invalid credentials on REST API

I'm trying to send a HttpPOST with Content-Type multipart/form-data using Apex, the API response is 'invalid credentials', the problem is not in the data because it works on Postman, I can't find where is the problem in my code:
My class:
public with sharing class XXXXXXXXXXXX {
#future(callout = true)
public static void fileUpload(){
String endpoint = 'endpoint';
List<ContentVersion> cvList = new List<ContentVersion>();
cvList = [SELECT id, Title, ContentDocumentId, FileExtension, VersionData FROM ContentVersion WHERE Id = '0685e000004zxRrAAI' LIMIT 1];
System.debug('Callout Log 1: ' + cvList);
if(!cvList.isEmpty()){
//form fileName with cv ID
String fileName = cvList[0].Id;
if(cvList[0].FileExtension != null && cvList[0].FileExtension != ''){
fileName = fileName + '.' + cvList[0].FileExtension;
}
System.debug('Callout log2: ' + fileName);
//callout ePOR service
String contentType = EinsteinVision_HttpBodyPart.GetContentType();
//Compose the form
string form64 = '';
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBodyParameter('message', 'message value');
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBodyParameter('login', 'login value');
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBodyParameter('password', 'password value');
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBodyParameter('to', 'to value');
form64 += EinsteinVision_HttpBodyPart.WriteBoundary();
form64 += EinsteinVision_HttpBodyPart.WriteBlobBodyParameter('image', EncodingUtil.base64Encode(cvList[0].VersionData), fileName);
Blob formBlob = EncodingUtil.base64Decode(form64);
String contentLength = String.valueOf(formBlob.size());
System.debug('Callout Log 3: ' + formBlob.size());
if(formBlob.size() > 12000000){
System.debug('Callout error');
}else{
Http http = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod('POST');
req.setHeader('Content-Length', contentLength);
req.setHeader('Content-Type', contentType);
req.setTimeout(120000);
HttpResponse response = http.send(req);
String responseBody = response.getBody();
System.debug('Callout Log 4: ' + response.getStatusCode());
System.debug('Callout Log 5: ' + String.valueOf(response.getBody()));
}
}
}
}
Class to handle Content-Type multipart/form-data:
public virtual class EinsteinVision_HttpBodyPart {
public EinsteinVision_HttpBodyPart() {
}
// The boundary is aligned so it doesn't produce padding characters when base64 encoded.
private final static string Boundary = '1ff13444ed8140c7a32fc4e6451aa76d';
public static String getBoundary() {
return Boundary;
}
/**
* Returns the request's content type for multipart/form-data requests.
*/
public static string GetContentType() {
return 'multipart/form-data; charset="UTF-8"; boundary="' + Boundary + '"';
}
/**
* Pad the value with spaces until the base64 encoding is no longer padded.
*/
public static string SafelyPad(
string value,
string valueCrLf64,
string lineBreaks) {
string valueCrLf = '';
blob valueCrLfBlob = null;
while (valueCrLf64.endsWith('=')) {
value += ' ';
valueCrLf = value + lineBreaks;
valueCrLfBlob = blob.valueOf(valueCrLf);
valueCrLf64 = EncodingUtil.base64Encode(valueCrLfBlob);
}
return valueCrLf64;
}
/**
* Write a boundary between parameters to the form's body.
*/
public static string WriteBoundary() {
string value = '--' + Boundary + '\r\n';
blob valueBlob = blob.valueOf(value);
return EncodingUtil.base64Encode(valueBlob);
}
/**
* Write a boundary at the end of the form's body.
*/
public static string WriteBoundary(
EndingType ending) {
string value = '';
if (ending == EndingType.Cr) {
// The file's base64 was padded with a single '=',
// so it was replaced with '\r'. Now we have to
// prepend the boundary with '\n' to complete
// the line break.
value += '\n';
} else if (ending == EndingType.None) {
// The file's base64 was not padded at all,
// so we have to prepend the boundary with
// '\r\n' to create the line break.
value += '\r\n';
}
// Else:
// The file's base64 was padded with a double '=',
// so they were replaced with '\r\n'. We don't have to
// do anything to the boundary because there's a complete
// line break before it.
value += '--' + Boundary + '--';
blob valueBlob = blob.valueOf(value);
return EncodingUtil.base64Encode(valueBlob);
}
/**
* Write a key-value pair to the form's body.
*/
public static string WriteBodyParameter(
string key,
string value) {
string contentDisposition = 'Content-Disposition: form-data; name="' + key + '"';
string contentDispositionCrLf = contentDisposition + '\r\n\r\n';
blob contentDispositionCrLfBlob = blob.valueOf(contentDispositionCrLf);
string contentDispositionCrLf64 = EncodingUtil.base64Encode(contentDispositionCrLfBlob);
string content = SafelyPad(contentDisposition, contentDispositionCrLf64, '\r\n\r\n');
string valueCrLf = value + '\r\n';
blob valueCrLfBlob = blob.valueOf(valueCrLf);
string valueCrLf64 = EncodingUtil.base64Encode(valueCrLfBlob);
content += SafelyPad(value, valueCrLf64, '\r\n');
return content;
}
/**
* Write a key-value pair to the form's body for a blob.
*/
public static string WriteBlobBodyParameter(string key, string file64, string fileName) {
String mimeType = resolveMimeType(fileName);
string contentDisposition = 'Content-Disposition: form-data; name="' + key + '"; filename="'+fileName+'"';
string contentDispositionCrLf = contentDisposition + '\r\n';
blob contentDispositionCrLfBlob = blob.valueOf(contentDispositionCrLf);
string contentDispositionCrLf64 = EncodingUtil.base64Encode(contentDispositionCrLfBlob);
string content = SafelyPad(contentDisposition, contentDispositionCrLf64, '\r\n');
string contentTypeHeader = 'Content-Type: ' + mimeType;
string contentTypeCrLf = contentTypeHeader + '\r\n\r\n';
blob contentTypeCrLfBlob = blob.valueOf(contentTypeCrLf);
string contentTypeCrLf64 = EncodingUtil.base64Encode(contentTypeCrLfBlob);
content += SafelyPad(contentTypeHeader, contentTypeCrLf64, '\r\n\r\n');
integer file64Length = file64.length();
String last4Bytes = file64.substring(file64.length()-4,file64.length());
// Avoid padding the file data with spaces, which SafelyPad does
// http://salesforce.stackexchange.com/a/33326/102
EndingType ending = EndingType.None;
if (last4Bytes.endsWith('==')) {
// The '==' sequence indicates that the last group contained only one 8 bit byte
// 8 digit binary representation of CR is 00001101
// 8 digit binary representation of LF is 00001010
// Stitch them together and then from the right split them into 6 bit chunks
// 0000110100001010 becomes 0000 110100 001010
// Note the first 4 bits 0000 are identical to the padding used to encode the
// second original 6 bit chunk, this is handy it means we can hard code the response in
// The decimal values of 110100 001010 are 52 10
// The base64 mapping values of 52 10 are 0 K
// See http://en.wikipedia.org/wiki/Base64 for base64 mapping table
// Therefore, we replace == with 0K
// Note: if using \n\n instead of \r\n replace == with 'oK'
last4Bytes = last4Bytes.substring(0,2) + '0K';
file64 = file64.substring(0,file64.length()-4) + last4Bytes;
// We have appended the \r\n to the Blob, so leave footer as it is.
ending = EndingType.CrLf;
} else if (last4Bytes.endsWith('=')) {
// '=' indicates that encoded data already contained two out of 3x 8 bit bytes
// We replace final 8 bit byte with a CR e.g. \r
// 8 digit binary representation of CR is 00001101
// Ignore the first 2 bits of 00 001101 they have already been used up as padding
// for the existing data.
// The Decimal value of 001101 is 13
// The base64 value of 13 is N
// Therefore, we replace = with N
last4Bytes = last4Bytes.substring(0,3) + 'N';
file64 = file64.substring(0,file64.length()-4) + last4Bytes;
// We have appended the CR e.g. \r, still need to prepend the line feed to the footer
ending = EndingType.Cr;
}
content += file64;
content += WriteBoundary(ending);
return content;
}
private static String resolveMimeType(String fileName) {
String fileType = fileName.substringAfterLast('.');
String mimeType = 'image/png'; // fallback value
if (fileType.equalsIgnoreCase('png')) {
mimeType = 'image/png';
} else if (fileType.equalsIgnoreCase('jpeg') || fileType.equalsIgnoreCase('jpg')) {
mimeType = 'image/jpg';
} else if (fileType.equalsIgnoreCase('pgm')) {
mimeType = 'image/x-portable-graymap';
} else if (fileType.equalsIgnoreCase('ppm')) {
mimeType = 'image/x-portable-pixmap';
}
return mimeType;
}
/**
* Helper enum indicating how a file's base64 padding was replaced.
*/
public enum EndingType {
Cr,
CrLf,
None
}
}
The second class is not mine and it should be good, the problem is on the first class [XXXXXXXXXXXX]
Thanks!

sign okex api in flutter

i have problem to sign api for okex
,in document of okex:
The OK-ACCESS-SIGN header is generated as follows:
Create a prehash string of timestamp + method + requestPath + body
(where + represents String concatenation). Prepare the SecretKey. Sign
the prehash string with the SecretKey using the HMAC SHA256. Encode
the signature in the Base64 format. Example:
sign=CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(timestamp +
'GET' + '/users/self/verify', SecretKey))
The timestamp value is the same as the OK-ACCESS-TIMESTAMP header with
millisecond format of ISO, e.g. 2020-12-08T09:08:57.715Z.
The request method should be in UPPERCASE: e.g. GET and POST.
The requestPath is the path of requesting an endpoint.
Example: /api/v5/account/balance
The body refers to the String of the request body. It can be omitted
if there is no request body (frequently the case for GET requests).
method i made fo sign is:
dynamic _getSign(String timestamp, String methodType, String url, String body) {
if (body.isEmpty) {
body = "";
}
String message = timestamp + methodType.toUpperCase() + url + body;
var hmacSha256 = Hmac(sha256, utf8.encode(oKSecretKey));
var mac = hmacSha256.convert(utf8.encode(message));
// var a = mac.bytes;
var a = base64Url.encode(mac.bytes);
print(a);
return a;
}
Future<String> getAccountInfo() async {
try {
String timestamp = getServerTime();
String url = '/api/v5/account/balance';
Response response = await OKEXApi.dio.get(url,
queryParameters: {},
options: Options(headers: {
"OK-ACCESS-KEY": oKACCESSKEY,
"OK-ACCESS-PASSPHRASE": oKACCESSPASSPHRASE,
"OK-ACCESS-TIMESTAMP": timestamp,
"OK-ACCESS-SIGN": _getSign(timestamp, "GET", url, ""),
'Accept': 'application/json',
'Content-type': 'application/json',
}));
print(response.data);
return response.data;
} on DioError catch (e) {
return e.error;
}
}
and for timestamp
String getServerTime() {
DateTime now = DateTime.now().toUtc();
String isoDate = now.toIso8601String();
return isoDate;
}
And when i send data, response is:
{"msg":"Invalid Sign","code":"50113"}
The spec says:
OK-ACCESS-TIMESTAMP header with millisecond format of ISO, e.g.
2020-12-08T09:08:57.715Z
Truncate the microseconds away like this:
final now = DateTime.now().toUtc();
final microlessNow = now.subtract(Duration(microseconds: now.microsecond));
final isoDate = microlessNow.toIso8601String();
print(isoDate);

How to decrypt AES 256 CBC with Dart

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);
}
}
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

Flutter and dart convert string to md5 then call as a string

For an api I am using, I have to convert a string password and a salt to a md5 string and call that string in a url post request,
my code looks like this:
generateMd5(String data) {
var content = new Utf8Encoder().convert(sp);
var md5 = crypto.md5;
var digest = md5.convert(content);
return digest.toString();
}
problem is i can't call it as a string. when i put "print(data)" it says its an Undefined name.
You need a method like this for your salted password token.
String makeToken(String password, String salt) =>
md5.convert(utf8.encode(password + salt)).toString().toLowerCase();
Calling print(makeToken('sesame', 'c19b2d')); yields 26719a1196d2a940705a59634eb18eab as shown in the test vector.
Supply the token as the t parameter and the salt as the s parameter of your API call.
You might find the following useful for your salt creation:
final _random = Random();
String randomToken(int length) => String.fromCharCodes(
List.generate(length, (_) {
var ch = _random.nextInt(52);
if (ch > 25) {
ch += 6;
}
return ch + 0x41;
}),
);
String newSalt() => randomToken(6);

Javascript AES with CryptoJS does not completely decrypt

All information i got are encrypted data (AES) and a key. The data must be a URL. I 've tried so many code-snippets (from stackoverflow) and found one snippets that worked for me partially.
// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
var iv = btoa(rawData.substring(0,16));
var crypttext = btoa(rawData.substring(16));
// Decrypt...
var plaintextArray = CryptoJS.AES.decrypt(
{
ciphertext: CryptoJS.enc.Base64.parse(crypttext),
salt: ""
},
CryptoJS.enc.Hex.parse(key),
{ iv: CryptoJS.enc.Base64.parse(iv) }
);
// Convert hex string to ASCII.
function hex2a(hex) {
var str = '';
for (var i = 0; i < hex.length; i += 2)
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
return str;
}
console.log(hex2a(plaintextArray.toString()));
The URL must be
http://test-example.com/hjhdsdfuisd
but the output is only
-example.com/hjhdsdfuisd.
I changed
var crypttext = btoa(rawData.substring(16));
to
var crypttext = btoa(rawData);
and got
­ô#XÍäÜ7±H4-example.com/hjhdsdfuisd.
What is my mistake?