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).
Related
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.
Is there anyway to encrypt and decrypt a message using Elliptic Curve (EC) Public Key and Private Key using Java?
I was trying to encrypt a message using Elliptic Curve (EC) public key and decrypt it using Elliptic Curve (EC) private key but I keep getting error:
java.security.NoSuchAlgorithmException: Cannot find any provider supporting EC
at javax.crypto.Cipher.getInstance(Cipher.java:539)
at encryptanddecrypt.EncryptAndDecrypt.encrypt(EncryptAndDecrypt.java:87)
at encryptanddecrypt.EncryptAndDecrypt.main(EncryptAndDecrypt.java:48)
BUILD SUCCESSFUL (total time: 0 seconds)
I have try google it but all failed because most of the solution use Encryption and Decryption message using RSA. Can someone help me and advice me what could be the issue over here?
package encryptanddecrypt;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.xml.bind.DatatypeConverter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class EncryptAndDecrypt {
private static final String ALGORITHM = "EC";
public static void main(String[] args) {
// TODO code application logic here
try{
String publicKeyPem="-----BEGIN PUBLIC KEY-----\n" +
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQrkzEdm2ClPnfouyeFkCDsjtRC/x\n" +
"Iaja7F0RbXn3/qKcYQMinaMIs17OhcJQpxasAi1GAPPqc3yF35ngnrj7Kg==\n" +
"-----END PUBLIC KEY-----";
String privateKeyPem="-----BEGIN EC PRIVATE KEY-----\n" +
"MHcCAQEEIE3Z7wMD6/dzQ7eij9xHq6PmyJF53Gk91HjYU2RoZgS7oAoGCCqGSM49\n" +
"AwEHoUQDQgAEQrkzEdm2ClPnfouyeFkCDsjtRC/xIaja7F0RbXn3/qKcYQMinaMI\n" +
"s17OhcJQpxasAi1GAPPqc3yF35ngnrj7Kg==\n" +
"-----END EC PRIVATE KEY-----";
byte[] publicKey = convertPublicPem(publicKeyPem);
byte[] privateKey = convertPrivatePem(privateKeyPem);
byte[] encryptedData = encrypt(publicKey,"Hi Lingkesh Here".getBytes());
byte[] decryptedData = decrypt(privateKey, encryptedData);
System.out.println(new String(decryptedData));
}catch(Exception e){
e.printStackTrace();
}
}
public static byte[] convertPublicPem(String PUBLIC_KEY_STRING){
String publicKeyPEM = PUBLIC_KEY_STRING
.replace("-----BEGIN PUBLIC KEY-----", "")
.replaceAll(System.lineSeparator(), "")
.replace("-----END PUBLIC KEY-----", "");
byte[] bytes = DatatypeConverter.parseBase64Binary(publicKeyPEM);
System.out.println(Arrays.toString(bytes));
return bytes;
}
public static byte[] convertPrivatePem(String PRIVATE_KEY_STRING){
String privateKeyPEM = PRIVATE_KEY_STRING
.replace("-----BEGIN EC PRIVATE KEY-----", "")
.replaceAll(System.lineSeparator(), "")
.replace("-----END EC PRIVATE KEY-----", "");
byte[] bytes = DatatypeConverter.parseBase64Binary(privateKeyPEM);
System.out.println(Arrays.toString(bytes));
return bytes;
}
public static byte[] encrypt(byte[] publicKey, byte[] inputData)
throws Exception {
PublicKey key = KeyFactory.getInstance(ALGORITHM)
.generatePublic(new X509EncodedKeySpec(publicKey));
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptedBytes = cipher.doFinal(inputData);
return encryptedBytes;
}
public static byte[] decrypt(byte[] privateKey, byte[] inputData)
throws Exception {
PrivateKey key = KeyFactory.getInstance(ALGORITHM)
.generatePrivate(new PKCS8EncodedKeySpec(privateKey));
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedBytes = cipher.doFinal(inputData);
return decryptedBytes;
}
public static KeyPair generateKeyPair()
throws NoSuchAlgorithmException, NoSuchProviderException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
SecureRandom random = SecureRandom.getInstance("SHA256PRNG", "SUN");
keyGen.initialize(256, random);
KeyPair generateKeyPair = keyGen.generateKeyPair();
return generateKeyPair;
}
}
I have the following code which works on Windows and Linux/Unix but will not decrypt an encrypted text that has been encrypted on Linux/Unix or vice versa.
I am aware that this will also generate the same encrypted text everytime, but I've removed some of the code to get to the very basic.
I am sure it's not related to Charset encoding, as the encoded String is the same on both OS, even the SecretKey is the same on both.
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
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 Encryptor {
private static final String password = "passwordKey";
private static final Charset UTF_8 = Charset.forName("UTF-8");
private static final byte [] salt = "test salt as string".getBytes(UTF_8);
public static String encrypt(String text) throws UnsupportedEncodingException {
IvParameterSpec ivSpec = new IvParameterSpec(password.getBytes(UTF_8));
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 10000, ivSpec);
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 10000);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBEWithSHA256AndAES_256");
SecretKey key = secretKeyFactory.generateSecret(keySpec);
Cipher cipher = Cipher.getInstance("PBEWithSHA256AndAES_256");
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
byte [] encrypted = cipher.doFinal(text.getBytes(UTF_8));
return Base64.encodeBase64String(encrypted);
}
public static String decrypt(String encryptedText) throws UnsupportedEncodingException {
IvParameterSpec ivSpec = new IvParameterSpec(password.getBytes(UTF_8));
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 10000, ivSpec);
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, 10000);
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBEWithSHA256AndAES_256");
SecretKey key = secretKeyFactory.generateSecret(keySpec);
Cipher cipher = Cipher.getInstance("PBEWithSHA256AndAES_256");
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
byte [] decoded = Base64.decodeBase64String(encryptedText);
byte [] decrypted = cipher.doFinal(decoded);
return new String(decrypted, UTF_8);
}
public static void main(String args []) throws UnsupportedEncodingException {
String encryptedFromWindows = "eFRvTevgk/oslll+234r5tdsss==";
System.out.println(decrypt(encryptedFromWindows));
}
}
Exception:
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)
The problem was a bug in the JDK version of _64. Upgraded to _121 and it worked. IT wasn't a known oracle bug but they had mentioned in another similar bug of issues with encryption in that release.
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'm writing an application for Android that uses symmetric key encryption to protect sensitive data. As far as I can tell, Android only directly supports "PBEWithMD5AndDES". How secure is this algorithm? Also, I've included my code below (non-andriod). Is my code correctly encrypting the data?
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
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;
public class CipherTest
{
private static class EncryptInfo
{
private final byte[] encryptedData;
private final byte[] initVector;
private final byte[] salt;
public EncryptInfo(byte[] encryptedData, byte[] initVector, byte[] salt)
{
this.encryptedData = encryptedData.clone();
this.initVector = initVector.clone();
this.salt = salt.clone();
}
public byte[] getEncryptedData()
{
return encryptedData;
}
public byte[] getInitVector()
{
return initVector;
}
public byte[] getSalt()
{
return salt;
}
}
private static final String keyGenAlgorithm = "PBEWithMD5AndDES";
private static final String keyAlgorithm = "DES";
private static final String cipherTransform = "PBEWithMD5AndDES/CBC/PKCS5Padding";
private static EncryptInfo encrypt(char[] password, byte[] data)
throws NoSuchAlgorithmException, InvalidKeySpecException,
NoSuchPaddingException, InvalidKeyException,
InvalidParameterSpecException, IllegalBlockSizeException,
BadPaddingException, UnsupportedEncodingException
{
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
PBEKeySpec keySpec = new PBEKeySpec(password, salt, 1024);
SecretKeyFactory secretKeyFactory = SecretKeyFactory
.getInstance(keyGenAlgorithm);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
keySpec.clearPassword();
byte[] key = secretKey.getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(key, keyAlgorithm);
Cipher cipher = Cipher.getInstance(cipherTransform);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] initVector = cipher.getParameters().getParameterSpec(
IvParameterSpec.class).getIV();
return new EncryptInfo(cipher.doFinal(data), initVector, salt);
}
public static byte[] decrypt(byte[] data, char[] password, byte[] salt,
byte[] initVector) throws NoSuchAlgorithmException,
InvalidKeySpecException, NoSuchPaddingException,
InvalidKeyException, InvalidAlgorithmParameterException,
IllegalBlockSizeException, BadPaddingException
{
PBEKeySpec keySpec = new PBEKeySpec(password, salt, 1024);
SecretKeyFactory secretKeyFactory = SecretKeyFactory
.getInstance(keyGenAlgorithm);
SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
keySpec.clearPassword();
byte[] key = secretKey.getEncoded();
SecretKeySpec secretKeySpec = new SecretKeySpec(key, keyAlgorithm);
Cipher cipher = Cipher.getInstance(cipherTransform);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(
initVector));
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception
{
char[] password = "password".toCharArray();
EncryptInfo info = encrypt(password, "Message".getBytes());
byte[] decyptedText = decrypt(info.getEncryptedData(), password, info
.getSalt(), info.getInitVector());
System.out.println(new String(decyptedText));
}
}
Both MD5 and DES are weak. If your data being encrypted is really valuable, you should look for some external crypto library for Android that offers AES and SHA256/SHA512 algorithms.
If you want to encrypt data using a symmetric key encryption, I recommend:
1) Use AES, because it is certified by the NSA for data classified as secret.
2) Use a well reviewed implementation so you don't have to research the proper way to configure the code. For example, AESCrypt.
You can find AESCrypt here: http://www.aescrypt.com/java_aes_crypt.html
I've seen AESCrypt used in several financial institutions. AESCrypt for java is a single class that calls JCE methods. Android, JCE is implemented by bouncycastle. I have seen bouncycastle used in several major financial institutions.