Determining attributes of flutter encrypt.dart - flutter

I am using encrypt.dart to AES encrypt a string ("text") based on a 32 digit password ("password") as follows:
encryptPass(String text, String password) {
final key = getKey(password);
final iv = encrypt.IV.fromLength(16);
final encrypter = encrypt.Encrypter(encrypt.AES(key)); //Uses AES/SIC/PKCS7
final e = encrypter.encrypt(text, iv: iv);
String encryptedString = e.base64.toString();
return encryptedString;
}
getKey(String masterPass) {
String keyString = masterPass;
if (keyString.length < 32) {
int count = 32 - keyString.length;
for (var i = 0; i < count; i++) {
keyString += ".";
}
}
final keyReturn = encrypt.Key.fromUtf8(keyString);
return keyReturn;
}
Side note: This works, but it produces the same value every time for a given input string, even though my "iv" and "salt" are supposedly random. How does this happen?
MAIN PROBLEM: I am trying to recreate this process using spongy castle in kotlin. The problem is that I don't know certain important attributes of the encrypt.dart AES functions. What values are used for:
salt length: 16, 32, 128, 256?? ("desiredKeyLength" var in encrypted.dart. not specified anywhere)
iteration count: (I think this is 100, but I am not certain.)
Secret Key algorithm: I assumed PBKDF2WithHmacSHA1 based on "final pbkdf2" of encrypted.dart.
key length: ?
Here is my current attempt at spongy castle implementation for reference:
fun encryptAESBasic(input: String, password: String): String {
Security.insertProviderAt(org.spongycastle.jce.provider.BouncyCastleProvider(), 1)
val masterpw = password.toCharArray()
val random = SecureRandom()
val salt = ByteArray(256)
random.nextBytes(salt)
val factory: SecretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
val spec: KeySpec = PBEKeySpec(masterpw, salt, 100, 128)
val tmp: SecretKey = factory.generateSecret(spec)
val key: SecretKey = tmp
val cipher = Cipher.getInstance("AES/SIC/PKCS7PADDING", "SC")
val iv = ByteArray(16)
SecureRandom().nextBytes(iv)
cipher.init(Cipher.ENCRYPT_MODE, key, IvParameterSpec(iv))
val cipherText: ByteArray = cipher.doFinal(input.toByteArray())
return cipherText.toString()
}

The Dart code uses a zero IV (an IV consisting of only 0x00 values), which is why always the same ciphertext is generated.
As you already figured out, the Dart code applies the SIC mode and PKCS7 padding by default. The SIC mode is another name for the CTR mode, which is a stream cipher mode and therefore does not require any padding. The PKCS7 padding used in the Dart code is therefore unnecessary.
Note that using CTR mode in conjunction with a static IV (such as a zero IV) is a fatal bug and in general extremely insecure (s. here).
As key derivation, the Dart code pads the password with . until the key size is 32 bytes, which is required for AES-256. This key derivation is also very insecure. When using a password, a reliable key derivation function such as PBKDF2 should always be used (as in the Kotlin Code).
The Dart code should therefore be revised and made more secure before porting to Kotlin. This requires the following changes:
A random IV is to be generated for each encryption.
PKCS7 padding should be disabled.
The code does not check the authenticity/integrity of the ciphertext. An additional authentication tag (MAC) must be applied for this purpose. It is recommended to switch from CTR to GCM mode, which is based on CTR mode but includes data authenticity/integrity in addition to confidentiality (authenticated encryption) and generates the tag implicitly.
A secure key derivation (e.g. PBKDF2, see Kotlin code) must be used. In combination with this, a random salt is to be generated for each key derivation (s. also the other answer).
Salt and IV (both not secret), as well as the tag are to be concatenated with the ciphertext (salt|IV|ciphertext|tag). Note that for GCM, many libraries perform concatenation of ciphertext and tag implicitly.
Of course - from a technical point of view - the Dart code can be ported to Kotlin, e.g.
fun encryptPass(text: String, password: String): String {
val secretKeySpec = SecretKeySpec(getKey(password), "AES") // Apply a reliable key derivation function (getKey() is insecure)
val cipher = Cipher.getInstance("AES/CTR/PKCS5PADDING") // Disable padding (CTR doesn't require padding)
val iv = ByteArray(16) // Generate random IV (CTR with static IV is extremely insecure)
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, IvParameterSpec(iv))
val cipherText: ByteArray = cipher.doFinal(text.toByteArray(Charset.forName("UTF-8"))) // Authenticity/integrity missing
return Base64.encodeToString(cipherText, Base64.DEFAULT); // Concatenation of salt, IV, ciphertext and authentication tag missing
}
fun getKey(masterPass: String): ByteArray {
return masterPass.padEnd(32, '.').toByteArray(Charset.forName("UTF-8"))
}
which gives the same result as the Dart code (the use of SpongyCastle is not necessary), but this code should not be used for security reasons.

The following data (the answers to your questions) were taken from https://github.com/leocavalcante/encrypt/blob/5.x/lib/src/encrypted.dart (lines 65-72):
iterationCount = 100
PBKDF2KeyDerivator(Mac('SHA-1/HMAC')) = PBKDF2WithHmacSHA1
The key length is taken from the key instantiation: final key = Key.fromLength(32);
and the salt length is equal to the key length: salt = SecureRandom(desiredKeyLength).bytes;
The algorithm mode defaults in https://github.com/leocavalcante/encrypt/blob/5.x/lib/src/algorithms/aes.dart defaults to SIC
"AES(this.key, {this.mode = AESMode.sic, this.padding = 'PKCS7'}"

Related

Trying to print hex string with CryptoJS

I am trying to use CryptoJS to encrypt something and then generate a hexadecimal string of the encrypted text.
function EncryptAES(text, key) {
var encrypted = CryptoJS.AES.encrypt(text, key);
return CryptoJS.enc.Hex.stringify(encrypted);
}
var encrypted = EncryptAES("Hello, World!", "SuperSecretPassword");
console.log(encrypted);
However, instead of a hexadecimal string, a blank line is printed to the console. What am I doing wrong?
CryptoJS.AES.encrypt() returns a CipherParams object that encapsulates several data, including the ciphertext as WordArray (s. here). By default, .toString() returns the hex encoded data for a WordArray:
function EncryptAES(text, key) {
var encrypted = CryptoJS.AES.encrypt(text, key);
return encrypted.ciphertext.toString()
}
var encrypted = EncryptAES("Hello, World!", "SuperSecretPassword");
console.log(encrypted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
Note that in your example the key material is passed as string and therefore interpreted as passphrase (s. here), inferring key and IV via a key derivation function in conjunction with a random 8 bytes salt, which is why the ciphertext changes each time for the same input data.
Therefore, decryption requires not only the ciphertext but also the salt, which is also encapsulated in the CipherParams object.
For a CipherParams object, .toString() returns the data in the Base64 encoded OpenSSL format consisting of the ASCII encoding of Salted__ followed by the 8 bytes salt and the actual ciphertext, and thus contains all the information needed for decryption.

How to encrypt/decrypt in flutter using ECB mode/AES256 without having IV?

I am trying to encrypt string in flutter/Dart. I have tried the below code but looks like having IV is mandatory.
final key = Key.fromBase64("Some_Key");
final iv = IV.fromBase64("Some_Key"); // I do not need IV for encryption/decryption
final encrypter = Encrypter(AES(key, mode: AESMode.ecb, padding: 'PKCS7'));
final encrypted = encrypter.encrypt(employeeNumber, iv: iv); //No IV needed
Could someone please let me know how to encrypt and decrypt the strings using AES 256 bit/ ECB mode / PKCS7 padding and without IV.
Please note that I do not need IV at the moment. Kindly help...
I also wanted to avoid IV, and then I found the following solution :
Make the dummy IV as
var ivBtyes = Uint8List.fromList([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]);
final iv = encrypt.IV(ivBtyes);
set the AES mode as ECB and padding as "PKCS7". then your code -
final encrypted = encrypter.encrypt(employeeNumber, iv: iv);
Though you are passing IV as dummy it will be ignored in ECB mode.

Problem signing PDF with iText, PKCS11 and SHA-256 Algorithm

I'm trying to sign a pdf using PCKS11 (USB Token) and iText, It works fine when I use SHA-1 algorithm, the signature is valid and everything works fine however when I change it to SHA256 the signature is not valid, Does anyone know why? I'll put my code below:
PKCS11 pkcs11 = PKCS11.getInstance("C:\\Windows\\System32\\example.dll", "C_GetFunctionList", null,false);
long[] slotList = pkcs11.C_GetSlotList(true);
String providerString = "name=*\nlibrary=C:\\Windows\\System32\\example.dll\n" + "slot=" + slotList [0];
SunPKCS11 sunPKCS11 = new SunPKCS11(new ByteArrayInputStream(providerString .getBytes()));
Provider provider = sunPKCS11;
KeyStore keyStore = KeyStore.getInstance("PKCS11", provider);
keyStore.load(null, password);
keyStore.aliases();
Security.addProvider(keyStore.getProvider());
List<String> aliases = Collections.list(keyStore.aliases());
String alias = aliases.get(0);
PrivateKey pk = (PrivateKey)keyStore.getKey(alias,password);
Certificate[] certChain = new Certificate[1];
certChain[0] = signerCert;
PdfReader reader = new PdfReader(inputData);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
PdfStamper stamper = PdfStamper.createSignature(reader, outStream, '\0',null,true);
PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
String fieldName = keyStore.getCertificateAlias(signerCert).replaceAll(".","");
appearance.setVisibleSignature(new Rectangle(420, 10, 70, 85), pageNumber,fieldName);
ExternalSignature es = new PrivateKeySignature(pk, DigestAlgorithms.SHA256,
keyStore.getProvider().getName());
ExternalDigest digest = new BouncyCastleDigest();
MakeSignature.signDetached(appearance,
digest, es,
certChain, null,
null, null,
0, CryptoStandard.CADES);
return outStream.toByteArray();
Why does it work with SHA-1 and not SHA-2? Where do you think the problem arises?
There are mismatches and errors in your signature.
download.pdf
This is the example file you shared in a comment to your question
Mismatch of Document Digest
The SHA-256 hash value of the signed bytes of your PDF is
9356BCD36F172806A3DCE7F062A66441E7C1DDC9203ABDAA0154A3F19208C8E3
but the embedded signature container claims it to be
5F892978FF2459157D631809A05F5DBCFCB55800236D2D5C3E4E4D94577012B4
According to your code that should not happen. Are you sure that PDF has been created by the code in your question? Or have you somehow changed it as you indicated by your I changed my hash like this comment and created the example document thereafter? Then this discrepancy most likely is caused by your change.
Broken Raw RSA Signature
After decrypting the raw signature bytes and removing the padding one gets
3031300D0609608648016503040201050004201DC71B824BAA3C7EC6744A0941CFADDAA893E8C1
This is incomplete. It corresponds to
SEQUENCE (2 elem)
SEQUENCE (2 elem)
OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1 sha-256 (NIST Algorithm)
NULL
OCTET STRING (32 byte) 1DC71B824BAA3C7EC6744A0941CFADDAA893E8C1XXXXXXXXXXXXXXXXXXXXXXXX
with the 12 bytes for that XXXXXXXXXXXXXXXXXXXXXXXX piece missing.
Thus, it looks like the signing device does not really support SHA256withRSA, at least not in combination with the example.dll PKCS#11 driver and the SunPKCS11 security provider.
As an aside, the OCTET STRING there should have been
EC7FCC5D003DFEC58B0ECB49CEEAD28495FFA8D798A1A88DA6051C1857B971EC
Thus, it looks like here is another mismatch, the 1DC71B824BAA3C7EC6744A0941CFADDAA893E8C1 actually there appears not to be related to this value.
SHA256.pdf
Can you check out this one? this one is made with itext's library and no code was changed.
In this file there indeed are no hash mismatches anymore, merely the signature value is cut off, here
3031300D0609608648016503040201050004204C8440B547E6A0EFD1489B8F5B5DFDA2DFA45DC1
corresponding to
SEQUENCE (2 elem)
SEQUENCE (2 elem)
OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1 sha-256 (NIST Algorithm)
NULL
OCTET STRING (32 byte) 4C8440B547E6A0EFD1489B8F5B5DFDA2DFA45DC1XXXXXXXXXXXXXXXXXXXXXXXX
The complete OCTET STRING should have been
4C8440B547E6A0EFD1489B8F5B5DFDA2DFA45DC19048B5E53D468FE6A8E4E973
So apparently indeed the chain of your signing device, the example.dll PKCS#11 driver, and the SunPKCS11 security provider from your JRE does not support SHA256withRSA.

crypto-js decrypt from Hex

I am trying to make a JavaScript function with package crypto-js to decode AES (CBC mode).
I input the data in an online decoding tool and it decrypted correctly, so I am sure the following data is correct, but I just can't reproduce it by JavaScript.
Here is the online decrypting (so I'm sure the data, key, iv are correct): http://aes.online-domain-tools.com/link/deb718giF4dUxZylq/
My code with crypto-js#3.1.8:
// data, key, iv are all Hex
var data = "bd6e0a73147a2c224c7c20346d0e9a138b744a5d94463cdff6dbb965055f974f097104399d2c40af2f0ac667f3857e70e9703bf27f6411f7e97c3449e8921f3c98e665914689b4b77b5bbcc8d8bc319e680eb89eedb1c25178923ae57fb3fb476755d6009f1aed88fffcb9b2ed3b4cf6f23d9c4c56da1dde6619e45a8d6f06412853ae1941cf554b6824112a913750a7485ed67fb38b950411310410de998f2597c2fcc81a305b0df369f54b75426176";
var key = 'befce5c6da98837ea421811c832817ae';
var iv = "a884a7edd5d06a48d6da9ad11fd36a75";
// transfer Hex to WordArray
var _data = CryptoJS.enc.Hex.parse(data);
var base64_data = _data.toString(CryptoJS.enc.Base64);
var _key = CryptoJS.enc.Hex.parse(key);
var _iv = CryptoJS.enc.Hex.parse(iv);
decrypted = CryptoJS.AES.decrypt(
base64_data, // pass base64
_key, // pass WordArray
{iv: _iv, // pass WordArray
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
})
console.log(decrypted.toString(CryptoJS.enc.Utf8));
// out put fail to match Utf8
It output Error: Malformed UTF-8 data
The decoded string should be: (the link is not important)
https://emogo-media-testing.s3.amazonaws.com/1503342403787_blob?AWSAccessKeyId=AKIAI5MUDCK6XYWKGAKA&Expires=1534882403&Signature=t1PFesQuOpOlIMKoOqje%2Bs7I%2Fhg
Any hint is appreciated. Thank you!
I know it has been a while since you asked the question but I will respond just so the next person does not stumble upon an unanswered question.
Your code works fine, it decrypts AES.CBC encrypted data correct, the problem lies with your input data.
Your encrypted data string should have looked like:
80b7c4881334675693ef9c95259e70b24d0736e98f8424233d5e37f353261c2a589287bc3f675449f7d8ed4e2289a4c06b22d7f83efc09cfb72abe3a76e193a8efbdc968232d29b9b58135bfa24d51e60e34791f652a0aa806d0be7734dd61a930a30c99f31f08740cdb182af07b19d5b4274deb958d984b3ccb9d6e2be0cfa3a026dd6b734dbf1dd3635bc7bcceface9c55dfb9455ca834a6dbd1aa0f3c23923ce6aeba59acbc80d681fee73487b9004496540830d44102b94e35eac291c4e3b8c9ac168ae799e46cde45ee652415ae69992d0f7527045fd42b82e9e6946cfb2dbcc3b93f19ff0e5035ab12250f7a917975b2f7c069cbd8a0ba0d94b318634a
for this example to work correctly.
The key you used is not a hex string but a text string. Your online example is no longer valid but I figured it out after a couple of tries.
If change the following line:
var _key = CryptoJS.enc.Hex.parse(key);
to:
var _key = CryptoJS.enc.Utf8.parse(key);
Your code example will work fine with your original data string.
When you decrypted the text on http://aes.online-domain-tools.com/ you probably had the plaintext textbox selected instead of hex for your key input.

Kraken API MATLAB client invalid signature error

I'm trying to do some authenticated calls to Kraken private endpoints but without success. I'm still getting an error EAPI:Invalid signature.
Does anybody know what's wrong?
Here's the code:
function [response,status]=kraken_authenticated(uri,postdata)
% test uri='0/private/AddOrder'
% test postdata='&pair=XBTEUR&type=buy&ordertype=limit&price=345.214&volume=0.65412&leverage=1.5&oflags=post'
url=['https://api.kraken.com/',uri];
% nonce
nonce = num2str(floor((now-datenum('1970', 'yyyy'))*8640000000));
[key,secret]=key_secret('kraken');
% 1st hash
Opt.Method = 'SHA-256';
Opt.Input = 'ascii';
sha256string = DataHash(['nonce=',nonce,postdata],Opt);
% 2nd hash
sign = crypto([uri,sha256string], secret, 'HmacSHA512');
header_1=http_createHeader('API-Key',key);
header_2=http_createHeader('API-Sign',char(sign));
header=[header_1 header_2];
[response,status] = urlread2(url,'POST',['nonce=',nonce,postdata],header);
end
Crypto function is in another file:
function signStr = crypto(str, key, algorithm)
import java.net.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import org.apache.commons.codec.binary.*
keyStr = java.lang.String(key);
key = SecretKeySpec(keyStr.getBytes('UTF-8'), algorithm);
mac = Mac.getInstance(algorithm);
mac.init(key);
toSignStr = java.lang.String(str);
signStr = java.lang.String(Hex.encodeHex( mac.doFinal( toSignStr.getBytes('UTF-8'))));
end
I've also tried
sign = crypto([uri,sha256string], base64decode(secret), 'HmacSHA512');
but without success.
This is guide for authenticated call HTTPS Header:
API-Key = API key
API-Sign = Message signature using HMAC-SHA512 of (URI path + SHA256(nonce + POST data)) and base64 decoded secret API key
This is guide for authenticated call POST Data:
nonce = always increasing unsigned 64 bit integer
otp = two-factor password (if two-factor enabled, otherwise not required)
I've tried to pass "nonce" parameter or all parameters in "postdata" to POST data but without success.
Thanks for help.
The problem is in function crypto here:
keyStr = java.lang.String(key);
key = SecretKeySpec(keyStr.getBytes('UTF-8'), algorithm);
As the base64 encoded private key from kraken is not necessarily UTF-8 encoded, you cannot use UTF-8 encoding to extract the key and pass UTF-8 string to the SecretKeySpec function. You need to use byte array instead.
Similar issues
https://code.google.com/p/google-apps-script-issues/issues/detail?id=5113
https://code.google.com/p/google-apps-script-issues/issues/detail?id=3121
Solution for javascript
github.com/Caligatio/jsSHA