i already wandered in many stackoverflows post but still cant figured out whats wrong with my code.
public class KripAsim {
String hasil;
public String encrypt(String text) {
try {
String PUBLIC_KEY="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvCNqSslgCndo8vfNrkXFDLXmst024Oi8D7LEiJFjYBva4kBKKISe8rKp58kCLLHjv90RN+Dy2KWcf0eFkKaqc3zILBI99JhV1z8TFOzmt5dfgW6fD1ucBfsK6pWxK84DddyOqKldwHlReqjuDHT2jLue51vpXaCa12WV5bMnGsfy3vZKnp699YCguqRpTR1MijZ9pz8WqldrR0a/DCaq5YxZ7lvjwuWIodQy3S3XRHAaeaUrFHFFLumzXAGuP447oRYR0p+1qsy8+wOtrsGm8m8bMg+C1XGMblkODtOFHz3wtrRZ5OwzgEm7J7odmSX8mSYBZYLcnUVqIFRsQkZLiwIDAQAB";
byte [] decoded = Base64.decode(PUBLIC_KEY,Base64.NO_WRAP);
KeyFactory keyFac = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
PublicKey pubkey = keyFac.generatePublic(keySpec);
Cipher rsa;
rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, pubkey);
hasil = Base64.encodeToString(rsa.doFinal(text.getBytes("UTF-8")),Base64.NO_WRAP);
return hasil;
} catch (Exception e) {
e.printStackTrace();
}
return hasil;
}
}
it returns null. please help. thanks in advance
The line PublicKey pubkey = keyFac.generatePublic(keySpec); throws an InvalidKeySpecException because you have incorrectly supplied a PKCS8EncodedKeySpec when you should have supplied an X509EncodedKeySpec.
You should also completely specify the transformation string in the Cipher.getInstance() method. The following fragment taken from your code illustrates this:
byte[] decoded = Base64.decode(PUBLIC_KEY, Base64.DEFAULT);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decoded);
PublicKey pubkey = KeyFactory.getInstance("RSA").generatePublic(keySpec);
Cipher rsa = Cipher.getInstance("RSA/ECB/OAEPwithSHA-256andMGF1Padding");
rsa.init(Cipher.ENCRYPT_MODE, pubkey);
String hasil = Base64.encodeToString(rsa.doFinal(text.getBytes("UTF-8")),
Base64.NO_WRAP);
return hasil;
Finally, you should avoid encrypting data directly with RSA. The correct method is to use a hybrid encryption scheme in which the data is encrypted with a block cipher, say AES in GCM mode, using a randomly generated AES key, and then this key is encrypted using RSA. There are many examples of this around for reference.
Related
I have been using the following two methods to encrypt and decrypt sensitive info.
public static String encryptSensitiveInfo(String strToEncrypt,
String saltToEncrypt) throws Exception {
String encryptedString = "";
byte[] encryptedValue;
Key key = new SecretKeySpec(saltToEncrypt.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
encryptedValue = cipher.doFinal(strToEncrypt.getBytes());
encryptedString = new String(Base64.encodeBase64(encryptedValue));
encryptedValue = null;
return encryptedString;
}
public static String decryptSensitiveInfo(String strToDecrypt,
String saltToDecrypt) throws Exception {
String decryptedString = "";
byte[] decryptedValue;
Key key = new SecretKeySpec(saltToDecrypt.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, key);
decryptedValue = cipher.doFinal(Base64.decodeBase64(strToDecrypt
.getBytes()));
decryptedString = new String(decryptedValue);
decryptedValue = null;
return decryptedString;
}
At the time of decryption I get "pad block corrupted" execption. Any help to resolve this issue would be very much appreciated. Thanks in advance.
You're correctly performing base 64 on the ciphertext because the ciphertext consists of random looking bytes. However, you forget to do the same with your key (which, inexplicably, is called saltToDecrypt in your code). If the keys do not match or if the ciphertext has become corrupted then you will almost certainly run into a BadPaddingException.
If the amount of ciphertext has changed an IllegalBlockSizeException is more likely and if the key isn't of the right size for AES, an InvalidKeyException.
The following method does not work. decodedMessage ends up with garbage in it instead of the expected results.
I'm following an example here that supposedly works.
public static void POCSimple()
{
String secretMessage = "short message";
PublicKey publicKey = null;
PrivateKey privateKey = null;
String encodedMessage = "";
byte[] encodedBytes = null;
String decodedMessage ="";
byte[] decodedBytes = null;
try
{
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
KeyPair kp = kpg.genKeyPair();
publicKey = kp.getPublic();
privateKey = kp.getPrivate();
Cipher c1 = Cipher.getInstance("RSA");
c1.init(Cipher.ENCRYPT_MODE, publicKey);
encodedBytes = c1.doFinal(secretMessage.getBytes());
encodedMessage = Base64.encodeToString(encodedBytes, Base64.DEFAULT);
Cipher c2 = Cipher.getInstance("RSA");
c2.init(Cipher.DECRYPT_MODE, privateKey);
decodedBytes = c2.doFinal(encodedBytes);
decodedMessage = Base64.encodeToString(decodedBytes, Base64.DEFAULT);
String mystring = "look at results";
}
catch (Exception e)
{
String status = e.toString();
}
}
Any help would be most appreciated.
Thanks,
Dean
Of course you are getting garbage, this is your sequence:
Plaintext message -> Encrypt -> Encode -> Encoded message
Encoded message -> Decrypt -> Decode -> GARBAGE
You need to undo the Base64 encoding before you can decrypt the message, you are doing the reverse process in the incorrect order!
Edit
Actually its worse, this is your sequence:
Plaintext message -> Encrypt -> Encode -> Encoded message
Encrypted message -> Decrypt -> Encode -> GARBAGE
Try this:
Cipher c1 = Cipher.getInstance("RSA");
c1.init(Cipher.ENCRYPT_MODE, publicKey);
encodedBytes = c1.doFinal(secretMessage.getBytes());
encodedMessage = Base64.encodeToString(encodedBytes, Base64.DEFAULT);
Cipher c2 = Cipher.getInstance("RSA");
c2.init(Cipher.DECRYPT_MODE, privateKey)
decodedBytes = Base64.decode(encodedMessage.toByteArray(), Base64.DEFAULT);
decryptedMessage = c2.doFinal(decodedBytes);
It turns out that in my original code, decodedBytes contained the properly decrypted bytes. The following command was turning decodedBytes into junk characters ...
decodedMessage = Base64.encodeToString(decodedBytes, Base64.DEFAULT);
I replaced that code with ...
String str = new String(decodedBytes, "UTF-8");
And this solved the problem probably because decodedBytes had never been Base64 encoded in the first place.
I also found that using straight RSA I can only encrypt a maximum of 245 bytes if I use a 2048 bit key. less if I use a 1024 bit key.
If larger strings need to be encrypted using asymmetric Public/Private keys then I need to first encrypt a string using symmetric AES and then encrypt the AES key with the public RSA key and send both the encrypted AES key and the encrypted message over the wire where the receiver can decrypt the AES key using their private RSA key. The AES key can be randomly generated in the sending code.
sorry to ask a particular question, but I need to generate in java code a 'signature' like the following code line in ruby:
signature = OpenSSL::PKey::RSA.new(File.read("PUBLIC_PEM_PATH")).public_encrypt('SECRET_KEY')
I have the .pem key file and the SECRET_KEY that's is something like: F6qxlwQTYWRM3gRfgftryKJHKYZiGXdoy5lDm4
How can I do this ?
Thanks!
UPDATE 1
I tried this :
File pubKeyFile = new File(keyFileName);
DataInputStream inputStream;
byte[] signature = null;
try {
inputStream = new DataInputStream(new FileInputStream(pubKeyFile));
byte[] pubKeyBytes = new byte[(int)pubKeyFile.length()];
inputStream.readFully(pubKeyBytes);
inputStream.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKeyBytes);
RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(pubSpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
signature = cipher.doFinal(secretKey.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
return signature;
And got this error:
java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag
UPDATE2
I managed to load a public key from a .pem file. But now, Im getting an error from the cipher.
public static byte[] getSignature(String keyFileName, byte[] secretKey){
byte[] signature = null;
try {
PublicKey pubKey = readKeyFromFile(keyFileName);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
signature = cipher.doFinal(secretKey);
} catch (Exception e) {
e.printStackTrace();
}
return signature;
}
private static PublicKey readKeyFromFile(String keyFileName) throws IOException {
InputStream in = new FileInputStream(keyFileName);
DataInputStream din = new DataInputStream(in);
try {
BigInteger m = BigInteger.valueOf(din.readLong());
BigInteger e = BigInteger.valueOf(din.readLong());
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(m, e);
KeyFactory fact = KeyFactory.getInstance("RSA");
RSAPublicKey pubKey = (RSAPublicKey) fact.generatePublic(keySpec);
return pubKey;
} catch (Exception e) {
throw new RuntimeException("Spurious serialisation error", e);
} finally {
din.close();
}
}
The error log:
javax.crypto.IllegalBlockSizeException: input must be under 8 bytes
Any thoughts??
So finally, after hours of research I came with a solution and finally could get read my public key from .pem file and generate an instance of this key. Hence, I managed to encrypt the data.
But I had to copy and paste the key content without any special character like '\n' and make a publicKeyString with it
----------BEGIN RSA PUBLIC KEY---------
key content
----------END RSA PUBLIC KEY---------
static public PublicKey publicKey(String publicKeyString) {
try {
byte[] decodedPublicKey = Base64.decode(publicKeyString, 0);
ASN1InputStream in = new ASN1InputStream(decodedPublicKey);
ASN1Primitive obj = in.readObject();
RSAPublicKeyStructure keyStruct = RSAPublicKeyStructure.getInstance(obj);
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(keyStruct.getModulus(), keyStruct.getPublicExponent());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
I failed even when I tried to use PEMReader from bouncy castle. All the problems have something to do with keys generated in Ruby version prior 1.9.3 as described with details here.
Anyway, thanks a lot for the attention disposed.
Looks like key wrapping (encryption) to me. Use bouncy castle to read the PEM file, then use Cipher.getInstance("RSA/ECB/PKCS1Padding") to encrypt the secret key, and base 64 encode the result... You could also try the mode Cipher.WRAP_MODE to see if that works. Note that encryption will always return a different result, the only way to test for compatibility is to decrypt it with the other software.
I encrypt data in Android application and encode it to send over to php webservice.
Then I decode it and decrypt it using $rsa->decrypt(base64_decode($data));
$rsa->decrypt return false. Why is that so?
This is the code in android
//get private key
String privateKeyString = "MIICWwIBAAKBgQDVIJ8H3Oszc5fWdgpwymWksF1WxkXJHIfdS6Ta1bHeqwEzPIkN f3iVk14LfaoSZpRb9Yvi/jvkXxIzJbHq6aKfnQOC6tKIiixvVvpCfxr1eV4urDdz H9RNy9bqGdXzTQdgQi+KRx0Dcy9RNsl7ZGLAGrUFRnPI4GTdH+7wm4QogQIDAQAB AoGAcUcKX7KC7HDm5h0NRY+94H/AzItLsi3Q5MT81Tc5d+EqHSJysdLrs4yFMtRS 3b7Z4dqrxDVefe4uDTNe0j3lqboKz8oKwAU+paKx3wubHb7aeQnfzwM9mPQJHgEO zBjlvbL4oEa/gklu3VohZAc1daqpPajdWuOQQp4S+jUllrECQQDrITlSjvkxt8ud /vYIcEXHew3iW4nzaAH3z4PRAGZofRpk/OusGZ6cdZoYMTZcdxYTCCbZ5eeyGukW 5QCadie1AkEA6Atx8Z0F7WhLI2lGvCGy+vIOL0vBDZSma0cvLYLAXMx8duoWQ9J2 LwT7SsnRXMeq/8wlNHL7mFEf+YFZBKKlHQJAO78kfrr/zUdjwREBWaGVyZuWKpeS FTyvi1W6rAgK/bAUXeb6x69241DqyAzxQEuuW0WuAZ5u4o39/qhQH++4JQJAAepe RW1TaDNNM3yh/dmVXabz4QYSEOeiPA55YDnNFrcFbAHgryyklxzGakaiOM7ZJYVs 5TLxyr8YsXmU34nsLQJALzC8CaFXJcnU0+6+KoKX7iq1aP3X4LgP4Gianix6pfRo aV8UHnfFLRSgPdn1ZYmKtJfnsJXJYoE+o9xEErb5EQ==";
// converts the String to a PublicKey instance
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey key = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(Base64.decode(privateKeyString.toString(), Base64.DEFAULT)));
// encrypts the message
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrpytedText = cipher.doFinal(Base64.encode(phoneUid.getBytes("CP1252"), Base64.DEFAULT));
data = new String(encrpytedText, "CP1252");
This is the code in php
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
//Set the encryption mode
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
//check for required fields
if (isset($_POST['data']) {
$data= $_POST['data'];
$key = '-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVIJ8H3Oszc5fWdgpwymWksF1W xkXJHIfdS6Ta1bHeqwEzPIkNf3iVk14LfaoSZpRb9Yvi/jvkXxIzJbHq6aKfnQOC 6tKIiixvVvpCfxr1eV4urDdzH9RNy9bqGdXzTQdgQi+KRx0Dcy9RNsl7ZGLAGrUF RnPI4GTdH+7wm4QogQIDAQAB
-----END PUBLIC KEY-----';
$rsa->loadKey($key );
$decrypted = $rsa->decrypt(base64_decode($data));
I solved my problem. Here is a reference for those who are doing similar thing to me. Hope that this will help other people.
In android (RSA encrypt with private key)
String encoded = null;
byte[] encrypted = null;
String plaintext = "...";
try {
String privKeyPEM = "...";
byte[] decoded = Base64.decode(privKeyPEM, Base64.DEFAULT);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privKey = kf.generatePrivate(spec);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, privKey);
encrypted = cipher.doFinal(plaintext.getBytes());
encoded = Base64.encodeToString(encrypted, Base64.DEFAULT);
}
catch (Exception e) {
e.printStackTrace();
}
In PHP (decrypt with public key) (using phpseclib)
$publickey = '...';
$rsa = new Crypt_RSA();
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
//decode and decrypt
$decoded = base64_decode($encrypted);
$rsa->loadKey($publickey);
$decrypted = $rsa->decrypt($decoded);
Are you passing the right parameters to cipher.doFinal?
Quoting the javadoc entry:
doFinal(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset)
Also, it looks like you're base64-encoding the plaintext before encryption when you should be encrypting the ciphertext after encryption?
I have a RSA public key in String format and I want to encrypt a string using the same to Base 64. Here is the code that I am using for this:
function encrypt(String pin){
String key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiOnM5t6w2ZD6dpA4/MzSTAOt0IYpnsmGSAIfIVgGntI+fI4wbvUvMIhaLN3fHrjyuNGFdYw+yuoXYkapajt6VTZJniaatSiq6bwQ7R0UAop6haFSAwjnmReqexJvcKyqUsTfcfFypPpsYRewh/48/jmc/6ND+ugxDd52prkPUrbj+nnO0z3DBoUCpgDMRvW2hWXv6kZ654gp+wIAQnxbdwRMy6FZbrHjkA3tc6U0CHK+KjxAfzWAK+yI+ofskM4qk50J7y9hUZ7lLikqWZWKiqh8xiDk1kgu+FIjVh+fylKpa3gWmPPn0fSpBJjuenc1OQVmZ718a3388DjzFlYOLwIDAQAB";
byte[] sigBytes2 = Base64.decode(key, Base64.DEFAULT);
Log.d("WS", "new key is: " + key);
try {
PublicKey publicKey = KeyFactory.getInstance("RSA")
.generatePublic(new X509EncodedKeySpec(sigBytes2));
encryptedBase64PIN = encode(publicKey, pin);
Log.d("WSA", "encoded key is: " + encryptedBase64PIN);
// getSecToken();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String encode(Key publicKey, String data)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] byteData = data.getBytes(); // convert string to byte array
Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedByteData = cipher.doFinal(byteData);
String s = Base64.encodeToString(encryptedByteData, Base64.NO_WRAP);
return s; // convert encrypted byte array to string and return it
}
When I send the same output to the server, it says "cannot decrypt the PIN". Why is this?
When I send the same output to the server, it says "cannot decrypt the PIN". Why is this?
There could be many reasons for this. If you have the code for the server decryption, please add it to your question.
Reasons may include:
Wrong encryption mode. You've selected RSA/ECB/NoPadding but perhaps the server is expecting something else?
Base64 encoding - is the server definitely expecting this?
The message is "cannot decrypt PIN". If the data you are encrypting is the PIN are you sure you should be encrypting ASCII bytes?