Unable to decrypt aes-192-gcm - aes-gcm

I'm using nodejs to encrypt and decrypt aes-192-gcm
here's my code:
const encrypted = decrypt.encryptText('aes-192-gcm', 'FnpkKuIoqZL5B3tnE0Htmg==', '1z3FtB6OitmFOIsP', 'helloWorld', 'base64');
const de = decrypt.decryptText('aes-192-gcm', 'FnpkKuIoqZL5B3tnE0Htmg==', '1z3FtB6OitmFOIsP', encrypted, 'utf-8');
console.log(encrypted);
console.log(de);
Functions used:
function encryptText(cipher_alg, key, iv, text, encoding) {
var cipher = crypto.createCipheriv(cipher_alg, key, iv);
encoding = encoding || "binary";
var result = cipher.update(text, "utf8", encoding);
result += cipher.final(encoding);
return result;
}
function decryptText(cipher_alg, key, iv, text, encoding) {
const decipher = crypto.createDecipheriv(cipher_alg, key, iv);
encoding = encoding || "binary";
let result = decipher.update(text, encoding);
result += decipher.final();
return result;
}
The error I'm getting:
Unsupported state or unable to authenticate data

The crypto module of NodeJS uses OpenSSL. This API has special parameters for GCM / AEAD ciphers. Methods to use them have been added to the API, such as getAuthTag and setAuthTag. Without the latter, the method always throws an exception for GCM mode decryption. The tag is (fortunately) not considered part of the ciphertext in NodeJS / OpenSSL. Other languages runtimes - such as Java - do consider it part of the ciphertext.

Couple of problems
you are passing a wrong encoding format for the decryptText()
AuthTag is required when using GCM, CCM and OCB.
I have attached a sample code based on the snippet you have shared.
var cipherTag;
const encrypted = encryptText('aes-192-gcm', 'FnpkKuIoqZL5B3tnE0Htmg==', '1z3FtB6OitmFOIsP', 'helloWorld', 'base64');
const de = decryptText('aes-192-gcm', 'FnpkKuIoqZL5B3tnE0Htmg==', '1z3FtB6OitmFOIsP', encrypted, 'base64');
console.log(encrypted);
console.log(de);
function encryptText(cipher_alg, key, iv, text, encoding) {
var cipher = crypto.createCipheriv(cipher_alg, key, iv);
encoding = encoding || "binary";
var result = cipher.update(text, "utf8", encoding);
result += cipher.final(encoding);
cipherTag = cipher.getAuthTag();
return result;
}
function decryptText(cipher_alg, key, iv, text, encoding) {
const decipher = crypto.createDecipheriv(cipher_alg, key, iv);
encoding = encoding || "binary";
decipher.setAuthTag(cipherTag);
let result = decipher.update(text, encoding, 'utf8');
result+= decipher.final('utf8');
return result.toString();
}
// Will output
b2SMQRBt/EgNgQ==
helloWorld

Related

Base64 encoder in Kotlin

can you help me with refactoring this method from java to kotlin, please ? I have a problem with ByteBuffer in return statement. Looks like in Kotlin it doesnt work that way.
public String encryptData(Object payload, Key secretKey)
throws NoSuchAlgorithmException, NoSuchPaddingException, JsonProcessingException, BadPaddingException,
IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException {
var initialVector = new byte[INITIAL_VECTOR_SIZE];
secureRandom.nextBytes(initialVector);
var cipher = Cipher.getInstance(AES_TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(initialVector));
var data = cipher.doFinal(objectMapper.writeValueAsString(payload).getBytes());
var byteBuffer = ByteBuffer.allocate(initialVector.length + data.length);
return new String(
Base64.getEncoder()
.encode(byteBuffer
.put(0, initialVector)
.put(INITIAL_VECTOR_SIZE, data))
.array());
}
It's cleaner to setup your byteBuffer outside of base 64 conversion. Once you have done that, you can:
...
return Base64.getEncoder().encodeToString(byteBuffer)

Authorization Error while rendering the Jasper Report

Occasionally getting "Unauthorized Error" when my Base64 encoded token contains a "+" sign (which is a valid Base 64 character)
I am using Visualize-JS to render the Jasper report in MVC Boilerplate. To authenticate every request, I am using the pre authorization flow where I am sending a Base64 encoded token from my application and on Jasper server decoding it.
Everything is working fine except, occasionally I am getting "Unauthorized Error". When I checked I found it is only failing when my Base64 encoded token contains a "+" sign (which is a valid Base 64 character). My C# code is URL encoding the + sign as %2B whereas the Java code while decoding the same URL converts %2B into a space. (Note - I am encoding in C# and decoding in Java since, Jasper server only supports JAR files).
Is anyone else facing this or have faced earlier and found the solution?
For now, as a work around I am replacing the space with a + sign but, I am searching for a better solution.
Here is the faulty token, C# code for encoding and Java code for decoding
Token - f0NNFeJKfvBiWZP7I7vTGHb8yhiDEmkoE35p6ueQceBiMUJbPoaF927UsHn7w2AdCbKmfjjfZyKxm8iwrTo37kLZPE91EuT4WJrQ/KjQ+r0=
C# Code for Encryption -
public string Encrypt(string plainText)
{
byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);
return HttpUtility.UrlEncode(Convert.ToBase64String(Encrypt(plainBytes, GetRijndaelManaged(_config.EncryptionKey))));
}
public RijndaelManaged GetRijndaelManaged(string secretKey)
{
byte[] keyBytes = new byte[16];
byte[] secretKeyBytes = Encoding.UTF8.GetBytes(secretKey);
Array.Copy(secretKeyBytes, keyBytes, Math.Min(keyBytes.Length, secretKeyBytes.Length));
return new RijndaelManaged
{
Mode = CipherMode.CBC,
Padding = PaddingMode.PKCS7,
KeySize = 128,
BlockSize = 128,
Key = keyBytes,
IV = keyBytes
};
}
public byte[] Encrypt(byte[] plainBytes, RijndaelManaged rijndaelManaged)
{
return rijndaelManaged.CreateEncryptor().TransformFinalBlock(plainBytes, 0, plainBytes.Length);
}
Java Code - encryptedText here is the token mentioned above
public String decrypt(String encryptedText){
encryptedText = decode(encryptedText);
byte[] cipheredBytes = Base64.getDecoder().decode(encryptedText);
byte[] keyBytes = getKeyBytes(secretKey);
return new String(decrypt(cipheredBytes, keyBytes, keyBytes), "UTF-8");
}
public static String decode(String url)
{
try {
String prevURL="";
String decodeURL=url;
while(!prevURL.equals(decodeURL))
{
prevURL=decodeURL;
decodeURL=URLDecoder.decode( decodeURL, "UTF-8" );
}
return decodeURL.replace(" ", "+");
} catch (UnsupportedEncodingException e) {
return "Issue while decoding" +e.getMessage();
}
}
private byte[] getKeyBytes(String key) throws UnsupportedEncodingException{
byte[] keyBytes= new byte[16];
byte[] parameterKeyBytes= key.getBytes("UTF-8");
System.arraycopy(parameterKeyBytes, 0, keyBytes, 0, Math.min(parameterKeyBytes.length, keyBytes.length));
return keyBytes;
}

Digital Signature different everytime

Digital signatures depend upon message and the secret key. Suppose receiver copies the message several times ( the reason for which we unique ids) , why will the signature be different (since the message and secret key are same)?
Bitcoin uses ECDSA, and the reason the signature is different for the same message and key is the random nonce, which is generated each time a signature is created.
An ECDSA signature is a pair (r,s) where r is the X coordinate of kG, and s = (m+r*x)/k (where k=nonce, m=message hash, x=private key, G=curve generator).
See also #3 in https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Signature_generation_algorithm
This answer is just a practical supplement to the theoretical answer provided above. This snippet digitally signs inside the for-loop the same message over and over again using the same private key and each time, it is printing out different digital signatures. Each of these signatures are valid and will verify using the signer's public key.
import java.security.Security;
import java.security.Signature;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.KeyPairGenerator;
import java.security.KeyPair;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
class liptic
{
static String the_message = "how are you, little tiger?";
public static void main(String args[]) throws Exception
{
Security.addProvider(new BouncyCastleProvider());
SecureRandom random = new SecureRandom();
ECParameterSpec ecSpec
=
ECNamedCurveTable.getParameterSpec("secp256k1");
KeyPairGenerator g =
KeyPairGenerator.getInstance("ECDH", "BC");
g.initialize(ecSpec, random);
KeyPair keyPair = g.generateKeyPair();
byte[] bob_pubkey = Hex.decode( "040581E4AEEEB1CEA57094D1AD97B8C721509B6E5D3690C70BBB8EB2C5FE8040"
+ "FB2C9B0A77EA2AD05C5E8DB499F647BC9A8BE829961950D6F5A45952C097CCB0BC");
Signature signer = Signature.getInstance("SHA256withECDSA", "BC");
ECPublicKeySpec pubkSpec = new ECPublicKeySpec(
ecSpec.getCurve().decodePoint(bob_pubkey), ecSpec);
ECPublicKey publicKey
= (ECPublicKey)KeyFactory.getInstance("ECDSA", "BC")
.generatePublic(pubkSpec);
byte[] signature = null;
byte[] input = the_message.getBytes();
signer.initSign(keyPair.getPrivate());
signer.update(input);
for (int i = 0; i < 5; i++) {
signature = signer.sign();
System.out.println(o(signature));
}
}
public static String o(byte[] bytes)
{
int n = 0;
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X", b));
n++;
if (n % 128 == 0) {
sb.append("\n");
}
}
return sb.toString();
}
}
Here are the digital signatures:
3046022100889CC42C4BAA07FF33AB34CADD8BCB0A44E77031D4A5F5A9849840DF3AB63FDA0221009CA5C49FC0EBE9F839A0CFAB18CEC91C9169FF439C1E2DFD724D06E2DB9FE258
30460221009D30465EFD3676982CBE12B998D41D012322C255594D5037F156143AEC7E7305022100A77FE7DEB580837A1A5A5D1B74334C56E9F26BA1834EE3AC93ECEB01349A6F1C
3045022100DF4AF153D808A9199C18C97E689F1214E7F59C621D6ECBAADFE206B83707CA2802203E592D0152E79E14084183206F4B6DBC95D68FBA2D23F65884A3B68FA79A4E04
304502202E9FA22709308D08106F76CBB6278B3F485A3C706EDA3FFAF5CE744D4B90E9510221009DD2370863D6C1CE36D828FF9B98347905F2D0856052C4A30B25DD00575B8921
3045022100AA46FEA1A80498E481D46B17EFD7FBE6656641CD719AF1F5DC0C77ADD334729D0220471472117374E0284074EBC81172E6271CA9D86F54AFCE6E6CF6863814EBF824
This is using the Elliptic Curve Digital Signature Algorithm.

base64 encoded PNG to Blob

I am fiddling around with creating a Blob out of a base64 encoded PNG...
final FormData formData = new FormData();
final String base64Image = "data:image/png;base64,iVBORw0K<reduced the data.....>gg==";
final Blob blob = new Blob([base64Image],"image/png");
formData.append('file', blob, "android.png");
req.send(formData);
I don't know what I a doing wrong but the contents of blob is something but not the png I would like it to be.
thx in advance...
[Update]
final FormData formData = new FormData();
final String base64Image = "iVBORw0KGgo<...reduce data...>kJggg==";
// BTW: I used the Base64 from dart-sdk/io/base64.dart
final List<int> intList = Base64.decode(base64Image);
final Int8Array int8array = new Int8Array.fromList(intList);
final String atobString = window.atob(base64Image);
// Does not work
// final Blob blob = new Blob([atobString]);
// The same...
// final Blob blob = new Blob([int8array]);
formData.append('file', blob, "android.png");
//formData.append('new-filename', "icon-share.png");
req.send(formData);
I think the number of bytes generated by Base64.decode are OK. The filesize was 1003 bytes and decoding also produces 1003 bytes.
[Update 2]
Here is the source I am talking about:
https://github.com/MikeMitterer/AndroidIconGenerator.DART/blob/master/test/src/restserver.dart
OK, here is the answer to my own question:
import 'dart:convert'
...
test(' -> Upload File to REST-Server', () {
final HttpRequest req = new HttpRequest();
loadEnd(HttpRequest request) {
if (request.readyState == HttpRequest.DONE) {
switch(request.status) {
case HttpStatus.HTTP_200_OK:
expect(response['path'].endsWith("android.png"),true);
break;
case HttpStatus.HTTP_0_COMMUNICATION_FAILED:
expect(request.status,HttpStatus.HTTP_200_OK);
break;
default:
expect(request.status,HttpStatus.HTTP_200_OK);
break;
}
}
}
req.open("POST", uriprovider.forUpload().toString());
// REST returns JSON Data
req.setRequestHeader('Accept', 'application/json');
req.onLoadEnd.listen(expectAsync1((ProgressEvent e) => loadEnd(req)));
final FormData formData = new FormData();
final String base64Image = "data:image/png;base64,iVBORw0KG<code reduce for sample>RU5ErkJggg==";
final String raw = "iVBORw0KG<code reduce for sample>RU5ErkJggg==";
final String contenttype = "image/png";
// Base64 is a modified version of dart-sdk/lib/io/base64.dart
final List<int> intList = BASE64.decode(raw);
final Int8Array int8array = new Int8Array.fromList(intList);
// Converting to Uint8Array brought the solution!
final Uint8Array uint8array = new Uint8Array(intList.length);
// does not work!
//var binary = window.atob(raw);
final Blob blob = new Blob([uint8array]);
formData.append('file', blob, "android.png");
req.send(formData);
});
Thanks to everyone pushing me in the right direction!
Need decode base64 (i.e. atob) url, before you encode to Blob. It's easy! Try it!

Objective C AES encrypt equal to java version

I have java code to encrypt a password,
public static String encryptToString(String content,String password) throws IOException {
return parseByte2HexStr(encrypt(content, password));
}
private static byte[] encrypt(String content, String password) {
try {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(password.getBytes());
kgen.init(128, secureRandom);
SecretKey secretKey = kgen.generateKey();
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance("AES");
byte[] byteContent = content.getBytes("utf-8");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] result = cipher.doFinal(byteContent);
return result;
} catch (Exception e) {
log.error(e.getMessage(),e);
}
return null;
}
public static String parseByte2HexStr(byte buf[]) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < buf.length; i++) {
String hex = Integer.toHexString(buf[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
sb.append(hex.toUpperCase());
}
return sb.toString();
}
Now I need to encrypt/decrypt it with objective-c, I do a lot of search and none method would generate the same encrypted output.
What's the objective-c version code equal to java code?
test case: encryptToString("test","password") => DA180930496EC69BFEBA923B7311037A
I believe the answers to this question are what you are looking for: Any cocoa source code for AES encryption decryption?
I adapted a function somebody posted as answer: https://gist.github.com/4335132
To use:
NSString *content = #"test";
NSData *dataToEncrypt = [content dataUsingEncoding:NSUTF8StringEncoding];
NSData *data = [dataToEncrypt AES128EncryptWithKey:#"password"];
NSString *hex = [data hexString];
It's not exactly the same because your Java code does not use the password itself for encrypting but uses it to seed a random number generator. But I think that this should still work with what you are trying to do.