Small question regarding a Java code for RSA please.
I am having a very simple piece of Java code.
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
public class RSA {
public static void main(String[] args) throws Exception {
String privateKeyString = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAER+iccdhb474gKs6QE9c3JNS3BMlPTyFD2EOP3/NSrBlZtvVpKyQdHxYZ0W6a/IixWc0WjDqqcVAtrwCILmHU7Q==";
String publicKeyString = "MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCCyp0Sx3AgDhXYN3ecGaFYt51dnlrbgJJoRnYMh52QmDg=";
String secretMessage = "My TOP SECRET Message";
byte[] buffer1 = Base64.getDecoder().decode(privateKeyString);
PKCS8EncodedKeySpec keySpec1 = new PKCS8EncodedKeySpec(buffer1);
KeyFactory keyFactory1 = KeyFactory.getInstance("RSA");
PrivateKey privateKey = (RSAPrivateKey) keyFactory1.generatePrivate(keySpec1);
byte[] buffer2 = Base64.getDecoder().decode(publicKeyString);
KeyFactory keyFactory2 = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec2 = new PKCS8EncodedKeySpec(buffer2);
PublicKey publicKey = (RSAPublicKey) keyFactory2.generatePublic(keySpec2);
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] secretMessageBytes = secretMessage.getBytes(StandardCharsets.UTF_8);
byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
String encodedMessage = Base64.getEncoder().encodeToString(encryptedMessageBytes);
Cipher decryptCipher = Cipher.getInstance("RSA");
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes);
String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8);
System.out.println("Step 1" + secretMessage);
System.out.println("Step 2" + encodedMessage);
System.out.println("Step 3" + decryptedMessage);
}
}
I would have expected this to work, and be able to see some kind of gibberish for "Step 2"
But instead, I am seeing this:
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : DER input, Integer tag error
at java.base/sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:251)
at java.base/java.security.KeyFactory.generatePrivate(KeyFactory.java:390)
May I ask what is wrong with this piece of code please?
Thank you
In the code there are the following issues:
A private and public EC key are imported (additionally, both keys are swapped). Since RSA encryption is to be performed, RSA keys are to be used instead.
The public key is imported with PKCS8EncodedKeySpec. However, PKCS8EncodedKeySpec is intended for importing a private PKCS#8 key. Since a public X.509/SPKI key is to be imported, X509EncodedKeySpec has to be used instead.
When instantiating the Cipher object, only the algorithm is specified (RSA), but not the padding. Therefore, a provider-dependent default value is used for the padding. To avoid unintentional use of an incorrect padding and cross-platform problems, the padding should also be explicitly specified instead (e.g. RSA/ECB/PKCS1Padding).
The fixed code is as follows:
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class MyClass {
public static void main(String args[]) throws Exception {
// Fix 1: Use RSA keys: The private RSA key is a 2048 bit DER encoded PKCS#8 key (Base64 encoded). The public RSA key is the associated 2048 bit DER encoded X.509/SPKI key (Base64 encoded). Check the RSA keys with an ASN.1 parser, e.g. https://lapo.it/asn1js/.
String privateKeyString = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6cXloNrocJ8swwj8xktPmEOTfTgJT7KkUwWIGOjBB1QxApgdn5+SUHsakvEJq3Fgn+FnuuN8cfcqWrbT9jURtgNGinJNJ+StPM/PCxfhSv+XbkK11CV2EcJMyDB/8S/9u4E2ht/N1kT4OF2/mVDAq2MjjeUMq8CLmQR63ZMXB8lwmsGJEl4Rwt9WBZNcZFCfuCeBYrKRS7mtLzd4BTXEf0UuiNB/KJrz38TKSI47v/dRbB34wBNn0cuNLHb8t/eDaOvzV6J8SZgOWuL85mng6Fm77QGjUteWgJN76+YhDZgJfsRq1Q67JAy3ZXDHi5X538DcM/o+0wYEqkXxK3iIbAgMBAAECggEASlJj0ExIomKmmBhG8q8SM1s2sWG6gdQMjs6MEeluRT/1c2v79cq2Dum5y/+UBl8x8TUKPKSLpCLs+GXkiVKgHXrFlqoN+OYQArG2EUWzuODwczdYPhhupBXwR3oX4g41k/BsYfQfZBVzBFEJdWrIDLyAUFWNlfdGIj2BTiAoySfyqmamvmW8bsvc8coiGlZ28UC85/Xqx9wOzjeGoRkCH7PcTMlc9F7SxSthwX/k1VBXmNOHa+HzGOgO/W3k1LDqJbq2wKjZTW3iVEg2VodjxgBLMm0MueSGoI6IuaZSPMyFEM3gGvC2+cDBI2SL/amhiTUa/VDlTVw/IKbSuar9uQKBgQDd76M0Po5Lqh8ZhQ3obhFqkfO5EBXy7HUL15cw51kVtwF6Gf/J2HNHjwsg9Nb0eJETTS6bbuVd9bn884JoRS986nVTFNZ4dnjEgKjjQ8GjfzdkpbUxsRLWiIxuOQSpIUZGdMi2ctTTtspvMsDsjRRYdYIQCe/SDsdHGT3vcUCybwKBgQDXDz6iVnY84Fh5iDDVrQOR4lYoxCL/ikCDJjC6y1mjR0eVFdBPQ4j1dDSPU9lahBLby0VyagQCDp/kxQOl0z2zBLRI4I8jUtz9/9KW6ze7U7dQJ7OTfumd5I97OyQOG9XZwKUkRgfyb/PAMBSUSLgosi38f+OC3IN3qlvHFzvxFQKBgQCITpUDEmSczih5qQGIvolN1cRF5j5Ey7t7gXbnXz+Umah7kJpMIvdyfMVOAXJABgi8PQwiBLM0ySXo2LpARjXLV8ilNUggBktYDNktc8DrJMgltayaj3HNd2IglD5rjfc2cKWRgOd7/GlKcHaTEnbreYhfR2sWrWLxJOyoMfuVWwKBgFalCbMV6qU0LfEo8aPlBN8ttVDPVNpntP4h0NgxPXgPK8Pg+gA1UWSy4MouGg/hzkdHaj9ifyLlCX598a5JoT4S0x/ZeVHd/LNI8mtjcRzD6cMde7gdFbpLb5NSjIAyrsIAX4hxvpnqiOYRePkVIz0iLGziiaMbfMwlkrxvm/LRAoGBALPRbtSbE2pPgvOHKHTGPr7gKbmsWVbOcQA8rG801T38W/UPe1XtynMEjzzQ29OaVeQwvUN9+DxFXJ6Yvwj6ih4Wdq109i7Oo1fDnMczOQN9DKch2eNAHrNSOMyLDCBm++wbyHAsS2T0VO8+gzLABviZm5AFCQWfke4LZo5mOS10";
String publicKeyString = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAunF5aDa6HCfLMMI/MZLT5hDk304CU+ypFMFiBjowQdUMQKYHZ+fklB7GpLxCatxYJ/hZ7rjfHH3Klq20/Y1EbYDRopyTSfkrTzPzwsX4Ur/l25CtdQldhHCTMgwf/Ev/buBNobfzdZE+Dhdv5lQwKtjI43lDKvAi5kEet2TFwfJcJrBiRJeEcLfVgWTXGRQn7gngWKykUu5rS83eAU1xH9FLojQfyia89/EykiOO7/3UWwd+MATZ9HLjSx2/Lf3g2jr81eifEmYDlri/OZp4OhZu+0Bo1LXloCTe+vmIQ2YCX7EatUOuyQMt2Vwx4uV+d/A3DP6PtMGBKpF8St4iGwIDAQAB";
String secretMessage = "My TOP SECRET Message";
byte[] buffer1 = Base64.getDecoder().decode(privateKeyString);
PKCS8EncodedKeySpec keySpec1 = new PKCS8EncodedKeySpec(buffer1);
KeyFactory keyFactory1 = KeyFactory.getInstance("RSA");
PrivateKey privateKey = (RSAPrivateKey) keyFactory1.generatePrivate(keySpec1);
byte[] buffer2 = Base64.getDecoder().decode(publicKeyString);
KeyFactory keyFactory2 = KeyFactory.getInstance("RSA");
X509EncodedKeySpec keySpec2 = new X509EncodedKeySpec(buffer2); // Fix 2: Apply X509EncodedKeySpec for the import of the public key
PublicKey publicKey = (RSAPublicKey) keyFactory2.generatePublic(keySpec2);
Cipher encryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); // Fix 3: Specify algorithm and padding when instantiating the Cipher object
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] secretMessageBytes = secretMessage.getBytes(StandardCharsets.UTF_8);
byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
String encodedMessage = Base64.getEncoder().encodeToString(encryptedMessageBytes);
Cipher decryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); // Fix 3: Specify algorithm and padding when instantiating the Cipher object
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes);
String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8);
System.out.println("Step 1" + secretMessage);
System.out.println("Step 2" + encodedMessage);
System.out.println("Step 3" + decryptedMessage);
}
}
With these changes, encryption and decryption work.
Related
I'm getting this error when I try to decrypt a message which has already been encrypted.
I've done this by first encrypting a message in this case "Testing message"
Then running the method again but decrypting instead of encrypting.
I've looked at the other questions regarding this but could not fix the problem
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
public class PBEusing {
public static void main(String[] args) throws Exception {
PBEusing pbe = new PBEusing();
String encrypt = pbe.pbe("encrypt", "passwordTest", "Testing message");
System.out.println(encrypt);
String decrypt = pbe.pbe("decrypt", "passwordTest", encrypt);
System.out.println(decrypt);
}
public static String pbe(String cipherMethod, String clientPassword, String clientMessage) throws Exception {
String method = cipherMethod.toUpperCase();
String output = "";
SecureRandom rnd = new SecureRandom();
byte[] iv = new byte[16];
rnd.nextBytes(iv);
byte[] plaintext = clientMessage.getBytes(StandardCharsets.UTF_8); // input message from user
byte[] salt = "01234567".getBytes(StandardCharsets.UTF_8);
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 10000, ivParamSpec);
PBEKeySpec keySpec = new PBEKeySpec(clientPassword.toCharArray());
try {
SecretKeyFactory kf = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128");
SecretKey secretKey = kf.generateSecret(keySpec);
// On J2SE the SecretKeyfactory does not actually generate a key, it just wraps the password.
// The real encryption key is generated later on-the-fly when initializing the cipher
System.out.println(new String(secretKey.getEncoded()));
// Encrypt
if (method.equals("ENCRYPT")) {
Cipher enc = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
enc.init(Cipher.ENCRYPT_MODE, secretKey, pbeParamSpec);
byte[] encrypted = enc.doFinal(plaintext);
output = new BASE64Encoder().encode(encrypted);
System.out.println("Encrypted text: " + output);
} else {
// Decrypt
Cipher dec = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
dec.init(Cipher.DECRYPT_MODE, secretKey, pbeParamSpec);
byte[] decrypted = dec.doFinal(plaintext);
String test = new BASE64Encoder().encode(decrypted);
//String message = new String(test, StandardCharsets.UTF_8);
output = test;
}
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
}
return output;
}
}
You're Base64 encoding the output of the encryption but aren't Base64 decoding it again before you try to decrypt it.
To decrypt an AES encrypted field through REST API, i've have one issue in understanding the IV(Initialization Vector) and how to use that to decrypt the first block of 16 characters.
Encrypted field from the REST response JSON (description form field):
"description":"84d1d37bdb7a3200750573ffbf96191f:0aZdRxsIqSpFtuszNr73na/J9JuMLNB>0J6T2f2FrV0sUlMmbW4prbZMmXGnLU4W6CDlb5F1lb8js\r\nRHw6tfyZd5ZL//ZUlozE916wvP+zd+>uUfjpk2Bl9o2uAu+1bsNoAVdtP5m5fbnkjxf9yLRzREVVO\r\nIwYQOxNI/CeX2dzF/Uc="
I was able to identify this
"0aZdRxsIqSpFtuszNr73na/J9JuMLNB>0J6T2f2FrV0sUlMmbW4prbZMmXGnLU4W6CDlb5F1lb8js>r\nRHw6tfyZd5ZL//ZUlozE916wvP+zd+>uUfjpk2Bl9o2uAu+1bsNoAVdtP5m5fbnkjxf9yLRzREVV>O\r\nIwYQOxNI/CeX2dzF/Uc="
as the actual description and,
"84d1d37bdb7a3200750573ffbf96191f"
to be somehow related to my question in this post.
Encryption method: AES 128 Bit.
Password: 1234567890123456
Original Text: “new description for new incident.
www.google.com
lets see if the initial part is same or it changes for this new incident”
Decrypted output: “bGOn>22H~KH:38/_for new incident.
www.google.com
lets see if the initial part is same or it changes for this new incident”
Decryption Used: AES/CBC/PKCS5Padding
How to decrypt the first block which is jumbled in the Decrypted output. In other words, how to interpret 84d1d37bdb7a3200750573ffbf96191f in terms of IV to decrypt the first 16 characters ?
Any help would be appreciated.
Below is the Java code:
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import javax.crypto.Cipher;
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.binary.Base64;
public class AESDecryption {
private static String key = "1234567890123456";
private static String encryptedStr = "0aZdRxsIqSpFtuszNr73na/J9JuMLNB0J6T2f2FrV0sUlMmbW4prbZMmXGnLU4W6CDlb5F1lb8js\r\nRHw6tfyZd5ZL//ZUlozE916wvP+zd+uUfjpk2Bl9o2uAu+1bsNoAVdtP5m5fbnkjxf9yLRzREVVO\r\nIwYQOxNI/CeX2dzF/Uc=";
private static String padding = "AES/CBC/PKCS5Padding";
private static int iterationCount = 65536;
private static int keyLength = 128;
private static String secretKeyAlg = "PBEWithHmacSHA1AndAES_128";
public static void main(String[] args) throws Exception {
String finalStrDec = null;
SecretKeyFactory factory = SecretKeyFactory.getInstance(secretKeyAlg);
PBEKeySpec spec = new PBEKeySpec(key.toCharArray(), generateSalt(), iterationCount, keyLength);
SecretKey secretKey = factory.generateSecret(spec);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(new byte[16]);
Cipher cipherDec = Cipher.getInstance(padding);
cipherDec.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
byte[] original = cipherDec.doFinal(Base64.decodeBase64(encryptedStr));
finalStrDec = new String(original);
System.out.println(finalStrDec);
}
public static byte[] generateSalt() throws UnsupportedEncodingException {
SecureRandom random = new SecureRandom();
byte bytes[] = new byte[20];
random.nextBytes(bytes);
String salt = new String(bytes);
return salt.getBytes("UTF-8");
}
}
Encryption and Decryption in Java is still very difficult for me to understand. I have been using the following class and methods. I wonder how to improve the safety and how long does the keystring (schlüssel) need to be?
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class AES
{
public static SecretKeySpec makeKey(String schlüssel) throws NoSuchAlgorithmException, UnsupportedEncodingException
{
byte[] key = (schlüssel).getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
return new SecretKeySpec(key, "AES");
}
public static String encryptString(String text, SecretKeySpec schlüssel) throws Exception
{
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, schlüssel);
byte[] encrypted = cipher.doFinal(text.getBytes());
BASE64Encoder myEncoder = new BASE64Encoder();
return myEncoder.encode(encrypted);
}
public static String decryptString(String text, SecretKeySpec schlüssel) throws Exception
{
BASE64Decoder myDecoder2 = new BASE64Decoder();
byte[] crypted2 = myDecoder2.decodeBuffer(text);
Cipher cipher2 = Cipher.getInstance("AES");
cipher2.init(Cipher.DECRYPT_MODE, schlüssel);
byte[] cipherData2 = cipher2.doFinal(crypted2);
return new String(cipherData2);
}
}
I have been reading about the topic. But I did not understand how to transfer the ideas into my code. Any help is appreciated, please be kind with an encryption beginner. Thank you.
There are a lot of things wrong in this class.
the class uses a cryptographic hash instead of a password hash - such as PBKDF2 - to derive a key from the password;
you are using ECB mode encryption (the default), you need to use at least CBC, together with an initialization vector (IV);
your class doesn't add any integrity protection, in other words the ciphertext is malleable;
It depends on the use case if you require the integrity protection. So I'll point you to this question for more information about password based encryption (PBE). Note that the answers may still deliver malleable ciphertext.
Furthermore the class contains the following Java mistakes:
it doesn't distinguish between runtime related exceptions (missing algorithms) and input related exceptions;
it uses the default platform encoding for your plaintext;
it is using a Sun internal class to perform the Base 64 encoding/decoding.
Note that people will probably point out to you that you are using 128 bit AES encryption. That's however quite strong and - certainly at this point in time - the least of your worries. Upgrading to 192 or 256 bit AES won't increase security significantly.
Refering to Maarten Bodeswes code I try to bring his code into the form I am using.
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class AESplus
{
public static SecretKeySpec makeKey(String password) throws NoSuchAlgorithmException, UnsupportedEncodingException
{
password = String.format("%040x", new BigInteger(1,password.getBytes(Charset.forName("UTF-8"))));
password = password.substring(password.length()-32, password.length());
final byte[] symKeyData = DatatypeConverter.parseHexBinary(password);
return new SecretKeySpec(symKeyData, "AES");
}
public static String encryptString(String text, SecretKeySpec key) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
{
final byte[] encodedMessage = text.getBytes(Charset.forName("UTF-8"));
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final int blockSize = cipher.getBlockSize();
// generate random IV using block size
final byte[] ivData = new byte[blockSize];
final SecureRandom rnd = SecureRandom.getInstance("SHA1PRNG");
rnd.nextBytes(ivData);
final IvParameterSpec iv = new IvParameterSpec(ivData);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
final byte[] encryptedMessage = cipher.doFinal(encodedMessage);
// concatenate IV and encrypted message
final byte[] ivAndEncryptedMessage = new byte[ivData.length + encryptedMessage.length];
System.arraycopy(ivData, 0, ivAndEncryptedMessage, 0, blockSize);
System.arraycopy(encryptedMessage, 0, ivAndEncryptedMessage, blockSize, encryptedMessage.length);
return DatatypeConverter.printBase64Binary(ivAndEncryptedMessage);
}
public static String decrytString(String crypttext, SecretKeySpec key) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException
{
final byte[] ivAndEncryptedMessage = DatatypeConverter.parseBase64Binary(crypttext);
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final int blockSize = cipher.getBlockSize();
// retrieve random IV from start of the received message
final byte[] ivData = new byte[blockSize];
System.arraycopy(ivAndEncryptedMessage, 0, ivData, 0, blockSize);
final IvParameterSpec iv = new IvParameterSpec(ivData);
// retrieve the encrypted message itself
final byte[] encryptedMessage = new byte[ivAndEncryptedMessage.length - blockSize];
System.arraycopy(ivAndEncryptedMessage, blockSize, encryptedMessage, 0, encryptedMessage.length);
cipher.init(Cipher.DECRYPT_MODE, key, iv);
final byte[] encodedMessage = cipher.doFinal(encryptedMessage);
// concatenate IV and encrypted message
final String message = new String(encodedMessage,Charset.forName("UTF-8"));
return message;
}
}
I have an RSA private key that I am trying to decrypt another files contents that has an AES key in it. So far all I can seem to get to return from the processes is jargon. Not really sure what I am doing wrong in the below code. I have looked on google and have seen this done at least a 100 different ways.
import java.io.*;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.GeneralSecurityException;
import java.security.spec.PKCS8EncodedKeySpec;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
public class RsaEncryption {
private Cipher _pkCipher;
public RsaEncryption() throws GeneralSecurityException {
// create RSA public key cipher
_pkCipher = Cipher.getInstance("RSA");
}
public String loadKey(File in, String privateKey) throws GeneralSecurityException, IOException, Exception {
privateKey = privateKey.replaceAll("-+.*?-+", "");
byte[] encodedKey = Base64.decodeBase64(privateKey);
// create private key
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
RSAPrivateKey pk = (RSAPrivateKey) kf.generatePrivate(privateKeySpec);
// read AES key
_pkCipher.init(Cipher.DECRYPT_MODE, pk);
byte[] encryptedBytes = FileUtils.readFileToByteArray(in);
ByteArrayInputStream fileIn = new ByteArrayInputStream(encryptedBytes);
CipherInputStream cis = new CipherInputStream(fileIn, _pkCipher);
DataInputStream dis = new DataInputStream(cis);
byte[] decryptedData = new byte[32];
dis.read(decryptedData);
String key = new String(decryptedData);
return key;
}
}
UPDATE
New way with bouncy castles pem converter still not working
import java.io.StringReader;
import java.io.File;
import java.io.IOException;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.GeneralSecurityException;
import java.security.interfaces.RSAPublicKey;
import javax.crypto.Cipher;
import org.apache.commons.io.FileUtils;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
public class RsaEncryption {
private Cipher _pkCipher;
private RSAPrivateKey _PrivateKey;
private RSAPublicKey _PublicKey;
public RsaEncryption(String privateKey) throws GeneralSecurityException, IOException {
loadKey(privateKey);
// create RSA public key cipher
_pkCipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");
}
private void loadKey(String privateKey) throws IOException {
PEMParser pemParser = new PEMParser(new StringReader(privateKey));
PEMKeyPair pemKeyPair = (PEMKeyPair) pemParser.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
KeyPair keyPair = converter.getKeyPair(pemKeyPair);
_PrivateKey = (RSAPrivateKey) keyPair.getPrivate();
_PublicKey = (RSAPublicKey) keyPair.getPublic();
pemParser.close();
}
public String decrypt(File in) throws GeneralSecurityException , IOException{
_pkCipher.init(Cipher.DECRYPT_MODE, _PrivateKey);
byte[] encryptedBytes = FileUtils.readFileToByteArray(in);
String key = new String(_pkCipher.doFinal(encryptedBytes));
System.out.println(key);
return key;
}
public RSAPrivateKey getPrivateKey() { return _PrivateKey; }
public RSAPublicKey getPublicKey() { return _PublicKey; }
}
RSA can only encrypt a small amount of data which must be processed as a chunk. You don't need a stream for that. Simply call
byte[] aesKey = _pkCipher.doFinal(FileUtils.readFileToByteArray(in));
to get the AES key.
JCE jars where in the wrong directory worked just fine once they got put in the correct directory.
The following code is a try to encrypt data using AES with asymmetric key:
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import javax.crypto.Cipher;
public class AsyncronousKeyTest {
private final Cipher cipher;
private final KeyFactory keyFactory;
private final RSAPrivateKey privKey;
private AsyncronousKeyTest() throws Exception {
cipher = Cipher.getInstance("AES/CBC/NoPaddin", "BC");
keyFactory = KeyFactory.getInstance("AES", "BC");
// create the keys
RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(new BigInteger(
"d46f473a2d746537de2056ae3092c451", 16), new BigInteger("57791d5430d593164082036ad8b29fb1",
16));
privKey = (RSAPrivateKey) keyFactory.generatePrivate(privKeySpec);
}
public void generateAuthorizationAct(OutputStream outputStream) throws Exception {
KeyFactory keyFactory = KeyFactory.getInstance("AES", "BC");
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger("d46f473a2d746537de2056ae3092c451",
16), new BigInteger("11", 16));
RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(pubKeySpec);
byte[] data = new byte[] {0x01};
byte[] encrypted = encryptAO(pubKey, data);
outputStream.write(encrypted);
}
/** Encrypt the AuthorizationObject. */
public byte[] encryptAO(Key pubKey, byte[] data) throws Exception {
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] cipherText = cipher.doFinal(data);
return cipherText;
}
public byte[] decrypt(byte[] cipherText) throws Exception {
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] decyptedData = cipher.doFinal(cipherText);
return decyptedData;
}
public static void main(String[] args) throws Exception {
System.out.println("start");
AsyncronousKeyTest auth = new AsyncronousKeyTest();
auth.generateAuthorizationAct(System.out);
System.out.println("done");
}
}
but at line
cipher = Cipher.getInstance("AES/CBC/NoPaddin", "BC");
it throws
NoSuchPaddingException: Padding NoPaddin unknown.
What is this? And how to solve?
"Padding" is not "Paddin". The "g" matters.
Also, "encrypting data with AES with asymmetric key" makes no sense. A RSA key, as its name says, is for RSA, not for AES. AES uses a symmetric key, namely an array of (arbitrary) bytes of length 16, 24 or 32 bytes. A RSA key is a mathematical objects consisting of two integers, one of which being quite big (typically 1024 bits).