I am using RSA to encrypt a secret and then sending it to another host. The code works perfectly when it's done on PC -> PC traffic, but throws the following Exception for Android -> PC traffic.
The javax.crypto.BadPaddingException: Blocktype mismatch: 0
sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:332)
sun.security.rsa.RSAPadding.unpad(RSAPadding.java:272)
com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:354)
com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:380)
javax.crypto.Cipher.doFinal(Cipher.java:2121)
What must I do to resolve this issue?
public byte[] encrypt(byte[] plaintext){
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(plaintext);
}
public byte[] decrypt(byte[] ciphertext){
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(ciphertext);
}
import android.util.Base64
import java.security.KeyFactory
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import javax.crypto.Cipher
class RSAUtils {
val ALGORITHM = "RSA"
val PUBLIC_KEY = "" // Enter Public key here
val PRIVATE_KEY = "" // Enter Private key here
fun encrypt(text: String): String? {
return encryptRSA(text)?.let { byteArrayToString(it) }
}
fun decrypt(text: String): String {
return decryptRSA(stringToByteArray(text))
}
private fun encryptRSA(text: String): ByteArray? {
var cipherText: ByteArray? = null
try {
// generate publicKey instance
val byteKey = stringToByteArray(PUBLIC_KEY)
val X509publicKey = X509EncodedKeySpec(byteKey)
val kf: KeyFactory = KeyFactory.getInstance(ALGORITHM)
val publicKey = kf.generatePublic(X509publicKey)
// get an RSA cipher object and print the provider
val cipher: Cipher = Cipher.getInstance(ALGORITHM)
// encrypt the plain text using the public key
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
cipherText = cipher.doFinal(text.toByteArray())
} catch (e: Exception) {
e.printStackTrace()
}
return cipherText
}
private fun decryptRSA(text: ByteArray): String {
var dectyptedText: ByteArray? = null
try {
// generate privateKey instance
val byteKey = stringToByteArray(PRIVATE_KEY)
val keySpec = PKCS8EncodedKeySpec(byteKey)
val kf: KeyFactory = KeyFactory.getInstance(ALGORITHM)
val privateKey = kf.generatePrivate(keySpec)
// get an RSA cipher object and print the provider
val cipher = Cipher.getInstance(ALGORITHM)
// decrypt the text using the private key
cipher.init(Cipher.DECRYPT_MODE, privateKey)
dectyptedText = cipher.doFinal(text)
} catch (ex: java.lang.Exception) {
ex.printStackTrace()
}
return String(dectyptedText!!)
}
private fun stringToByteArray(text: String): ByteArray {
return Base64.decode(text, Base64.DEFAULT)
}
private fun byteArrayToString(byteArray: ByteArray): String {
return Base64.encodeToString(byteArray, Base64.DEFAULT)
}
}
https://github.com/kushalgupta0565/RSAEncryption - working fine in android, you just need to pass your PUBLIC/PRIVATE keys
Related
I am generating a public and private key pair in javascript. The public key is being sent to the server where I have java code that encrypts the string. When I try to decrypt the string in Javascript using a private key I get an error. Here is the Javascript code I used.
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class CryptographyService {
private publicKey: string;
private privateKey: string;
public randomKey: string;
private keyPair: CryptoKeyPair;
public exportedKey: string;
constructor() {
/**Generate the public and private key */
this.generateKeys()
.then(() => {
return this.exportKey(this.keyPair.publicKey);
})
.then(() => {});
}
public setPublicKey(key: string) {
this.publicKey = key;
}
public setRandomKey(key: string) {
this.randomKey = key;
}
public setPrivateKey(key: string) {
this.privateKey = key;
}
public async encryptMessage(text: String) {
const { default: NodeRSA } = await import('node-rsa');
const key = NodeRSA(this.publicKey, 'pkcs8-public');
return key.encrypt(text, 'base64');
}
// For subscription key encryption and decryption
private async generateKeys() {
const keyPair = await crypto.subtle.generateKey(
{
name: 'RSA-OAEP', //algorithm
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: 'SHA-256'
},
true,
['encrypt', 'decrypt']
);
this.keyPair = keyPair; /**{
publicKey:
privateKey:
} */
}
private async exportKey(key: CryptoKey) {
/** exportKey() it takes as input a CryptoKey object and gives you the key in an external, portable format.
* crypto.subtle.exportKey(format, key); : returns a promise
* spki format is used to import/export RSA or Elliptic Curve public keys.
*/
const exported = await crypto.subtle.exportKey('spki', key); //Returns an ArrayBuffer
const exportedAsString = this.ab2str(exported); // Converts ArrayBuffer to String
/**btoa encodes a string to base64 */
const exportedAsBase64 = window.btoa(exportedAsString);
this.exportedKey = exportedAsBase64;
}
// Uses private key to decrypt message sent from the backend
public async decryptMessage(input: string) {
const ciphertext = this.str2ab(input);
const decrypted = await window.crypto.subtle.decrypt(
{
name: 'RSA-OAEP'
},
this.keyPair.privateKey,
ciphertext
);
const dec = new TextDecoder();
const decodedMessage = dec.decode(decrypted);
return decodedMessage;
}
private ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
}
private 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;
}
}
The java code to convert the string to PublicKey Object
String pubKey = loginRequest.publicKey;
PublicKey pk = null;
try {
byte[] keyBytes = Base64.decodeBase64(pubKey);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
pk = kf.generatePublic(spec);
} catch (Exception e) {
System.out.println("Exception in generating primary key: " + e.getMessage());
}
Encrypting the string with the publicKey
public static byte[] encrypt(String text, PublicKey key) {
byte[] cipherText = null;
try {
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance(ALGORITHM);
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
// encrypt the plain text using the public key
cipher.init(Cipher.ENCRYPT_MODE, key, oaepParams);
cipherText = cipher.doFinal(text.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
return cipherText;
}
I get back an encrypted string when when I try to decode it in Javascript I get an error. Any idea what I need to change in my code to fix this?
I am trying to port my working python code for generating signature to java but they both are producing different signatures. Here is my code for python which is generating the signature.
import pdb
pdb.set_trace()
signer = PKCS1_v1_5.new(priv_key)
digest = SHA256.new()
digest.update(message)
val = signer.sign(digest)
return val
I am calling this function using this statement
signature = b64encode(sign(msg1, private))
Here the msg1 is
msg1 = 'test'.encode("utf8")
and private is the private key which I am importing using
RSA.importKey("<Location of key>"
I am trying to write a similar java code for implementing the same functionality with this given code
public static void main(String[] args) throws Exception {
String payload = "test";
String dig = makeDigest( payload, "SHA-256");
Key k = getPrivateKey("private_key.der");
String signature = encrypt(dig, "RSA", k);
System.out.print(signature);
}
public static String makeDigest(String payload, String digestAlgo) {
String strDigest = "";
try {
MessageDigest md = MessageDigest.getInstance(digestAlgo);
byte[] p_b = payload.getBytes("UTF-8");
md.update(p_b);
byte[] digest = md.digest();
char[] encoded = Hex.encodeHex(digest);
strDigest = new String(encoded);
System.out.println(strDigest);
}
catch (Exception ex) {
ex.printStackTrace();
}
return strDigest;
}
public static String encrypt(String sha, String encryptAlgo, Key k) {
String strEncrypted = "";
try {
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance(encryptAlgo,"BC");
cipher.init(Cipher.ENCRYPT_MODE, k);
byte[] encrypted = cipher.doFinal(sha.getBytes("UTF-8"));
//System.out.println(new String(encrypted));
byte[] encoded = Base64.getEncoder().encode(encrypted);
strEncrypted = new String(encoded);
}
catch (Exception ex) {
ex.printStackTrace();
}
return strEncrypted;
}
public static PrivateKey getPrivateKey(String filename)
throws Exception {
byte[] keyBytes = Files.readAllBytes(Paths.get(filename));
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
Security.addProvider(new BouncyCastleProvider());
KeyFactory kf = KeyFactory.getInstance("RSA","BC");
return kf.generatePrivate(spec);
}
Both thee codes are generating different signatures. I feel something I am doing wrong in selecting the algorithm on Java side. I have tried with
RSA/NONE/PKCS1Padding
and
RSA/ECB/PKCS1Padding
but still, it is different signature as compared to python code.
Any lead on this will be very helpful.
I have a requirement in which I have to communicate with a
Web Service by sending three parameters that are encrypted: ID, key and initialization vector (IV). In order to do this, I need to use Triple DES (3DES/DESede) encryption as well as RSA encryption. The steps required to do this are:
Start with the ID in clear text. Decode the ID from a base64 string to an array of bytes. The original ID is not base64 encoded but this step must be performed.
Encrypt the base64 decoded ID using 3DES and generate the key and IV
Encrypt the key and IV generated with RSA (using a certificate provided)
Convert encrypted ID, encrypted key and IV to base64 strings.
I was provided with a C# example on how to do this and the code looks as displayed below:
namespace SampleEncryption
{
public class SampleWebServiceInvocation
{
private const string CERT_SUBJECT = "sample.cer";
public string GetReport()
{
ReportService.RService ws = new ReportService.RService();
ReportService.ReportRequest req = new ReportService.ReportRequest();
string key = string.Empty;
string iv = string.Empty;
string id = "1234567890123456";
string encrypt = new CryptEx().EncryptEx(id, CERT_SUBJECT, ref key, ref iv);
req.AccountNumber = encrypt;
req.Key = key;
req.InitializationVector = iv;
ReportService.ReportResponse resp = ws.getReport(req);
if (resp.Success)
return resp.RedirectUrl;
return string.Empty;
}
}
/// <summary>
/// CryptorEx
/// </summary>
public class CryptEx
{
private X509Certificate2 _certificate;
private byte[] _encryptedKey;
private byte[] _encryptedIV;
private byte[] _encryptedText;
private byte[] _decryptedKey;
private byte[] _decryptedIV;
private byte[] _decryptedText;
/// <summary>
/// Default (empty) constructor
/// </summary>
public CryptEx()
{
}
/// <summary>
/// Publicly exposed encrypt method
/// </summary>
public string EncryptEx(string decryptedText, string certSubject, ref string key, ref string iv)
{
string data;
_decryptedText = Convert.FromBase64String(decryptedText);
try
{
_certificate = GetSignerCert(certSubject);
_encryptedText = EncryptWithTripleDES();
EncryptWithRSA();
key = Convert.ToBase64String(_encryptedKey);
iv = Convert.ToBase64String(_encryptedIV);
data = Convert.ToBase64String(_encryptedText);
}
catch (Exception)
{
data = string.Empty;
}
return data;
}
private byte[] EncryptWithTripleDES()
{
//Create a crypto provider for the text
using (TripleDESCryptoServiceProvider tripDes = new TripleDESCryptoServiceProvider())
{
tripDes.Mode = CipherMode.CBC;
ICryptoTransform encryptor = tripDes.CreateEncryptor();
//Encrypt
using (MemoryStream ms = new MemoryStream())
{
CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
cs.Write(_decryptedText, 0, _decryptedText.Length);
cs.FlushFinalBlock();
cs.Close();
ms.Close();
_decryptedKey = tripDes.Key;
_decryptedIV = tripDes.IV;
return ms.ToArray();
}
}
}
private void EncryptWithRSA()
{
using (RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)_certificate.PublicKey.Key)
{
_encryptedKey = rsa.Encrypt(_decryptedKey, false);
_encryptedIV = rsa.Encrypt(_decryptedIV, false);
}
}
private static X509Certificate2 GetSignerCert(string certName)
{
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
store.Open(OpenFlags.ReadOnly);
// First, find certificates that haven't expired
X509Certificate2Collection validCerts = store.Certificates.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
X509Certificate2Collection scorecardCerts = validCerts.Find(X509FindType.FindBySubjectName, certName, false);
if (scorecardCerts.Count == 0)
throw new ApplicationException(string.Format("Unable to find certificate with name of {0}", certName));
if (scorecardCerts.Count > 1)
throw new ApplicationException(string.Format("More than one certificate has a name of {0}\r\n{1}",
certName, GetSubjectNames(scorecardCerts)));
return scorecardCerts[0];
}
finally
{
store.Close();
}
}
private static string GetSubjectNames(X509Certificate2Collection signingCert)
{
StringBuilder sb = new StringBuilder();
foreach (X509Certificate2 cert in signingCert)
sb.AppendLine(cert.Subject);
return sb.ToString();
}
}
}
In my case, I need to do this but in Java, so here I expose the logic of my implementation.
For Triple DES:
public class DESedeCrypto implements SymmetricCryptoManager {
/**
* Constant for the algorithm being used for Triple DES (CBC)
*/
public static final String DESEDE_CBC_TRANSFORMATION_ALG = "DESede/CBC/NoPadding";
#Override
public DESedeEncryptionResult encrypt(byte[] srcData, String cryptoAlgorithm) throws Exception {
DESedeEncryptionResult result = null;
byte[] encryptedBytes = null;
byte[] initializationVector = null;
if (srcData == null || srcData.length == 0) {
throw new InvalidEncryptionTargetException();
}
KeyGenerator keyGen = KeyGenerator.getInstance("DESede");
SecretKey secretKey = keyGen.generateKey();
Cipher cipher = Cipher.getInstance(cryptoAlgorithm);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
result = new DESedeEncryptionResult();
if (cryptoAlgorithm.equals(DESEDE_CBC_TRANSFORMATION_ALG)) {
IvParameterSpec spec = cipher.getParameters().getParameterSpec(IvParameterSpec.class);
initializationVector = spec.getIV();
result.setInitializationVector(initializationVector);
}
encryptedBytes = cipher.doFinal(srcData);
result.setResultBytes(encryptedBytes);
result.setKeyBytes(secretKey.getEncoded());
return result;
}
}
For RSA:
public class RSACrypto implements AsymmetricCryptoManager {
/**
* Tranformation algorithm for OAEP.
*/
private static final String OAEP_RSA_TRANSFORMATION = "RSA/ECB/PKCS1Padding";
/**
* Constructor of the class.
*/
public RSACrypto() {
}
#Override
public byte[] encryptWithCertificate(InputStream inputStream, byte[] targetBytes) throws Exception {
byte[] encryptedBytes = null;
if (targetBytes == null || targetBytes.length == 0) {
throw new InvalidEncryptionTargetException();
}
if (inputStream == null) {
throw new InvalidCertificatePathException("Resource specified for operation is not valid");
}
X509Certificate certificate = CryptoUtils.readCertificateFromInputStream(inputStream);
encryptedBytes = getEncryptedBytes(certificate, targetBytes);
return encryptedBytes;
}
private byte[] getEncryptedBytes(X509Certificate certificate, byte[] targetBytes) throws Exception {
byte[] encryptedBytes = null;
PublicKey publicKey = certificate.getPublicKey();
Cipher cipher = Cipher.getInstance(OAEP_RSA_TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedBytes = cipher.doFinal(targetBytes);
return encryptedBytes;
}
}
Here's how I read the certificate:
public static X509Certificate readCertificateFromInputStream(InputStream inputStream) throws Exception {
X509Certificate certificate = null;
CertificateFactory certFactory = CertificateFactory.getInstance(X509_CERT_TYPE);
certificate = (X509Certificate) certFactory.generateCertificate(inputStream);
return certificate;
}
And here is the logic of the whole encryption process:
String base64id = new String(CryptoUtils.encodeBase64(base64id.getBytes()));
DESedeEncryptionResult encryptionResult = desedeCrypto.encrypt(CryptoUtils.decodeBase64(base64id), DESedeCrypto.DESEDE_CBC_TRANSFORMATION_ALG);
byte[] rsaEncryptedKey = rsaCrypto.encryptWithCertificate(certForKeyInputStream, encryptionResult.getKeyBytes());
byte[] rsaEncryptedIv = rsaCrypto.encryptWithCertificate(certForIvInputStream, encryptionResult.getInitializationVector());
String encryptedId = CryptoUtils.getBase64EncodedStr(encryptionResult.getResultBytes());
String finalEncryptedKey = CryptoUtils.getBase64EncodedStr(rsaEncryptedKey);
String finalIv = CryptoUtils.getBase64EncodedStr(rsaEncryptedIv);
The ID used for testing is: 1234567890123456
When I send the results to the Web Service I receive a message that the encryption cannot be done. In order to make sure, I tested both implementations (Java and c#). C# generated values are working when I send them to the web service, but Java implementation is not working.
Do you know if there's some specific type of algorithm I'd need to add to the Java implementation, I mean in Triple DES we use DESede/CBC/NoPadding but maybe c# uses another. Same for RSA.
Maybe somebody can give a me clue or recommendation regarding this? Thanks in advance.
Google have provided the following example code showing how to generate a secure token for their second version of Recaptcha:
public class STokenUtils {
private static final String CIPHER_INSTANCE_NAME = "AES/ECB/PKCS5Padding";
public static final String createSToken(String siteSecret) {
String sessionId = UUID.randomUUID().toString();
String jsonToken = createJsonToken(sessionId);
return encryptAes(jsonToken, siteSecret);
}
private static final String createJsonToken(String sessionId) {
JsonObject obj = new JsonObject();
obj.addProperty("session_id", sessionId);
obj.addProperty("ts_ms", System.currentTimeMillis());
return new Gson().toJson(obj);
}
private static String encryptAes(String input, String siteSecret) {
try {
SecretKeySpec secretKey = getKey(siteSecret);
Cipher cipher = Cipher.getInstance(CIPHER_INSTANCE_NAME);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return BaseEncoding.base64Url().omitPadding().encode(cipher.doFinal(input.getBytes("UTF-8")));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static String decryptAes(String input, String key) throws Exception {
SecretKeySpec secretKey = getKey(key);
Cipher cipher = Cipher.getInstance(CIPHER_INSTANCE_NAME);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(
BaseEncoding.base64Url().omitPadding().decode(input)), "UTF-8");
}
private static SecretKeySpec getKey(String siteSecret){
try {
byte[] key = siteSecret.getBytes("UTF-8");
key = Arrays.copyOf(MessageDigest.getInstance("SHA").digest(key), 16);
return new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
}
The full code can be found at: https://github.com/google/recaptcha-java
I'm wanting to generate this token in Ruby 2.1+ and have got this far but it outputs incorrect data. I'm trying to slowly debug it, but in the meantime I'm wondering if anyone can see any obvious flaws in my process?
stoken_json = hash_to_json({'session_id' => SecureRandom.uuid, 'ts_ms' => Time.now.to_i})
cipher = OpenSSL::Cipher::AES128.new(:ECB)
private_key_digest = Digest::SHA1.hexdigest(private_key)[0...16]
cipher.encrypt
cipher.key = private_key_digest
encrypted_stoken = cipher.update(stoken_json) << cipher.final
encoded_stoken = Base64.urlsafe_encode64(encrypted_stoken).gsub(/\=+\Z/, '')
Turns out I was close. I needed to digest not hexdigest the private key:
private_key_digest = Digest::SHA1.digest(private_key)[0...16]
So the final code is:
stoken_json = hash_to_json({'session_id' => SecureRandom.uuid, 'ts_ms' => (Time.now.to_f * 1000).to_i})
cipher = OpenSSL::Cipher::AES128.new(:ECB)
private_key_digest = Digest::SHA1.digest(private_key)[0...16]
cipher.encrypt
cipher.key = private_key_digest
encrypted_stoken = cipher.update(stoken_json) << cipher.final
encoded_stoken = Base64.urlsafe_encode64(encrypted_stoken).gsub(/\=+\Z/, '')
There didn't seem to be a built-in way to strip the padding from the base64 string, thus the .gsub at the end.
I also needed the timestamp in milliseconds so that part has been modified too.
In the recaptcha gem there is a method hash_to_json that I'm using, otherwise I suspect you'd use the JSON gem.
Data is encrypted using OpenSSL in PHP and i would like to decrypt java but getting error in java
Code for Encryption in PHP-
public function getEncryptedString($cardNumber,$key_id){
$encryptedCardNumber = '';
$key_name = "key_{$key_id}";
$pub_key_path =$key_name.".public";
$fp=fopen ($pub_key_path,"r"); //Open the public key (key_8.public)
$pub_key = fread($fp,8192); //Read public key key (key_8.public) into
fclose($fp);
openssl_public_encrypt($cardNumber,$encryptedCardNumber,$pub_key);
if($key_id > 4) return rawurlencode(base64_encode($encryptedCardNumber));
else return addslashes($encryptedCardNumber);
}
Code for Decryption in JAVA-
public static String getDecryptedValue(int keyId,String encryptedCCNumber ,String passPhrase){
String result="";
String privateKeyFileName="key_8.private";
String privateKeyLocation= PropertiesUtil.getProperty("PUBLIC_PRIVATE_KEY_LOCATION");
String privateKeyFileNameLocation=privateKeyLocation+privateKeyFileName;
String decryptedValue= getDecryptedMessage(privateKeyFileNameLocation,encryptedCCNumber,passPhrase);
return result;
}
public static String getDecryptedMessage(String privateKeyFileNameLocation, String encryptedCCNumber,String passPhrase)
{
byte[] decodedBytesCCNumber= Base64.decodeBase64(encryptedCCNumber.getBytes());
byte[] decryptedMessage=null;
try {
Cipher cipher = Cipher.getInstance("RSA");
PrivateKey privateKey = getPrivateKey(privateKeyFileNameLocation,passPhrase);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
decryptedMessage = cipher.doFinal(decodedBytesCCNumber);
} catch (Throwable t) {
t.printStackTrace();
}
System.out.println("new String(decryptedMessage)"+new String(decryptedMessage));
return new String(decryptedMessage);
}
private static PrivateKey getPrivateKey(String privateKeyFileNameLocation,String passPhrase) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException {
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream(privateKeyFileNameLocation), passPhrase.toCharArray());
String alias = (String) ks.aliases().nextElement();
KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry(alias, new KeyStore.PasswordProtection(passPhrase.toCharArray()));
return keyEntry.getPrivateKey();
}
Java code is giving below error.
java.io.IOException: toDerInputStream rejects tag type 45
at sun.security.util.DerValue.toDerInputStream(DerValue.java:847)
at sun.security.pkcs12.PKCS12KeyStore.engineLoad(PKCS12KeyStore.java:1221)
at java.security.KeyStore.load(KeyStore.java:1214)
You are URL-encoding a Base64-encoding of the ciphertext, but you are only deciphering a base64-decoding of it. Either lose the URL-encoding or decode it at the receiver.