I would like to encrypt data in UWP appliaction writen in javascript.
var publicKey = Windows.Security.Cryptography.Core.PersistedKeyProvider.openPublicKeyFromCertificate(cert, "", Windows.Security.Cryptography.Core.CryptographicPadding.none);
var buffer = Windows.Security.Cryptography.CryptographicBuffer.createFromByteArray(bytes);
var iv = null;
var encrypted = Windows.Security.Cryptography.Core.CryptographicEngine.encrypt(publicKey, buffer, iv);
But I get exception on the last line:
0x80090027 - JavaScript runtime error: Parametr není správný.
(Invalid parameter)
But which parameter?
Certificate is created by command
New-SelfSignedCertificate -Type Custom -Subject "test" -KeyAlgorithm RSA -KeyLength 2048 -CertStoreLocation "Cert:\CurrentUser\My"
and obtained by Windows.Security.Cryptography.Certificates.CertificateStores.
When RSA is used, buffer must have some defined size. If it is smaller exception (Invalid parameter - without any details) is raised.
When padding is changed from CryptographicPadding.none to CryptographicPadding.rsaPkcs1V15 encrypting works.
Related
I have an asymmetric RSA key pair stored in two separate files. I want to generate a new symmetric key and encrypt it with public RSA key in my postbuild.js GULP script, so the user cannot access it. Then I want to send it to the C# server, where it would be decrypted and used.
I use the following JavaScript code in Node.js for encryption:
const generateAndEncryptKey = () => {
const symmetricKey = crypto.randomBytes(32);
const publicKey = fs.readFileSync("pubkey.pem", "utf8");
const encryptedSymmetricKey = crypto.publicEncrypt({
key: publicKey,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256",
}, Buffer.from(symmetricKey)).toString("base64");
return encryptedSymmetricKey;
}
The above code somehow works and generates a base64 string that I later send to the server. I'm not sure if this is the correct way to do this.
But I'm unable to find a way to decrypt this string in C#. I tried to use the BouncyCastle library and the following code:
public string DecryptKey(string encryptedKey) {
var privateKey = #"-----BEGIN RSA PRIVATE KEY-----
...shortened...
-----END RSA PRIVATE KEY-----";
var bytesToDecrypt = Convert.FromBase64String(encryptedKey);
var decryptEngine = new Pkcs1Encoding(new RsaEngine());
using (var txtreader = new StringReader(privateKey)) {
AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)new PemReader(txtreader).ReadObject();
decryptEngine.Init(false, keyPair.Private);
}
var decrypted = Encoding.UTF8.GetString(decryptEngine.ProcessBlock(bytesToDecrypt, 0, bytesToDecrypt.Length));
return decrypted;
}
But the ProcessBlock method always throws an InvalidCipherTextException "unknown block type".
Can someone help me to find out what am I doing wrong or point me to another better way of achieving this?
Decryption with the C# code fails because in the NodeJS code OAEP/SHA256 is used as padding and in the C# code PKCS#1 v1.5 padding. For decryption to work, both paddings must be identical. The padding in the C# code can be adapted to that of the NodeJS code as follows:
var decryptEngine = new OaepEncoding(new RsaEngine(), new Sha256Digest());
Also, the decrypted key must not be UTF-8 decoded as this corrupts the data. Either it is returned as byte[], or if conversion to a string is desired, a suitable binary-to-text encoding such as Base64 or hex must be used.
With these changes decryption works in the C# code.
I encrypt a text in Java and want to decrypt in Javascript and vice versa, my Java code looks like this
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec k = new SecretKeySpec(this.key.getBytes(), "AES");
c.init(Cipher.ENCRYPT_MODE, k);
byte[] encryptedData = c.doFinal(this.value.getBytes());
byte[] iv = c.getIV();
sb.append(new String(Base64.getEncoder().encode(iv)));
sb.append(';');
sb.append(new String(Base64.getEncoder().encode(encryptedData)));
// send sb.toString() to the other end
I tried node-forge and elliptic for decryption in Nodejs, in node-forge I have an error complaining about tag which I don't have, can someone provide a solution in Nodejs.
Javascript Code
function convertFromMxBase64(FormatEncryptedString) {
const speratorIndex = FormatEncryptedString.indexOf(';');
const encryptedBase64 = mxFormatEncryptedString.substr(speratorIndex + 1);
const ivBase64 = mxFormatEncryptedString.substr(0, speratorIndex);
return {
iv: forge.util.createBuffer(forge.util.decode64(ivBase64), 'utf8'),
payload: forge.util.createBuffer(forge.util.decode64(encryptedBase64), 'utf8')
};
}
function decrypt() {
let { iv, payload } = convertFromMxBase64(mxCombi);
const key = forge.util.createBuffer(theKey, 'utf8');
var cipher = forge.cipher.createDecipher("AES-GCM", key); // forge.rc2.createDecryptionCipher(key);
cipher.start({ iv });
cipher.update(payload);
cipher.finish();
var encrypted = cipher.output;
// outputs encrypted hex
console.log(encrypted.toHex());
}
The GCM mode uses by definition a tag which is needed for the authenticity/integrity check.
The tag issue on the NodeJS side is caused by not taking the authentication tag into account. On the Java side this is not necessary because the SunJCE provider does this automatically by appending the tag to the ciphertext: Therefore, the last 16 bytes of the result are the tag. On the NodeJS side, in contrast, ciphertext and tag are handled detached, so that both parts must be explicitly separated, e.g.:
var ciphertext = forge.util.createBuffer(payload.data.substring(0, payload.length() - 16));
var tag = forge.util.createBuffer(payload.data.substring(payload.length() - 16));
Tag and ciphertext must be passed to the cipher instance as follows:
cipher.start({ iv:iv, tag:tag }); // apply tag
cipher.update(ciphertext); // apply ciphertext
Another bug is that the UTF-8 encoding is used when creating iv and payload. This is wrong, the data must be passed as binary string, i.e. 'utf8' must either be replaced by 'binary' or removed completely.
iv: forge.util.createBuffer(forge.util.decode64(ivBase64)),
payload: forge.util.createBuffer(forge.util.decode64(encryptedBase64))
Also, in getBytes() of the Java code an encoding should be specified, e.g. getBytes(StandardCharsets.UTF_8). If this is not done, a platform-dependent default encoding is used, which means that different encodings can be applied depending on the environment.
With these changes, decryption works on the NodeJS side.
I have a Pentaho process that is is base64 encoding a binary (pdf) via java - with this command:
Step: Load File Content In Memory
Outputs: pdf_content
Then
Step: Javascript
Outputs: encString
var encString = new Packages.java.lang.String( Packages.org.apache.commons.codec.binary.Base64.encodeBase64( pdf_content.getBytes() ) );
Then
Step: Rest Client (posts data)
On the NodeJs side
const binary = new Buffer(base64Encoded, 'base64');
The problem is that the binary file on the node side differs from that on the source (i have access to and can copy the files around).
Further confusing the matter I introduced an intermediate step saving the base64Encoded string out to disk prior to decoding it. I also then opened the original file (from the Pentaho Side) and encoded that using the
Buffer(fs.readFileSync(originalPath)).toString('base64')
and compared the base 64 encoded versions of each. I was hoping to confirm that the algorithm is different (although there is no guarantee). The files themselves were the same length, and started and finished with no differences. Scattered about the file were bunch of minor differences. 1 byte here 1 byte there.
Other bits: Apache.Commons...Base64 uses "rfc 2045" Buffer on the Node Js side uses "rfc 4648" (I may be misreading the description). Questions,
Is there a known way to communicate base64 between a Java encoding and Node Js decoding?
Are there other recommendation on base64 encoding in Pentaho?
Here is how you encode it on the Java side:
in Java 7 using Google guava's com.google.common.io.BaseEncoding and com.google.common.io.Files:
byte[] data = BaseEncoding.base64().encode(bytes).getBytes();
Files.write(data, new File(outputFile));
in Java 8 using java.util.Base64 and java.nio.file.Files/java.nio.file.Paths:
byte[] data = Base64.getEncoder().encode(bytes);
Files.write(Paths.get(outputFile), data);
and decode on the node side (synchronous version):
var fs = require("fs");
var buf fs.readFileSync("fromJava.dat");
var res = new Buffer(buf.toString(), "base64").toString("utf-8");
And here are the corresponding Java-side tests (for Java8 native java.util.Base64 and for Guava's com.google.common.io.BaseEncoding) invoking node.js process and proving correctness:
#Test
public void testJ8() throws IOException {
String input = "This is some UTF8 test data, which also includes some characters "
+ "outside the 0..255 range: ❊ ✓ ❦. Let's see how we get them to node and back";
byte[] data = Base64.getEncoder().encode(input.getBytes());
Files.write(Paths.get("fromJava.dat"), data);
Process p = Runtime.getRuntime().exec(new String[]{"node", "-e",
"console.log(new Buffer(require('fs').readFileSync('fromJava.dat').toString(), 'base64').toString('utf-8'))"});
assertEquals(input, new Scanner(p.getInputStream()).useDelimiter("\n").next());
}
Test run:
Process finished with exit code 0
#Test
public void testJ7() throws IOException {
String input = "This is some UTF8 test data, which also includes some characters "
+ "outside the 0..255 range: ❊ ✓ ❦. Let's see how we get them to node and back";
byte[] data = BaseEncoding.base64().encode(input.getBytes()).getBytes();
Files.write(data, new File("fromJava.dat"));
Process p = Runtime.getRuntime().exec(new String[]{"node", "-e",
"console.log(new Buffer(require('fs').readFileSync('fromJava.dat').toString(), 'base64').toString('utf-8'))"});
assertEquals(input, new Scanner(p.getInputStream()).useDelimiter("\n").next());
}
Test run:
Process finished with exit code 0
Both tests executed with Java 8 on OSX/unix but Guava 19 used here is fully compatible with Java 7 and if the node executable is on the path in Windows then there is no reason why the tests would not run there (provided it can also evaluate a script after the -e argument, no idea).
Right I've a small problem. I'm using a Javascript library (jsencrypt) to encrypt a message in a browser. This message is then sent to the backend where it is decrypted using a Java library (bouncycastle). My problem is although I can encrypt and decrypt messages using both libraries they don't seem to want to work together. So when I encrypt my message in a browser and send it to the backend I end up getting garbled gibberish. Does anyone have any idea what's going on here?
JSENCRYPT
var text = "This is another msg!";
var pubkey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwyTZf5gRWJdEevtK7sJSz14lhs1Jw7+aFhGtr4cbDGxdiXH8J+BwuYmBc6QFMhRw7AeYcgkx9zPb3SICzr+oK17RMA6T66dH+GPXp75LFUmfONfk2JdSeO80mMODGctSuefWDvoQ24Cq0Bz+ysrhP7hRqvJso5a0GMNPwt8ErtWfz4HZjSsaaZ7gXga2h5dq1OTcGNfevkDN9CJtFW/0Wwb/F6cnXngVHE41rsN4POUB3IWcX2CrCGxSraa+xsT/P7AJ8HRJ4wcjl9G2K/rlHJ8ZXZKlIuWwEzx0/F0IjE+S93tLpDgt6YJxjWqYqjL2uuJAGmEU323+PWA3jFTC+QIDAQAB";
var encrypt = new JSEncrypt();
encrypt.setPublicKey(pubkey);
var ciphertext = encrypt.encrypt(text);
console.log("ciphertext : " + ciphertext);
var decrypt = new JSEncrypt();
decrypt.setPrivateKey($("#privkey").val());
var plaintext = decrypt.decrypt(ciphertext);
console.log("plaintext : " + plaintext);
BOUNCYCASTLE
String cipherText = "jQ/I+oyyIfG5ARIHZsa6MfxwHciCt+3p6l+bLh4NPinq2s8eDjbO9O8abhVt2xuBQQcPAIaqbiP3Y3vRFYLOD2O+inKWiL1SpSBxvUb0XlWMgLmOqWUL6w6sL2iEla3i5EbdlrkK0uLA7QOUc6/fGVyLVe8VL7Vv4BGlo/cxR2FN74HK4MtLFRNaLKejwD6WbCNQoz4sIMA/Ez8GRSVEMyeYVZoWELShvyIRCqVADboAeuEP5l+oFlzgQfW6HFdpPnX+9TnHrbezdWhXiuJiD1Mq4VTicsya50MNcXJuPDV7NINYZs72UCS8NTYvfVkFc2lO7EUlDvvJ7Ns4wWuuWQ==";
PemReader pemReader = new PemReader(new InputStreamReader(new FileInputStream("priv.pem")));
PemObject pemObject = null;
try
{
pemObject = pemReader.readPemObject();
} finally {
pemReader.close();
}
PrivateKey privateKey = EncryptionUtil.generatePrivateKey(pemObject.getContent());
byte[] plainText = EncryptionUtil.asymDecrypt(privateKey, cipherText.getBytes());
System.out.println(new String(plainText));
#EbbeM.Pedersen
You are properly getting different default padding modes. Keywords
like RSA-OAEP padding & PKCS#1 padding comes to mind.
This was indeed the issue. I changed the default padding in bouncycastle to PKCS#1 and it all works now.
Thanks a million.
I try to decrypt an cipher with AES in GCM mode with the SJCL library in Javascript (from within CasperJS).
When I execute the code below the error I receive is:
error: TypeError: 'undefined' is not a function (evaluating 'b.encrypt([0,
0,0,0])')
The code:
var masterkey = '39537496606860671661230109146651832357';
var cipher = 'Sa2Rk3bbdiaI7mO/';
var iv = '59804781381539321505720964105';
var authdata = '199590863504973848417387014842606357793';
var decff = sjcl.mode.gcm.decrypt(masterkey, cipher, iv, authdata);
console.log (decff);
As you can see, I am basically just calling the decrypt function as the SJCL docs told me to.
The encryption was done in python with this code: https://github.com/bozhu/AES-GCM-Python Wich I found is this thread: AES in GCM mode in Python
Is there anything special I have to consider when encrypting in one language and decrypting in another? Im afraid so...
Can I check somehow if the encryption information are valid AES/GCM?
Im not really sure how to proceed here since Im no JS or Python or encryption expert.
For background information:
I try to achieve a more or less secure encryption in pure python (so I can run it on Google App Engine) and the fitting decryption in pure JS.
Thanks for any help.
You cannot directly decrypt try converting your encrypted string, cypher, IV and auth data to bitArray.
const data = sjcl.mode.gcm.decrypt(cipherArray, encryptedBitArray, iv, authArray, 128);
Here 128 is size you can use 256 as well.
Also try to append your IV with the encypted string.
const bitArray = sjcl.codec.base64.toBits(content);
const bitArrayCopy = bitArray.slice(0);
const ivdec = bitArrayCopy.slice(0, 4);
const encryptedBitArray = bitArray.slice(4);
var key = sjcl.codec.base64.toBits("2d73c1dd2f6a3c981afc7c0d49d7b58f");
let cipher = new sjcl.cipher.aes(key);