I use AES encryption in my react-native app as below
import CryptoJS from 'crypto-js' ;
encryptFun() {
var data = "123456";
var key = CryptoJS.enc.Latin1.parse('1234567812345678');
var iv = CryptoJS.enc.Latin1.parse('1234567812345678');
var encrypted = CryptoJS.AES.encrypt(
data,
key,
{iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.ZeroPadding
});
console.log('encrypted: ' + encrypted) ;
var decrypted = CryptoJS.AES.decrypt(encrypted,key,{iv:iv,padding:CryptoJS.pad.ZeroPadding});
console.log('decrypted: '+decrypted.toString(CryptoJS.enc.Utf8));
}
Out come= encrypted: aK7+UX24ttBgfTnAndz9aQ==
following is the code I use in my backend using java for get the decrypt
public static String desEncrypt() throws Exception {
try
{
String data = "aK7+UX24ttBgfTnAndz9aQ==" ;
String key = "1234567812345678";
String iv = "1234567812345678";
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(data);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original);
return originalString;
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
output= decryted : = 123456[][][][][][][][][][]
Iam getting out put as 16 bit as above.
what I want is out put should come as only 123456.
I Suggest you use java.util.Base64 for decoding. The following worked out correctly. I would also suggest using trim in return originalString and see if it works out.
public class Decrypt {
public static void main(String[] args) {
try
{
String data = "aK7+UX24ttBgfTnAndz9aQ==" ;
String key = "1234567812345678";
String iv = "1234567812345678";
Decoder decoder = Base64.getDecoder();
byte[] encrypted1 = decoder.decode(data);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original);
System.out.println(originalString.trim());
}
catch (Exception e) {
e.printStackTrace();
}
}
}
As you padded with zeros in the JS you can't decrypt with NoPadding, solutions would be to operate on the array or the String
trim the array
int i = original .length - 1;
while (i >= 0 && original [i] == 0) {
--i;
}
return new String(Arrays.copyOf(original , i + 1));
trim the String
return new String(original).trim();
remove manually the zero values
return new String(original).replace("\0", "");
Or, as it seems there is no ZeroPAdding in Java implemented (Cipher Documentation), take a look at How Encrypt with AES CBC Zero Padding in Javascript and decrypt with Java that suggests to use CryptoJS.pad.Pkcs
Controller part to generate RSA key ( public & private )
package com.secure.encryption.decryption.controller;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.secure.encryption.decryption.rsa.keystore.KeyStore;
import com.secure.encryption.decryption.rsa.service.RSAKeyPairGenerator;
import com.secure.encryption.decryption.util.AesUtil;
#RestController
public class EncryptDecryptController {
#Autowired
RSAKeyPairGenerator rsa;
#Autowired
KeyStore keystore;
#CrossOrigin
#GetMapping(value = "get/rsa/public")
public Map<String, Object> generateRSA_PUBLIC_PRIVATE_KEY()
{
Map<String, Object> response = new HashMap<>();
/**
* Below function creates rsa public & private key
* While this api only share PUBLICKEY
* Whereas PRIVATEKEY is stored in bean/model
*/
String public_KEY = null;
public_KEY = rsa.KeyPairGenerator();
response.put("public", public_KEY);
return response;
}
#CrossOrigin
#PostMapping(value = "/decrypt/AES_encyptedKEY/with/RSA_privateKEY/decryptdata/with/aes")
public Map<String, Object> decrypt(#RequestBody Map<String, Object> req) throws UnsupportedEncodingException
{
/**
* First decrypt AES-Key which is encrypted with RSA Public Key
* Use RSA privateKey for decryption
*/
String Decrypted_AES_Key = rsa.decrypt(req.get("phrase").toString(),keystore.getPrivateKey());
byte[] decoded = Base64.getDecoder().decode(req.get("data").toString());
String encryptedAES_Data = new String(decoded, StandardCharsets.UTF_8);
/**
* Decode data to base 64
* Use AES key to decrypt the data
*/
AesUtil aesUtil = new AesUtil(128, 1000);
String decryptedAES_Data = aesUtil.decrypt(
req.get("salt").toString(),
req.get("iv").toString(),
Decrypted_AES_Key,
encryptedAES_Data);
/**
* Map actual data as response
*/
req.put("data", decryptedAES_Data);
return req;
}
#CrossOrigin
#GetMapping(value = "/decryptfrom/backend/aes/plain/decrypt/frontend")
public Map<String, Object> sendAESencryptedData()
{
/**
* Generate random key
* Encrypt data using same
* pass key to UI
* Decrypt using same key, iv and salt
*/
Map<String, Object> response = new HashMap<>();
int i = (int) (new Date().getTime()/1000);
//String iv = generateIv().toString();
AesUtil aesUtil = new AesUtil(128, 1000);
String phrase = String.valueOf(i);//"my secret key 123";
//String salt = new String(generateSalt(32));
String iv = "bb6a69ace7a11a38fba164238e000c7c";
String salt = "6c3674b6469467ab0b9f2b57ce36e78d";
String encryptedAES_Data =
Base64.getEncoder().encodeToString(
aesUtil.encrypt(
salt,
iv,
phrase,
"ganesha")
);
response.put("data", encryptedAES_Data);
response.put("salt", salt);
response.put("iv", iv);
response.put("key", phrase);
return response;
}
/*
public IvParameterSpec generateIv() {
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
return new IvParameterSpec(iv);
}
private byte[] generateSalt(int size) {
try {
byte[] salt = new byte[size];
SecureRandom rand = SecureRandom.getInstance("SHA1PRNG");
rand.nextBytes(salt);
return salt;
} catch (Exception e) {
System.err.println(e);
}
return null;
}
*/
}
Service to generate and RSA random Key
package com.secure.encryption.decryption.rsa.service;
public interface RSAKeyPairGenerator {
public String KeyPairGenerator();
public byte[] encrypt(String data, String publicKey);
public String decrypt(String data, String base64PrivateKey);
}
Implementation Class
package com.secure.encryption.decryption.rsa.service;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.secure.encryption.decryption.rsa.keystore.KeyStore;
#Service
public class RSAKeyPairGeneratorImpl implements RSAKeyPairGenerator{
#Autowired
KeyStore keystore;
#Override
public String KeyPairGenerator() {
Map<String, Object> keypair = new HashMap<String, Object>();
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair pair = keyGen.generateKeyPair();
final String privatestring = Base64.getEncoder().encodeToString(pair.getPrivate().getEncoded());
final String publicstring = Base64.getEncoder().encodeToString(pair.getPublic().getEncoded());
keystore.setPrivateKey(privatestring);
return publicstring;
} catch (Exception e) {
System.err.println(e);
}
return null;
}
private static PublicKey getPublicKey(String base64PublicKey){
PublicKey publicKey = null;
try{
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(base64PublicKey.getBytes()));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return publicKey;
}
private static PrivateKey getPrivateKey(String base64PrivateKey){
PrivateKey privateKey = null;
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(base64PrivateKey.getBytes()));
KeyFactory keyFactory = null;
try {
keyFactory = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
try {
privateKey = keyFactory.generatePrivate(keySpec);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return privateKey;
}
public byte[] encrypt(String data, String publicKey) {//throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
try {
//AES/CBC/PKCS5Padding
//RSA/ECB/PKCS1Padding
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey));
return cipher.doFinal(data.getBytes());
} catch (Exception e) {
System.err.println(e);
}
return null;
}
private static String decrypt(byte[] data, PrivateKey privateKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(data));
}
public String decrypt(String data, String base64PrivateKey) {//throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
try {
return decrypt(Base64.getDecoder().decode(data.getBytes()), getPrivateKey(base64PrivateKey));
} catch (InvalidKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
AesUtil class
package com.secure.encryption.decryption.util;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.tomcat.util.codec.binary.Base64;
public class AesUtil {
private final int keySize;
private final int iterationCount;
private final Cipher cipher;
public AesUtil(int keySize, int iterationCount) {
this.keySize = keySize;
this.iterationCount = iterationCount;
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
}
catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw fail(e);
}
}
public byte[] encrypt(String salt, String iv, String passphrase, String plaintext) {
try {
SecretKey key = generateKey(salt, passphrase);
byte[] encrypted = doFinal(Cipher.ENCRYPT_MODE, key, iv, plaintext.getBytes("UTF-8"));
System.out.println(encrypted);
return encrypted;//Base64.getEncoder().encodeToString(encrypted);
//String s = Base64.getEncoder().encodeToString(encrypted);
}
catch (UnsupportedEncodingException e) {
throw fail(e);
}
}
public String decrypt(String salt, String iv, String passphrase, String ciphertext) {
try {
SecretKey key = generateKey(salt, passphrase);
byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, iv, base64(ciphertext));
return new String(decrypted, "UTF-8");
}
catch (UnsupportedEncodingException e) {
return null;
}catch (Exception e){
return null;
}
}
private byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) {
try {
cipher.init(encryptMode, key, new IvParameterSpec(hex(iv)));
return cipher.doFinal(bytes);
}
catch (InvalidKeyException
| InvalidAlgorithmParameterException
| IllegalBlockSizeException
| BadPaddingException e) {
return null;
}
}
private SecretKey generateKey(String salt, String passphrase) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), hex(salt), iterationCount, keySize);
SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
return key;
}
catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
return null;
}
}
public static byte[] base64(String str) {
return Base64.decodeBase64(str);
}
public static byte[] hex(String str) {
try {
return Hex.decodeHex(str.toCharArray());
}
catch (DecoderException e) {
throw new IllegalStateException(e);
}
}
private IllegalStateException fail(Exception e) {
return null;
}
}
Steps followed
Consume the RSA key
Generate random AES (key, iv, salt) - using crypto.js
Use AES and encrypt vulnerable data
Use RSA public key for encrypting AES key
Send encrypted data & key over network
Service consumes data decrypt key to AES
Use AES key to decrypt data
Send the data back to front end
Html page for reference
<!DOCTYPE html>
<html>
<body>
<script src="jquery.min.js"></script>
<script src="jsencrypt.min.js"></script>
<script type="text/javascript" src="crypto-js.min.js"></script>
<script type="text/javascript" src="aes.js"></script>
<script type="text/javascript">
const payloadsample = {
"addressLine1": "301,Kamala Mills Compound",
"addressLine2": "Gr Flr, Tulsi Pipe Rd, Lower Parel ",
"addressLine4": "Mumbai, Maharashtra",
"zipcode": 400071
};
/**
Step 1 ) - get data
**/
/**
Step 2 ) - get RSA pub Key
**/
function hybridEncryption()
{
$.ajax({
type: 'GET',//post
url: 'http://localhost:1818/get/rsa/public',
success: function(res) {
let RSAEncrypt = new JSEncrypt();
RSAEncrypt.setPublicKey(res.public);//set RSA public key
const key = Math.random().toString(36).slice(2);//Generate random AES key
console.log("key ", key);
var iv = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex);
var salt = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex);
var aesUtil = new AesUtil(128, 1000);
debugger
console.log(key)
var data = JSON.stringify({ payloadsample });
var ciphertext = aesUtil.encrypt(salt, iv, key, data);
/**
Step 3 ) - generate key
**/
senData(RSAEncrypt, iv, salt, key, btoa(ciphertext))
},
error:function(e) {
console.error(e);
},
contentType: "application/json",
dataType: 'json'
});
}
function senData(RSAEncrypt, iv, salt, key, base64Content)
{
const payload = {
"phrase":RSAEncrypt.encrypt(key),//encrypt with RSA
"data":base64Content,
"iv":iv,
"salt":salt
}
console.log("sending : ", payload);
$.ajax({
type: 'POST',
url: 'http://localhost:1818/decrypt/AES_encyptedKEY/with/RSA_privateKEY/decryptdata/with/aes',
data: JSON.stringify (payload), // or JSON.stringify ({name: 'jonas'}),
success: function(data) {
console.log(data);
},
error:function(e) {
console.error(e);
},
contentType: "application/json",
dataType: 'json'
});
}
hybridEncryption();
/**
* Incase of Backend encryption to Front end Decryption
* decryptBE() - will get AES encrypted data with associated data
* */
function decryptBE()
{
$.ajax({
type: 'GET',//post
url: 'http://localhost:1818/decryptfrom/backend/aes/plain/decrypt/frontend',
success: function(res) {
debugger
var aesUtil = new AesUtil(128, 1000);
var ciphertext = aesUtil.decrypt(res.salt, res.iv, res.key, res.data);
console.log(ciphertext);
},
error:function(e) {
console.error(e);
},
contentType: "application/json",
dataType: 'json'
});
}
</script>
</body>
</html>
This is a working example using reference
Other lib used is aes.js
var AesUtil = function(keySize, iterationCount) {
this.keySize = keySize / 32;
this.iterationCount = iterationCount;
};
AesUtil.prototype.generateKey = function(salt, passPhrase) {
var key = CryptoJS.PBKDF2(
passPhrase,
CryptoJS.enc.Hex.parse(salt),
{ keySize: this.keySize, iterations: this.iterationCount });
return key;
}
AesUtil.prototype.encrypt = function(salt, iv, passPhrase, plainText) {
var key = this.generateKey(salt, passPhrase);
var encrypted = CryptoJS.AES.encrypt(
plainText,
key,
{ iv: CryptoJS.enc.Hex.parse(iv) });
return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
}
AesUtil.prototype.decrypt = function(salt, iv, passPhrase, cipherText) {
var key = this.generateKey(salt, passPhrase);
var cipherParams = CryptoJS.lib.CipherParams.create({
ciphertext: CryptoJS.enc.Base64.parse(cipherText)
});
var decrypted = CryptoJS.AES.decrypt(
cipherParams,
key,
{ iv: CryptoJS.enc.Hex.parse(iv) });
return decrypted.toString(CryptoJS.enc.Utf8);
}
rest are
JSEncrypt v2.3.1 https://npmcdn.com/jsencrypt#2.3.1/LICENSE.txt
and finally crypto-js.min.js
Thank you i hope this would be usefull
Related
Recently I am trying to find a way to encrypt data on PHP with a Public Key generated on javascript using window.crypto.subtle.generateKey()
async function GenerateKeys() {
let key = await window.crypto.subtle.generateKey(
{
name: "RSA-OAEP",
modulusLength: 4096,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: {name: "SHA-256"}
},
true,
["encrypt", "decrypt"]
)
let pvkey = await window.crypto.subtle.exportKey(
"pkcs8",
key.privateKey
)
let pbkey = await window.crypto.subtle.exportKey(
"spki",
key.publicKey
)
let pemPvKey = `-----BEGIN PRIVATE KEY-----\n${window.btoa(ab2str(pvkey))}\n-----END PRIVATE KEY-----`;
let pemPbKey = `-----BEGIN PUBLIC KEY-----\n${window.btoa(ab2str(pbkey))}\n-----END PUBLIC KEY-----`;
return [pemPvKey, pemPbKey]
}
I then send the public key to my PHP script and generate data
$pbkey = "-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA52N7Q/kQxNrVaGCLOlD0IgrSROPWt029GTqRKMdFQSFMAZPD0V5TZPLDylbtmvLhDajKugYpjHfDGD44cXiYy1jZeVCiFas09gAGBKmgFY4Ixsl/+opF2hlPjvnugn2aPKpSLgoX9f1DpXBDEpuHJ+AVSTxL+C3uE1PgQPYy2ots15Km4W2AnV+p81UfLdafStQ40gbkUOHvzFkwizazm4q1Scjh2Fc+RR6FXy+ySp54yuRqHuMS8QTdXVMBChqs5lNwuu6V+BEryXMFbDx2fW9qCWShVTc2lq4KzvXRE/65L0sttMA7oP/WzsVW3zeNFgla8g4dxvCftwrwGviMay9N+vsa902TClR2xj7/JSzQXSW0E4Am4ZB4bCJh04f08M0nMBY6yWWV3+QpBG+9CeXHfwPrdqT2OR5N7Jq+xslCfbf4D/9itjDdvaBZP5Z6BYiOF3QzeTk+V2rfAWZdOUcuWAdP3gczQKdJM11bTxOKNCiYmBu773aDXCNFpaHDatqUgnNrVp7guOP1owOdd7qxWsplzylQUcWuhuWczs7/X/UPwT7vBTkg0pb8Ujrr+KyNmsxsJTefg1z4xcbxtgqgoDRxXu92V9iVl1HVssAM8IZdc+naEIyOjt+3OPZkaCwts38U7nYUDd96ovHjMs0hFlTxqsltwyfPFAVygLUCAwEAAQ==
-----END PUBLIC KEY-----";
openssl_public_encrypt("secret", $encrypted, $pbkey, OPENSSL_PKCS1_OAEP_PADDING );
echo base64_encode($encrypted);
Finally, I use the generated cipher on javascript by using decrypt function (the key is stored in localstorage btw in pkcs8 format)
function FormatPrivateKey(pemPvKey) {
return pemPvKey.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replace(/[\r\n]/gm, "");
}
function GetKeys() {
const pvkey = localStorage.getItem("pvkey");
const pbkey = localStorage.getItem("pbkey");
return [pvkey, pbkey]
}
async function Decrypt(message) {
const keys = GetKeys();
let pemPvKey = keys[0];
const pvkey = await window.crypto.subtle.importKey(
"pkcs8",
str2ab(window.atob(FormatPrivateKey(pemPvKey))),
{
name: "RSA-OAEP",
hash: {name: "SHA-256"}
},
false,
["decrypt"]
);
return await window.crypto.subtle.decrypt(
{
name: "RSA-OAEP"
},
pvkey,
str2ab(window.atob(message))
);
}
When I try to decrypt the data, I receive this error:
Uncaught DOMException: The operation failed for an operation-specific reason
I do not know why this happens.
PHP/OpenSSL only supports OAEP with SHA1 for both digests, see here.
So to decrypt the ciphertext generated with PHP OpenSSL, SHA-1 must be specified in Decrypt() or more precisely in importKey() instead of SHA-256.
Consequently, SHA-1 should also be specified in the key generation. However, this is not mandatory because of the export/reimport. It would be necessary if the generated private key was used directly (i.e. without reimport) for decryption, which is not the case in this scenario.
In the example below, I generated the keys with the unmodified JavaScript code and the ciphertext with the PHP code. The decryption is successful when using the described bugfix.
(async () => {
async function Decrypt(message) {
const keys = GetKeys();
let pemPvKey = keys[0];
const pvkey = await window.crypto.subtle.importKey(
"pkcs8",
str2ab(window.atob(FormatPrivateKey(pemPvKey))),
{
name: "RSA-OAEP",
hash: {name: "SHA-1"} // Fix!
},
false,
["decrypt"]
);
return await window.crypto.subtle.decrypt(
{
name: "RSA-OAEP"
},
pvkey,
str2ab(window.atob(message))
);
}
function GetKeys() {
const pvkey = `-----BEGIN PRIVATE KEY-----
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
-----END PRIVATE KEY-----`;//localStorage.getItem("pvkey");
const pbkey = "";//localStorage.getItem("pbkey");
return [pvkey, pbkey]
}
function FormatPrivateKey(pemPvKey) {
return pemPvKey.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replace(/[\r\n]/gm, "");
}
// https://stackoverflow.com/a/11058858
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
var ciphertext = "Oc2XZkps7x345NtfOjFLQZPIu3TPJ6Lmws8RYdCC42RlSnI3MMUm5RNSdXV3uzDpusOYXH7kZAndbfqaak5t3hC8W7P+RWlUcDXgKrlqr3RfQmHZYKwZG3wfbgPmJMPS3+FHNG19fKISUAntCvkF625UdvPL5PPSymlDQf3VrD+jjeTXephuBpTEENwZg5lPuV2mI+QvhFzq2qaG5ij7UHCBfkTKRmWfXTS1gUUnYf1zzsWXX6THVV3PS4V6cKrDYm/hLVnU0giL2pdLRWWlyrrnPTWbSVlYSla1iheOdg+QCt9ujCkQ73aJA7nxIB711XYWxkm9VNq6sauzvaTk3QyJAabKwK8jQn0DBuvbgiaHoOhpCwOOqJWb10B7qnqxzj9u+ufmdZkWtVS++IibXXbZy2nN0P/DCT43RJHrQsUxDNfLqXp/ZnTQEjyHvGMvk6c5yL2KA3Q94wDBSiukBp8Y8Gtcuxmse+EjTbi2QE/eNqBjovxtLuU2GqjgTAgfEmgvncpa4W8cq71Der0khVopg67tIUgPhAZXJKCzz+C1Rw/L40vED7+2g5OYhMid/nyj5oFn3BwQ52feZVq2KdR14feH2bHnJnLYHTRVYBSU42LDTA+dNO8sbQ2lJyF24+y+rHx1X80Xz8YiSfJWnW4D4hpDjK7cRO9WLWQhYaE=";
console.log(ab2str(await Decrypt(ciphertext)));
})();
To use OAEP with SHA-256 on the PHP side phpseclib is an option.
So I am trying to implement some methods to encrypt then decrypt some data. I don't have any experience with this, and I've tried to follow along with some posts online on how to go about this.
When i pass the encrypted 'hello' into the decrypt function, i get this:
let a = importPublicKeyAndEncrypt('hello')
CryptoKey {type: "public", extractable: true, algorithm: {…}, usages: Array(1)} W29iamVjdCBBcnJheUJ1ZmZlcl0=
importPrivateKeyAndDecrypt(a)
Promise {<pending>}
DOMException: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
at importPrivateKeyAndDecrypt (<anonymous>:26:60)
The decrypt function works correctly when i use an encrypted messages from one of the posts i saw for PKCS#8 but not when i generate my own keys.
Here is the code
What am i doing wrong?
// PEM encoded X.509 key
const publicKey = `
-----BEGIN PUBLIC KEY-----
<removed for space>
-----END PUBLIC KEY-----`;
// PEM encoded PKCS#8 key
const privateKey = `
-----BEGIN PRIVATE KEY-----
<removed for space>
-----END PRIVATE KEY-----`;
async function importPublicKeyAndEncrypt(str) {
try {
const pub = await importPublicKey(publicKey);
console.log(pub);
const encrypted = await encryptRSA(pub, new TextEncoder().encode(str));
const encryptedBase64 = window.btoa(ab2str(encrypted));
console.log(encryptedBase64.replace(/(.{64})/g, '$1\n'));
} catch (error) {
console.log(error);
}
}
async function importPrivateKeyAndDecrypt(str) {
try {
const priv = await importPrivateKey(privateKey);
const decrypted = await decryptRSA(priv, str2ab(window.atob(str)));
console.log(decrypted);
} catch (error) {
console.log(error);
}
}
async function importPublicKey(spkiPem) {
return await window.crypto.subtle.importKey(
'spki',
getSpkiDer(spkiPem),
{
name: 'RSA-OAEP',
hash: 'SHA-256',
},
true,
['encrypt']
);
}
async function importPrivateKey(pkcs8Pem) {
return await window.crypto.subtle.importKey(
'pkcs8',
getPkcs8DerDecode(pkcs8Pem),
{
name: 'RSA-OAEP',
hash: 'SHA-256',
},
true,
['decrypt']
);
}
async function encryptRSA(key, plaintext) {
let encrypted = await window.crypto.subtle.encrypt(
{
name: 'RSA-OAEP',
},
key,
plaintext
);
return encrypted;
}
async function decryptRSA(key, ciphertext) {
let decrypted = await window.crypto.subtle.decrypt(
{
name: 'RSA-OAEP',
},
key,
ciphertext
);
return new TextDecoder().decode(decrypted);
}
function getSpkiDer(spkiPem) {
const pemHeader = '-----BEGIN PUBLIC KEY-----';
const pemFooter = '-----END PUBLIC KEY-----';
var pemContents = spkiPem.substring(
pemHeader.length,
spkiPem.length - pemFooter.length
);
var binaryDerString = window.atob(pemContents);
return str2ab(binaryDerString);
}
function getPkcs8DerDecode(pkcs8Pem) {
const pemHeader = '-----BEGIN PRIVATE KEY-----';
const pemFooter = '-----END PRIVATE KEY-----';
var pemContents = pkcs8Pem.substring(
pemHeader.length,
pkcs8Pem.length - pemFooter.length
);
var binaryDerString = window.atob(pemContents);
return str2ab(binaryDerString);
}
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
There are only two minor flaws.
First, the return statements are missing in importPublicKeyAndEncrypt() and in importPrivateKeyAndDecrypt() (although the latter is not necessary for the current code snippet).
Also, it is necessary to wait for the promise of importPublicKeyAndEncrypt() before importPrivateKeyAndDecrypt() can be called.
With these fixes the code works:
// PEM encoded X.509 key
const publicKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAunF5aDa6HCfLMMI/MZLT
5hDk304CU+ypFMFiBjowQdUMQKYHZ+fklB7GpLxCatxYJ/hZ7rjfHH3Klq20/Y1E
bYDRopyTSfkrTzPzwsX4Ur/l25CtdQldhHCTMgwf/Ev/buBNobfzdZE+Dhdv5lQw
KtjI43lDKvAi5kEet2TFwfJcJrBiRJeEcLfVgWTXGRQn7gngWKykUu5rS83eAU1x
H9FLojQfyia89/EykiOO7/3UWwd+MATZ9HLjSx2/Lf3g2jr81eifEmYDlri/OZp4
OhZu+0Bo1LXloCTe+vmIQ2YCX7EatUOuyQMt2Vwx4uV+d/A3DP6PtMGBKpF8St4i
GwIDAQAB
-----END PUBLIC KEY-----`;
// PEM encoded PKCS#8 key
const privateKey = `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6cXloNrocJ8sw
wj8xktPmEOTfTgJT7KkUwWIGOjBB1QxApgdn5+SUHsakvEJq3Fgn+FnuuN8cfcqW
rbT9jURtgNGinJNJ+StPM/PCxfhSv+XbkK11CV2EcJMyDB/8S/9u4E2ht/N1kT4O
F2/mVDAq2MjjeUMq8CLmQR63ZMXB8lwmsGJEl4Rwt9WBZNcZFCfuCeBYrKRS7mtL
zd4BTXEf0UuiNB/KJrz38TKSI47v/dRbB34wBNn0cuNLHb8t/eDaOvzV6J8SZgOW
uL85mng6Fm77QGjUteWgJN76+YhDZgJfsRq1Q67JAy3ZXDHi5X538DcM/o+0wYEq
kXxK3iIbAgMBAAECggEASlJj0ExIomKmmBhG8q8SM1s2sWG6gdQMjs6MEeluRT/1
c2v79cq2Dum5y/+UBl8x8TUKPKSLpCLs+GXkiVKgHXrFlqoN+OYQArG2EUWzuODw
czdYPhhupBXwR3oX4g41k/BsYfQfZBVzBFEJdWrIDLyAUFWNlfdGIj2BTiAoySfy
qmamvmW8bsvc8coiGlZ28UC85/Xqx9wOzjeGoRkCH7PcTMlc9F7SxSthwX/k1VBX
mNOHa+HzGOgO/W3k1LDqJbq2wKjZTW3iVEg2VodjxgBLMm0MueSGoI6IuaZSPMyF
EM3gGvC2+cDBI2SL/amhiTUa/VDlTVw/IKbSuar9uQKBgQDd76M0Po5Lqh8ZhQ3o
bhFqkfO5EBXy7HUL15cw51kVtwF6Gf/J2HNHjwsg9Nb0eJETTS6bbuVd9bn884Jo
RS986nVTFNZ4dnjEgKjjQ8GjfzdkpbUxsRLWiIxuOQSpIUZGdMi2ctTTtspvMsDs
jRRYdYIQCe/SDsdHGT3vcUCybwKBgQDXDz6iVnY84Fh5iDDVrQOR4lYoxCL/ikCD
JjC6y1mjR0eVFdBPQ4j1dDSPU9lahBLby0VyagQCDp/kxQOl0z2zBLRI4I8jUtz9
/9KW6ze7U7dQJ7OTfumd5I97OyQOG9XZwKUkRgfyb/PAMBSUSLgosi38f+OC3IN3
qlvHFzvxFQKBgQCITpUDEmSczih5qQGIvolN1cRF5j5Ey7t7gXbnXz+Umah7kJpM
IvdyfMVOAXJABgi8PQwiBLM0ySXo2LpARjXLV8ilNUggBktYDNktc8DrJMgltaya
j3HNd2IglD5rjfc2cKWRgOd7/GlKcHaTEnbreYhfR2sWrWLxJOyoMfuVWwKBgFal
CbMV6qU0LfEo8aPlBN8ttVDPVNpntP4h0NgxPXgPK8Pg+gA1UWSy4MouGg/hzkdH
aj9ifyLlCX598a5JoT4S0x/ZeVHd/LNI8mtjcRzD6cMde7gdFbpLb5NSjIAyrsIA
X4hxvpnqiOYRePkVIz0iLGziiaMbfMwlkrxvm/LRAoGBALPRbtSbE2pPgvOHKHTG
Pr7gKbmsWVbOcQA8rG801T38W/UPe1XtynMEjzzQ29OaVeQwvUN9+DxFXJ6Yvwj6
ih4Wdq109i7Oo1fDnMczOQN9DKch2eNAHrNSOMyLDCBm++wbyHAsS2T0VO8+gzLA
BviZm5AFCQWfke4LZo5mOS10
-----END PRIVATE KEY-----`;
async function importPublicKeyAndEncrypt(str) {
try {
const pub = await importPublicKey(publicKey);
//console.log(pub);
const encrypted = await encryptRSA(pub, new TextEncoder().encode(str));
const encryptedBase64 = window.btoa(ab2str(encrypted));
//console.log(encryptedBase64.replace(/(.{64})/g, '$1\n'));
return encryptedBase64;
} catch (error) {
console.log(error);
}
}
async function importPrivateKeyAndDecrypt(str) {
try {
const priv = await importPrivateKey(privateKey);
const decrypted = await decryptRSA(priv, str2ab(window.atob(str)));
//console.log(decrypted);
return decrypted;
} catch (error) {
console.log(error);
}
}
async function importPublicKey(spkiPem) {
return await window.crypto.subtle.importKey(
'spki',
getSpkiDer(spkiPem),
{
name: 'RSA-OAEP',
hash: 'SHA-256',
},
true,
['encrypt']
);
}
async function importPrivateKey(pkcs8Pem) {
return await window.crypto.subtle.importKey(
'pkcs8',
getPkcs8DerDecode(pkcs8Pem),
{
name: 'RSA-OAEP',
hash: 'SHA-256',
},
true,
['decrypt']
);
}
async function encryptRSA(key, plaintext) {
let encrypted = await window.crypto.subtle.encrypt(
{
name: 'RSA-OAEP',
},
key,
plaintext
);
return encrypted;
}
async function decryptRSA(key, ciphertext) {
let decrypted = await window.crypto.subtle.decrypt(
{
name: 'RSA-OAEP',
},
key,
ciphertext
);
return new TextDecoder().decode(decrypted);
}
function getSpkiDer(spkiPem) {
const pemHeader = '-----BEGIN PUBLIC KEY-----';
const pemFooter = '-----END PUBLIC KEY-----';
var pemContents = spkiPem.substring(
pemHeader.length,
spkiPem.length - pemFooter.length
);
var binaryDerString = window.atob(pemContents);
return str2ab(binaryDerString);
}
function getPkcs8DerDecode(pkcs8Pem) {
const pemHeader = '-----BEGIN PRIVATE KEY-----';
const pemFooter = '-----END PRIVATE KEY-----';
var pemContents = pkcs8Pem.substring(
pemHeader.length,
pkcs8Pem.length - pemFooter.length
);
var binaryDerString = window.atob(pemContents);
return str2ab(binaryDerString);
}
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
(async () => {
let ciphertext = await importPublicKeyAndEncrypt('hello');
console.log("Ciphertext:\n", ciphertext.replace(/(.{48})/g, '$1\n'));
let decryptedData = await importPrivateKeyAndDecrypt(ciphertext);
console.log("Decrypted data:", decryptedData);
})();
I use jsencrypt
Client side:
const publicKey = `-----BEGIN PUBLIC KEY-----
keydatakeydata
-----END PUBLIC KEY-----`;
export function encrypt(text) {
let encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
let encrypted = encrypt.encrypt(text);
return encrypted.toString();
}
NodeJs
function decryptString(ciphertext) {
const privateKey = fs.readFileSync('./rsa_512_priv.pem').toString();
const decrypted = crypto.privateDecrypt({
key: privateKey,
padding: crypto.constants.RSA_PKCS1_PADDING
}, Buffer.from(ciphertext, 'base64'));
return decrypted.toString("utf8");
}
keys were generated with
openssl genrsa -out rsa_1024_priv.pem 1024
openssl rsa -pubout -in rsa_1024_priv.pem -out rsa_1024_pub.pem
i encrypt my string in JAVA like:
public String encrypt(String toEncrypt) {
try {
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] encVal = c.doFinal(toEncrypt.getBytes());
return Base64.getEncoder().encodeToString(encVal);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
and I want to decrypt it in typescript:
public static decrypt(key, value){
key = btoa(key);
let decryptedData = CryptoJS.AES.decrypt(value, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return decryptedData.toString( CryptoJS.enc.Utf8 );
}
the decryptedData.toString( CryptoJS.enc.Utf8 ) returns the following Error: Malformed UTF-8 data
I followed along this tutorial
here is a working example
JAVA:
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class Crypter {
private final String key = "1234567890123456";
public String encrypt(String toEncrypt) {
try {
IvParameterSpec iv = new IvParameterSpec(key.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());
return new String(Base64.getEncoder().encode(encrypted));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public String decrypt(String toDecrypt) {
try {
IvParameterSpec iv = new IvParameterSpec(key.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] cipherText = cipher.doFinal(Base64.getDecoder().decode(toDecrypt));
return new String(cipherText);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
TypeScript:
import * as CryptoJS from 'crypto-js';
export class Crypter{
public static encrypt(key, value){
key = CryptoJS.enc.Utf8.parse(key);
let ciphertext = CryptoJS.AES.encrypt(value, key, {iv: key}).toString();
return ciphertext;
}
public static decrypt(key, value){
key = CryptoJS.enc.Utf8.parse(key);
let decryptedData = CryptoJS.AES.decrypt(value, key, {
iv: key
});
return decryptedData.toString( CryptoJS.enc.Utf8 );
}
}
I'm trying to decode an RSA 2048 bit message encoded with a public key using the corresponding private key. The environment is google chrome and I'm using the window.crypto.subtle APIs.
I generated the key couple and encoded the message using openssl tools:
# generate keys and put the private one in file private_key.pem
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
# extract public key in file public_key.pem
openssl rsa -pubout -in private_key.pem -out public_key.pem
# encode message input.txt using the public key
openssl rsautl -encrypt -oaep -inkey public_key.pem -pubin -in input.txt -out msg_rsa.enc
# convert the encoded msg in base 64 format
base64 msg_rsa.enc > msg_rsa_64.enc
This is the javascript code I'm using to decode the message:
function str2ab(str) {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
async function importPrivateKey(pem) {
pem = pem.replace( /[\r\n]+/gm, "" );
// fetch the part of the PEM string between header and footer
const pemHeader = "-----BEGIN PRIVATE KEY-----";
const pemFooter = "-----END PRIVATE KEY-----";
const pemContents = pem.substring(pemHeader.length, pem.length - pemFooter.length);
// base64 decode the string to get the binary data
const binaryDerString = window.atob(pemContents);
// convert from a binary string to an ArrayBuffer
const binaryDer = str2ab(binaryDerString);
return window.crypto.subtle.importKey(
"pkcs8",
binaryDer,
{
name: "RSA-OAEP",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256",
},
true,
["decrypt"]
);
}
async function decryptRSA(_key, ciphertext) {
let decrypted = await window.crypto.subtle.decrypt(
{
name: "RSA-OAEP"
},
_key,
ciphertext
);
const dec = new TextDecoder();
return dec.decode(decrypted);
}
const fromBase64 = base64String => Uint8Array.from(atob(base64String), c => c.charCodeAt(0));
window.onload = init;
async function init() {
const privateKey = '-----BEGIN PRIVATE KEY-----\
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC3jmTi3O1k2YXs\
AM6nNTTIzDq5YWkxYrYb6cpO9eYuzmphgRnVDR6a1YWRXMoCuCfuNXcDGywzudRn\
bBMw0FHKLUqCttVHGpZYu0+0tRR10ubxiz/xnd/aCmRYHcmUNn8Qdh3KU59A9HK5\
HhYFf1vhK8r3fkoO4CjoGo1ROzXyMybUSy+4mSNscUtt5LwrVn48vXvG5i5B4DRT\
nM4cINmutEzA2s5cDt+dzU4Py71fKBRDRIGGn0vdVSoZKbWuhm5WewyRewCk7HFc\
PALCi5/1A7VKDAHUC4FlXmuG2+wzdchEyxMj6oLR7+BkKFQaTmuMM/22cGBjVTVt\
pSr3iDovAgMBAAECggEBAIuTQW+oovNu3IDq1DkdIjgV5AmW4tBkySlMi0OjhBbP\
auEdtDDnOwBtoJU6Q3nx4psmGItKHEBw6+yAp88UeT0NV30x3delhfGO7Trx/s7h\
Qi8lvcfSTqeUA11luSR0lAZGaryw/YX820eccw5XG9yK2ll7tIC/PxvPJOpB5fF2\
XGxGrionTjHDzXJ1OWX0i0aZlNNufInJAHhlt7aT3GiQMKcQs+AUb/+bWxI3Hln8\
KcL13EUlD4pJW8vtTK3gCnQNKKMoPB5Ugqe5BrU8ElkBz+zSKDnVwt5bgjrlucYz\
rKJxWr6/qTRZkzmvkhaJeNzzepfwkFsQ/eHcxYrtuDECgYEA8OXkQ2SqYDZwizCd\
SuVkx2zHm3zXYRSlRtnXYoUdJyTyuZ4k2GvXBrlwRsOJ14emVkHKyR5enmNjwrW5\
dcD2tbBzavcqOYAMiHcKklcS/gWgPx0X5QFHU33vr8u51BQWCz75lxddWNKxVAN/\
cUTugONtS4+EP4dSZhuxHt6RscsCgYEAwxA9QmZrI54hjMkIyqwmviRmIk42S5kk\
OxbhxBbt5qVueLRB092JyGmCR2kYiqdHCYkGFDOE4kni6Bsszq5CSJvhiATFeZeX\
ldFQeZqAiRY2rAd7xD1upMug/cK3ODA8k3k/e72CtyxtBTR01q29SnPx5p/57MrI\
3ogddHlGvK0CgYEA3VqhELwjQh1D9OJK5lM683SlRd7FGdOauyvYmhKu4xU0ZBNI\
0ATnpKoo3R04P+/JjGEQMRXS4794H6ZUMDuLdxAYPiW3ivZ6jbq04BtavEf3I4dc\
OXWfULzbzbFpo9KBHvxS4974S3Hut8AvDqnEbnKML25EmwuBT4oKis8BGVkCgYEA\
nusPDZbFeNou+TUbzYrdcZHUB+TyhTq58s4clxYbMgrbaslozAQ0aavT8Pvle6j2\
zgTth+3FOFr72x+wrJ358I/W+Wrxu7NOU0eZqci/KXCIkDT0l5d5GhewDK3jeYqK\
/5cLqnNmGHfARjpLak9X5V162erBwjIf3nTEkozvnW0CgYB6L1CX3DkTFH3OBcJe\
SvV18RDUfNI8MpUKcpofmwwvgER3GrehSZHRVxVwNbnJOqbh/oiwmmNJieKrFsk9\
EzCRBVWdZByHHYW2js+gCrAp+ghnl1QEAeCU7YTxCJ2fZIAmfB9X4u/7ARtVxnZY\
mOWlm65KUYr5lf2Ws5plL4pCRA==\
-----END PRIVATE KEY-----';
const ciphertext = 'F6/NwENdUZSl+vrgpWVkyWPQuYaTGDNZPIvj4KmIRHVx4qybxN24LPIgk0Rl84KHcLFadZWCjNpM\
vg3l826OaKZAtwvIp9IxVrMbvtNOymY6A1koKvC9ema92SR4DC9hmTtMxhUB6r3XgACtRLFqMfg+\
zYSHfFqQEGJg3yZ43hfzIq/gCfHPk5sZXASq5WY5b9yd4gRonn5D4OCD6xna/r5ovHfrpO/Fwe8N\
eeY2gqTAdtzvtmOw/HLQhGANejpJYr1IriQbepM7jLjBkJX+uCn38O1MxpQb7s5RXTvGvoEoofWV\
Cq8gNFhgnVFuurdZUiY0bn58UwaVFdwzEfDSUQ==';
try {
const key = await importPrivateKey(privateKey);
const decoded = await decryptRSA(key, fromBase64(ciphertext));
console.log(decoded);
} catch(error) {
console.log(error);
}
}
Running the code, I got an exception in window.crypto.subtle.decrypt with the rather useless message "DOMException".
What am I doing wrong?
Thanks
There's only one flaw: The posted code currently uses OAEP with SHA256. The ciphertext can be decrypted with the posted key if OAEP with SHA1 is applied as padding.
In addition, the function fromBase64 can be used to Base64 decode the key into a TypedArray, so the function str2ab is actually not needed (but of course this is not an error, just redundant).
const fromBase64 = base64String => Uint8Array.from(atob(base64String), c => c.charCodeAt(0));
const getPkcs8Der = pkcs8Pem => {
pkcs8Pem = pkcs8Pem.replace( /[\r\n]+/gm, "" );
const pkcs8PemHeader = "-----BEGIN PRIVATE KEY-----";
const pkcs8PemFooter = "-----END PRIVATE KEY-----";
pkcs8Pem = pkcs8Pem.substring(pkcs8PemHeader.length, pkcs8Pem.length - pkcs8PemFooter.length);
return fromBase64(pkcs8Pem);
}
async function importPrivateKey(pkcs8Pem) {
return await window.crypto.subtle.importKey(
"pkcs8",
getPkcs8Der(pkcs8Pem),
{
name: "RSA-OAEP",
hash: "SHA-1", // Replace SHA-256 with SHA-1
},
true,
["decrypt"]
);
}
async function decryptRSA(key, ciphertext) {
let decrypted = await window.crypto.subtle.decrypt(
{
name: "RSA-OAEP"
},
key,
ciphertext
);
const dec = new TextDecoder();
return dec.decode(decrypted);
}
window.onload = init;
async function init() {
const privateKey =
'-----BEGIN PRIVATE KEY-----\
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC3jmTi3O1k2YXs\
AM6nNTTIzDq5YWkxYrYb6cpO9eYuzmphgRnVDR6a1YWRXMoCuCfuNXcDGywzudRn\
bBMw0FHKLUqCttVHGpZYu0+0tRR10ubxiz/xnd/aCmRYHcmUNn8Qdh3KU59A9HK5\
HhYFf1vhK8r3fkoO4CjoGo1ROzXyMybUSy+4mSNscUtt5LwrVn48vXvG5i5B4DRT\
nM4cINmutEzA2s5cDt+dzU4Py71fKBRDRIGGn0vdVSoZKbWuhm5WewyRewCk7HFc\
PALCi5/1A7VKDAHUC4FlXmuG2+wzdchEyxMj6oLR7+BkKFQaTmuMM/22cGBjVTVt\
pSr3iDovAgMBAAECggEBAIuTQW+oovNu3IDq1DkdIjgV5AmW4tBkySlMi0OjhBbP\
auEdtDDnOwBtoJU6Q3nx4psmGItKHEBw6+yAp88UeT0NV30x3delhfGO7Trx/s7h\
Qi8lvcfSTqeUA11luSR0lAZGaryw/YX820eccw5XG9yK2ll7tIC/PxvPJOpB5fF2\
XGxGrionTjHDzXJ1OWX0i0aZlNNufInJAHhlt7aT3GiQMKcQs+AUb/+bWxI3Hln8\
KcL13EUlD4pJW8vtTK3gCnQNKKMoPB5Ugqe5BrU8ElkBz+zSKDnVwt5bgjrlucYz\
rKJxWr6/qTRZkzmvkhaJeNzzepfwkFsQ/eHcxYrtuDECgYEA8OXkQ2SqYDZwizCd\
SuVkx2zHm3zXYRSlRtnXYoUdJyTyuZ4k2GvXBrlwRsOJ14emVkHKyR5enmNjwrW5\
dcD2tbBzavcqOYAMiHcKklcS/gWgPx0X5QFHU33vr8u51BQWCz75lxddWNKxVAN/\
cUTugONtS4+EP4dSZhuxHt6RscsCgYEAwxA9QmZrI54hjMkIyqwmviRmIk42S5kk\
OxbhxBbt5qVueLRB092JyGmCR2kYiqdHCYkGFDOE4kni6Bsszq5CSJvhiATFeZeX\
ldFQeZqAiRY2rAd7xD1upMug/cK3ODA8k3k/e72CtyxtBTR01q29SnPx5p/57MrI\
3ogddHlGvK0CgYEA3VqhELwjQh1D9OJK5lM683SlRd7FGdOauyvYmhKu4xU0ZBNI\
0ATnpKoo3R04P+/JjGEQMRXS4794H6ZUMDuLdxAYPiW3ivZ6jbq04BtavEf3I4dc\
OXWfULzbzbFpo9KBHvxS4974S3Hut8AvDqnEbnKML25EmwuBT4oKis8BGVkCgYEA\
nusPDZbFeNou+TUbzYrdcZHUB+TyhTq58s4clxYbMgrbaslozAQ0aavT8Pvle6j2\
zgTth+3FOFr72x+wrJ358I/W+Wrxu7NOU0eZqci/KXCIkDT0l5d5GhewDK3jeYqK\
/5cLqnNmGHfARjpLak9X5V162erBwjIf3nTEkozvnW0CgYB6L1CX3DkTFH3OBcJe\
SvV18RDUfNI8MpUKcpofmwwvgER3GrehSZHRVxVwNbnJOqbh/oiwmmNJieKrFsk9\
EzCRBVWdZByHHYW2js+gCrAp+ghnl1QEAeCU7YTxCJ2fZIAmfB9X4u/7ARtVxnZY\
mOWlm65KUYr5lf2Ws5plL4pCRA==\
-----END PRIVATE KEY-----';
const ciphertext =
'F6/NwENdUZSl+vrgpWVkyWPQuYaTGDNZPIvj4KmIRHVx4qybxN24LPIgk0Rl84KHcLFadZWCjNpM\
vg3l826OaKZAtwvIp9IxVrMbvtNOymY6A1koKvC9ema92SR4DC9hmTtMxhUB6r3XgACtRLFqMfg+\
zYSHfFqQEGJg3yZ43hfzIq/gCfHPk5sZXASq5WY5b9yd4gRonn5D4OCD6xna/r5ovHfrpO/Fwe8N\
eeY2gqTAdtzvtmOw/HLQhGANejpJYr1IriQbepM7jLjBkJX+uCn38O1MxpQb7s5RXTvGvoEoofWV\
Cq8gNFhgnVFuurdZUiY0bn58UwaVFdwzEfDSUQ==';
try {
const key = await importPrivateKey(privateKey);
const decrypted = await decryptRSA(key, fromBase64(ciphertext));
console.log(decrypted);
} catch(error) {
console.log(error);
}
}
I have this error in android studio:
E/Buffer Error: Error converting result
java.lang.NullPointerException: lock == null E/JSON Parser: Error
parsing data org.json.JSONException: End of input at character 0 of
my code is add_student.java
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
public class add_student extends AppCompatActivity {
Button add;
EditText name, phone, email;
String jname, jphone, jemail;
int value;
JSONParser jsonParser;
ProgressDialog progressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_student);
add=(Button)findViewById(R.id.add);
name=(EditText)findViewById(R.id.name);
email=(EditText)findViewById(R.id.email);
phone=(EditText)findViewById(R.id.phone);
jsonParser=new JSONParser();
jname=name.getText().toString();
jphone=phone.getText().toString();
jemail=email.getText().toString();
add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new AddStudentTask().execute();
}
});
}
class AddStudentTask extends AsyncTask<String,String,String>{
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog=new ProgressDialog(add_student.this);
progressDialog.setTitle("Wait...");
progressDialog.show();
}
#Override
protected String doInBackground(String... strings) {
List<NameValuePair> list= new ArrayList<NameValuePair>();
list.add(new BasicNameValuePair("Name",jname));
list.add(new BasicNameValuePair("Mobile",jphone));
list.add(new BasicNameValuePair("Email",jemail));
JSONObject jsonObject=jsonParser.makeHttpRequest("http://10.0.2.2/billapp/add_student.php","POST",list);
try{
if(jsonObject!=null && !jsonObject.isNull("value")){
value=jsonObject.getInt("value");
}else{
value=0;
}
}catch (Exception e){
Log.d("ERROR",e.getMessage());
}
return null;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
if(value==1){
Toast.makeText(getApplicationContext(),"Done", Toast.LENGTH_LONG).show();
}else {
Toast.makeText(getApplicationContext(),"Error", Toast.LENGTH_LONG).show();
}
progressDialog.dismiss();
}
}
}
JSONParser.java
import android.util.Log;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.List;
public class JSONParser {
static InputStream is = null;
static JSONObject jObj = null;
static String json = "";
// constructor
public JSONParser() {
}
// function get json from url
// by making HTTP POST or GET mehtod
public JSONObject makeHttpRequest(String url, String method,
List<NameValuePair> params) {
// Making HTTP request
try {
// check for request method
if(method == "POST"){
// request method is POST
// defaultHttpClient
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new UrlEncodedFormEntity(params,"utf-8"));
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
is = httpEntity.getContent();
}else if(method == "GET"){
// request method is GET
DefaultHttpClient httpClient = new DefaultHttpClient();
String paramString = URLEncodedUtils.format(params, "utf-8");
url += "?" + paramString;
HttpGet httpGet = new HttpGet(url);
HttpResponse httpResponse = httpClient.execute(httpGet);
HttpEntity httpEntity = httpResponse.getEntity();
is = httpEntity.getContent();
}
} catch (UnsupportedEncodingException e) {
//e.printStackTrace();
} catch (ClientProtocolException e) {
// e.printStackTrace();
} catch (IOException e) {
//e.printStackTrace();
}
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
is, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
json = sb.toString();
} catch (Exception e) {
Log.e("Buffer Error", "Error converting result " + e.toString());
}
// try parse the string to a JSON object
try {
jObj = new JSONObject(json);
} catch (JSONException e) {
Log.e("JSON Parser", "Error parsing data " + e.toString());
}
// return JSON String
return jObj;
}
}
add_student.php
<?php
require_once __DIR__ . '/db_connect.php';
$response = array();
if(isset($_POST['Name'])){
$name = $_POST['Name'];
$email = $_POST['Email'];
$mobile = $_POST['Mobile'];
$db = new DB_CONNECT();
mysql_query("SET NAMES utf8");
$result=mysql_query("INSERT INTO student('Name','Email','Mobile') VALUES ('$name','$email','$mobile')");
if($result){
$response['value']=1;
}else{
$response['value']=0;
}
}else{
$response['value']=-1;
}
echo json_encode($response);
?>
if any one know how i can solve this problem please till me