Dart/Flutter Blowfish CBC - dart

How to do Blowfish CBC decryption in Flutter/Dart? I can't find any libraries that support it.
dbcrypt supports only password hashing and no cbc mode.
Thank you.

import 'package:encrypt/encrypt.dart' as encrypt;
import 'package:encrypt/encrypt.dart';
class _MyHomePageState extends State<MyHomePage> {
final plainText = 'some plain text here';
final key = encrypt.Key.fromUtf8('16 characters key');
final iv = IV.fromLength(16);
final encrypter = Encrypter(AES(key,mode: AESMode.cbc,padding: 'PKCS7'));
final encrypted = encrypter.encrypt(plainText, iv: iv);
final decrypted = encrypter.decrypt(encrypted, iv: iv);
print(decrypted);
print(encrypted.base64);
pubspec.yaml:
dependencies:
encrypt: ^4.0.0

Related

How create a Signed URL to upload Files in Google Cloud Storage using Dart

I'm trying to create an API in Dart to generate a Signed URL to upload a file in Cloud Storage but even following the google documentation I can't successfully generate. In all my attempts, when making the request the API returns me: The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.
I think the error is in generating the signature, or in the hash of the canonical request
final canonicalRequest = getCanonicalRequest(
canonicalUri,
canonicalQueryString,
canonicalHeaders,
signedHeaders,
);
final bytes = utf8.encode(canonicalRequest);
final hashedCanonicalRequest = HEX.encode(sha256.convert(bytes).bytes);
final stringToSign = getStringToSign(
dateTimestamp,
credentialScope,
hashedCanonicalRequest,
);
final signature = await getSignature(stringToSign);
Future<String> getSignature(String stringToSign) async {
final privateKey =
await parseKeyFromFile<pointy.RSAPrivateKey>('private.pem');
final signer =
Signer(RSASigner(RSASignDigest.SHA256, privateKey: privateKey));
final signature = signer.sign(stringToSign);
return HEX.encode(signature.bytes);
}

Why is CryptEncode() in mql4 different from other languages?

I was trying to understand CryptEncode() in order to send and receive strings between mt4 indicator and PHP server.
But when a string is encrypted on mql4 side it's totally different then when it's encrypted on the PHP side. Same key and same encryption algorithm is used on both ends, but still the encryption is different on both sides.
And when I use an online tool to decrypt the PHP encrypted string it successfully decrypts the sting, whereas when I use the mql4 generated encrypted string it says final block is not properly padded.
Code:
string text="this is the text to be encoded";
string keystr="d41d8cd98f00b204e9800998ecf8427e";
uchar src[],dst[],key[];
StringToCharArray(text,src);
StringToCharArray(keystr,key);
int encd=CryptEncode(CRYPT_AES256,src,key,dst);
encd=CryptEncode(CRYPT_BASE64,dst,key,src);
if(encd>0)
{
PrintFormat("Encoded data: size=%d, string='%s'",ArraySize(src),CharArrayToString(src));
}
else
Print("Error in CryptEncode. Error code=",GetLastError());
Why is it that mql4 generated encrypted string different from a string generated by any other language? How to avoid this issue? Is there any alternative encryption library available to avoid this issue?
It is not different, it is just that MQL4 only supports a very specific implementation of AES encryption and unless you use the correct settings in your other code you will not achieve compatibility between the two platforms.
Specifically you need to ensure the following are implemented:
Padding Mode: Zeros
Cipher Mode: ECB (so no IV)
KeySize: 256
BlockSize: 128
You can try the online AES encryption/decryption tool to verify your results available here: The online toolbox
// PUTTING ALL THINGS TOGETHER
<?php
$plaintext = '123456';
$key = 'MyKey';
$encryptedMessage = EncryptMQL($plaintext, $key);
echo ($encryptedMessage);
echo nl2br("\n");
$decryptedMessage = DecryptMQL($encryptedMessage, $key);
echo ($decryptedMessage);
//--------------------------------------------------
function EncryptMQL($plaintext, $key){
$data .= "\0"; // string of MQL is terminated by null character
$key .= "\0";
$hash = hash('sha256', $key, true); // true (raw binary) | false (lowercase hexits)
$result = openssl_encrypt(padZero($plaintext), "AES-256-ECB", $hash, OPENSSL_ZERO_PADDING);
// note: result = AES (base64 encoded)
return $result;
}
function DecryptMQL($encryptedtext, $key) {
$key .= "\0";
$hash = hash('sha256', $key, true); // true (raw binary) | false (lowercase hexits)
$decrypted = openssl_decrypt($encryptedtext, "AES-256-ECB", $hash, OPENSSL_ZERO_PADDING);
return(unpadZero($decrypted));
}
//--------------------------------------------------
function padZero($data, $blocksize = 16) {
$pad = $blocksize - (strlen($data) % $blocksize);
return $data.str_repeat("\0", $pad);
}
function unpadZero($data) {
return rtrim($data, "\0");
}
?>

Create and sign JWT in Scala for Apple AppStore Connect

i want to create a JWT in a scala application for talking to the Apple's AppStore Connect api. i'm following the guide here
i'm getting an invalid signature on jwt.io when creating a JWT with the below code. a request to appstore connect results in a 401
i can verify that the JWT encodes the header and payload correctly on http://jwt.io
looking at this library, i think i'm selecting the correct curve algorithm:
After creating the token, one must sign it with an Apple-provided private key, using the Elliptic Curve Digital Signature Algorithm (ECDSA) with the P-256 curve and the SHA-256 hash algorithm, or ES256.
i'm not sure what is wrong - maybe i'm not generating the S value correctly?
def generateJWT(): String = {
val privateKeyBytes: Array[Byte] =
Base64.getDecoder.decode("base64 representation of private key")
val S = BigInt(privateKeyBytes)
val curveParams = ECNamedCurveTable.getParameterSpec("P-256")
val curveSpec: ECParameterSpec =
new ECNamedCurveSpec("P-256", curveParams.getCurve(), curveParams.getG(), curveParams.getN(), curveParams.getH());
val privateSpec = new ECPrivateKeySpec(S.underlying, curveSpec)
val privateKey =
KeyFactory.getInstance("EC").generatePrivate(privateSpec)
val unixTimestamp: Long = Instant.now.getEpochSecond
val fiveMins = unixTimestamp.toInt + 300
val header = Map[String, Object](
"kid" -> "appstore connect Key Identifier",
"typ" -> "JWT",
"alg" -> "ES256"
)
val payload = Map[String, Object](
"iss" -> "issuer ID from the API Keys page in App Store Connect",
"aud" -> "appstoreconnect-v1",
"exp" -> new Integer(fiveMins)
)
Jwts
.builder()
.setHeaderParams(header.asJava)
.setClaims(payload.asJava)
.signWith(SignatureAlgorithm.ES256, privateKey)
.compact()
}
I can suggest two things to try out:
JwtBuilder signWith(SignatureAlgorithm var1, Key var2) is deprecated. Can you try using JwtBuilder signWith(Key var1, SignatureAlgorithm var2) , and see if that succeeds?
If not, you can try using bountycastle , which does work for me. Following is the code snippet for getting the private key.
def getPrivateKey(): PrivateKey = {
val pemParser = new PEMParser(new FileReader(<Your Apple-AuthKey file with extension .p8>))
val converter = new JcaPEMKeyConverter
val privateKeyInfo = pemParser.readObject.asInstanceOf[PrivateKeyInfo]
val pKey = converter.getPrivateKey(privateKeyInfo)
pemParser.close()
pKey
}

Having Trouble Encrypting on iOS and decrypting on Node.js using RAW RSA

I am trying to encrypt something on the iOS side and decrypt it on my node.js server. On the server, I am using the library forge. I was able to encrypt something and decrypt it all on node.js, and that worked. I encrypted like this: const encryptedPassword = publicKey.encrypt(password, 'RAW'); and decrypted like this: const password = privateKey.decrypt(encryptedPassword, 'RAW');.
Now, instead of encrypting in the server, I would like to encrypt on my iOS app, but still decrypt using the same way. I found this library, swift-rsautils. https://github.com/btnguyen2k/swift-rsautils/blob/master/Swift-RSAUtils/RSAUtils.swift It has this function called encryptWithRSAKey, which is what I am using. Since it is raw encryption, I tried to pass in padding SecPaddingNone. However, unfortunately it doesn't work and I am unable to decrypt on the the server. The error message is invalid length, and the length of the base64 data does seem a lot bigger. Does anyone know how I can fix this problem?
Here is my iOS code:
let dataString = text.dataUsingEncoding(NSUTF8StringEncoding)
let certificateLabel = "certificate"
let certificateRef = self.getCertificateFromKeyChain(certificateLabel)
let certificateData = self.getDataFromCertificate(certificateRef)
let cryptoImportExportManager = CryptoExportImportManager()
let publicKeyRef = cryptoImportExportManager.importPublicKeyReferenceFromDERCertificate(certificateData)
let encryptedData = self.encryptWithRSAKey(data, rsaKeyRef: publicKeyRef!, padding: SecPadding.None)
let base64EncryptedString = encryptedData?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
I am then sending this base64 encrypted string to the server and trying to decrypt using the private key. It doesn't work unfortunately.
This isn't the answer to your exact question, since I haven't used that specific library, but I have played a little with encryption in javascript and node.js.
I was able to implement the eccjs library which is the Stanford Javascript Crypto Library (SJCL) built with asymmetric elliptical curve support.
On the node.js side:
var ecc = require('eccjs');
var cryptoKeys = ecc.generate(ecc.ENC_DEC); //crypto_keys.enc is the pubic key for encoding. crypto_keys.dec is the private key for decoding.
//send the public key to the client
app.get('/PublicKey', function(req, res){
res.setHeader('Cache-Control', 'private, no-cache, no-store, must-revalidate');
res.setHeader('Expires', '-1');
res.setHeader('Pragma', 'no-cache');
res.setHeader('Content-type', 'text/plain');
res.send('var publicKey = ' + JSON.stringify(cryptoKeys.enc) + ';');
});
//authenticate a user name and a password (encrypted by client) against the domain controller
app.get('/Authenticate', function(req, res){
res.setHeader('Content-type', 'text/plain');
var url = "ldap://na-us-dc01.am.corp.airliquide.com";
var userPrincipalName = req.query.username + "#US-AIRLIQUIDE";
try
{
var cipherMessage = JSON.parse(req.query.encryptedPassword);
var password = ecc.decrypt(cryptoKeys.dec, cipherMessage);
//... Authentication goes here ...
}
catch(err)
{
console.log("Error with authentication: ",err);
res.send("Error with authentication: " + JSON.stringify(err,null,' '));
}
});
In the client:
<script src="ecc.js"></script>
<script src="../PublicKey"></script> <!-- This returns the variable publicKey which has been set equal to the server's public key -->
<script>
function login() {
var plainTextPassword = document.getElementById('password').value;
var cipherTextPassword = ecc.encrypt(publicKey, plainTextPassword);
var username = document.getElementById('name').value;
console.log(ecc, publicKey, cipherTextPassword);
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = (function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
document.getElementById('result').innerHTML = xhttp.responseText;
console.log("Response: " + xhttp.responseText);
}
}).bind(this);
xhttp.open("GET", "../Authenticate?username=" + username + "&encryptedPassword=" + JSON.stringify(cipherTextPassword), true);
xhttp.send();
}
</script>
I'm sure this solution is not perfectly secure and I ended up not using it, and instead implemented HTTPS. However this should provide you with the necessary pieces to do your own asymmetric encryption if that's your ultimate goal.
SecPadding.None has been removed from Swift3 and the code of Swift-RSAUtils has changed so I cannot reproduce your problem.
However I am able to encrypt and then decrypt data with the following code:
let data = "Data to be encrypted".data(using: String.Encoding.utf8)!
let e = RSAUtils.encryptWithRSAKey(data, rsaKeyRef: publicSecKeyRef, padding: SecPadding())
let d = try! RSAUtils.decryptWithRSAPrivateKey(encryptedData: e!, privkeyBase64: privkey)
Can you try again with latest version of Swift-RSAUtils at https://github.com/btnguyen2k/swiftutils ?
Edit: I noticed that you got error "invalid message length". Be noted that RSA cannot encrypt a very large amount of data in one go. It can encrypt a message up to key's size - 11 length.
To workaround that limitation, Swift-RSAUtils splits the long data into small chunks, encrypts each chunk and merges them all together. So, at server side, you should do similarly: split the encrypted data into chunks of key's size, decrypts each one and merges them to the final result.

How to authenticate Game Center User from 3rd party node.js server

I've been trying to get the new iOS Game Center GKPlayer method, generateIdentityVerificationSignatureWithCompletionHandler, working so we can securely rely on the Game Center credentials for authentication. We're using Node.js as the backend server, and I've been trying to verify the signature but to no avail.
Here is the code on the server side that I have - if there's anyone who can chime in on what's missing, that'd be appreciated. The question has been answered somewhat here: How to authenticate the GKLocalPlayer on my 'third party server'?, but Node.js hasn't specifically been tackled. Note that the code below doesn't ensures the validity of the certificate with a signing authority (yet).
//Client sends the payload below
//json.playerId - UTF-8 string
//json.bundleId - UTF-8 string
//json.timestamp - Hex string
//json.salt - base64 encoded
//json.publicKeyURL - UTF-8 string
//json.signature - base64 encoded
var json = JSON.parse(req.body);
console.log(JSON.stringify(json));
//get the certificate
getCertificate(json.publicKeyURL, function(cert){
//read file from fs for now, since getCertificate returns cert in DER format
fs = require('fs');
fs.readFile('/gc-sb.pem', 'utf8', function (err,data) {
if (err) {
console.log(err);
} else {
console.log(data);
var verifier = crypto.createVerify("sha1WithRSAEncryption");
verifier.write(json.playerId, "utf8");
verifier.write(json.bundleId, "utf8");
verifier.write(json.hexTimestamp, "hex");
verifier.write(json.salt, "base64");
var isValid = verifier.verify(data, json.signature, "base64");
console.log("isvalid: " + isValid);
}
});
});
One thing I've found using the crypto module in node.js is that it seems to want the certificate in PEM format, and I believe the format retrieved from Apple is DER. Until I figure out how to convert the DER file to PEM, I've temporarily converted it using
openssl x509 -in gc-sb.cer -inform der -outform pem -out gc-sb.pem
The main thing for me is being able to validate the signature first. Conversion of the certificate and verifying it against a signing authority will come later :)
EDIT: I've figured it out - I was hashing the playerId, bundleId, timestamp and salt, and then using the hashed value as information to verify. I needed to just put those pieces of information into the verifier to verify without the SHA-1 hash (since the verifier will be taking care of it). I've modified the code above to "make it work". Hope this helps anyone that comes across this.
Here is how you can validate gamecenter identity using nodejs. It convert also the der certificate format to pem on the fly.
var crypto = require('crypto');
var request = require('request');
var ref = require('ref');
var token = require('./test.json');
request({url: token.publicKeyURL, encoding: null}, function (error, response, body) {
if (!error && response.statusCode == 200) {
var verifier = crypto.createVerify("sha1");
verifier.update(token.playerId, "utf8");
verifier.update(token.bundleId, "utf8");
var buf = ref.alloc('uint64');
ref.writeUInt64BE(buf, 0, token.timestamp.toString());
verifier.update(buf);
verifier.update(token.salt, 'base64');
var pmd = '-----BEGIN CERTIFICATE-----';
var base64 = body.toString('base64');
var size = base64.length;
for (var i = 0; i < size; i = i + 64) {
var end = i + 64 < size ? i + 64 : size;
pmd = pmd + '\n' + base64.substring(i, end);
}
pmd = pmd + '\n-----END CERTIFICATE-----';
var valid = verifier.verify(pmd, token.signature, "base64");
console.log(valid);
}
});
It's seems there's a npm package for it.
https://github.com/maeltm/node-gamecenter-identity-verifier

Resources