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.
Related
I've exported an AWS certificate using the methods described in this tutorial:
https://docs.aws.amazon.com/acm/latest/userguide/sdk-export.html
But I want to decrypt the password encrypted private key in order to use it in my program. I wrote the following code to do that:
String private_key = "<encrypted private key of the form -----BEGIN ENCRYPTED PRIVATE KEY----->"
String passwd = "password used to export the key"
try{
EncryptedPrivateKeyInfo encryptPKInfo = new EncryptedPrivateKeyInfo(private_key.getBytes());
Cipher cipher = Cipher.getInstance(encryptPKInfo.getAlgName());
PBEKeySpec pbeKeySpec = new PBEKeySpec(passwd.toCharArray());
SecretKeyFactory secFac = SecretKeyFactory.getInstance(encryptPKInfo.getAlgName());
Key pbeKey = secFac.generateSecret(pbeKeySpec);
AlgorithmParameters algParams = encryptPKInfo.getAlgParameters();
cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams);
KeySpec pkcs8KeySpec = encryptPKInfo.getKeySpec(cipher);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(pkcs8KeySpec);
}
catch(Exception e){
e.printStackTrace();
}
But this code throws:
java.io.IOException: extra data given to DerValue constructor
Any suggestions what I'm missing here?
I am using AES encryption and have encrypted a message with a key generated from key generator. Encryption and Decryption is working as expected.
But when the application is restarted and I am trying to decrypt the same message again, it is giving error as the secret key is not stored anywhere which was used to encrypt the message and on restart of application we don't have that secret key anymore.
Cannot invoke javax.crypto.Cipher.getIV() because AES.encryptionCipher is null.
I don't want to store the secret key in database. Can I store the secretKey and write it in a file and place that file in src/main/resources folder.Will it be a good approach?
Is there any other place where I could store the secret key and at the time of decrypting the meesages I could load the key from that place and decrypt messages.
This is what I have done so far.
private static SecretKey key;
private static int KEY_SIZE = 128;
private static int T_LEN = 128;
private static Cipher encryptionCipher;
public static String encrypt(String message) {
byte[] messageInBytes = message.getBytes();
byte[] encryptedBytes = null;
try {
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(KEY_SIZE);
key = generator.generateKey();
encryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
encryptionCipher.init(Cipher.ENCRYPT_MODE, key);
encryptedBytes = encryptionCipher.doFinal(messageInBytes);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException e) {
e.printStackTrace();
}
return encode(encryptedBytes);
}
public static String decrypt(String encryptedMessage) {
byte[] messageInBytes = decode(encryptedMessage);
byte[] decryptedBytes = null;
try {
Cipher decryptionCipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = new GCMParameterSpec(T_LEN, encryptionCipher.getIV());
decryptionCipher.init(Cipher.DECRYPT_MODE, key, spec);
decryptedBytes = decryptionCipher.doFinal(messageInBytes);
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException e) {
e.printStackTrace();
}
return new String(decryptedBytes);
}
private static String encode(byte[] data) {
return Base64.getEncoder().encodeToString(data);
}
private static byte[] decode(String data) {
return Base64.getDecoder().decode(data);
}
I had similar situation, still have some open part. Suggested solution solve some, but still there are some open question, hopefully we can have common approach with expert inputs.
Keytool to generate Secret Key
keytool -genseckey -alias aeskey -keyalg AES -keysize 128 -keypass password -keystore project.keystore -storetype pkcs12
refer the Oracle documentation for details.
Java provide standard Java keystore(JKS), Secret key can be stored in jks file before starting application and this file can be read again on restart.
JKS also need password to access, in this approach, challenge is how to keep java keystore password securely
my suggestion is keystore password can be saved securely in new platform like kubernetes ( any other suggestion?)
Reference source code to retrieve Secret Key from Java Keystore:
KeyStore ks = KeyStore.getInstance("pkcs12");
String keyStorePassword = appSecurityConfig.getJksPass(); //get keystore password from external configuration, like kubernetes secret
//appSecurityConfig is configuration class
char [] password = keyStorePassword.toCharArray();
ks.load(new FileInputStream(appSecurityConfig.getKeystoreFile()), password); //keystoreFile -> JKS file name
SecretKey secretKey = (SecretKey) ks.getKey("kskey", password); //kskey -> is secreykey alias
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.
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.
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();
}