I want to encrypt a large message and perform a measurement with different key sizes.
public static void generate(int keylen)
{
KeyPairGenerator keygen = null;
try
{
keygen = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
keygen.initialize(keylen);
key = keygen.generateKeyPair();
}
When I select 512 as the key size, my program throws the following error message: javax.crypto.IllegalBlockSizeException: Data must not be longer than 53 bytes
public static byte[] encrypt(String message, PublicKey pk)
{
Cipher cipher = null;
try
{
cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pk);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
byte[] cipherText = null;
try {
ciphertext = cipher.doFinal(message.getBytes(StandardCharsets.UTF_8));
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return cipherText;
}
I know that the RSA algorithm can only encrypt data that has a maximum byte length of the RSA key length in bits divided with eight minus eleven padding bytes, i.e. number of maximum bytes = key length in bits / 8 - 11.
I also know that 512 is not secure. I want to measure different key sizes with their respective time e.g. 512, 1024, 2048, 4096.....
Is there a way to encrypt the message without using a hybrid method because I also need to measure the time.
I read that there is a method to divide the message into blocks. I am not sure how this works exactly.
Related
I am using the below code to create a hmac key and returning it as a string.
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("HmacSHA256");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
SecretKey key = keyGen.generateKey();
byte[] encoded = key.getEncoded();
String s=Base64.encodeToString(encoded, Base64.DEFAULT);
Log.i("Hmac key before encrypt",s);
try {
KeyStore keystore = KeyStore.getInstance("AndroidKeyStore");
keystore.load(null, null);
KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keystore.getEntry("temp", null);
RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherBytes = cipher.doFinal(encoded);
return Base64.encodeToString(cipherBytes,Base64.DEFAULT);
} catch (UnrecoverableEntryException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
How can I store this in the android keystore?. I have tried using the below code:
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.ProtectionParameter param = new KeyStore.PasswordProtection("test".toCharArray());
keyStore.setEntry("key1",hmacKey,param);
I get an errors no matter what format hmacKey is in: String/Bytes or javax.crypto.SecretKey. Below are the errors:
In case of passing Key hmacKey:
Wrong 2nd argument type. Found: 'java.security.Key', required: 'java.security.KeyStore.Entry'
Same in cases where I pass a string or byte array.
If I typecast the parameter to java.security.KeyStore.Entry, it still doesn't work.
Is this the correct way of doing so? Can anyone give pointers as to how the HMAC key can be stored in the keystore using an alias. How can convert the hmack key to java.security.KeyStore.Entry format?
The Android key store was created to allow you to use asymmetric keys and symmetric keys outside your application code. As specified in the training material:
Key material never enters the application process. When an application performs cryptographic operations using an Android Keystore key, behind the scenes plaintext, ciphertext, and messages to be signed or verified are fed to a system process which carries out the cryptographic operations. If the app's process is compromised, the attacker may be able to use the app's keys but will not be able to extract their key material (for example, to be used outside of the Android device).
So the idea of generating the key inside the application code - and thus outside the key store - is not a good idea. How to generate a secret key inside the key store is defined for HMAC keys in the API for the KeyGenParameterSpec class:
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_HMAC_SHA256, "AndroidKeyStore");
keyGenerator.initialize(
new KeyGenParameterSpec.Builder("key2", KeyProperties.PURPOSE_SIGN).build());
SecretKey key = keyGenerator.generateKey();
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);
...
// The key can also be obtained from the Android Keystore any time as follows:
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
key = (SecretKey) keyStore.getKey("key2", null);
Other key types can be found in the KeyProperties class
The C# is on the client end while Java code is used in a service. Windows phone encrypts the data while Java decrypts the data using the same symmetric key.
Below is my C# method for encryption
public static string EncryptAesTest(string data, string password)
{
SymmetricKeyAlgorithmProvider SAP = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesEcbPkcs7);
CryptographicKey AES;
HashAlgorithmProvider HAP = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Sha512);
Windows.Security.Cryptography.Core.CryptographicHash Hash_AES = HAP.CreateHash();
string encrypted;
try
{
byte[] hash = new byte[16];
Hash_AES.Append(CryptographicBuffer.CreateFromByteArray(System.Convert.FromBase64String(password)));
byte[] temp;
CryptographicBuffer.CopyToByteArray(Hash_AES.GetValueAndReset(), out temp);
Array.Copy(temp, 0, hash, 0, 16);
Array.Copy(temp, 0, hash, 15, 16);
AES = SAP.CreateSymmetricKey(CryptographicBuffer.CreateFromByteArray(hash));
IBuffer Buffer = CryptographicBuffer.CreateFromByteArray(Encoding.UTF8.GetBytes(data));
encrypted = CryptographicBuffer.EncodeToBase64String(CryptographicEngine.Encrypt(AES, Buffer, null));
return encrypted;
}
catch
{
return "encryption error";
}
}
Below is my Java class for decryption
private SecretKeySpec secretKey;
public void setKey() {
skey = "mykey";
MessageDigest sha = null;
try {
key = skey.getBytes("UTF-8");
logger.debug("Key length ====> " + key.length);
sha = MessageDigest.getInstance("SHA-512");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
secretKey = new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public String decrypt(String strToDecrypt) {
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, this.secretKey);
setDecryptedString(new String(cipher.doFinal(Base64
.decodeBase64(strToDecrypt))));
} catch (Exception e) {
System.out.println("Error while decrypting: " + e.toString());
}
return null;
}
The key generation is not complete. For some reason, your C# code uses sets the last byte of the key to the same value as the first byte with the following code:
Array.Copy(temp, 0, hash, 0, 16);
Array.Copy(temp, 0, hash, 15, 16);
(To my understanding, this could should throw some exception, because you can't copy 16 bytes into the 16 byte array hash if you begin at index 15.)
You could do the same (bad) thing in Java
public void setKey() {
skey = "mykey";
MessageDigest sha = null;
try {
key = skey.getBytes("UTF-8");
logger.debug("Key length ====> " + key.length);
sha = MessageDigest.getInstance("SHA-512");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
key[15] = key[0]; // added
secretKey = new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
Things to consider:
ECB mode does not provide semantic security and it should never be used. Use at the very least CBC mode with a random IV for each encryption under the same key.
Passwords should be hashed multiple times. A single hash makes it easy for an attacker to brute-force the password, because this is operation is fast. You should use Password-based Encryption with a strong key derivation function like PBKDF2 (more than 100,000 iterations), scrypt or bcrypt. Don't forget to use a random salt.
Authenticate your ciphertexts. You would want to detect (malicious) manipulations of your ciphertexts in transit. This can be done either with an authenticated mode like GCM or EAX, or with an encrypt-then-MAC scheme by running a MAC algorithm over your ciphertexts. A strong MAC is HMAC-SHA256.
Like many others over time I'm having issues with encryption in a system I'm maintaining.
Process A generates some encrypted text which later Process B must decode. They share the same code for this purpose which is as below:
public class DesEncryption {
private Cipher mEcipher;
private Cipher mDcipher;
private byte[] salt = {
(byte) 0x08, (byte) 0x90, (byte) 0xA6, (byte) 0x4B,
(byte) 0xBB, (byte) 0x51, (byte) 0x3C, (byte) 0xDE
};
// Iteration count
int iterationCount = 19;
DesEncryption(String passPhrase) throws EncryptionException {
try {
// Create the key
KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
SecretKey key = SecretKeyFactory.getInstance(
"PBEWithMD5AndDES").generateSecret(keySpec);
mEcipher = Cipher.getInstance(key.getAlgorithm());
mDcipher = Cipher.getInstance(key.getAlgorithm());
// Prepare the parameter to the ciphers
AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
// Create the ciphers
mEcipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
mDcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
} catch (java.security.InvalidAlgorithmParameterException e) {
throw new EncryptionException(e);
} catch (java.security.spec.InvalidKeySpecException e) {
throw new EncryptionException(e);
} catch (javax.crypto.NoSuchPaddingException e) {
throw new EncryptionException(e);
} catch (java.security.NoSuchAlgorithmException e) {
throw new EncryptionException(e);
} catch (java.security.InvalidKeyException e) {
throw new EncryptionException(e);
}
}
public String encrypt(String str) throws EncryptionException {
try {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");
// Encrypt
byte[] enc = mEcipher.doFinal(utf8);
// Encode bytes to base64 to get a string
return new sun.misc.BASE64Encoder().encode(enc);
} catch (javax.crypto.BadPaddingException e) {
throw new EncryptionException(e);
} catch (IllegalBlockSizeException e) {
throw new EncryptionException(e);
} catch (UnsupportedEncodingException e) {
throw new EncryptionException(e);
} catch (java.io.IOException e) {
throw new EncryptionException(e);
}
}
public String decrypt(String str) throws EncryptionException {
try {
// Decode base64 to get bytes
byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);
// Decrypt
byte[] utf8 = mDcipher.doFinal(dec);
// Decode using utf-8
return new String(utf8, "UTF8");
} catch (javax.crypto.BadPaddingException e) {
throw new EncryptionException(e);
} catch (IllegalBlockSizeException e) {
throw new EncryptionException(e);
} catch (UnsupportedEncodingException e) {
throw new EncryptionException(e);
} catch (java.io.IOException e) {
throw new EncryptionException(e);
}
}
}
The two processes run on separate servers, both Centos (5.3 for process A, 6.4 for Process B)
There are no apparent issues with Process A - The string to be encoded is done so reliably.
When process B starts, everything appears to be fine. It decodes and decrypts the required strings correctly.
At some point over the course of about 24 hours however, this stops working. At this point, I get the 'BadPaddingException "Given final block not properly padded"' Exceptions. This then continues every time the code is executed with any encoded string until such time as the process is restarted, at which point everything works again, including decoding strings that failed moments before.
At the time when this goes wrong, calling decrypt(encrypt("test")) will fail too so this seems to be unrelated to the actual encrypted value and more to do with the encryption and decryption getting out of sync some how.
If anyone could offer any suggestions about where I may be going wrong with this I'd appreciate it.
Many thanks in advance...
Andrew
I am working on decrypting a binary file encrypted in C# using Rijndael encryption method. The file is copied to an android device. The decryption logic works fine when run in a java based desktop test program. But it throws java.io.IOException: last block incomplete when run in android. I am using the code below.
public static void Decrypt(String fileIn, String fileOut, byte[] key, byte[] IV, long offset)
{
// First we are going to open the file streams
FileInputStream fsIn;
try
{
fsIn = new FileInputStream(fileIn);,,
FileOutputStream fsOut = new FileOutputStream(fileOut);
// create cipher object
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(IV));
// create the encryption stream
CipherInputStream cis = new CipherInputStream(fsIn, cipher);
// set a buffer and keep writing to the stream
int bufferLen = KiloByte;
byte[] buffer = new byte[bufferLen];
int bytesRead = 0;
// read a chunk of data from the input file
while ( (bytesRead = cis.read(buffer, 0, bufferLen)) != -1)
{
// write to file
fsOut.write(buffer, 0, bytesRead);
}
fsOut.flush();
// close streams
fsOut.close();
cis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
The key is generated by using the function
public static byte[] GetKey(String password, byte[] IV, int length)
throws NoSuchAlgorithmException, InvalidKeySpecException
{
// Length is kept 16 to make it compatible with all platforms
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec ks = new PBEKeySpec(password.toCharArray(), IV, 1000, length*8);
SecretKey s = f.generateSecret(ks);
Key k = new SecretKeySpec(s.getEncoded(),"AES");
return k.getEncoded();
}
I have gone through many posts on internet related to the topic. Based on that, I have made sure that I use byte array rather String. But still getting this issue.
I have a private key file encripted with DES/ECB/PKCS5Padding (56 bit DES key generated by a secret phrase) and I want to decrypt it.
I don't know why, but everytime I try to decript, the method doFinal of my cipher class is throwing this error:
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.DESCipher.engineDoFinal(DashoA13*..) at
javax.crypto.Cipher.doFinal(DashoA13*..) at...
Here is my code:
public static PrivateKey readPrivateKeyFromFile(File file, String chaveSecreta) {
try {
SecureRandom r = new SecureRandom(chaveSecreta.getBytes());
KeyGenerator keyGen = KeyGenerator.getInstance("DES");
keyGen.init(56, r);
Key key = keyGen.generateKey();
byte[] privateKeyBytes = decryptPKFile(file, key);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = null;
try {
privateKey = keyFactory.generatePrivate(privateKeySpec);
} catch (InvalidKeySpecException e) {
JOptionPane.showMessageDialog(null, "Erro 01, tente mais tarde");
}
return privateKey;
} catch (NoSuchAlgorithmException e) {
JOptionPane.showMessageDialog(null, "Erro 02, tente mais tarde");
}
return null;
}
public static byte[] decryptPKFile(File file, Key key){
try{
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
byte[] cipherText = readBytes(file);
cipher.init(Cipher.DECRYPT_MODE, key);
System.out.println(cipher);
System.out.println(cipherText);
byte[] text = cipher.doFinal(cipherText);
return text;
}catch(Exception e){
e.printStackTrace();
return null;
}
}
public static byte[] readBytes(File file) {
try {
FileInputStream fs = new FileInputStream(file);
byte content[] = new byte[(int) file.length()];
fs.read(content);
return content;
} catch (FileNotFoundException e) {
System.out.println("Arquivo não encontrado!");
e.printStackTrace();
} catch (IOException ioe) {
System.out.println("Erro ao ler arquivo!");
ioe.printStackTrace();
}
return null;
}
Any syggestions?
You're trying to decrypt ciphertext with a random number generator created using a specific seed. However, you don't specify the algorithm, and the algorithm may change internally as well. Android is even known to generate a fully random value instead for some versions.
You need to use a SecretKeyFactory not a KeyGenerator. And you will of course need the 8-byte key data. The only way to retrieve this in your case is to find the SecureRandom algorithm/implementation before and re-calculate the key.
Now any ciphertext will decrypt with any key. DES ECB only provides (some sort of) confidentiality, not integrity. The problem is that it will decrypt into garbage. Now if you try to remove the padding from garbage you will likely get a padding error.
If you're "lucky" - once in about 256 times - you will get a result. This happens when the decrypted block ends with 01 or 0202, that's valid padding. The result will - of course - be garbage as well, but it will not end with a BadPaddingException. In your case the SecureRandom instance is likely to return the same incorrect value over and over though, so this may never happen.
In the future, please use PBKDF2 and feed it the encoded password. Clearly note the character encoding used, Java SE uses the lowest 8 bits of the char array. Never ever use String.getBytes() as the default encoding may differ between systems.