TrippleDES without base 64 encoding: Given final block not properly padded - encryption

I'm trying to encrypt and decrypt a string using TrippleDES algorythm and without using Base64 encoding (my app will be talking to another app that has these requirements). Everything worked beautifully when I was testing stuff using Base64 encoding/decoding, but when I switched to doing it plain-text style (like the app I'm calling requires), everything broke.
I've read this post Given final block not properly padded which says the key is wrong on decoding, but that can't be, because these lines actually pass in the same variables for both the key and transformation:
ecipher = Cipher.getInstance(transformation);
dcipher = Cipher.getInstance(transformation);
ecipher.init(Cipher.ENCRYPT_MODE, key, iv);
dcipher.init(Cipher.DECRYPT_MODE, key, iv);
Also, I've printed out the lengths of both the encoded string and the array, their lengths are multiples of 8.
My output with I'm getting:
originalText: Abcdefgh
number of bites: 16
cryptText: d4167d9e2b3b1b2d1f940bc45099da0a
cryptText.length: 32
cryptText.getBytes().length: 32
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.SunJCE_f.b(DashoA13*..)
at com.sun.crypto.provider.DESedeCipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
Java Result: 1
My full code (slightly modified version of this tutorial http://eternusuk.blogspot.com/2008/09/java-triple-des-example.html):
package com.test.encrypt;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import org.apache.commons.codec.binary.Hex;
public class TrippleDESTest
{
private Cipher ecipher;
private Cipher dcipher;
private String algorithm = "DESede";
private String transformation = "DESede/CBC/PKCS5Padding";
private String keyPhrase = "123456789012345678901234"; //your keyphrase 24 bit
private SecretKey key;
private IvParameterSpec iv;
private static TrippleDESTest cryptoUtil;
private String ENCODING = "UTF-8";
public static TrippleDESTest getInstance() throws Exception
{
if (cryptoUtil == null)
{
cryptoUtil = new TrippleDESTest();
}
return cryptoUtil;
}
private TrippleDESTest() throws Exception
{
DESedeKeySpec keySpec = new DESedeKeySpec(keyPhrase.getBytes());
key = SecretKeyFactory.getInstance(algorithm).generateSecret(keySpec);
iv = new IvParameterSpec(new byte[8]);
ecipher = Cipher.getInstance(transformation);
dcipher = Cipher.getInstance(transformation);
ecipher.init(Cipher.ENCRYPT_MODE, key, iv);
dcipher.init(Cipher.DECRYPT_MODE, key, iv);
}
public String encrypt(String str) throws Exception
{
byte[] utf8 = str.getBytes(ENCODING);
byte[] enc = ecipher.doFinal(utf8);
System.out.println("number of bites: " + enc.length);
return Hex.encodeHexString(enc);
}
public String decrypt(String str) throws Exception
{
byte[] dec = str.getBytes();
byte[] utf8 = dcipher.doFinal(dec);
return Hex.encodeHexString(utf8);
}
public static void main(String[] args) throws Exception
{
TrippleDESTest test = TrippleDESTest.getInstance();
String originalText = "Abcdefgh";
System.out.println("originalText: " + originalText);
String cryptText = test.encrypt(originalText);
System.out.println("cryptText: " + cryptText);
System.out.println("cryptText.length: " + cryptText.length());
System.out.println("cryptText.getBytes().length: " + cryptText.getBytes().length);
System.out.println("decote text: " + test.decrypt(cryptText));
}
}// end class TrippleDESTest
Thanks in advance!

You are performing the hexadecimal encoding in the wrong order. You need to decode the ciphertext, instead of encoding the plain text in your decrypt method.

Related

Java TripleDES PKCS5Padding decryption to C# - bad data / padding error

I'm trying to write the C# equivalent for the following Java code:
protected static final String DES_ECB_PKCS5PADDING = "DESede/ECB/PKCS5Padding";
public static String decryptValueDirect(String value, String key)
throws NoSuchAlgorithmException, NoSuchPaddingException,
GeneralSecurityException, IllegalBlockSizeException,
BadPaddingException {
byte[] bytes = Base64.decodeBase64(value);
Cipher cipher = Cipher.getInstance(DES_ECB_PKCS5PADDING);
cipher.init(Cipher.DECRYPT_MODE, convertSecretKey(key.getBytes()));
byte[] decryptedValue = cipher.doFinal(bytes);
String nstr = new String(decryptedValue);
return nstr;
}
protected static SecretKey convertSecretKey(byte[] encryptionKey) throws GeneralSecurityException {
if (encryptionKey == null || encryptionKey.length == 0)
throw new IllegalArgumentException("Encryption key must be specified");
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(TRIPLEDES);
KeySpec keySpec = new DESedeKeySpec(encryptionKey);
return keyFactory.generateSecret(keySpec);
}
The source text is a base64 encoded, then encrypted and then base64 encoded for transport on a rabbit queue. Our vendor who handles the encryption provided the above for decryption in Java, but has no idea about C#.
The only input on the encryption side is a key, a random string. We use the same string for encryption/decryption 012345678901234567890123456789 in our dev env. That is the only input, no salt, hashing (that i see) or pw iterations. The only requirement is that it is at least 24 chars long.
My C# code is below and a fiddle of my attempt is here.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
public class Program
{
public static void Main()
{
//Message Data value
//We are using encrypted multibyte.
string myData = #"ROE8oYeV7B6faUsvfIx0Xe55vSs9IR5DlWGRbSM+lmKmLcaJsA13VudwWlAEYtLUD8+nMXShky0grSxsk0Z9cQe5V45XnAIfUhnyzI9a0jtMFC8XnIZ5dbclPO/V73QnieIZDkbNV5cPo3BM+l79ai96KB/gkF3xuerFPxvWejtPyWbOyO+FfNyFps4gAYDITsYIAEH39VP4eipmQ5zc18BA39lajQ3UaVewSxz7H+x3Ooe2SzJT/TQWRkioJSEFwexqzkHiLOQ0MOCIVD9xTWpLYnsL3LMwyF6H8f0PY4Fc57LVGhvUZ7dsB9NWUAnmG3uqbsonNFVhuXyvJTWNyFOHwFzOMx6XDLJJFHGZhaHg2VrescfnpUtonQY08RgojBngyJNRqK8URAvI3bqKq8Y7F/9HmEtMIIQe6KuuTmU=";
string myKey = "012345678901234567890123456789";//Development Env Key.
Console.WriteLine("Decrypt1:");
string s = Decrypt1(myData, myKey);
Console.ReadLine();
}
public static string Decrypt1(string value, string decryptionKey)
{
string decryptString = "";
TripleDESCryptoServiceProvider tDESalg = new TripleDESCryptoServiceProvider();
MD5CryptoServiceProvider hashMD5Provider = new MD5CryptoServiceProvider();
try
{
byte[] decodedData = Convert.FromBase64String(value);
tDESalg.Mode = CipherMode.ECB;
tDESalg.Padding = PaddingMode.PKCS7;//According to MS, same as PKCS5PADDING
byte[] Key = hashMD5Provider.ComputeHash(Encoding.UTF8.GetBytes(decryptionKey));
//byte[] IV = tDESalg.IV;
byte[] IV = new byte[tDESalg.BlockSize / 8]; //The size of the IV property must be the same as the BlockSize property divided by 8
var memoryStream = new MemoryStream(decodedData);
var cryptoStream = new CryptoStream(memoryStream, tDESalg.CreateDecryptor(Key, IV), CryptoStreamMode.Read);
var reader = new StreamReader(cryptoStream);
decryptString = reader.ReadToEnd();
byte[] decryptData = Convert.FromBase64String(decryptString);
}
catch (Exception e)
{
Console.WriteLine("A Cryptographic error occurred: {0}", e.Message + e.StackTrace);
return null;
}
return decryptString;
}
}
Searching seems to point to the same answer, the key, encoding, ... all must be the same. I just don't know what that would be the equivalent for the Java source provided. :) Any suggestions will be helpful.
MD5 has a 16-byte output, Triple DES (3DES) requires a 24-byte key. There is a key size mis-match.
The C# and Java key derivations are substantially different:
C#:
byte[] Key = hashMD5Provider.ComputeHash(Encoding.UTF8.GetBytes(decryptionKey));
returns 16-bytes.
Java:
SecretKeyFactory.getInstance(TRIPLEDES)
returns 24-bytes.
There is a key option (2TDEA) where a 16-byte key is used and the first 8-bytes will be duplicated to create the last 8-bytes. NIST has deprecated this option.
Some implementations will accept a 16-byte key and extend the key to 24-bytes and some will not. You should provide all 24-bytes to 3DES, do not rely on an implementation to create the 24-byte key.
Note: The question was updated so it is not clear that the the actual encrytpion key is derived.

How to get RSA Public Key and Private Key and get the modulus and exponent in plain text?

I am kinda new to cryptography, and a requirement needs me to
"Create or retrieve user’s RSA-OAEP key. There should be one
public/private key pair for each user for a device"
and send it to the server in this form:
{"modulus":"qMBRpdYrAy5aMmo31NErUizh5sbweguSmh4wlK6uJEIDl+kwTlROnE34KOFExeTbJSX0WygPi+vWl0yNq7buIMUKpytossAAWut5khO3CQJxTk7G2gnEPNUUXHiExGgNrLzcSLv8YIlfVALhoRWyC67KOL+a+3taNq3h+BHeWhM=","exponent":"AQAB"}
I tried the OpenSSL commands to generate RSA public/private key pair, and it comes out like this:
Public key (have changed the value by a bit for security):
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4NMwqhswK6Py+N1OlPBn3JelqEdZ8YwSn4j1Kvp5HK+pS/5gcABkx/89buDhhtKvZ8mfSNkhKHU2WuBPIikGAvEKbbbQ8DHKubHa07X1xTgY+qMyxTLaHnaEC6oOi6BixPfo6QAm+SnqxBWuvDPBzGqSS+/p5r7aydLBAlkoZ+DzpBbAHIIK72c+xqCL8oOFz4QqsVMakdKQ+fVdM1e8U2ak4ygHOleqJgcl8+xH7TFJsvUOfsgsMlz2FmNXWhCYUdOKglP8ZUVMuiAW9KEjAFta4xEf3bBGRDYss7mLQF5/RHbpzYeNwQ1RVp8bhTumpQytqJZshUj8DA3oqMFUr xyz#xyz.xyz
Private key (have altered the value by a bit for security):
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,67A126EE3266B641AF4AC698AA18FE01
3w4GI7HfymD0A8+LokUbsIiI50CJfszBjO5PR4tW532ccKVMQRKywBMANUWx5B66
Mkr/uY9lH8mJOhWrwwiXCUFKMtDGcC06NHbIWIAu/TP85TidwhtGABqSYkjh+8Hm
SlwTMe2xjPq+X8Xc8+vW6Ng8mvXST0Ezz4DiTKA9yeH0eVxIQ4WIpvO7X9793xSD
FoXQ5sCY7Mr6GBNxln4f9cy5BwEsEGa86dtzXVkK8Gu8xRy28GE4W7D6/eNkgl6B
ovEuuC7IDibP/qrGAq/Nxdrl6QrnFTd2FcQfbR4jeArC+IaeXAxXgqhhTJH9ySk8
ZnEaMIruEGvRbChwzlvqS/sCMcS8eoD22Di94Gmkv4thSIWuk1MTRMTKFTMAgVOb
8shCKgCR5FJXjPHV8sUhIwk4TrQWX70fWM3QmBq3ue2AnGonWXhzXQU7jB36zATm
dhpsHZ2/80BuB/hMnFJpsjcYU16pm9BunSMs7tyMW3X3F91x6liC3j2ItUjEkPME
P6eZE2KDM+QxlDLfebL+bGMN6rYvEmfvKo44nwNIMnJM9J3ZYNM9KGt87B4loVwn
TeWIGrCQ9SRCpiAVbZj+M9DDDuqxSoA0wxSDrcYjWt8trzS20AWj7lsxBQgUvpBX
nuFAQgMgT4DK9X2z9ESplXi/l2uZ0iDBTN4SEHI3oR3ar2rWSjoQrTTfOg7cYlF1
ewLR6toCEEvturC4vLyWyrDIu3P/jiSz6eiSTeI9W02rQ/qILUrouKx1LwviIKR2
OGQnkzm3iiNq0jykzObwCsDLuY6rA4nv/ZBsjLDWB34gveKSzOrtx4dzqmtcv0Kq
ndua6xdaPmpV3n4slRD1PxSwNgKb4qwlYuQMPKLhCXUq4yG59IOoH7yfxS5UZ7wa
yndGMLMPmylcHDLX02U90X3feUcC9IiE7z6pOILy4uC28Z2X5KYjoK07pwA+5lNt
9RvryaK4IXysJZ5zqsBUaeYlqqBATcEPYn3YXbT5cSaxkv4lI36g6iG7/QmA3PGt
1l57kBW2xnUSrqm5XtZZMrsSu2iZ9Hiuh73SRkODjg7ToEMtwLECkN1TRL9PVEQj
QHAxauWleC+2yB0+1XH7/CywkYk2HxeNQJJDsWU+XJM/RFGFNV481LwfU0Hw58sZ
ai4R2LmDDoy4wwtcQGkY13ZHT8h/jxP4/Sr36GJdVkhhUCDnpFfdNHebgflj0PQg
eTHVY/6GqfnKvneOGtDRR4EkBFopUV5OLzuRX3z/rlHRV1iPCaqhooL9XO320JYq
xY5YMWq7tvgzE5jtqo65AwO8WWs12NTzG1KRhcXCyYE4Da1T6k7l8++MOeVGZy1v
qDxEddTfiAzIvCme2lEiYOb2/UUNAhEM+Ave/lfWmirW5dSAOppZenHUnuZh9eVd
iFVswrAxcd4BqA5GGczzu9EIqdzpspTrnG3hxOf+tXEXf3TTAH/sTVfQGQHO1iRd
UTh9FGgHk3WMswBnYyfpbSOR8Mghab966RRYP2xBCVJEvCymYuUE11x6vsvQFpGS
X2SEhOpzgINuKZRTuVmhK1oXRt3BZO8yQ13t+wtQCP6a8azS+Sc436aDqBlWQfJ2
-----END RSA PRIVATE KEY-----
How can I extract the modulus and exponent in the form that server expects from this data?
You can simply get the format from RFC 4253 but you need to find out what the mpint (multi-precission integer) format is from RFC 4251.
Here's the thing in Java (just Java), simply because the JSch library is so horrible.
package nl.owlstead.crypto;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.Base64.Encoder;
public class SSHPublicKeyDecoder {
private static byte[] SSH_RSA_HEADER = "ssh-rsa".getBytes(StandardCharsets.US_ASCII);
private static RSAPublicKey decodeRSAPublicKey(byte[] encodedRSAPublicKey)
throws NoSuchAlgorithmException, InvalidKeySpecException {
ByteBuffer buf = ByteBuffer.wrap(encodedRSAPublicKey);
byte[] header = getSSHEncodedValueFromBuffer(buf);
if (!Arrays.equals(header, SSH_RSA_HEADER)) {
throw new IllegalArgumentException("Not an RSA public key");
}
byte[] eDataSigned = getSSHEncodedValueFromBuffer(buf);
BigInteger e = new BigInteger(eDataSigned);
byte[] nDataSigned = getSSHEncodedValueFromBuffer(buf);
BigInteger n = new BigInteger(nDataSigned);
KeyFactory rsaKeyFactory;
try {
rsaKeyFactory = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException ex) {
throw new IllegalStateException(
"KeyFactory should exist for RSA", ex);
}
RSAPublicKeySpec spec = new RSAPublicKeySpec(n, e);
return (RSAPublicKey) rsaKeyFactory.generatePublic(spec);
}
private static byte[] getSSHEncodedValueFromBuffer(ByteBuffer buf) {
int size = buf.getInt();
if (size < 0) {
throw new IllegalArgumentException("Bad SSH encoded value in format");
}
byte[] data = new byte[size];
buf.get(data);
return data;
}
private static byte[] toUnsigned(BigInteger value) {
if (value.compareTo(BigInteger.ZERO) <= 0) {
throw new IllegalArgumentException("Negative numbers cannot be encoded as unsigned integers");
}
if (value.equals(BigInteger.ZERO)) {
return value.toByteArray();
}
final byte[] signedBigEndian = value.toByteArray();
if (signedBigEndian[0] == 0x00) {
return Arrays.copyOfRange(signedBigEndian, 1, signedBigEndian.length);
}
return signedBigEndian;
}
private SSHPublicKeyDecoder() {
}
public static void main(String[] args) throws Exception {
String[] parts = args[0].split("\\s+");
String part2 = parts[1];
byte[] encodedRSAPublicKey = Base64.getDecoder().decode(part2);
RSAPublicKey pubKey = decodeRSAPublicKey(encodedRSAPublicKey);
String format = encodeServerPublicKey(pubKey);
System.out.println(format);
}
private static String encodeServerPublicKey(RSAPublicKey pubKey) {
byte[] nData = toUnsigned(pubKey.getModulus());
byte[] eData = toUnsigned(pubKey.getPublicExponent());
Encoder base64Encoder = Base64.getEncoder();
String format = String.format(
"{\"modulus\":\"%s\",\"exponent\":\"%s\"}%n",
base64Encoder.encodeToString(nData),
base64Encoder.encodeToString(eData));
return format;
}
}

Decrypting with the wrong password, final block not properly padded

I have the following code to define a cipher class
import java.util.*;
import javax.crypto.Cipher;
import java.security.SecureRandom;
import java.security.NoSuchAlgorithmException;
import java.security.AlgorithmParameters;
import javax.crypto.*;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKey;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
public class Cipher{
private SecureRandom rand;
private SecretKeyFactory kFact;
private Cipher AESCipher;
private SecretKey key;
public Cipher(char[] mpw, byte[] salt){
try{
kFact = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
rand = SecureRandom.getInstance("SHA1PRNG");
AESCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
PBEKeySpec spec = new PBEKeySpec(mpw, salt, 1024, 256);
key = new SecretKeySpec(kFact.generateSecret(spec).getEncoded(),"AES");
}catch(Exception e){
System.out.println("no such algorithm");
}
}
/*Henc[k,m] will return c such that Hdec[k,HEnc[k,m]] = m
*/
public ArrayList<byte[]> HEnc(byte[] message) throws Exception{
ArrayList<byte[]> res = new ArrayList<byte[]>(2);
AESCipher.init(Cipher.ENCRYPT_MODE ,key);
AlgorithmParameters params = AESCipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ctxt = AESCipher.doFinal(message);
res.add(0,iv);
res.add(1,ctxt);
return res;
}
public byte[] HDec(byte[] iv, byte[] cipher) throws Exception{
AESCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv) );
System.out.println("decrypting");
return AESCipher.doFinal(cipher);
}
/*public abstract byte[] HDec(SecretKey k, byte[] cipher);
*/
I am interested in decrypting cipher-text with incorrect passwords, to do so i defined the following test class,
import java.util.*;
import java.io.*;
public class testCipher{
public static void main(String[] args) throws Exception{
while(true){
Scanner sc = new Scanner(System.in);
System.out.println("Enter master password");
String pass = sc.nextLine();
System.out.println("Enter incorrect password");
String fakepass = sc.nextLine();
System.out.println("Enter message to encrypt");
String message = sc.next();
String salt = "123";
HCipher goodEnc = new HCipher(pass.toCharArray(),salt.getBytes());
HCipher badEnc = new HCipher(fakepass.toCharArray(),salt.getBytes());
byte[] toEncrypt = message.getBytes();
ArrayList<byte[]> cipher = goodEnc.HEnc(toEncrypt);
byte[] ciphertxt = cipher.get(1);
byte[] iv = cipher.get(0);
while(true){
System.out.println("Enter 1 to decrypt with correct pass, 2 to decrypt with incorrect pass and 0 to end simulation");
int i = sc.nextInt();
if(i == 1){
System.out.println("Decrypting with correct password");
byte[] plaintxt = goodEnc.HDec(iv, ciphertxt);
System.out.println(new String(plaintxt));
}
if(i == 2){
System.out.println("Decrypting with incorrect password");
byte[] plaintxt = badEnc.HDec(iv, ciphertxt);
System.out.println(new String(plaintxt));
}
if(i == 0){
break;
}
}
}
}
}
Encrypting and Decrypting using the correct password works well. However, when I try to decrypt using an incorrect password, I get the follwing error:
Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:420)
at javax.crypto.Cipher.doFinal(Cipher.java:1966)
at HCipher.HDec(HCipher.java:54)
at testCipher.main(testCipher.java:52)
I am guessing it has something to do with my IV but im not sure how to fix it. Does anyone have any suggestions?
AES is a block cipher that encrypts in blocks of 16 bytes. Padding is used to fill up your last block before encryption so it fits in an even number of blocks.
You are specifying PKCS5Padding with this:
AESCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
The padding is created in such a way that it can be recognized and removed during decrypt. If you decrypt with the wrong key the Cipher won't be able to identify a valid pad and thus give you the BadPaddingException
If you instantiate your decrypt Cipher without padding (and thus take on that responsibility yourself) you can avoid this exception.

Java - How to fix InvalidKeyException: Wrong algorithm type - RSA

I got a Private JKS file and a Password file for it. I saw the internet, wrote a java program which will print out the private key as a string.
Similarly I got a public key file with .cer extension. Similarly I printed out the contents of this file too as a string
My idea is to write a java program which has 2 methods encrypt and decrypt.I wrote all the stuff. But when I try to use my encrypt and decrypt function i am seeing InvalidKeyException: Wrong algorithm type. I printed out the algorithm from the jks file and I see it as RSA. In the public certificate the algorithm is printed as SHA1withRSA
here are my programs
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import org.apache.commons.codec.binary.Base64;
public class ExtractKeys {
private static String privateKeyalgorithm = "";
private static String publicKeyAlgorithm = "";
public static void main(String args[]) throws Exception {
String publicCertificate = "C:\\QA\\keys\\sis.cer";
String privateKeyStore = "C:\\QA\\keys\\sis.jks";
String privateKeyStorePassword = "sis";
String alias = "sis";
String aliasPassword = "sis";
ExtractKeys myep = new ExtractKeys();
myep.printPrivateKey(privateKeyStore, privateKeyStorePassword, alias, aliasPassword);
myep.printPublicKey(publicCertificate);
}
public String printPublicKey(String source) throws Exception {
X509Certificate cert = null;
InputStream fis = null;
ByteArrayInputStream bais = null;
Base64 encoder = new Base64(64);
fis = new FileInputStream(source);
byte value[] = new byte[fis.available()];
fis.read(value);
bais = new ByteArrayInputStream(value);
java.security.cert.CertificateFactory cf = java.security.cert.CertificateFactory.getInstance("X.509");
cert = (X509Certificate) cf.generateCertificate(bais);
String cert_begin = "-----BEGIN CERTIFICATE-----\n";
String end_cert = "-----END CERTIFICATE-----";
publicKeyAlgorithm = cert.getSigAlgName();
byte[] derCert = cert.getEncoded();
String pemCertPre = new String(encoder.encode(derCert));
String pemCert = cert_begin + pemCertPre + end_cert;
System.out.println(pemCert);
System.out.println(publicKeyAlgorithm);
return pemCert;
}
public String printPrivateKey(String fileName, String keystorepass, String aliasName, String aliaspass) throws Exception {
KeyStore ks = KeyStore.getInstance("JKS");
Base64 encoder = new Base64(64);
char[] keyStorePassPhrase = keystorepass.toCharArray();
File certificateFile = new File(fileName);
ks.load(new FileInputStream(certificateFile), keyStorePassPhrase);
char[] aliasPassPhrase = aliaspass.toCharArray();
KeyPair kp = getPrivateKey(ks, aliasName, aliasPassPhrase);
PrivateKey privKey = kp.getPrivate();
privateKeyalgorithm = privKey.getAlgorithm();
String b64 = encoder.encodeAsString(privKey.getEncoded());
System.out.println("-----BEGIN PRIVATE KEY-----");
System.out.println(b64);
System.out.println("-----END PRIVATE KEY-----");
System.out.println(privateKeyalgorithm);
return b64;
}
private KeyPair getPrivateKey(KeyStore keystore, String alias, char[] password) {
try {
// Get private key
Key key = keystore.getKey(alias, password);
if (key instanceof PrivateKey) {
// Get certificate of public key
Certificate cert = keystore.getCertificate(alias);
// Get public key
PublicKey publicKey = cert.getPublicKey();
// Return a key pair
return new KeyPair(publicKey, (PrivateKey) key);
}
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
return null;
}
public String returnPrivateKeyAlgo() {
return privateKeyalgorithm;
}
public String returnPublicKeyAlgo() {
return publicKeyAlgorithm;
}
The outputs are strings of public key, private key, and their alogorithms
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZehr60/61+4ao
8Bigkamzt3RJZcY9ggE4YuQ1ALY8pDXARDQ3OqPohySw4y+Ebrk4y/Rwzm21mhaU
jC33BNSNUetRAgBDU+/nT3/gRZdIeJRfFdiDtl/Ms2LYxo41nYTeGJEqoW3fivI2
cUG/tDKSPjaoGwnz/kVNIsZXJEcZCAfdIg1UH0wBeQ5qLR4rsseE0I7vVAfvDsMl
-----END PRIVATE KEY-----
RSA
-----BEGIN CERTIFICATE-----
MIIGiTCCBXGgAwIBAgIKYMbAHgAAAAUgKzANBgkqhkiG9w0BAQUFADB5MRMwEQYK
CZImiZPyLGQBGRYDY29tMRYwFAYKCZImiZPyLGQBGRYGc2Nod2FiMRYwFAYKCZIm
-----END CERTIFICATE-----
SHA1withRSA
Now I got the public and private keys as string values. along with their algorithms.
Next I wrote another sample program.I got 2 methods in it. encrypt and decrypt which also accepts these keys.
So ideally i would be passing the public key to encrypt method and the private key to decrypt method
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
import **.CryptoService;
import **.CryptoServiceException;
public class CryptoServiceImpl implements CryptoService {
public byte[] encrypt(byte[] data, String key) throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA");
Base64 decoder = new Base64(64);
// decode the base64 encoded string
byte[] decodedKey = decoder.decode(key);
// rebuild key using SecretKeySpec
final SecretKeySpec originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "RSA");
cipher.init(Cipher.ENCRYPT_MODE, originalKey);
final String encryptedString = Base64.encodeBase64String(cipher.doFinal(data));
return encryptedString.getBytes();
} catch (Exception e) {
throw new CryptoServiceException("Cannot encrypt data using key '", e);
}
}
public byte[] decrypt(byte[] data, String key) throws Exception {
try {
Cipher cipher = Cipher.getInstance("RSA");
Base64 decoder = new Base64(64);
// decode the base64 encoded string
byte[] decodedKey = decoder.decode(key);
// rebuild key using SecretKeySpec
final SecretKeySpec originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "RSA");
cipher.init(Cipher.DECRYPT_MODE, originalKey);
final String decryptedString = new String(cipher.doFinal(Base64.decodeBase64(new String(data))));
return decryptedString.getBytes();
} catch (Exception e) {
throw new CryptoServiceException("Cannot decrypt data using key '", e);
}
}
}
Now I got a Junit which when executed throws this exception
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import junit.framework.Assert;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import **.CryptoServiceImpl;
public class TestCryptoService {
String privateKeyStore = "C:\\QA\\keys\\sis.jks";
String privateKeyStorePassword = "sistest";
String alias = "sis";
String aliasPassword = "sistest";
static ExtractKeys myep = new ExtractKeys();
CryptoService service = new CryptoServiceImpl();
String publicCertificate = "C:\\QA\\keys\\sis.cer";;
#BeforeClass
public static void setUpBeforeClass() throws Exception {
}
#AfterClass
public static void tearDownAfterClass() throws Exception {
}
#Before
public void setUp() throws Exception {
}
#After
public void tearDown() throws Exception {
}
#Test
public void testCryptoServiceForAESWithAsymmetricKeys() throws Exception {
String publicKey = myep.printPublicKey(publicCertificate);
byte[] encryptedValue = service.encrypt(new String("abcd").getBytes(), publicKey);
System.out.println(new String(encryptedValue));
String privateKey = myep.printPrivateKey(privateKeyStore, privateKeyStorePassword, alias, aliasPassword);
byte[] decryptedValue = service.decrypt(encryptedValue, privateKey);
System.out.println(new String(decryptedValue));
Assert.assertEquals("abcd", new String(decryptedValue));
}
}
and Here's the exception
*.CryptoServiceException: Cannot encrypt data using key '
at *.impl.CryptoServiceImpl.encrypt(CryptoServiceImpl.java:59)
at *.TestCryptoService.testCryptoServiceForAESWithAsymmetricKeys(TestCryptoService.java:101)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:95)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:56)
at java.lang.reflect.Method.invoke(Method.java:620)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.security.InvalidKeyException: Wrong algorithm type
at com.ibm.crypto.provider.RSAKeyFactory.engineTranslateKey(Unknown Source)
at com.ibm.crypto.provider.RSAKeyFactory.toRSAKey(Unknown Source)
at com.ibm.crypto.provider.RSACipher.engineGetKeySize(Unknown Source)
at javax.crypto.Cipher.b(Unknown Source)
at javax.crypto.Cipher.a(Unknown Source)
at javax.crypto.Cipher.a(Unknown Source)
at javax.crypto.Cipher.a(Unknown Source)
at javax.crypto.Cipher.init(Unknown Source)
at javax.crypto.Cipher.init(Unknown Source)
at *.impl.CryptoServiceImpl.encrypt(CryptoServiceImpl.java:55)
... 28 more
A certificate is not a public key although it contains one; more importantly, either a private key or public key or certificate for a public-key-crypto (aka asymmetric) scheme like RSA is not a symmetric key. Java SecretKeySpec is only for symmetric keys used with symmetric algorithms, like AES.
You need to represent your RSA keys using subtypes of java.security.PrivateKey and java.security.PublicKey. The way you do this is with a KeyFactory for RSA, or a CertificateFactory (as you already did).
For a PKCS#8-encoded private key, which is what you encoded and wrote out, decode it from PEM/base64 to byte[], then:
PKCS8EncodedKeySpec spec = new PKCSEncodedKeySpec (bytearray);
KeyFactory factory = KeyFactory.getInstance("RSA");
PrivateKey privkey = factory.generatePrivate (spec);
// use privkey in a Cipher.getInstance("RSA") to decrypt or sign
For a certificate do basically what you already did:
InputStream is = /* something that returns contents of cert file */;
// you *can* decode from PEM/base64 first, but you don't need to;
// CertificateFactory-X.509 handles both
CertificateFactory cf = CertificateFactory.getInstance ("X.509");
Certificate cert = cf.generateCertificate (is);
// use cert.getPublicKey() for RSA encrypt or verify
Aside: sigAlgName for a certificate does NOT tell about the key in the cert. It is entirely possible for a cert signed SHA1withRSA to contain a DSA or ECC key that cannot be used for RSA, and conversely possible for a cert that contains a perfectly good RSA key to be signed with a different RSA variant (like SHA256withRSA) or an entirely different algorithm (like sha1-DSA or sha2-ECDSA).
Also note: you are explicitly importing java.security.cert.Certificate; that's good. JRE also has an older and deprecated java.security.Certificate which you should not use, but if you just say Certificate some IDEs (like mine) may get confused and give you the wrong one.

AES decrypt for j2me

I read on this link
AES Encryption/Decryption with Bouncycastle Example in J2ME
about how to encrypt a string using AES as supplied by Bouncy Castle. The encryption method code works fine but decryption doesn't work.
Can anyone suggest how I can decrypt the encrypted string?
I've used the following code to test:
import com.codename1.util.Base64;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
/**
*
* #author SAMUEL
*/
public class Tester {
static String strEnc = "Hi This is my String";
final static String strPassword = "password12345678";
private static byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data)throws Exception
{
int minSize = cipher.getOutputSize(data.length);
byte[] outBuf = new byte[minSize];
int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
int length2 = cipher.doFinal(outBuf, length1);
int actualLength = length1 + length2;
byte[] result = new byte[actualLength];
System.arraycopy(outBuf, 0, result, 0, result.length);
return result;
}
private static byte[] decrypt(byte[] cipher, byte[] key, byte[] iv) throws Exception
{
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(false, ivAndKey);
return cipherData(aes, cipher);
}
private static byte[] encrypt(byte[] plain, byte[] key, byte[] iv) throws Exception
{
PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv);
aes.init(true, ivAndKey);
return cipherData(aes, plain);
}
public static void main(String [] args) throws Exception{
byte[] enc= encrypt(strEnc.getBytes(),"password12345678".getBytes(), "password12345678".getBytes());
String encrypted = Base64.encode(enc);
System.out.println("Encrypted is:"+encrypted);
byte[] dec= decrypt(encrypted.getBytes(),"password12345678".getBytes() , "password12345678".getBytes());
System.out.println("Decrypted file is:"+dec);
}
}
Output is:
Encrypted is:sw0SrUIKe0DmS7sRd9+XMgtYg+BUiAfiOsdMw/Lo2RA=
And the exception stacktrace is:
Exception in thread "main" org.bouncycastle.crypto.DataLengthException: last block incomplete in decryption
at org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher.doFinal(PaddedBufferedBlockCipher.java:281)
at com.mycompany.myapp.Tester.cipherData(Tester.java:28)
at com.mycompany.myapp.Tester.decrypt(Tester.java:40)
at com.mycompany.myapp.Tester.main(Tester.java:57)
You are forgetting to base 64 decode the ciphertext before decryption. You should also create a new String from the decrypted bytes.
When encoding/decoding character strings, always specify the encoding explicitly, otherwise you will be using the platform default, which is not the same on each and every platform.
So use for instance new String(dec, "UTF-8") to recreate the plaintext, and specify "UTF-8" for each and every new String() and toBytes() method.

Resources