Generate digital signature with nodejs for Alipay API - node.js

I am implenting ALIPAY API, to my project. In order to make some request I need to generate a signature with RSA private.pem - public.pem. The documentions is only showing JAVA exemple, but I need to implement it in NodeJS. So I try using crypto.sign but cannot get this to work.
Here is the JAVA code :
const g = (content, privateKey, charset) => {
try {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(priPKCS8);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(priKey);
signature.update(content.getBytes(charset));
byte[] signed = signature.sign();
return Base64.getEncoder().encodeToString(signed);
} catch (Exception e) {
throw new RuntimeException("the key's format maybe not right");
}
}
Can any one help to convert this into NodeJS code.
HERE IS MY WORKING SOLUTIONS:
/**
* #param {*} // let parameters = new Map(); parameters.set("service", "alipay.overseas.secmerchant.maintain.queryStatus");
*/
signRSA: async (parameters) => {
let unnecessaryParams = Array.from(parameters.entries())
.filter(a1 => a1[1] === undefined || a1[1].length === 0 || a1[1] === "null")
.map(a1 => a1[0]);
unnecessaryParams.forEach(a1 => parameters.delete(a1));
let charset = ["_input_charset", "charset"].filter(a1 => parameters.has(a1)).map(a1 => parameters.get(a1))[0];
charset = charset === undefined ? "UTF-8" : charset;
//MD5,RSA,RSA2
let signContent = Array.from(parameters.entries())
.filter(a1 => {
return a1[0] !== "sign" && a1[0] !== "sign_type";
})
.sort((a1, a2) => a1[0].localeCompare(a2[0]))
.map(a1 => a1[0] + "=" + a1[1]).join("&");
let key = fs.readFileSync('./services/alipay/rsa_private_key.pem');
// let RSA_sign = r.sign_it(signContent, key, "RSA2")
let RSA_sign = crypto.createSign('RSA-SHA256')
RSA_sign.update(signContent)
let s1 = RSA_sign.sign(key, "base64")
parameters.set("sign", s1);
return Array.from(parameters.entries()).map(a1 => a1[0] + "=" + encodeURIComponent(a1[1])).join("&");
}

I've created a Node.js version of this code, it should sign content using the provided private key.
I've also created a verifySignature function to check this, and created the equivalent code in Java (using your code as a template), just to ensure we're getting the same result in both languages.
Node.js
const crypto = require("crypto");
// Sample keys I've generated
const privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAICFIt6pLMSNn9scI/h85V/DamtsFqfN4DB+VSPpGMAcwEByKVsiBzdX7JJsr8uquoVWEmFPwymxUVwIy+MOK03QEaQDns0W/SeVNXyWvDTO2w65vV9hIEO1VyiQiBA8n0yoQGfbn2KBnv7SggJDpy10cF3lx5SdHV96lF+qyBs7AgMBAAECgYAQEJRLQNpXt1xEB0B3zyTc05B53Qj38MQVS7VYx8pQOUfOxnZk7dv5DwSDSRKwYqbuA9RIVbAPhhqlZnQV7a9722QBFcEKLh6/65HCX248t/v6/x1kj9p6rcbLuFuakjKXs4AznWmb4YV2Flh9/qx9keUzdS5/UPJZVU2+grAAAQJBAO/3JjTRgiGQxEdA4MVpfs0lh/+gzbDszL1In5USAPKyGPDqYIktAfF3cBc7BKyK3kBenJi3S5Qv5R3u2Ly6H0cCQQCJG5oAvEt8JPL7ZyxHzdTP7+7hWXRGOyK2Ar0CO3fbg+SGls5liJY3HNHc0lrdyG1A+5Z6AKUNYCgfbOhqmeZtAkEA5CzhUoYJNDuAt7Q0RuLqZM2URPk1vU9d23qr68ajyiKZXrOuuaFnYKDOn/hJmHuvnAua4gggwLbOKSlNRB/CzwJAKedztBHYiELKuKeZ0wBHsJ3GRr2OWgCs5TAFEG+YfFDdQX1J66JJNuLqCTGJcAtXyOqb3QHhcCsZWDFy/1G2KQJBAKRnm7D0BHLOK37Xp3MxGva4tFP6VPdqjgIVBW3PM8BcwTcw8VML3kkFh2y0gJdLRxzFwDDJgfG6Cxp8i1gqB+4=";
const publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAhSLeqSzEjZ/bHCP4fOVfw2prbBanzeAwflUj6RjAHMBAcilbIgc3V+ySbK/LqrqFVhJhT8MpsVFcCMvjDitN0BGkA57NFv0nlTV8lrw0ztsOub1fYSBDtVcokIgQPJ9MqEBn259igZ7+0oICQ6ctdHBd5ceUnR1fepRfqsgbOwIDAQAB";
const content = "We are such stuff as dreams are made on, and our little life is rounded with a sleep";
const signature = signContent(content, base64KeyToPEM(privateKey, "PRIVATE"), "utf8");
console.log("Signature:", signature);
console.log("Verify signature:", verifySignature(content, base64KeyToPEM(publicKey, "PUBLIC"), signature, "utf8"));
function signContent(content, privateKey, encoding) {
const sign = crypto.createSign("SHA256");
sign.write(content, encoding);
sign.end();
return sign.sign(privateKey, "base64");
}
function verifySignature(content, publicKey, signature, encoding) {
const verify = crypto.createVerify("SHA256");
verify.write(content, encoding);
verify.end();
return verify.verify(publicKey, Buffer.from(signature, "base64"));
}
function base64KeyToPEM(base64Key, keyType) {
return [`-----BEGIN ${keyType} KEY-----`, ...splitStringIntoChunks(base64Key, 64), `-----END ${keyType} KEY-----`].join("\n");
}
function splitStringIntoChunks(input, chunkSize) {
const chunkCount = Math.ceil(input.length / chunkSize)
return Array.from( { length: chunkCount } ).map((v, chunkIndex) => input.substr(chunkIndex * chunkSize, chunkSize));
}
Java
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.security.spec.*;
import java.util.Base64;
public class DigitalSignature {
public static void main(String[] args)
{
try {
// Sample keys I've generated
final String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAICFIt6pLMSNn9scI/h85V/DamtsFqfN4DB+VSPpGMAcwEByKVsiBzdX7JJsr8uquoVWEmFPwymxUVwIy+MOK03QEaQDns0W/SeVNXyWvDTO2w65vV9hIEO1VyiQiBA8n0yoQGfbn2KBnv7SggJDpy10cF3lx5SdHV96lF+qyBs7AgMBAAECgYAQEJRLQNpXt1xEB0B3zyTc05B53Qj38MQVS7VYx8pQOUfOxnZk7dv5DwSDSRKwYqbuA9RIVbAPhhqlZnQV7a9722QBFcEKLh6/65HCX248t/v6/x1kj9p6rcbLuFuakjKXs4AznWmb4YV2Flh9/qx9keUzdS5/UPJZVU2+grAAAQJBAO/3JjTRgiGQxEdA4MVpfs0lh/+gzbDszL1In5USAPKyGPDqYIktAfF3cBc7BKyK3kBenJi3S5Qv5R3u2Ly6H0cCQQCJG5oAvEt8JPL7ZyxHzdTP7+7hWXRGOyK2Ar0CO3fbg+SGls5liJY3HNHc0lrdyG1A+5Z6AKUNYCgfbOhqmeZtAkEA5CzhUoYJNDuAt7Q0RuLqZM2URPk1vU9d23qr68ajyiKZXrOuuaFnYKDOn/hJmHuvnAua4gggwLbOKSlNRB/CzwJAKedztBHYiELKuKeZ0wBHsJ3GRr2OWgCs5TAFEG+YfFDdQX1J66JJNuLqCTGJcAtXyOqb3QHhcCsZWDFy/1G2KQJBAKRnm7D0BHLOK37Xp3MxGva4tFP6VPdqjgIVBW3PM8BcwTcw8VML3kkFh2y0gJdLRxzFwDDJgfG6Cxp8i1gqB+4=";
final String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAhSLeqSzEjZ/bHCP4fOVfw2prbBanzeAwflUj6RjAHMBAcilbIgc3V+ySbK/LqrqFVhJhT8MpsVFcCMvjDitN0BGkA57NFv0nlTV8lrw0ztsOub1fYSBDtVcokIgQPJ9MqEBn259igZ7+0oICQ6ctdHBd5ceUnR1fepRfqsgbOwIDAQAB";
final String content = "We are such stuff as dreams are made on, and our little life is rounded with a sleep";
String signature = signContent(content, privateKey, "UTF-8");
System.out.println("Signature: " + signature);
System.out.println("verifySignature: " + verifySignature(signature, content, getPublicKeyFromBase64(publicKey), "UTF-8"));
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
private static PublicKey getPublicKeyFromBase64(String publicKeyBase64) throws Exception {
return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyBase64)));
}
public static String signContent(String content, String privateKey, String charset) throws Exception
{
try {
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(priPKCS8);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(priKey);
signature.update(content.getBytes(charset));
byte[] signed = signature.sign();
return Base64.getEncoder().encodeToString(signed);
} catch (Exception e) {
throw new RuntimeException("signContent: Exception occurred: " + e.getMessage());
}
}
public static Boolean verifySignature(String signatureBase64, String content, PublicKey publicKey, String charset) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(content.getBytes(charset));
return signature.verify(Base64.getDecoder().decode(signatureBase64));
}
}

Related

AES/GCM/NoPadding in swift iOS

I want to implement AES GCM encryption/decryption using shared secret.
But Backend server is throwing error with my keys. This is working fine in Android app with Java. Although size of encrypted text is same in both iOS and Android.
My iOS code.
private func getCryptoPublicKey() -> (privateKey: Curve25519.KeyAgreement.PrivateKey,publicKey :Curve25519.KeyAgreement.PublicKey){
let ephemeralKey = Curve25519.KeyAgreement.PrivateKey()
//AES.KeyWrap.wrap(SymmetricKey(size: .bits256), using: <#T##SymmetricKey#>)
let ephemeralPublicKey = ephemeralKey.publicKey
let privateKey = ephemeralKey.rawRepresentation.base64EncodedString()
let publicKey = ephemeralPublicKey.rawRepresentation.base64EncodedString()
Singleton.shared.clientPrivateKey = privateKey
Singleton.shared.clientPublicKey = publicKey
return (ephemeralKey,ephemeralPublicKey)
}
let cryptoData = self.getCryptoPublicKey()
let publicKeyData = Data.init(base64Encoded: ServerPublicKey)! //ServerPublicKey is shared by server
let serverPublicKey = try! Curve25519.KeyAgreement.PublicKey(rawRepresentation: publicKeyData)
let clientSharedSecret = try! cryptoData.privateKey.sharedSecretFromKeyAgreement(with: serverPublicKey)
let clientSharedSecretSymmetricKey = clientSharedSecret.x963DerivedSymmetricKey(using: SHA256.self, sharedInfo: Data(), outputByteCount: 32)
let clientSealedBoxDatakClientId = try! AES.GCM.seal(kClientId.data(using: .utf8)!, using: clientSharedSecretSymmetricKey).combined!
let clientSealedBoxDatakClientSecret = try! AES.GCM.seal(kClientSecret.data(using: .utf8)!, using: clientSharedSecretSymmetricKey).combined!
let clientSealedBoxDataCredent = try! AES.GCM.seal("client_credentials".data(using: .utf8)!, using: clientSharedSecretSymmetricKey).combined!
let clientIDEncryptedBase64Str = clientSealedBoxDatakClientId.base64EncodedString()
let clientSecretEncryptedBase64Str = clientSealedBoxDatakClientSecret.base64EncodedString()
let clientCredentialsEncryptedBase64Str = clientSealedBoxDataCredent.base64EncodedString()
JAVA CODE:
`
private void makeKeyExchangeParams() {
KeyPairGenerator kpg;
try {
kpg = KeyPairGenerator.getInstance("X25519", new org.bouncycastle.jce.provider.BouncyCastleProvider());
kpg.initialize(AES_KEY_SIZE);
KeyPair kp = kpg.generateKeyPair();
publickey = kp.getPublic();
keyAgreement = KeyAgreement.getInstance("XDH", new org.bouncycastle.jce.provider.BouncyCastleProvider());
keyAgreement.init(kp.getPrivate());
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
}
}
package com.bankofbaroda.bobabhivyakti.Utilities.Encryption;
import android.os.Build;
import androidx.annotation.RequiresApi;
import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
public class CryptoService {
public static final int GCM_IV_LENGTH = 12;
public static final int GCM_TAG_LENGTH = 16;
#RequiresApi(api = Build.VERSION_CODES.O)
public static String getEncryptedText(String plainText, SecretKey secretKey) {
if(plainText == null) {
plainText = "";
}
try {
byte[] ivBytes = new byte[GCM_IV_LENGTH];
SecureRandom random = new SecureRandom();
random.nextBytes(ivBytes);
String iv = Base64.getEncoder().encodeToString(ivBytes);
byte[] cipherText = encrypt(plainText.getBytes(), secretKey, ivBytes);
String text = Base64.getEncoder().encodeToString(cipherText);
text = iv+text;
return text;
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
#RequiresApi(api = Build.VERSION_CODES.O)
public static String getDecryptedText(String cipherText, SecretKey secretKey) {
try {
String iv = cipherText.substring(0,16);
byte[] ivBytes = Base64.getDecoder().decode(iv);
cipherText = cipherText.substring(16);
byte[] data = Base64.getDecoder().decode(cipherText);
return decrypt(data, secretKey, ivBytes);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
private static byte[] encrypt(byte[] plaintext, SecretKey key, byte[] nonce) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
return cipher.doFinal(plaintext);
}
private static String decrypt(byte[] cipherText, SecretKey key, byte[] nonce) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
}
}
`
I am not able to get what is exact issue here. Do I have to change something on Java side?

How to verify certificate chain in Flutter?

I have a mobile app written in Flutter that stores an intermediate cert. Other mobile clients can send certificates to this device and it should decide whether the sent certificate was issued based on the intermediate cert or not. Shortly, I have to verify the integrity of the certificate chain.
I implemented a test function in Node.js/TS but I don't know how I can do the same thing in Flutter.
Here is the example code:
import fs from "fs";
import nodeForge from "node-forge";
function validateCert(certPem: string) {
const pki = nodeForge.pki;
try {
const caCert = fs.readFileSync("pki_int_ca.pem", { encoding: "utf-8" });
const caStore = pki.createCaStore([caCert]);
const certToVerify = pki.certificateFromPem(certPem);
const verified = pki.verifyCertificateChain(caStore, [certToVerify]);
console.log("Certificate Verified: " + verified);
} catch (err) {
console.log(err);
}
}
I am using this library https://github.com/jeroentrappers/x509 to verify certificate chain in flutter.
here is my code:
import 'dart:convert';
import 'dart:typed_data';
import 'package:x509b/x509.dart' as x509;
import 'package:asn1lib/asn1lib.dart';
import 'flutter_log.dart';
//caCert is the root certificate. and serverCert is the certificate need to be verifed
bool verifyCertificate(String caCert, String serverCert) {
// var strX1PublicKeyInfo = "-----BEGIN PUBLIC KEY-----\nSOME PUBLIC KEY\n-----END PUBLIC KEY-----";
// var strX2Certificate = "-----BEGIN CERTIFICATE-----\nSOME CERTIFICATE\n-----END CERTIFICATE-----";
var x1PublicKey = (x509.parsePem(caCert).single as x509.X509Certificate).publicKey as x509.RsaPublicKey;
var x2Certificate = x509.parsePem(serverCert).single as x509.X509Certificate;
var x2CertificateDER = decodePEM(serverCert);
var asn1Parser = ASN1Parser(x2CertificateDER);
var seq = asn1Parser.nextObject() as ASN1Sequence;
var tbsSequence = seq.elements[0] as ASN1Sequence;
var signature = x509.Signature(Uint8List.fromList(x2Certificate.signatureValue!));
var verifier = x1PublicKey.createVerifier(x509.algorithms.signing.rsa.sha256);
return verifier.verify(tbsSequence.encodedBytes, signature);
}
Uint8List decodePEM(String pem) {
var startsWith = [
'-----BEGIN PUBLIC KEY-----',
'-----BEGIN PRIVATE KEY-----',
'-----BEGIN CERTIFICATE-----',
];
var endsWith = [
'-----END PUBLIC KEY-----',
'-----END PRIVATE KEY-----',
'-----END CERTIFICATE-----'
];
pem=pem.trim();
//HACK
for (var s in startsWith) {
if (pem.startsWith(s)) pem = pem.substring(s.length);
}
for (var s in endsWith) {
if (pem.trim().endsWith(s)) {
Log().i("certificate:substring");
pem = pem.trim().split(s)[0];
}
}
//Dart base64 decoder does not support line breaks
pem = pem.replaceAll('\n', '');
pem = pem.replaceAll('\r', '');
return Uint8List.fromList(base64.decode(pem));
}
Take a look at the package basic_utils (https://github.com/Ephenodrom/Dart-Basic-Utils). It contains a lot of helper methods on X509 certificates.
dependencies:
basic_utils: ^5.4.3
CertificateChainCheckData checkChain(List<X509CertificateData> x509);
You can parse the PEM files via X509Utils class and pass it as a list. It will check the order of the chain and the signature.
Here is an example from the unit tests :
test('Test checkChain()', () {
var data = X509Utils.pkcs7fromPem(pkcs7);
var result = X509Utils.checkChain(data.certificates!);
expect(result.pairs!.length, 2);
expect(result.pairs!.elementAt(0).isValid(), true);
expect(result.pairs!.elementAt(1).isValid(), true);
var f = File('test_resources/rapid_ssl_broken_chain.pem');
var pem = f.readAsStringSync();
var chain = X509Utils.parseChainString(pem);
result = X509Utils.checkChain(chain);
expect(result.pairs!.length, 1);
expect(result.pairs!.elementAt(0).isValid(), false);
expect(result.pairs!.elementAt(0).dnDataMatch, true);
expect(result.pairs!.elementAt(0).signatureMatch, false);
});

Swift encryption and NodeJS decryption producing inconsistent results

I'm not very familiar with Crypto, but i did ensure that the iv length buffer returns the correct length, encryptionKey used are the same.
The expected result from NodeJS, is a preset IV Length of 16 randombytes + encryptedText generated with aes-256-cbc combined into a hex String of length 64.
Tests encrypting in Node and decrypting it produces the expected result. But when iOS sends the payload it decrypts into an unknown string.
However, when iOS encrypts and sends the data. I'm unable to decrypt it to get the expected string.
For iOS i'm using the CommonCryto library
import CommonCrypto
struct AES {
private let key: String
init?(key: String) {
guard key.count == kCCKeySizeAES256 else {
debugPrint("Error: Failed to set a key.")
return nil
}
self.key = key
}
func encrypt(string: String) -> Data? {
return crypt(data: string.data(using: .utf8), operation: kCCEncrypt)
}
private func crypt(data: Data?, operation: Int) -> Data? {
guard let data = data else {
return nil
}
var ivBytes: [UInt8]
var inBytes: [UInt8]
var outLength: Int
if operation == kCCEncrypt {
ivBytes = [UInt8](repeating: 0, count: kCCBlockSizeAES128)
guard kCCSuccess == SecRandomCopyBytes(kSecRandomDefault, ivBytes.count, &ivBytes) else {
fatalError("IV creation failed!")
}
inBytes = Array(data)
outLength = data.count + kCCBlockSizeAES128
} else {
ivBytes = Array(Array(data).dropLast(data.count - kCCBlockSizeAES128))
inBytes = Array(Array(data).dropFirst(kCCBlockSizeAES128))
outLength = inBytes.count
}
var outBytes = [UInt8](repeating: 0, count: outLength)
var bytesMutated = 0
guard kCCSuccess == CCCrypt(CCOperation(operation), CCAlgorithm(kCCAlgorithmAES), CCOptions(kCCOptionPKCS7Padding), Array(key), key.count, &ivBytes, &inBytes, inBytes.count, &outBytes, outLength, &bytesMutated) else {
fatalError("Cryptography operation \(operation) failed")
}
var outData = Data(bytes: &outBytes, count: bytesMutated)
if operation == kCCEncrypt {
ivBytes.append(contentsOf: Array(outData))
outData = Data(ivBytes)
}
return outData
}
}
And how i decrypt in NodeJS:
const decrypt = functions.https.onCall(async (data, context) => {
const uid = context && context.auth && context.auth.uid;
if(!uid) {
return sendErrorResponse({
payload: 'Unauthorised',
statusCode: 401,
});
}
const { password } = data;
const MID = password.length / 2;
const textPart = [password.slice(0,MID),password.slice(MID)];
const iv = Buffer.from(textPart.shift(),'hex');
const encryptedText = Buffer.from(textPart.join(),'hex');
const decipher = crypto.createDecipheriv('aes-256-cbc', encryptionKey, iv).setAutoPadding(false);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
});
Look for cross plate-form AES encryption

PCLCrypto exception 'CryptographicException: Bad PKCS7 padding. Invalid length'

I'm struggling with the PCLCryptho libraby, I can't get it working without retrieving the exception 'CryptographicException: Bad PKCS7 padding. Invalid length'. Running the code once is working, but running it multiple times after each other fails (with different input strings). The decryption takes place after a new instance of the program. I'm running this code on iOS with Xamarin Forms. Here's my code (I'm using the same VI each time and save the salt in the Settinsg for now):
public static string EncryptAnswer(string answer, string passWord)
{
try
{
var keyMaterial = CreateKey(passWord);
var cipherTextBuffer = GetBytes(answer);
var symmetricAlgorithm = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
var symmetricKey = symmetricAlgorithm.CreateSymmetricKey(keyMaterial);
using (var encryptor = WinRTCrypto.CryptographicEngine.CreateEncryptor(symmetricKey, GetBytes("vivivivivivivivi")))
{
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (var bWriter = new BinaryWriter(cs))
{
bWriter.Write(cipherTextBuffer, 0, cipherTextBuffer.Length);
cs.FlushFinalBlock();
}
}
return GetString(ms.ToArray());
}
}
}
catch (Exception e)
{
return string.Empty;
}
}
public static string DecryptAnswer(string encryptedAnswer, string passWord)
{
try
{
var cipherTextBuffer = GetBytes(encryptedAnswer);
var keyMaterial = CreateKey(passWord);
var symmetricAlgorithm = WinRTCrypto.SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithm.AesCbcPkcs7);
var symmetricKey = symmetricAlgorithm.CreateSymmetricKey(keyMaterial);
using (var decryptor = WinRTCrypto.CryptographicEngine.CreateDecryptor(symmetricKey, GetBytes("vivivivivivivivi")))
{
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write))
using (var binWriter = new BinaryWriter(cs))
{
binWriter.Write(cipherTextBuffer, 0, cipherTextBuffer.Length);
}
return GetString(ms.ToArray());
}
}
}
catch (Exception e)
{
}
return string.Empty;
}
public static byte[] CreateSalt()
{
var salt = WinRTCrypto.CryptographicBuffer.GenerateRandom(8);
CrossSettings.Current.AddOrUpdateValue("Salt", GetString(salt));
return salt;
}
private static byte[] GetSalt()
{
var saltString = CrossSettings.Current.GetValueOrDefault<string>("Salt");
var salt = GetBytes(saltString);
return salt;
}
private static byte[] CreateKey(string passWord)
{
var key = NetFxCrypto.DeriveBytes.GetBytes(passWord, GetSalt(), 1000, 32);
return key;
}
private static byte[] GetBytes(string str)
{
return Encoding.Unicode.GetBytes(str);
}
private static string GetString(byte[] bytes)
{
return Encoding.Unicode.GetString(bytes, 0, bytes.Length);
}
This seems to be equal to the answers and examples I found. Can someone tell me what's wrong?

AES-128-ECB - Inconsistent encryption result of node js and java

node js code:
function AES_encrypt(){
var bKey = new Buffer('24Qn9974h50D9DNi', 'utf-8');
var bInput = new Buffer(‘test’, 'utf-8');
console.log(bKey.length);
var cipher = crypto.createCipher('AES-128-ECB',bKey);
//cipher.setAutoPadding(auto_padding=false);
var crypted = cipher.update(bInput,null,'base64');
crypted+=cipher.final('base64');
console.log(crypted);
return crypted;
}
get Result:57b6b7oulw7eO5h7efZ9/w==
java code:
main java:
String data = AES.encryptToBase64("test","24Qn9974h50D9DNi");
AES java:
public static String encryptToBase64(String data, String key){
try {
byte[] valueByte = encrypt(data.getBytes("utf-8"), key.getBytes("utf-8");
return new String(Base64.encode(valueByte));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("encrypt fail!", e);
}
}
public static byte[] encrypt(byte[] data, byte[] key) {
if(key.length!=16){
throw new RuntimeException("Invalid AES key length (must be 16 bytes)");
}
try {
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec seckey = new SecretKeySpec(enCodeFormat,"AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, seckey);// 初始化
byte[] result = cipher.doFinal(data);
return result; // 加密
} catch (Exception e){
throw new RuntimeException("encrypt fail!", e);
}
}
get Result:wA1JU6VxMaVl8Ck8pBrX8A==
Use crypto.createCipheriv to solve the issue,
http://nodejs.org/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv
You need to pad the string "test" to 16 bytes. I believe Java uses PKCS padding by default (but there are other padding schemes, too).
String data = AES.encryptToBase64("test","24Qn9974h50D9DNi");

Resources