How to decrypt of the encrypted value in dart? - encryption

I am testing the Cryptographic hashing functions for Dart.  I can't find any information about DECRYPTION?
Can anyone show me how to decryption of the encrypted value?
And this is example;
import 'dart:convert';
import 'package:crypto/crypto.dart';
void main() async {
  var key = utf8.encode('p#ssw0rd');
  var bytes = utf8.encode("Dart and Aqueduct makes my life easier. Thank you.");
  // TODO: ENCRYPTION
  var hmacSha256 = new Hmac(sha256, key); // HMAC-SHA256
  var digest = hmacSha256.convert(bytes);
 
  print(“————ENCRYPTION—————“);
  print("HMAC digest as bytes: ${digest.bytes}");
  print("HMAC digest as hex string: $digest");
  print('\r\n');
  // TODO: DECRYPTION
     ????????????
  print(“————DECRYPTION—————“);
  print(?????????);
}

The easiest way IMO is to use encrypt:
import 'package:encrypt/encrypt.dart';
final key = Key.fromUtf8('put32charactershereeeeeeeeeeeee!'); //32 chars
final iv = IV.fromUtf8('put16characters!'); //16 chars
//encrypt
String encryptMyData(String text) {
final e = Encrypter(AES(key, mode: AESMode.cbc));
final encrypted_data = e.encrypt(text, iv: iv);
return encrypted_data.base64;
}
//dycrypt
String decryptMyData(String text) {
final e = Encrypter(AES(key, mode: AESMode.cbc));
final decrypted_data = e.decrypt(Encrypted.fromBase64(text), iv: iv);
return decrypted_data;
}

An HMAC is a message authentication code. It is a digest used to verify that a message has not been tampered with, similar to a signature. It is constructed using a one-way hash function, in this case SHA256. It is not an encryption, and it cannot be reversed, therefore the value you have cannot be decrypted.
The only thing you can do with an HMAC is, given the HMAC and the corresponding plain-text, verifying the origin of the plain-text and that it has not been tampered with.
Looking at the GitHub page for the Dart crypto library, it looks like it only supports digest algorithms. There are no encryption algorithms listed, so you will need to use a different library if you want to do two-way encryption which can actually be decrypted. The Cipher library looks like it could be promising in this regard.

PointyCastle (https://pub.dartlang.org/packages/pointycastle) has been recommended in the past, but it appears to have not yet been updated for Dart 2. :(

Well actually searching a lot couldn't find anything better than this. The encrypt 0.2.0 new and has Dart 2 support. https://pub.dartlang.org/packages/encrypt#-installing-tab-
I need to something that makes My Flutter Mobile App and Aqueduct can share secure communication. Leo Cavalcante is a great guy. While we were talking yesterday he comes up quick answer. So this is another love of Dart. Because every people support each other.
It Supports:
AES (Block Cipher)
Salsa20 (Stream Cipher)
RSA (Asymmetric) (working)
Use this package as a library
Depend on it
Add this to your package's pubspec.yaml file:
dependencies:
encrypt: "^0.2.0"
Install it
You can install packages from the command line:
with pub:
$ pub get
with Flutter:
$ flutter packages get
Alternatively, your editor might support pub get or flutter packages get. Check the docs for your editor to learn more.
Import it
Now in your Dart code, you can use:
import 'package:encrypt/encrypt.dart';

If anyone is looking for a rather simpler solution without having to depend on the library, I have written my custom solution on encrypting and decrypting string values in dart, which obliviously can be used in flutter too. The code is rather straightforward, so I am not going to talk about the code on how it works. The strategy used in encryption is x-or encryption-decryption.
var encryptionKey ='your encryption key';
static String encrypt(String data) {
var charCount = data.length;
var encrypted = [];
var kp = 0;
var kl = encryptionKey.length - 1;
for (var i = 0; i < charCount; i++) {
var other = data[i].codeUnits[0] ^ encryptionKey[kp].codeUnits[0];
encrypted.insert(i, other);
kp = (kp < kl) ? (++kp) : (0);
}
return dataToString(encrypted);
}
static String decrypt(data) {
return encrypt(data);
}
static String dataToString(data) {
var s = "";
for (var i = 0; i < data.length; i++) {
s += String.fromCharCode(data[i]);
}
return s;
}

Related

Decode(Base64) and Decrypt(AES/CBC/PKCS5PADDING) with CryptoJS - React

I am working on the web application using react as front-end and spring mvc as back-end. I need to store some user information in local storage of the browser. I do not want to store that info in local storage as a plain text. So I thought to go for AES encryption at server side and pushing those data back to JS side. For that I need client side decryption framework. I found crypto-js as very useful for all these things. I am not able to understand where I am lacking at client side to decrypt and decode.
I am explaining my Spring Side Encryption Code first which is absolutely fine:
public class EncryptDecrypt {
private static final String SECRET_KEY_1 = "ssdkF$HUy2A#D%kd";
private static final String SECRET_KEY_2 = "weJiSEvR5yAC5ftB";
private IvParameterSpec ivParameterSpec;
private SecretKeySpec secretKeySpec;
private Cipher cipher;
public EncryptDecrypt() throws UnsupportedEncodingException, NoSuchPaddingException, NoSuchAlgorithmException {
ivParameterSpec = new IvParameterSpec(SECRET_KEY_1.getBytes("UTF-8"));
secretKeySpec = new SecretKeySpec(SECRET_KEY_2.getBytes("UTF-8"), "AES");
cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
}
public String encrypt(String toBeEncrypt) throws NoSuchPaddingException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(toBeEncrypt.getBytes());
return Base64.encodeBase64String(encrypted);
}
}
At the client side, I am not able to decode and decrypt the code with simple things. Here is my client side code:
var CryptoJS = require("crypto-js");
var data = "Ggydx4oA1+SKBw+unA8BUUm2tnvkQbp1terdF2PEGFYSEZL/ye08op/0b0BauGtIl1dBIodrlKXo2de3MykYmocd3ctxFtIIki01V+M8XeQj6B384o0G+H7NpVx5tCJjPDvdqVRObtxCTqu3r8QRzYTNcMM5bRhbYxCYl8/NRyPQJnmcJDlRBeVOoJiQNA7Qd5UJD/mNivoyMUfYGV7/DlpylQWWwEAHVdgcb865i8jnf3vqURehAXYoaD6Bgodi1EM4H007uv0o6NEOk3H4jQ==";
var key = "weJiSEvR5yAC5ftB";
// Decode the base64 data so we can separate iv and crypt text.
var rawData = atob(data);
var iv = "ssdkF$HUy2A#D%kd";
var crypttext = rawData.substring(16);
console.log(rawData);
// Decrypt...
var plaintextArray = CryptoJS.AES.decrypt(
{ ciphertext: CryptoJS.enc.Base64.parse(crypttext) },
key,
{ iv: iv }
);
console.log(plaintextArray);
console.log(CryptoJS.enc.Base64.stringify(plaintextArray));
var decryptedData = JSON.parse(CryptoJS.enc.Base64.stringify(plaintextArray).toString(CryptoJS.enc.Utf8));
console.log(decryptedData);
P.S: I have sent JSON to client side and so that I am parsing it in the end. I am newbie for encryption and decryption. I am really stuck with what my client side code should look a like. Please help.
You shouldn't pass string as key in CryptoJS. In this case it considers this string not as key, but as password. And generate key from password by using PBKDF. Working example below:
var data = "Ggydx4oA1+SKBw+unA8BUUm2tnvkQbp1terdF2PEGFYSEZL/ye08op/0b0BauGtIl1dBIodrlKXo2de3MykYmocd3ctxFtIIki01V+M8XeQj6B384o0G+H7NpVx5tCJjPDvdqVRObtxCTqu3r8QRzYTNcMM5bRhbYxCYl8/NRyPQJnmcJDlRBeVOoJiQNA7Qd5UJD/mNivoyMUfYGV7/DlpylQWWwEAHVdgcb865i8jnf3vqURehAXYoaD6Bgodi1EM4H007uv0o6NEOk3H4jQ==";
var rawData = CryptoJS.enc.Base64.parse(data);
var key = CryptoJS.enc.Latin1.parse("weJiSEvR5yAC5ftB");
var iv = CryptoJS.enc.Latin1.parse("ssdkF$HUy2A#D%kd");
var plaintextData = CryptoJS.AES.decrypt(
{ ciphertext: rawData },
key,
{ iv: iv });
var plaintext = plaintextData.toString(CryptoJS.enc.Latin1);
console.log(plaintext);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js"></script>
BTW, you shouldn't use the same IV every time. In this case you miss the base purpose of IV and CBC mode. Your overall security becomes equal to ECB mode.

Encryption interop between Java (Cipher) & JavaScript (crypto-js)

I've been tasked to fix an interoping encryption algorithm that was working perfectly fine from before but suddenly went haywire for reasons unknown, no one has touched any of the code for both of the languages (Java & JS).
I'm not really well verse with cryptography so I don't know what possible solutions to look for or work with. The task was basically to have this encryption code on Java translated to JavaScript that would both have a resulting Base64 string that was to be decrypted through Java.
The following are the code snippets for the encryption being done with Java & JS and decryption process on Java:
Java Encryption
public static String encryptMsg(String message) {
#SuppressLint("GetInstance") Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
byte[] cipherText = cipher.doFinal(message.getBytes(UTF_CHARSET));
return Base64.encodeToString(cipherText, Base64.DEFAULT);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
e.printStackTrace();
} catch (NullPointerException e) {
//Do nothing, nothing to encrypt
}
return null;
}
JavaScript Encryption
function encryptData(data, key) {
const options = {
mode: Crypto.mode.ECB,
padding: Crypto.pad.Pkcs7
}
const secret = Crypto.enc.Utf8.parse(key)
const encrypted = Crypto.AES.encrypt(data, secret, options)
return encrypted.ciphertext.toString(Crypto.enc.Base64)
}
Java Decryption
public static String decryptMsg(String base64cipherText) {
#SuppressLint("GetInstance") Cipher cipher = null;
try {
cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, secret);
String decryptString = new String(cipher.doFinal(Base64.decode(base64cipherText, Base64.DEFAULT)), UTF_CHARSET);
return decryptString;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
e.printStackTrace();
} catch (NullPointerException e) {
//Do nothing, nothing to decrypt
}
return null;
}
Currently the results return null on the encrypted string using the JavaScript encryption function when being decrypted so it's probably encrypting correctly(?) I'm not sure what I'm missing or doing wrong here...
Seems you are missing IV (initialization vector).
don't really know what an IV is or if it's needed here, the encryption Java code doesn't state it anywhere
The IV is an initialization vector allowing to reuse a key to encrypt multiple messages (or blocks), please have a look at the CBC block mode as you are using it.
I am not sure for JavaScript API, but at least I can give you example in Java. As well you can have a look at my blog about crypto examples
Java Encryption
SecureRandom rnd = new SecureRandom();
byte[] iv = new byte[SYMMETRIC_BLOCK_SIZE / 8];
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
SecretKey symmetricKey = new SecretKeySpec(encryptionParams.getKey(), SYMMETRIC_KEY_ALG);
Cipher cipher = Cipher.getInstance(SYMMETRIC_CIPHER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, symmetricKey, ivParamSpec);
byte[] encrypted = cipher.doFinal(encryptionParams.getPlaintext());
/* and encoded form can contain form of base64( IV + ciphertext ) */
For CBC mode the IV must be random. If you don't specify the IVParameter, it will be generated and you can read it from cipher.getIV();. The IV can be public, it is usually prepended before the ciphertext, as the IV is needed to decrypt the ciphertext itself.
Java Decryption
/* if IV is prepended before the ciphertext, it can be fetched as sub-array
of the decoded message */
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(SYMMETRIC_CIPHER_NAME);
cipher.init(Cipher.DECRYPT_MODE, symmetricKey, ivParamSpec);
byte[] decrypted = cipher.doFinal(encryptionParams.getCiphertext());
In this example there's no Mac (message authentication code included), you can have a look at the linked example.
For JavaScript you should have a look at the used API, but the principle stays the same (you have to generate, use, pass and provide the IV too somehow). This blog seems to contain be more complete code.
var iv = CryptoJS.lib.WordArray.random(128/8);
var encrypted = CryptoJS.AES.encrypt(msg, key, {
iv: iv,
padding: CryptoJS.pad.Pkcs7,
mode: CryptoJS.mode.CBC
});

OAEPwithMD5andMGF1Padding in node-rsa

I am trying to figure out how to do RSA encryption with OAEPwithMD5andMGF1Padding in node-rsa.
Below is my code in node.js.
var NodeRSA = require('node-rsa');
var fs = require('fs');
var publicKey = '-----BEGIN PUBLIC KEY-----\n*****\n-----END PUBLIC KEY-----';
var privateKey = '-----BEGIN RSA PRIVATE KEY-----\n*****\n-----END RSA PRIVATE KEY-----'
const constants = require('constants');
var options1 = {
environment: 'node',
encryptionScheme: {
scheme: 'pkcs1_oaep',
hash: 'md5', //hash using for scheme
}
}
var text = 'This is the string to be encrypted using RSA!';
var encryptKey = new NodeRSA(publicKey, 'pkcs8-public', options1);
encryptKey.setOptions(options1)
var encrypted = encryptKey.encrypt(text, 'base64');
console.log(encrypted);
console.log(encryptKey.isPublic(true))
var options2 = {
environment: 'node',
encryptionScheme: {
scheme: 'pkcs1_oaep', //scheme
hash: 'md5', //hash using for scheme
}
}
var decryptKey = new NodeRSA(privateKey, 'pkcs1', options2);
decryptKey.setOptions(options2)
var decrypted = decryptKey.decrypt(encrypted, 'utf8');
console.log('decrypted: ', decrypted);
Result of running the above code.
f1zi49yKJSqkWW2J3Jt2lf1fe79JgqufFawYESOJRqhM4YEcGQBcaP39yptn7vShhsJBCTUOsbiV1YcW/YUzoaSQzX9YU0iTMara7h+LNLUrq4FZ2twy5X3uyAP1sUD1SnvQvlRJqrAh23UAwnx31rv6ySC+XgpLPR7wHYaDbSgyQKiF3qhGRj2SIAZ6weziNPfEm9FifBVjnWMvGDQYbjLbanbnSriN+bWpRtXKH9pQqMoskkiMwCviJdKtKzz/vVr0littPLnw0ojbsGSPKQPS3U3xCH3QiBmxEegc0uy3sJdk6aH/2SMuoPzGu7VS+PsLQctxnvKNnC9qsLFWyA==
true
decrypted: This is the string to be encrypted using RSA!
Below is my code in JAVA
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
public class DecryptATT {
public static void main(String[] args) throws Exception {
String encryptedData = "f1zi49yKJSqkWW2J3Jt2lf1fe79JgqufFawYESOJRqhM4YEcGQBcaP39yptn7vShhsJBCTUOsbiV1YcW/YUzoaSQzX9YU0iTMara7h+LNLUrq4FZ2twy5X3uyAP1sUD1SnvQvlRJqrAh23UAwnx31rv6ySC+XgpLPR7wHYaDbSgyQKiF3qhGRj2SIAZ6weziNPfEm9FifBVjnWMvGDQYbjLbanbnSriN+bWpRtXKH9pQqMoskkiMwCviJdKtKzz/vVr0littPLnw0ojbsGSPKQPS3U3xCH3QiBmxEegc0uy3sJdk6aH/2SMuoPzGu7VS+PsLQctxnvKNnC9qsLFWyA==";
// Cipher decrypt = Cipher.getInstance("RSA/ECB/OAEPwithMD5andMGF1Padding");
Cipher decrypt = Cipher.getInstance("RSA/ECB/OAEPwithSHA1andMGF1Padding");
RSAPrivateKey privateKey = getPrivateKey();
System.out.println("test");
decrypt.init(Cipher.DECRYPT_MODE, privateKey);
byte[] original = decrypt.doFinal(Base64.getDecoder().decode(encryptedData));
System.out.println(new String(original));
}
public static RSAPrivateKey getPrivateKey() throws Exception {
String keyPath = "/Users/C.SubbiahVeluAngamuthu/Desktop/Samsung/Docs/att/Keys/3_my_testing/pkcs8_key";
File privKeyFile = new File(keyPath);
BufferedInputStream bis = null;
try {
bis = new BufferedInputStream(new FileInputStream(privKeyFile));
} catch (FileNotFoundException e) {
throw new Exception("Could not locate keyfile at '" + keyPath + "'", e);
}
byte[] privKeyBytes = new byte[(int) privKeyFile.length()];
bis.read(privKeyBytes);
bis.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
KeySpec ks = new PKCS8EncodedKeySpec(privKeyBytes);
RSAPrivateKey privKey = (RSAPrivateKey) keyFactory.generatePrivate(ks);
return privKey;
}
}
Below is the result of running the JAVA code
test
This is the string to be encrypted using RSA!
But when I change the cipher instance from RSA/ECB/OAEPwithSHA1andMGF1Padding to "RSA/ECB/OAEPwithMD5andMGF1Padding"(which I am assuming is the one that I mentioned in encryptionScheme of node.js program) it throws the below error
test
Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadOAEP(RSAPadding.java:499)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:293)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:363)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at DecryptATT.main(DecryptATT.java:26)
Could some one help me where it is going wrong ?
RSAES-OAEP is parametrized by
the hash Hash used by OAEP, and its width in octet noted hLen
the size of the public key, k octets
the Mask Generation Function (MGF) used by OAEP
Almost invariably, the MGF is MFG1, which itself is parametrized by the hash Hash' used by MFG1, and its width in octet noted hLen' (the ' is not in the standard, I'm making up this notation).
You guessed it, there's noting stating that Hash and Hash' are the same, or even that hLen= hLen'.
And, believe me, unless something special is done about it, under a typical Java environement "RSA/ECB/OAEPwithMD5andMGF1Padding" (if supported) will use MD5 for Hash but default to SHA-1 for Hash'; when perhaps node.js uses MD5 for both.
Facing a similar problem with SHA-256 rather than MD5, we can coerce the nice Java runtime to do the Right Thing with
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privKey, new OAEPParameterSpec(
"SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT
));
I fear you won't be so lucky, since MGF1ParameterSpec seems to never have had an MD5 specifier; but perhaps give a try to new MGF1ParameterSpec("MD5") to get one before giving up.
If one really needs to get the job done under Java, one option is to roll one's RSAES-OAEP with MD5 on top of Cipher invoked with "RSA/ECB/NoPadding", which will perform textbook RSA, by far the most complex building block (at least, all the key management, modular arithmetic, and ability to offload to an HSM is taken care of). That's few dozens lines of code, including MFG1.
Another option might be BouncyCastle.
It's a bad idea to keep using MD5, even in MFG1. And it is an unmitigated disaster to use it as the main hash if adversaries can choose a part of the message at a time when they know what's before that part. If in doubt, don't use MD5.

Converting node.js Jwt example to .Net

I am trying to use a web api. They require that I use Jwt tokens. I am trying to do this in a asp.net 4.5.2 web app on azure. I have downloaded the Microsoft.IdentityModel.Tokens Nuget package and would like to use it to generate the needed tokens.
The web api I am trying to use has an example in node.js on how to generate the proper token. Here is their example:
var jwt = require('jsonwebtoken');
var payload = {
iss: api_key,
exp: ((new Date()).getTime() + 5000)
};
//Automatically creates header, and returns JWT
var token = jwt.sign(payload, api_secret);
All of the examples for the nuget package do a ton more than what is shown in the node.js example. Things like creating a secret value, claims identity, signing credentials and all kinds of other stuff.
I have the api_key value and the api_secret value. Can someone please tell me how to do the same thing using the nuget package as what they show using node.js?
After doing some research I think I have a solution. This is the simplest it gets in c# unfortunately, but I think its a passable solution considering we are talking apples and oranges. It seems C# is twice as verbose about the options and doesn't have sensible defaults for anything.
using System;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
namespace stackoverflow
{
class Program
{
static void Main(string[] args)
{
String api_key = "apiKey123123";
String api_secret = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
var signingKey = Convert.FromBase64String(api_secret);
JwtHeader jwtHeader = new JwtHeader(
new SigningCredentials(
new SymmetricSecurityKey(signingKey),
SecurityAlgorithms.HmacSha256Signature
)
);
JwtPayload jwtPayload = new JwtPayload {
{"iss", api_key},
{"exp", ((DateTimeOffset)DateTime.UtcNow).AddMilliseconds(5000).ToUnixTimeMilliseconds() }
};
var jwt = new JwtSecurityToken(jwtHeader, jwtPayload);
var jwtHandler = new JwtSecurityTokenHandler();
Console.Write(jwtHandler.WriteToken(jwt));
Console.ReadLine();
}
}
}
Only import was this package:
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.2.1" />

Customer-supplied encryption keys with dart/gcloud

Using dart-lang/gcloud to read and write files to Google Cloud Storage, is it possible to provide customer-supplied encryption keys?
The Dart gcloud library is built upon dart-lang/googleapis which itself is interfaces with the Cloud Storage REST API, but the HTTP client it uses is so abstracted that it's hard to tell how to set the headers necessary for custom encryption.
Currently there is no support for custom encryption keys in package:gcloud.
Though the Storage constructor accepts an http.Client. So you can supply your own client which adds the headers, something along the lines of:
import 'package:http/http.dart' as http;
import 'package:gcloud/storage.dart' as storage;
class ClientWithKeys extends http.client {
final String encryptionAlgorithm;
final String encryptionKey;
final String encryptionSHA256;
ClientWithKeys(this.encriptionAlgorithm,
this.encriptionKey,
this.encryptionSHA256);
Future<StreamedResponse> send(request) {
request.headers['x-goog-encryption-algorithm'] = encryptionAlgorithm;
request.headers['x-goog-encryption-key'] = encryptionKey;
request.headers['x-goog-encryption-key-sha256'] = encryptionSHA256;
return super.send(request);
}
}
code() {
final client = new ClientWithKeys('<algo>', '<key>', '<sha256>');
final api = new storage.Storage(client, '<project-id>');
...
client.close();
}

Resources