I am encrypting a string using RSA algorithm and encryption and decryption logic is
public class RsaEncrypt {
private static final String ALGORITHM = "RSA";
public static void main(String[] args) {
String filePath = "/home/Desktop/abc.jks";
char[] password = "changeit".toCharArray();
String alias = "123";
KeyStore ks = null;
try {
//loading the keystore
ks = KeyStore.getInstance("JKS");
InputStream readStream = new FileInputStream(filePath);
ks.load(readStream, password);
Certificate cert = ks.getCertificate(alias);
PublicKey publicKey = cert.getPublicKey();
PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password);
RsaEncrypt e = new RsaEncrypt();
String result = e.encrypt("abvhdh", publicKey);
String decryptResult = e.decrypt(result.getBytes(), privateKey);
} catch (Exception e) {
e.printStackTrace();
}
}
//Encryption of a string
public String encrypt(String text,PublicKey publicKey) {
String retVal = null;
byte[] cipherText = null;
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
cipherText = cipher.doFinal(text.getBytes());
cipherText = Base64.getEncoder().encode(cipherText);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return new String(cipherText) ;
}
// Decryption of a string
private String decrypt(byte[] text, PrivateKey privatekey) {
byte[] dectyptedText = null;
try {
final Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privatekey);
dectyptedText = cipher.doFinal(Base64.getDecoder().decode(text));
} catch (Exception ex) {
ex.printStackTrace();
} catch (Throwable e) {
throw new RuntimeException(e);
}
return new String(dectyptedText);
}
The Result is fine.But if i encrypt same string one more time i am getting different encryption value.
For that i am using "RSA/ECB/NoPadding" cipher instead of "RSA",then If i encrypt one string many times i am getting same encryption value.
But when i decrypt,The reuslt string contains some null characters
Example
input : abcd output : abcd \u0000 \u0000 \u0000 \u0000....
How can i resolve this problem and what is the best way to get same encryption value if we encrypt multiple times?????
The Result is fine.But if i encrypt same string one more time i am getting different encryption value.
That is correct and it is even required property of the RSA encryption. By default PKCS1.5 padding is used (RSA/ECB/PKCS1Padding) which contains some random bytes. Safer option is RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING mode which is even more random.
For that i am using "RSA/ECB/NoPadding" cipher instead of "RSA",then If i encrypt one string many times i am getting same encryption value. But when i decrypt,The reuslt string contains some null characters
Using RSA without padding (NoPadding) is very unsafe (it is called textbook RSA).
Padding extends the original encrypted value to full space length (e.g. 2048 bits) and then the RSA magic (exponentiation) will be executed. Using the NoPadding parameter you are telling the crypto library that you will do the padding yourself. In that case you are expected to remove the padding after decryption (in your case zero padding)
I hope you are doing that for learning / academic purposes, not some real security project. You may have a look at my blog about encryption to get some examples.
btw: you should not use RSA to encrypt the plaintext itself. Rather use symmetric encryption to encrypt the plaintext and then RSA to encrypt the symmetric encryption key.
Related
This error is already discussed in several links.
I got the similar kind of errors, But my scenario is little different than others.
I need
only Decryption. not Encryption.
In my case, an encrypted message is already stored in the DB. I got that as String and now need to only decrypt it to get the original String.
Any suggestions will be very helpful.
My piece of code is as follows:
String encodedStr = "c2J7RiP1CY++gfdE7Ke/xD1fSOOdO7DvprdZZUDws9Yl8DmYJV64zGV9vvK/TMEfALqSPH8KcZik8XvmLowpjJWzYY2Of1aytHaya0ULwmopklRWlkaS5sjL80jjyhaRN2O+zQNarbAQy8g3VtXX643T2AhDTsT+NKFsCcpH4xbqmViwD1GXQQLsLwuKZx1POAtyC0UaMjQ2SrzR+eVEiSgKPDABDKIRbOyBEXRcJMK/t/P7uIk9tJ/p1X2JqQOO7GMO/1x7rvF6Pb1Fik2tmQv5qL1W6/kV97/VT5Hpi9uh6zdCFO7sstYvgkxxbs3gTyJ5raWlATVU6a/0B/Q50w==";
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair pair = keyGen.generateKeyPair();
PrivateKey privateKey = pair.getPrivate();
PublicKey publicKey = pair.getPublic();
Cipher cipher = Cipher.getInstance("RSA");
// Encryption is not required.
//cipher.init(Cipher.ENCRYPT_MODE, privateKey);
//String ss =Base64.encodeBase64String(cipher.doFinal(encodedStr.getBytes("UTF-8")));
//System.out.println(ss);
// Only decryption is required.
cipher.init(Cipher.DECRYPT_MODE, publicKey);
String sss = new String(cipher.doFinal(Base64.decodeBase64(encodedStr)), "UTF-8");
System.out.println(sss);
And I'm getting below exception everytime at the line cipher.doFinal(Base64.decodeBase64(encodedStr) :
Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:383)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:294)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:356)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:389)
at javax.crypto.Cipher.doFinal(Cipher.java:2164)
at com.sprint.neo.bc4j.util.TestMain.main(TestMain.java:20)
Though I don't require encryption, the Cipher.ENCRYPT_MODE is working fine.
But incase of Cipher.DECRYPT_MODE , it's throwing exception.
Please suggest me, how can I only decrypt an already existing encrypted String from DB, to get the original String value.
Here is the piece of code which works:
public static void main(String[] args) {
byte[] txt = "message".getBytes();
byte[] encText;
try{
// Load the keystore
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
char[] password = "keystorePassword".toCharArray();
Key rsakey = ks.getKey("mykeyalias", password);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// Encrypt
Certificate cert = ks.getCertificate("mykeyalias");
try
{
cipher.init(Cipher.ENCRYPT_MODE, cert.getPublicKey());
encText = cipher.doFinal(txt);
System.out.println(encText.toString());
}
catch (Throwable e)
{
e.printStackTrace();
return;
}
// Decrypt
cipher.init(Cipher.DECRYPT_MODE, rsakey);
String decrypted = new String(cipher.doFinal(encText));
System.out.println(decrypted);
} catch (Exception e) {
System.out.println("error" + e);
}
}
Your publicKey generated will be different each time and hence the encrypted String will be different. The string generated will no longer be valid for decryption.
Try to read key secret from a file and use same encrypted string to decrypt.
In this code, this line is causing an exception:
clearText = c.doFinal(Base64.decode(encryptedText, Base64.DEFAULT));
javax.crypto.BadPaddingException: pad block corrupted
I got the code from:
http://www.techrepublic.com/blog/software-engineer/attention-android-developers-keep-user-data-safe/
Any ideas?
private String decrypt (String encryptedText) {
byte[] clearText = null;
try {
SecretKeySpec ks = new SecretKeySpec(getKey(), "AES");
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, ks);
clearText = c.doFinal(Base64.decode(encryptedText, Base64.DEFAULT));
return new String(clearText, "UTF-8");
} catch (Exception e) {
return null;
}
}
Details: I am encrypting it on the android as well
owlstead's advice was helpful, but for this case when using the code in
Attention Android developers: Keep user data safe
http://www.techrepublic.com/blog/software-engineer/attention-android-developers-keep-user-data-safe/
I made some changes to the code that might be helpful for other people in the future. I completely deleted the getkey method.
private static String seed;
/**
* Encrypts the text.
* #param clearText The text you want to encrypt
* #return Encrypted data if successful, or null if unsucessful
*/
protected String encrypt(String clearText) {
byte[] encryptedText = null;
try {
byte[] keyData = seed.getBytes();
SecretKey ks = new SecretKeySpec(keyData, "AES");
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, ks);
encryptedText = c.doFinal(clearText.getBytes("UTF-8"));
return Base64.encodeToString(encryptedText, Base64.DEFAULT);
} catch (Exception e) {
return null;
}
}
/**
* Decrypts the text
* #param encryptedText The text you want to encrypt
* #return Decrypted data if successful, or null if unsucessful
*/
protected String decrypt (String encryptedText) {
byte[] clearText = null;
try {
byte[] keyData = seed.getBytes();
SecretKey ks = new SecretKeySpec(keyData, "AES");
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, ks);
clearText = c.doFinal(Base64.decode(encryptedText, Base64.DEFAULT));
return new String(clearText, "UTF-8");
} catch (Exception e) {
return null;
}
}
Java + Android + Encryption + Exception means just one thing normally, somebody is using the SecureRandom class again as a key derivation function. This fails when the SecureRandom implementation of "SHA1PRNG" does not behave as the one in Sun's implementation in Java SE. Especially if the seed is added to the state of the random number generator instead of the seed being used as a starting point of the PRNG.
Basically, simply use SecretKey aesKey = new SecretKeySpec(byte[] keyData, "AES") instead, or - if you start off with a password - try and generate the key using PBKDF2.
For me, the problem is in getKey()
Make sure that two invocation of getKey() return the same value.
I used new SecureRandom(password.getBytes()) to generate key. It worked on Windows, but on Android, it returned different value for different call.
I Reffred From this : https://androidfreetutorial.wordpress.com/2017/03/14/android-encryptiondecryption-with-aes-algorithm/
Change to "AES" From "AES/ECB/PKCS7Padding";
I have RSA Public-Key on server side in below format:
<string xmlns="http://www.cherripik.com/">
<RSAKeyValue><Modulus>abc</Modulus><Exponent>abc</Exponent></RSAKeyValue>
</string>
I have tried almost all possible ways but could not able to encrypt string with this public key on android side. Could anyone give me an example in which i will encrypt any of the string like "abc" with this public key and also decrypt that encrypted key to back to "abc". It will be very helpful to me.
Advance Thanks.
Below are the ways which i have used but no success. It gave some value but it is not correct.
public String encrypt(String message, String Modulus, String Exponent) {
String outputEncrypted = "";
try {
byte[] modulusBytes = Base64Coder.decode(Modulus);
byte[] exponentBytes = Base64Coder.decode(Exponent);
BigInteger modulus = new BigInteger(modulusBytes );
BigInteger exponent = new BigInteger(exponentBytes);
RSAPublicKeySpec rsaPubKey = new RSAPublicKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey pubKey = fact.generatePublic(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = new String("abc").getBytes("UTF-8");
byte[] cipherData = cipher.doFinal( plainBytes );
String encryptedString = new String(Base64Coder.encode(cipherData));
Log.i(this.getClass().getSimpleName(), "encryptedString : "+encryptedString);
} catch (Exception e) {
// TODO: handle exception
}
return outputEncrypted;
}
One more thing when I create encrypted string with above method. It will give 346 characters encrypted string. But on my server, I have only encrypt and decrypt method. On server encrypt method, it will producing 344 character. Lastly when I put my encrypted string to server method to verify that my encrypted string is correct. Server throw this error.
<string xmlns="http://www.Myserver.com/">Error occurred while decoding OAEP padding.</string>
private static String decrypt(String cipherString, PrivateKey key) {
byte[] dectyptedText = null;
byte[] stringText = null;
try {
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance(ALGORITHM);
//chiper init in encrypt mode
cipher.init(Cipher.ENCRYPT_MODE, key);
//tried to get bytes out of encrypted string
stringText = cipher.doFinal(cipherString.getBytes());
// decrypt the text using the private key
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(stringText);
} catch (Exception ex) {
ex.printStackTrace();
}
return new String(dectyptedText);
}
I want to convert the cipher text into bytes generated by the encryptor to a string and store in a database. Then get the string and decrypt it whenever its needed. Is there anyone that could help me solving the issue I'm having?
I does not make sense to convert the byte-Array to String.
You have to either save the bytes directly (which would require an appropriate column in the database, for example BLOB), or you could encode the byte-Array, for example using Base64 (I would recommend the latter).
(If your problems are with the "public-crypto thingy", you may want to use the public key to encrypt, but the private key to decrypt. If you dont know what that means, check out some literature about public-key crypto, please.)
Since your problem seems to be with your key, you possibly need a public key and a private key, not only a private key.
Have a look at this simple RSA demo:
encryptionAlgorithm = "RSA/ECB/PKCS1Padding";
algorithm = "RSA";
try {
SecureRandom random = SecRandom.getDefault();
// Since you are working with asymmetric crypto, you need a keypair:
KeyPairGenerator kpg = KeyPairGenerator.getInstance(algorithm);
kpg.initialize(2048, random);
KeyPair kp = kpg.generateKeyPair();
// encrypting something with asymmetric crypto needs a public key:
Cipher cipher1 = Cipher.getInstance(encryptionAlgorithm);
cipher1.init(Cipher.ENCRYPT_MODE, kp.getPublic());
byte[] text = "This is a test".getBytes("ASCII");
System.out.println("text = " +(new String(text)));
byte[] ciphertext = cipher1.doFinal(text);
// here you could store & load your sipertext
System.out.println("ciphertext = " + ciphertext);
// decrypting something with asymmetric crypto needs a private key:
Cipher cipher2 = Cipher.getInstance(encryptionAlgorithm);
cipher2.init(Cipher.DECRYPT_MODE, kp.getPrivate());
byte[] cleartext = cipher2.doFinal(ciphertext);
System.out.println("cleartext = " +(new String(cleartext)));
} catch (Exception e) {
e.printStackTrace();
}
First of all, this is not a duplicate question. I am facing a very strange issue.
Following is what I do.
Case 1:
Generate Key Pair
Encrypt Using Private Key
Decrypt Using Public Key
Everything works fine.
Case 2:
Load Certificate from Mozila Firefox Key Store
Use Certificate A
Encrypt Using Private Key of Certificate A
Decrypt Using Public Keu of Certificate A
Everything works fine.
Case 3:
Load Certificate from Internet Explorer Key Store
Use Certificate A
Encrypt Using Private Key of Certificate A
Decrypt Using Public Keu of Certificate A
At Decrypt time, I get error of BadPadding exception
Following is snippet of each of my codes.
Generating Key Pair
KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
Load Mozilla KeyStore
String strCfg = System.getProperty("user.home")+ File.separator + "jdk6-nss-mozilla.cfg";
Provider p1 = new sun.security.pkcs11.SunPKCS11(strCfg);
Security.addProvider(p1);
keyStore = KeyStore.getInstance("PKCS11");
keyStore.load(null, "password".toCharArray());
Content of Config file
name=NSS
slot=2
library=C:/Program Files/Mozilla Firefox/softokn3.dll
nssArgs="configDir='C:/Documents and Settings/pratik.vohera.DIGI-CORP/Application Data/Mozilla/Firefox/Profiles/t48xsipj.default' certPrefix='' keyPrefix='' secmod='secmod.db' flags=readOnly"
Load IE KeyStore
keyStore = KeyStore.getInstance("Windows-MY");
keyStore.load(null, null);
Get Public and Private key from KeyStore
if (keyStore != null) {
Enumeration<String> enumaration = null;
try {
enumaration = keyStore.aliases();
} catch (KeyStoreException e1) {
e1.printStackTrace();
}
ArrayList<String> certiList;
while (enumaration.hasMoreElements()) {
String aliases = enumaration.nextElement();
certiList = new ArrayList<String>();
certiList.add(aliases);
try {
selectedCert = keyStore.getCertificate(aliases);
selectedpublickey = (RSAPublicKey) selectedCert.getPublicKey();
selectedAlias = aliases;
selectedprivateKey = (PrivateKey) keyStore.getKey(selectedAlias, null);}
} catch (KeyStoreException e) {
e.printStackTrace();
}
}
Encryption
private static String publicEncrypt(String text, Key pubKey) throws Exception {
BASE64Encoder bASE64Encoder = new BASE64Encoder();
byte[] plainText = text.getBytes();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String encryptedText = bASE64Encoder.encode(cipher.doFinal(plainText));
return encryptedText;
}
Decryption
private static String privateDecrypt(String text, Key priKey)throws Exception {
BASE64Decoder base64Decoder = new BASE64Decoder();
byte[] encryptText = base64Decoder.decodeBuffer(text);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, priKey);
String decryptedString = new String(cipher.doFinal(encryptText));
return decryptedString;
}
Exception Stacktrace
javax.crypto.BadPaddingException: Data must start with zero
at sun.security.rsa.RSAPadding.unpadV15(Unknown Source)
at sun.security.rsa.RSAPadding.unpad(Unknown Source)
at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at test.testclass.privateDecrypt(testclass.java:198)
at test.testclass.test(testclass.java:137)
at test.testclass.main(testclass.java:120)
I have been working on this for a long time. This is very important. Do let me know if any further information is required.
In the third case, the problem is: You try to encrypt using the private key and decrypt using the public key which is wrong. You should always decrypt using the private key.