This question already has an answer here:
Problems outsourcing RSA encryption and decryption
(1 answer)
Closed 9 years ago.
i'm developing an android app. Unfortunately i have a compatibility problem. My source code works with firmware 4.1, but crashes with devices with firmware 2.2. The error seems to be a "ClassNotFoundException" caused by the ObjectInputStream.
My sourcecode for the encryption and decryption:
private Key getPublicKey() {
try {
InputStream fis = activity.getResources().openRawResource(R.raw.publick);
ObjectInputStream ois = new ObjectInputStream(fis);
Key RSApublicKey = (Key) ois.readObject();
return RSApublicKey;
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
private Key getPrivateKey() {
try {
InputStream fis = activity.getResources().openRawResource(R.raw.privatek);
ObjectInputStream ois = new ObjectInputStream(fis);
Key RSAprivateKey = (Key) ois.readObject();
return RSAprivateKey;
}
catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
catch (ClassNotFoundException e) {
Log.e("error", "Error: " + e);
e.printStackTrace();
}
return null;
}
public byte[] encrypt(String data) {
byte[] encrypted = null;
try {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey());
encrypted = cipher.doFinal(data.getBytes());
}
catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
catch (BadPaddingException e) {
e.printStackTrace();
}
catch (InvalidKeyException e) {
e.printStackTrace();
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (NoSuchPaddingException e) {
e.printStackTrace();
}
catch (NoSuchProviderException e) {
e.printStackTrace();
}
return encrypted;
}
public String decrypt(byte[] encrypted) {
byte[] decrypted = null;
try {
Cipher cipher;
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, getPrivateKey());
decrypted = cipher.doFinal(encrypted);
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (NoSuchPaddingException e) {
e.printStackTrace();
}
catch (InvalidKeyException e) {
e.printStackTrace();
}
catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
catch (BadPaddingException e) {
e.printStackTrace();
}
catch (NoSuchProviderException e) {
e.printStackTrace();
}
return new String(decrypted);
}
The error in logcat:
03-23 10:13:19.706: E/Error(427): Error: java.lang.ClassNotFoundException: com.android.org.bouncycastle.jce.provider.JCERSAPrivateCrtKey
Do you have any ideas, how i could get it work with android 2.2?
You are better off using the Key.getEncoded() method to serialize the key. The Key.getFormat() already hints that RSA private keys should default to PKCS#8 encoding.
You can then use the PKCS8EncodedKeySpec class and an instance of an "RSA" KeyFactory to deserialize the resulting byte array.
The serialized keys may not work correctly over different implementations of PrivateKey (as you've already found out by now), and PKCS#8 is a well defined and often used standard.
Well, if it works on 4.1+ but not on 2.2, I guess it's a class from android that has been included after 2.2. You can test this by trying to compile with android 2.2 a project that uses this class and see if you can. If this is the case just find it and include it in your project.
Related
I encrypted data successfully using the biometric manager flow as shown by google but on trying the decrypt the encrypted data, I keep hitting the IllegalBlockSizeException clause. I need help please.
This is my class that handles all the fingerprint encryption( which works well) but decryption doesn't work, since it keeps hitting the IllegalBlockSizeException clause
public class FingerPrintEncrypter {
private static FingerPrintEncrypter fingerPrintEncrypter;
private static final String ENCRYPTION_BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
private static final String ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7;
private static final String ENCRYPTION_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
byte[] initVector;
private FingerPrintEncrypter(){
}
public static synchronized FingerPrintEncrypter getInstance(){
if(fingerPrintEncrypter == null){
fingerPrintEncrypter = new FingerPrintEncrypter();
}
return fingerPrintEncrypter;
}
/*
gets the Cipher obj in encryptionMode for encryption tasks.
*/
public Cipher getCipherForEncryption(String aliasKeyName){
try {
Cipher cipher = getCipher();
SecretKey secretKey = getOrCreateSecreteKey(aliasKeyName);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
initVector = cipher.getIV();
return cipher;
} catch (InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
/*
gets the Cipher obj in decryptionMode for decryption tasks.
*/
public Cipher getCipherForDecryption(String aliasKeyName){
try{
Cipher cipher = getCipher();
SecretKey secretKey = getOrCreateSecreteKey(aliasKeyName);
IvParameterSpec decryptParamSpec = new IvParameterSpec(initVector);
cipher.init(Cipher.DECRYPT_MODE, secretKey, decryptParamSpec);
return cipher;
}catch (InvalidKeyException e){
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
return null;
}
private SecretKey getOrCreateSecreteKey(String aliasKeyName) {
try {
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
keyStore.load(null);
SecretKey key = ((SecretKey)keyStore.getKey(aliasKeyName, null));
if(key == null){
// key previously create so just grab it
generateSecretKey(aliasKeyName);
}
return key;
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
}
return null;
}
private void generateSecretKey(String aliasKeyName){
try {
// key not create create a new one
KeyGenParameterSpec parameterSpec = new KeyGenParameterSpec.Builder(aliasKeyName,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(ENCRYPTION_BLOCK_MODE)
.setEncryptionPaddings(ENCRYPTION_PADDING)
.setUserAuthenticationRequired(true)
.build();
KeyGenerator keyGenerator = KeyGenerator.getInstance(ENCRYPTION_ALGORITHM, ANDROID_KEY_STORE);
keyGenerator.init(parameterSpec);
keyGenerator.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
private Cipher getCipher() {
try {
return Cipher.getInstance(ENCRYPTION_ALGORITHM + "/"
+ ENCRYPTION_BLOCK_MODE + "/"
+ ENCRYPTION_PADDING);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
return null;
}
public String encryptData(String info, Cipher cipher){
try {
byte[] encryptedData = cipher.doFinal(info.getBytes("UTF-8"));
return Base64.encodeToString(encryptedData, Base64.NO_WRAP);
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
public String decryptData(String stringToDecrypt, Cipher cipher){
try {
return new String(cipher.doFinal(Base64.decode(stringToDecrypt, Base64.NO_WRAP)));
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
}
}
Here is my encryption method also Decryption method its working fine with Tag Basically server need TAG is it possible server find Tag by my Hex String because every String i send Tag to the server and also with Hex, is it have any other optimal solution
aesKey=context.getString(R.string.aes_key).getBytes();
iv=context.getString(R.string.input_vector).getBytes();
public Map encrypt(String string){
byte[] plainText= new byte[0];
try {
plainText = string.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/GCM/NoPadding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
try {
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(aesKey, "AES"), new IvParameterSpec(iv));
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
byte[] cipherText = new byte[0];
byte[] tag = new byte[0];
try {
cipherText = cipher.doFinal(plainText);
tag= Arrays.copyOfRange(cipherText, cipherText.length -16, cipherText.length);
cipherText= Arrays.copyOfRange(cipherText, 0, cipherText.length -16);
Log.d("testing", "encrypt: "+byteArrayToHexString(tag));
Map<String, Object> map=new HashMap();
map.put("content",byteArrayToHexString(cipherText));
map.put("tag",tag);
Log.d("testing", "encrypt: "+new Gson().toJson(map));
return map;
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return new HashMap();
}
Server need Hex so i convert it into Hex
public String byteArrayToHexString(final byte[] bytes) {
StringBuilder sb = new StringBuilder();
for(byte b : bytes){
sb.append(String.format("%02x", b&0xff));
}
return sb.toString();
}
My Decryption Function is that return me the String i receive Tag and Hex String from the server
public String decrypt(String content,byte[] tag){
byte[] cipherText = hexStringToByteArray(content+byteArrayToHexString(tag));
Cipher cipher = null;
try {
cipher = Cipher.getInstance("AES/GCM/NoPadding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
try {
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(aesKey, "AES"), new IvParameterSpec(iv));
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
byte[] plainText = new byte[0];
try {
plainText = cipher.doFinal(cipherText);
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
try {
Log.d("testing", "encrypt: plain text:"+new String(plainText,"UTF-8"));
return new String(plainText,"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
i have a realtime database on firebase , and i want to encrypt only single field.
I have no idea to encrypt my user data on firebase.
is that possible to do?
Yes, it is possible, Just send encrypted field while saving at firebase and at time of fetching data again decrypt the string.
You can refer below code
public class KeyStoreClass {
private static final String ALGORITHM = "Blowfish";
private static final String MODE = "Blowfish/CBC/PKCS5Padding";
private static final String IV = "abcdefgh";
private static final String KEY = "MyKey";
public String encrypt(String value) {
if (value == null || value.isEmpty())
return "";
SecretKeySpec secretKeySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM);
Cipher cipher = null;
try {
cipher = Cipher.getInstance(MODE);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
try {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(IV.getBytes()));
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
byte[] values = new byte[0];
try {
values = cipher.doFinal(value.getBytes());
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return Base64.encodeToString(values, Base64.DEFAULT);
}
public String decrypt(String value) {
if (value == null || value.isEmpty())
return "";
byte[] values = new byte[0];
try {
values = Base64.decode(value, Base64.DEFAULT);
} catch (Exception e) {
values = value.getBytes();
e.printStackTrace();
}
SecretKeySpec secretKeySpec = new SecretKeySpec(KEY.getBytes(), ALGORITHM);
Cipher cipher = null;
try {
cipher = Cipher.getInstance(MODE);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
try {
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(IV.getBytes()));
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
try {
return new String(cipher.doFinal(values));
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return "";
}
}
To use it :
To Encrypt : keystore.encrypt(item.getPhNo())
To Decrypt : keystore.decrypt(patientDetail);
I want to encode my Url-params with Base64 and crypt(AES with CFB Mode), but it will not working and I can't get the error. Can someone help me? Here is my code:
private String toBase64Crypt(String cryptString) {
try {
SecretKeySpec key = new SecretKeySpec(pwd.getBytes("UTF8"), "AES");
byte[] cryptByte = cryptString.getBytes("UTF8");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
cryptString = Base64.encodeToString(cipher.doFinal(cryptByte),Base64.DEFAULT);
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return cryptString;
}
In my application i want to store some secure data by encrypting it. When the user wants i need to show it to him by decrypting it.
This is working fine.
But the problem is i need to store both encrypted message and initialization vector for every message. This initialization vector is generated while encrypting and i have to use this while decrypting to get the original message.
So if the user stores 1000 messages i need to store those 1000 encrypted messages and corresponding 1000 initialization vectors.I want to avoid storing initialization vector for every message.
Please tell me the way to AES-256 encryption with out Initialization vector.
Below is my code for encrypting and decrypting
/*
* This method will do the AES-256 encryption.
*/
private byte[] encrypt(char[] raw, String cardno) {
// This raw is some unique key like password.
SecretKeyFactory factory = null;
SecretKey tmp = null;
Cipher cipher = null;
byte[] ciphertext = null;
AlgorithmParameters params = null;
try {
factory = SecretKeyFactory.getInstance("PBEWITHSHA-256AND256BITAES-CBC-BC");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
KeySpec spec = new PBEKeySpec(raw, mSalt, 1024, 256);
try {
if (factory != null)
tmp = factory.generateSecret(spec);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
if (tmp != null)
mSecret = new SecretKeySpec(tmp.getEncoded(), "AES");
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
try {
if (cipher != null)
cipher.init(Cipher.ENCRYPT_MODE, mSecret);
} catch (InvalidKeyException e) {
e.printStackTrace();
}
if (cipher != null)
params = cipher.getParameters();
try {
mIV = params.getParameterSpec(IvParameterSpec.class).getIV();
} catch (InvalidParameterSpecException e) {
e.printStackTrace();
}
try {
ciphertext = cipher.doFinal(cardno.getBytes("UTF-8"));
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return ciphertext;
}
/*
* This will decrypt the encrypted data based on provided key
*/
private byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
//This raw is initialization vector generated while encrypting
Cipher cipher = null;
byte[] decrypted = null;
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
try {
cipher.init(Cipher.DECRYPT_MODE, mSecret, new IvParameterSpec(raw));
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
try {
decrypted = cipher.doFinal(encrypted);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return decrypted;
}
A better way is to simply add the initialization vector at the start of each encrypted message you save. As the padding already may add some bytes to the message, this should not matter much regarding the use case.
Don't forget, the IV always has a fixed size for a particular block cipher: the block size, which you can retrieve using cipher.getBlockSize() in Java. You can simply use cipher.doFinal(buf, offset, length) instead of cipher.doFinal(buf) after retrieving the IV.
If you really don't want to store the IV, you could calculate the IV from the full path name (the absolute path, or from some root, if needed), and perform a hash such as SHA-256 over it. As long as the path is unique, the SHA-256 should be relatively close to a known but random IV, which is what would be most safe. Of course, if you rename/move the file...
Note that you are trying to safe yourself only about 16KB of initialization vector (1000 x 16, which is the block size). That's not a lot.
To be secure, you must use an initialization vector (and a unique one) for each message. There is no way to get around it.