As per given steps, I have performed encryption but am getting an error.
Generate a 16-digit random number (session key). Say RANDOMNO.
RANDOMNO = 1111222233334444
Encrypt RANDOMNO using RSA/ECB/PKCS1Padding and encode using Base64. Say ENCR_KEY.
ENCR_KEY = B64Encode(RSA/ECB/PKCS1Encryption(RANDOMNO,ICICIPubKey.cer))
Perform AES/CBC/PKCS5Padding encryption on request payload using RANDOMNO as key and ivinitialization vector. Say ENCR_DATA.
ENCR_DATA = B64Encode(AES/CBC/PKCS5Padding(REQUEST_DATA, RANDOMNO, IV))
Now the client may choose to send IV in request from one of the two options below.
Send Base64 Encoded IV in “iv” tag.
public byte[] generateRandomBytes() {
SecureRandom ng=new SecureRandom();
byte[] randomBytes=new byte[16];
ng.nextBytes(randomBytes);
return randomBytes;
}
//new method for encryption -we need to check
public String encryptRandomKeyWithCertificate(byte[] randomNumber) throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, FileNotFoundException, CertificateException {
//step2: encrypt the random number with certificate
FileInputStream fin = new FileInputStream("D:\\cedge_uat\\ICICIUATpubliccert.cer");
CertificateFactory f = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) f.generateCertificate(fin);
PublicKey publicKey = certificate.getPublicKey();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipherData = cipher.doFinal(randomNumber);
String encodedData = Base64.getEncoder().encodeToString(cipherData);
return encodedData;
}
public String encryptRequestWithKey(String text, byte[] randomNumber) throws Exception {
//step3: encrypt the requestString with randomkeyEncrypted
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
BASE64Decoder decoder = new BASE64Decoder();
SecretKeySpec keySpec = new SecretKeySpec(randomNumber, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(new byte[16]);
cipher.init(Cipher.ENCRYPT_MODE, keySpec,ivSpec);
byte[] cipherData = cipher.doFinal(text.getBytes());
// BASE64Encoder encoder = new BASE64Encoder();
//return encoder.encode(cipherData).replaceAll("[\r\n]+", "");
String encodedData = Base64.getEncoder().encodeToString(cipherData);
return encodedData;
}
public static void main(String[] args) throws IOException {
String requestString = "CORP_USER=";
byte[] randomNumber;
String encryptedKey;
try {
randomNumber=encryption.generateRandomBytes();
encryptedKey = encryption.encryptRandomKeyWithCertificate(randomNumber);
String encryptedData =encryption.encryptRequestWithKey(requestString,randomNumber);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
According to the Wikipedia page on AES encryption, an AES key can be 128, 192 or 256 bits; i.e. 16, 24 or 32 bytes.
You are supplying a key whose size depends on the "random" number string you are generating. It looks like it will be between 1 and 19 digits plus a possible sign. When you call getBytes() on that string, you will get a byte array with anywhere between 1 and 20 bytes. That is typically NOT one of the acceptable key sizes for AES.
What you should do is use SecureRandom.nextBytes(byte[]) and supply a byte array of one of the three acceptable key sizes for AES.
Related
I wrote a simple Encryption and Decryption helper class for my android app to encrypt and store Strings securely.
It consists of a single static public method to encrypt, then it calls a private static method to decrypt the encrypted message and returns it. I wrote the method this way to check if the message is intact after encryption/decryption.
I wrote a simple JUnit test with a String and called AssertEquals on the String before and after sending it to the Crypto encryption method.
I get this following errors from running the test:
javax.crypto.AEADBadTagException: Tag mismatch!
The error stack:
at com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:571)
at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1046)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:983)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:845)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at util.Crypto.decrypt(Crypto.java:94)
at util.Crypto.encrypt(Crypto.java:64)
at com.example.ali.meappley.CryptoTest.encryptAndDecryptTest(CryptoTest.java:29)
I'm new to cryptography, but I read different stackoverflow replies and couldn't find anything of help. Some users suggested calling cipher.update(someByteArray) before calling cipher.doFinal(someByteArray) but I couldnt manage to get it working. Any suggestions?
This is my helper class
public class Crypto {
//public methods
//public static encrypt method
public static String encrypt(String messageToEncrypt, #Nullable byte[] associatedData) throws NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidAlgorithmParameterException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException {
byte[] plainBytes = messageToEncrypt.getBytes();
/////////////////////////////////////////////////////////////////
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[16];
secureRandom.nextBytes(key);
SecretKey secretKey = new SecretKeySpec(key, "AES");
byte[] iv = new byte[12]; //NEVER REUSE THIS IV WITH SAME KEY
secureRandom.nextBytes(iv);
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //128 bit auth tag length
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
if (associatedData != null) {
cipher.updateAAD(associatedData);
}
byte[] cipherText = cipher.doFinal(plainBytes);
ByteBuffer byteBuffer = ByteBuffer.allocate(4 + iv.length + cipherText.length);
byteBuffer.putInt(iv.length);
byteBuffer.put(iv);
byteBuffer.put(cipherText);
byte[] cipherMessage = byteBuffer.array();
Arrays.fill(key,(byte) 0); //overwrite the content of key with zeros
///////////////////////////////////////////////////////////////////
byte[] decrypted = decrypt(cipherMessage, null, key);
return decrypted.toString();
}
//public static decrypt method
private static byte[] decrypt(byte[] cipherMessage, #Nullable byte[] associatedData, byte[] key) throws NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidAlgorithmParameterException,
InvalidKeyException,
BadPaddingException,
IllegalBlockSizeException {
ByteBuffer byteBuffer = ByteBuffer.wrap(cipherMessage);
int ivLength = byteBuffer.getInt();
if(ivLength < 12 || ivLength >= 16) { // check input parameter
throw new IllegalArgumentException("invalid iv length");
}
byte[] iv = new byte[ivLength];
byteBuffer.get(iv);
byte[] cipherText = new byte[byteBuffer.remaining()];
byteBuffer.get(cipherText);
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv));
if (associatedData != null) {
cipher.updateAAD(associatedData);
}
cipher.update(cipherText);
byte[] plainText= cipher.doFinal(cipherText);
return plainText;
}
There are a few issues with your code:
1) In your encrypt-method remove the following line (or shift it behind the decrypt-call).
Arrays.fill(key, (byte) 0); // overwrite the content of key with zeros
Otherwise the key for encryption and decryption differ.
2) In your encrypt-method also pass the associatedData in your decrypt-call i.e. replace
byte[] decrypted = decrypt(cipherMessage, null, key);
with
byte[] decrypted = decrypt(cipherMessage, associatedData, key);
The associatedData passed for encryption and decryption have to match for validity. For the purpose of the associatedData see e.g. https://crypto.stackexchange.com/questions/6711/how-to-use-gcm-mode-and-associated-data-properly
3) In your decrypt-method remove the line
cipher.update(cipherText);
For the purpose of the update-method see e.g. What does cipher.update do in java?
All three issues give rise to an AEADBadTagException.
4) I suspect for testing purposes your encrypt-method returns decrypted.toString() which however only gives you the object's class and hashcode. It would make more sense to return e.g. new String(decrypted).
I am trying to decrypt a string encrypted via Apple's CommonCrypto API. It is using AES/CBC/PKCS7Padding. It takes the input string and a user provided password and generates the corresponding encrypted string which is Base64 encoded.
However, I have tried almost everything I found on SO on the related context but failed to decrypt the string. The issue is javax.crypto.BadPaddingException: pad block corrupted which essentially means a bad key. The iOS encryption manually pads a short key < 25 Bytes (in this case only) by appending a char 0. Now the CCryptStatus takes in the key length as 32 Bytes so my first question is that,
Despite providing a key less than the required length how is CommonCrypto encrypting the string with a short key?
Following are some specs of one such example
input String "XnV6f4YLAzkLvJ2CIJTC5g==2uXT5CSYKLUrL7+MkJ4IDw=="
_core _StringCore
userPassword String "123456789"
paddedKey String "1234567890000000000000000"
keyData NSData? 25 bytes 0x0000600000a4c360
numBytesEncrypted size_t 10
dataLength size_t 16
encryptedData NSMutableData? 10 bytes 0x0000604000842e20
ptrToData UnsafeMutableRawPointer
lengthOfData size_t 32
keyLength size_t 32
operation CCOperation 1
algoritm CCAlgorithm 0
options CCOptions 1
cryptStatus CCCryptorStatus 0
data NSString "Hello Jash" 0x0000600000631500
result String "Hello Jash"
Following is my decrypt method in Java using Cipher API based on a snippet found in a SO question.
String message = "XnV6f4YLAzkLvJ2CIJTC5g==2uXT5CSYKLUrL7+MkJ4IDw==";
String userKey = "123456789";
private static String decrypt (String message, String userKey) throws UnsupportedEncodingException,
NoSuchPaddingException,
NoSuchAlgorithmException,
InvalidKeyException,
ShortBufferException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchProviderException {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
if (message.length() >= 48) {
ivFromEncryptedString = message.substring(0, Math.min(message.length(), 24));
messageFromEncryptedString = message.substring(24, message.length());
System.out.println(ivFromEncryptedString);
System.out.println(messageFromEncryptedString);
byte[] data = decodeBase64(messageFromEncryptedString);
byte[] ivData = decodeBase64(ivFromEncryptedString);
paddedKey = padShortKeys(userKey);
byte[] keyBytes = paddedKey.getBytes(CHARSET);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivData);
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
byte [] encrypted = new byte[cipher.getOutputSize(data.length)];
int ctLength = cipher.update(data, 0, data.length, encrypted, 0);
ctLength += cipher.doFinal(encrypted, ctLength);
} catch (Exception e) {
System.out.println(e);
} finally {
return encrypted;
}
}
return null;
}
private static String encodeBase64(byte [] in){
return Base64.getEncoder().encodeToString(in);
}
private static byte[] decodeBase64(String str) throws UnsupportedEncodingException {
return DatatypeConverter.parseBase64Binary(str);
}
Can someone point me in the right direction what am I missing here? I am not a security expert or even intermediate individual. I have tried my best to grasp these concepts without diving into much detail. Is there any way to obtain same results in this Java code and decrypt the string?
I tried using SHA-256 for MessageDigest which turns the 25 Bytes padded key to 32 Bytes but the exception clearly indicates that there is something wrong with the key.
What I am trying to do is to encrypt a string with AES, encrypt the AES key getEncoded() value with RSA, then decrypt that AES getEncoded() value so that I get my original string. The public key is loaded from the users certificate, and the private key from file.
The code is given below.
public class Main {
public static void main(String[] args) throws Exception {
String myString = "My Message";
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
SecretKey secretKey = keyGenerator.generateKey();
byte[] initializationVector = new byte[128 / 8];//16
SecureRandom prng = new SecureRandom();
prng.nextBytes(initializationVector);
Cipher AESCipherForEncryption = Cipher.getInstance("AES/CBC/PKCS5PADDING");
AESCipherForEncryption.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(initializationVector));
byte[] byteVersionOfMyMessage = myString.getBytes();
byte[] byteVersionOfCipherText = AESCipherForEncryption.doFinal(byteVersionOfMyMessage);
String cipherText = new BASE64Encoder().encode(byteVersionOfCipherText);
InputStream in1 = new FileInputStream("user.crt");
CertificateFactory cf1 = CertificateFactory.getInstance("X509");
Certificate c1 = cf1.generateCertificate(in1);
X509Certificate toSendcert = (X509Certificate) c1;
PublicKey publicKey = toSendcert.getPublicKey();
String cipherTextRSA = encryptRSA(publicKey, new String(secretKey.getEncoded()));
String decypheredRSA = decryptRSA(getPrivateKey("user.pk8", "RSA"), cipherTextRSA);
System.out.println(cipherTextRSA);
System.out.println(decypheredRSA);
SecretKey originalKey = new SecretKeySpec(new String(decypheredRSA.getBytes("UTF-8")).getBytes(), 0, new String(decypheredRSA.getBytes("UTF-8")).getBytes().length, "AES");
Cipher AESCipherForDecryption = Cipher.getInstance("AES/CBC/PKCS5PADDING");
AESCipherForDecryption.init(Cipher.DECRYPT_MODE, originalKey, new IvParameterSpec(initializationVector));
byte[] byteVersionOfDecriptedText = AESCipherForDecryption.doFinal(new BASE64Decoder().decodeBuffer(cipherText));
String decMessage = new String(byteVersionOfDecriptedText);
System.out.println(decMessage);
}
public static String encryptRSA(PublicKey pubKey, String message) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
Base64.Encoder encoder = Base64.getEncoder();
String encryptedString = encoder.encodeToString(cipher.doFinal(message.getBytes("UTF-8")));
return encryptedString;
}
public static PrivateKey getPrivateKey(String filename, String algorithm) throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();
String temp = new String(keyBytes);
String privKeyPEM = temp.replace("-----BEGIN PRIVATE KEY-----", "");
privKeyPEM = privKeyPEM.replace("-----END PRIVATE KEY-----", "");
privKeyPEM = privKeyPEM.replace("\n", "");
byte[] decoded = Base64.getDecoder().decode(privKeyPEM);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance(algorithm);
return kf.generatePrivate(spec);
}
public static String decryptRSA(PrivateKey prKey, String encrypted) throws Exception {
Base64.Decoder decoder = Base64.getDecoder();
byte[] input = decoder.decode(encrypted);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, prKey);
return new String(cipher.doFinal(input));
}
The error that I keep getting is:
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 28 bytes
at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:509)
at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1067)
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1038)
at javax.crypto.Cipher.implInit(Cipher.java:805)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1396)
at javax.crypto.Cipher.init(Cipher.java:1327)
at com.company.Main.main(Main.java:79)
If I don't encrypt and decrypt the secretKey.getEncoded() value, and just use AES without RSA it works properly. Also working with RSA, if I just encrypt some string with a public key, and decrypt it with a private it works. My question would be: "How could I properly encrypt and decrypt the secretKey.getEncoded() value with RSA, so that I can properly encrypt and decrypt myString?".
new String(secretKey.getEncoded())
This won't work as AES keys contain random bytes, and not every byte is a character representative. The problem with the standard string conversion in Java is that it drops unknown characters and bytes instead of generating an exception during encoding / decoding.
RSA operates on bytes, you should not turn the key into string and then back again into bytes as the transformation may be lossy (e.g. dropping 4 of the 32 bytes).
Alternatively - and probably even better - you may want to try the wrapping modes of cipher instead. This should be compatible with some hardware solutions out there. In that case you don't even have to call getEncoded.
OAEP encryption and authenticated encryption modes such as GCM should be preferred over PKCS#1 padding (the default for the Sun providers) and CBC mode encryption.
I am using AESCrypt (gradle :compile 'com.scottyab:aescrypt:0.0.1')
to encrypt and decrypt the data.
TextView tv=(TextView)findViewById(R.id.demotext);
String encrypted="",decrypted="";
try {
encrypted = AESCrypt.encrypt("password","This is the best thing to go by");
decrypted = AESCrypt.decrypt("password",encrypted);
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
System.out.println("EncryptedData:"+encrypted);
System.out.println("DecryptedData:"+decrypted);
tv.setText("Encrypted:"+encrypted +"\n"+"Decrypted:"+decrypted);
The code works perfectly fine in this case, I get the same input as decrypted text.
But, when I try to use already encrypted string using the same method (AES) from the site http://aesencryption.net/ as shown in the screenshot:
And copy paste that encrypted text like:
decrypted = AESCrypt.decrypt("password","sttA+FbNm3RkTovjHI8CcAdStXiMl45s29Jqle+y+pA=");
And then run the code then I get error saying :
javax.crypto.BadPaddingException: error:1e06b065:Cipher functions:EVP_DecryptFinal_ex:BAD_DECRYPT
But when I use the decrypted text into the same site it works fine as shown in the screenshot below.
private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
Probably due to the algorithm to convert the passphrase 'password' to SecretKeySpec
This is the algorithm in AESCrypt
private static SecretKeySpec GenerateKey (final String password) throws NoSuchAlgorithmException, UnsupportedEncodingException {
final MessageDigest digest = MessageDigest.getInstance (HASH_ALGORITHM);
byte [] bytes = password.getBytes ("UTF-8");
digest.update (bytes, 0, bytes.length);
byte [] key = digest.digest ();
log ("SHA-256 key" key);
SecretKeySpec secretKeySpec = new SecretKeySpec (key, "AES");
secretKeySpec return;
}
And this is the (Java) example aesencryption.net
sha = MessageDigest.getInstance ("SHA-1");
key = sha.digest (key);
key = Arrays.copyOf (key, 16); // Use only first 128 bit
SecretKey = new SecretKeySpec (key, "AES");
The first one applies SHA256 hashing, and the second SHA-1 after completing up to 16 bytes, so the key is different.
I think you are encrypting and decrypting AES in the right way. You do not need to change anything.
But if you want to be compatible with aesencryption.net, you need to implement the same key generation algorithm. The code is not too good. I try to summarize
//Code from aesencryption.net
// Generate key
MessageDigest sha = null;
key = myKey.getBytes ("UTF-8");
sha = MessageDigest.getInstance ("SHA-1");
key = sha.digest (key);
key = Arrays.copyOf (key, 16); // Use only first 128 bit
SecretKey = new SecretKeySpec (key, "AES");
public static String encrypt (String strToEncrypt) {
Cipher cipher = Cipher.getInstance ("AES / ECB / PKCS5Padding");
cipher.init (Cipher.ENCRYPT_MODE, SecretKey);
Base64.encodeBase64String return (cipher.doFinal (strToEncrypt.getBytes ("UTF-8"))));
}
public static String decrypt (String strToDecrypt) {
Cipher cipher = Cipher.getInstance ("AES / ECB / PKCS5PADDING");
cipher.init (Cipher.DECRYPT_MODE, SecretKey);
return new String (cipher.doFinal (Base64.decodeBase64 (strToDecrypt))));
}
I can also provide my own code extracted from an Android app witch requires to store private user data. Data is ciphered with an AES key protected with an user passphrase
public static String SIMMETRICAL_ALGORITHM = "AES";
//Generate cipher key with user provided password
private static String getPassphraseSize16(String key) {
if (TextUtils.isEmpty(key)) {
return null;
}
char controlChar = '\u0014';
String key16 = key + controlChar;
if (key16.length() < 16) {
while (key16.length() < 16) {
key16 += key + controlChar;
}
}
if (key16.length() > 16) {
key16 = key16.substring(key16.length() - 16, key16.length());
}
return key16;
}
//AES cipher with passphrase
public static byte[] encrypt(byte[] message, String passphrase)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
String passphrase16 = getPassphraseSize16(passphrase);
SecretKeySpec secretKey = new SecretKeySpec(passphrase16.getBytes(), SIMMETRICAL_ALGORITHM);
Cipher cipher = Cipher.getInstance(SIMMETRICAL_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encoded = cipher.doFinal(message);
return encoded;
}
//AES decipher with passphrase
public static byte[] decrypt(byte[] encodedMessage, String key) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
String passphrase16 = getPassphraseSize16(key);
SecretKeySpec secretKey = new SecretKeySpec(passphrase16.getBytes(), SIMMETRICAL_ALGORITHM);
Cipher cipher = Cipher.getInstance(SIMMETRICAL_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte decoded[] = cipher.doFinal(encodedMessage);
return decoded;
}
I am doing a simple program to encrypt/decrypt using RSA algorithm in Java. I create a cipher object as follows:
//Create a Cipher object
Cipher rsaCipher = Cipher.getInstance("RSA/ECB/NoPadding");
I do the encryption by calling the encrypt function:
String cipher=encrypt(textByte, pair, rsaCipher);
System.out.println("The Encryption using RSA Algorithm : "+cipher);
And the decryption as:
//Decryption
String plain=decrypt(Base64.decodeBase64(cipher),pair, rsaCipher);
System.out.println("The Decryption using RSA Algorithm : "+plain);
When I display the output, the decryption output returns a long space before the original text:
However, when I edit the code for creating the Cipher object to be:
//Create a Cipher object
Cipher rsaCipher = Cipher.getInstance("RSA");
i.e, removed the operation mode and padding arguments, the problem get resolved and the output becomes:
Where is the problem. In the first case (when the space appears), I specified NoPadding? Why the spaces appears in the decrypted message ? Even if I used padding, I expect this should not happen.
EDIT:
This is the encrypt and decrypt methods:
public static String encrypt(byte[] textBytes, KeyPair pair, Cipher rsaCipher) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException
{
//get the public key
PublicKey pk=pair.getPublic();
//Initialize the cipher for encryption. Use the public key.
rsaCipher.init(Cipher.ENCRYPT_MODE, pk);
//Perform the encryption using doFinal
byte[] encByte = rsaCipher.doFinal(textBytes);
// converts to base64 for easier display.
byte[] base64Cipher = Base64.encodeBase64(encByte);
return new String(base64Cipher);
}//end encrypt
public static String decrypt(byte[] cipherBytes, KeyPair pair, Cipher rsaCipher) throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException
{
//get the public key
PrivateKey pvk=pair.getPrivate();
//Create a Cipher object
//Cipher rsaCipher = Cipher.getInstance("RSA/ECB/NoPadding");
//Initialize the cipher for encryption. Use the public key.
rsaCipher.init(Cipher.DECRYPT_MODE, pvk);
//Perform the encryption using doFinal
byte[] decByte = rsaCipher.doFinal(cipherBytes);
return new String(decByte);
}//end decrypt
Your problem is indeed with the padding. Some kind of padding, either PKCS#1 1.5 or OAEP padding in practice, is required for secure RSA functionality. Furthermore, it is required to find the start and end of the encrypted plain text.
The modular exponentiation of RSA is performed using large integers. The results of these operations are then represented as octet strings. These octet strings are basically big endian, unsigned, fixed length representation of an integer. These integers are left padded with 00 valued bytes (this is called the I2OS primitive in the RSA standard). So what you are seeing is the result of the modular exponentiation, with the 00 padding still in place.
Long story short, always use a padding scheme. Nowadays, OAEP would be preferable. Use it together with hybrid encryption scheme, or use a higher level container format such as CMS or PGP.
//This is a complete encryption and decryption module using
//Algorithm: JWEAlgorithm.RSA_OAEP_256
//Encryption Method: A128CBC_HS256
public static String encrypt(String text) throws Exception {
// Set the plain text
Payload payload = new Payload(text);
// Create the header
JWEHeader header = new JWEHeader(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A128CBC_HS256);
// Create the JWE object and encrypt it
JWEObject jweObject = new JWEObject(header, payload);
jweObject.encrypt(new RSAEncrypter(getPublicKey()));
// Serialise to compact JOSE form...
String jweString = jweObject.serialize();
LOG.info("Generated Encrypted Key : {}", jweString);
return jweString;
}
public static String decrypt(String text) throws Exception {
// Parse into JWE object...
JWEObject jweObject = JWEObject.parse(text);
jweObject.decrypt(new RSADecrypter(getPrivateKey()));
// Get the plain text
Payload payload = jweObject.getPayload();
System.out.println(payload.toString());
return payload.toString();
}
private static RSAPublicKey getPublicKey() throws Exception {
String filename = "/home/vaibhav/Setups/cert/pub.der";
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)f.length()];
dis.readFully(keyBytes);
dis.close();
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return (RSAPublicKey) kf.generatePublic(spec);
}
private static RSAPrivateKey getPrivateKey() throws Exception {
String filename = "/home/vaibhav/Setups/cert/private.pkcs8";
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)f.length()];
dis.readFully(keyBytes);
dis.close();
PKCS8EncodedKeySpec spec1 = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) kf.generatePrivate(spec1);
}