How to decrypt a data using rsa privatekey - java

I am using JAVA
My friend uses SYMBIAN
I and my friend have same rsa modulus. If I encrypt the data using public key then my friend is able to decrypt the same. But if my friend encrypt the data with public key then I am not able to decrypt the data. I got an error as "Data must start with zero "
public static byte[] encrypt(byte[] encrptdByte) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] encryptionByte = null;
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptionByte = cipher.doFinal(encrptdByte);
return encryptionByte;
}
public static void decrypt(byte[] encrptdByte) throws NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
byte[] encryptionByte = null;
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
encryptionByte = cipher.doFinal(encrptdByte);
System.out.println("Recovered String ::: " + new String(encryptionByte));
}
Thanks
Sunil

The decrypt function uses publicKey - where does it come from? Note that data encrypted with a public key must be decrypted with the corresponding private key and not with the same public key. Asymmetric encryption such as RSA have the notion of key pairs where each key in the pair can decrypt data encrypted with the other key, in contrast to symmetric encryption such as AES where the same key works for both encryption and decryption.

To add the previous post, it's impractical to encrypt / decrypt data on a large scale using assymetric encryption (because it's significantly slower than symmetric encryption). The most practical use of assymmetric encryption (like RSA) is to encrypt the symmetric keys (for AES or similar algorithm) that were used to encrypt the data and also to sign a secure hash of the message digest (SHA-256 etc).
The encrypted message is typically sealed in an "envelope" that contains the encrypted message as well as the keys used for encryption. The keys are of course encrypted with the recipients public key, thereby ensuring that only the holder the private key can retrieve the keys.
Finally, the sender of the message may optionally compute a secure hash of the message and encrypt it with the sender's private key. The recipient decrypts the encrypted hash (using the sender's public key) and compares with the computed hash to verify the identity of the sender.

Related

Decrypting CommonCrypto encrypted Base 64 encoded string in Java (AES/CBC/PKCS7Padding)

I am trying to decrypt a String with a known key in Java using standard Cipher API.
The encrypted String comes from a Web Service using the standard CommonCrypto Library which responds with some statistics as encrypted strings at regular intervals.
The specs are AES/CBC/PKCS7Padding with KeySize = 32 Bytes and BlockSize = 16 Bytes, and Encoding UTF-8 (raw) & Base64. I intend to write a Java client that can request these statistics, decrypt them and store them for later analyses.
Question 1. Does the CommonCrypto automatically pad keys with extra chars if the key is short? For instance less than 16 Bytes or 32 Bytes.
Question 2. What encoding measures should I take to ensure an identical encryption/decryption on both ends?
Example Strings and Key
String message = "mQp9sp8ri1E0V1Xfso1d5g==Mrf3wtaqUjASlZmUO+BI8MrWsrZSC0MxxMocswfYnqSn/VKB9luv6E8887eCxpLNNAOMB0YXv6OS7rFDFdlvC53pCHo3cVZiLJFqgWN/eNiC9p4RMxyFCcOzWrwKzT5P8sy55DwE25DNJkvMthSaxK5zcP1OdLgBiZFOSxYRsX4rBk7VP7p5xr2uTGjRL+jmGgB9u3TmeCNCr8NxGLNt6g==";
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);
MessageDigest sha = MessageDigest.getInstance("SHA-256"); //key
keyBytes = sha.digest(keyBytes);
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);
}
Also with the current code status I am getting placehoder characters instead of the desired result.
Thanks in advance folks. :)
CommonCrypto is unclear, which implementation are you using? Apple, Apache, Java Class Cipher or another, please supply a link to it.
Never assume an encryption will pad the key or IV, they should always be provided in the exact length, there is no standard for such padding. If they need padding (they shouldn't) do it yourself.
Typically if encrypted data needs to be expressed as a character string Base64 encoding is used.
As James states, for one-shot encryption just use doFinal(ByteBuffer input, ByteBuffer output) which
encrypts or decrypts data in a single-part operation.
Note: A 9 digit key only has about 33-bits of security which is not close to sufficient. Simple using a hash function is insufficient for deriving an encryption key from a password, instead PBKDF2 or Argon2 should be used.

Encrypt a SecretKey with RSA in Java

I'm working on a client-server secure protocol where I need to use RSA in Java to encrypt a SecretKey for HMAC digests because the key has to be sent to the server. The encryption has two stages; first, I need to encrypt the symmetric key with a public asymmetric key, then, that encrypted message is encrypted with a private asymmetric key.
For this purpose I generate the SecretKey as:
public SecretKey generate(){
KeyGenerator generator = KeyGenerator.getInstance("HMACSHA256");
k = generator.generateKey();
return k;
}
Later, I use this code to encrypt any byte array with a public key:
public byte[] encryptPublic(PublicKey key, byte[] array){
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encrypted = cipher.doFinal(array);
return encrypted;
}
The code for encryption with a private key is the same but using a private key.
For the RSA encryption I'm using 1024 bit long asymmetric keys so I have two main questions:
How can I turn my SecretKey to a byte array in order to encrypt it with RSA and a public key?
As the public key encryption produces a byte array with 128 bytes, how can I encrypt that message again with a private key if the key is 1024 bits long and can only encrypt a 117 byte long message?
How can I turn my SecretKey to a byte array in order to encrypt it with RSA and a public key?
That's called wrapping:
public static byte[] wrapKey(PublicKey pubKey, SecretKey symKey)
throws InvalidKeyException, IllegalBlockSizeException {
try {
final Cipher cipher = Cipher
.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
cipher.init(Cipher.WRAP_MODE, pubKey);
final byte[] wrapped = cipher.wrap(symKey);
return wrapped;
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new IllegalStateException(
"Java runtime does not support RSA/ECB/OAEPWithSHA1AndMGF1Padding",
e);
}
}
Note that this explicitly doesn't convert to byte[] first. That's because the key might well be within e.g. a hardware security module. In a HSM the wrapping may be possible, but the conversion to byte[] in local memory would usually not be possible.
As the public key encryption produces a byte array with 128 bytes, how can I encrypt that message again with a private key if the key is 1024 bits long and can only encrypt a 117 byte long message?
You shouldn't do this and you cannot do this either. The reason that you shouldn't do it because encryption with the private key does not provide confidentiality, as anybody would have access to the public key.
Padding is required to perform secure RSA encryption. The padding overhead (of 11 bytes for PKCS#1 v1.5 style padding) is there prohibiting you to encrypt with the private key.
Note that the entire operation: encryption with a private key isn't even specified in PKCS#1 - it's not a legit operation.
Usually the much more secure ephemeral-ephemeral (EC)DH is used to establish keys in transport protocols, using the private key(s) only for authentication. You may want to take a hint from the (draft versions of) TLS 1.3. Or you may just want to use TLS or the handshake portion of it.

c# RSA encrypt with private key

Encryption and Decryption successful when encrypt with public key and decrypt with private key :
C# encryption with public key(Successful)
public string EncryptData(string data) {
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(xml); //public key
var cipher = rsa.Encrypt(Encoding.UTF8.GetBytes(data), false);
return Convert.ToBase64String(cipher );
}
Java decryption with private key(Successful)
public static void decrypt() throws Exception{
byte[] modulusBytes = Base64.getDecoder().decode(mod);
byte[] dByte = Base64.getDecoder().decode(d);
BigInteger modulus = new BigInteger(1, (modulusBytes));
BigInteger exponent = new BigInteger(1, (dByte));
RSAPrivateKeySpec rsaPrivKey = new RSAPrivateKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey privKey = fact.generatePrivate(rsaPrivKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privKey);
byte[] cipherData = Base64.getDecoder().decode(cipherByte);
byte[] plainBytes = cipher.doFinal(cipherData);
System.out.println(new String(plainBytes));
}
Problem is Here
When c# encrypt with private key and java decrypt with public key bad padding error occur:
C# encryption with private key(Fail)
public stringEncryptData(string data) {
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(xml); //private key
var cypher = rsa.Encrypt(Encoding.UTF8.GetBytes(data), false);
return Convert.ToBase64String(cypher);
}
java decryption with public key (Fail)
public static void decryptPublic() throws Exception{
byte[] modulusBytes = Base64.getDecoder().decode(mod);
byte[] expBytes = Base64.getDecoder().decode(exp);
BigInteger modulus = new BigInteger(1, (modulusBytes));
BigInteger exponent = new BigInteger(1, (expBytes));
RSAPublicKeySpec pubKey = new RSAPublicKeySpec(modulus, exponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PublicKey publicKey = fact.generatePublic(pubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, publicKey );
byte[] cipherData = Base64.getDecoder().decode(cipherByte);
byte[] plainBytes = cipher.doFinal(cipherData);
System.out.println(new String(plainBytes));
}
I understand public key should use to do encryption and private key for decryption.But in my situation, i need to sent out public key to mutiple clients for decryption on a text encrypted by its private key. Text should be non readable by others except client.
Can anyone see what problem on my code, or suggest a better solution to my problem.
RSA encryption is only secure if a (secure) padding scheme is being used. RSA encryption schemes have been specified in PKCS#1 standards by RSA laboratories (now part of EMC2). These have been copied into RFC, such as RFC 3447: Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.1.
For the purposes of this document, an encryption scheme consists of
an encryption operation and a decryption operation, where the
encryption operation produces a ciphertext from a message with a
recipient's RSA public key, and the decryption operation recovers the
message from the ciphertext with the recipient's corresponding RSA
private key.
So encryption with a private key is an undefined operation.
So what to do now:
securely distribute private keys instead of public keys
generate key pairs and securely transport the public key to the sender
if you require authentication/integrity instead of confidentiality, use signature generation instead of encryption
And, whatever you do, read into Public Key Infrastructure (PKI). It's a far stretching subject that you need to understand before you can apply it.
Encrypting with the private key/decrypting with the public key is a legitimate operation in RSA, however it is not used to protect data, it is instead used to authenticate the source of the data and its integrity. In this context the encryption operation is more usually called "signing".
Encrypting using the private key to protect data as you describe is insecure and so the fact that it is not easily done is likely intentional and intended to prevent incorrect use of the algorithm.
Distributing your private key to clients as suggested in the comments is also unwise since you have no control over who they may pass the key onto (accidentally or otherwise).
If you wish to encrypt data so that it can be decrypted by multiple distinct parties, then you should have each of them provide you with their own public key, and use that to encrypt the data separately for each client.

Is this a safe way to use java to encrypt (using public private key) messages that might be sent across insecure networks

I am not an expert on this so I need to defer to an expert. Is the following example a reasonably secure way to encrypt and decrypt a message (of an unknown length) for transmission a potentially insecure network (i.e. Email, HTTP requests, or other means). By "reasonably secure" I mean, would prevent a casual, or semi determined third party from reading the message.
Encrypt a message with a random AES key, and protect AES key by encrypting it with a public key.
public static String encrypt(String data, PublicKey publicKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
// Create AES secret key
Cipher aes = Cipher.getInstance("AES");
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(256);
SecretKey key = kgen.generateKey();
SecretKeySpec aeskeySpec = new SecretKeySpec(key.getEncoded(), "AES");
// Encrypt data with AES Secret key
aes.init(Cipher.ENCRYPT_MODE, aeskeySpec);
byte[] dataEncoded = aes.doFinal(data.getBytes());
// Encrypt the secret AES key with the public key
Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] aesKeyEncoded = rsa.doFinal(key.getEncoded());
// Output both secret AES key and data
return
Base64.getEncoder().encodeToString(aesKeyEncoded) + "~" +
Base64.getEncoder().encodeToString(dataEncoded);
}
Decrypt the AES secret key, and then decrypt the message:
public static String decrypt(String data, PrivateKey privateKey) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException {
String[] parts = data.split("~");
// Decrypt AES secret key
byte[] encodedSecretKey = Base64.getDecoder().decode(parts[0]);
Cipher rsa = Cipher.getInstance("RSA");
rsa.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decodedSecretKey = rsa.doFinal(encodedSecretKey);
SecretKeySpec key = new SecretKeySpec(decodedSecretKey, "AES");
// Decrypt message
Cipher aes = Cipher.getInstance("AES");
aes.init(Cipher.DECRYPT_MODE, key);
byte[] decodedData = aes.doFinal(Base64.getDecoder().decode(parts[1]));
return new String(decodedData);
}
Using the above methods:
public static void main(String args[]) throws NoSuchAlgorithmException, BadPaddingException, NoSuchPaddingException, IllegalBlockSizeException, InvalidKeyException {
// Generate public/private key
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048, new SecureRandom());
KeyPair kp = generator.generateKeyPair();
System.out.println(" Public key = " + Base64.getEncoder().encodeToString(kp.getPublic().getEncoded()));
System.out.println("Private key = " + Base64.getEncoder().encodeToString(kp.getPrivate().getEncoded()));
String mytext = "test message with some test data.";
String e = encrypt(mytext, kp.getPublic());
String d = decrypt(e, kp.getPrivate());
System.out.println(" text = " + mytext);
System.out.println("Decoded text = " + d);
}
As long as you can trust the RSA public key the general idea is OK. If you just send the public key to the other side, then it is not.
You also need to protect your ciphertext by adding integrity and authenticity. You can easily do this by switching to AES / GCM mode (which is only available in Java 8, or using Bouncy Castle). Currently you are using the unsafe AES / ECB mode of encryption.
You should try and use RSA with OAEP padding, instead of PKCS#1 v1.5 padding. In general, you should not rely on default character encodings (getBytes()) or cipher modes.
So in the end: no, that's not secure. Try and use TLS is you want to avoid the many pitfalls.

Exchange crypto key through sockets

What i am making is a chat program that encrypts/decrypts messages send. So far i've established the basic all-to-all communication with clients and have put a server-to-client simple encryption with AES, basically i have given them the same key. Now i want to establish a key exchange algorithm that will allow every client to have its own key. I have read multiple algorithms, at first i wanted to establish a Diffie-Hellman but i found some RSA examples that made more sence. This is what i have:
public static void main(String[] args) throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128);
Key aesKey = keyGenerator.generateKey();
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
byte[] aesKeyBytes = aesKey.getEncoded();
System.out.println("1. aesKeyBytes= "+ bytesToHex(aesKeyBytes));
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] cipherText = cipher.doFinal(aesKeyBytes);
System.out.println("2. cipherText= "+bytesToHex(cipherText));
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
byte[] decryptedKeyBytes = cipher.doFinal(cipherText);
System.out.println("3. decryptedKeyBytes= "+bytesToHex(decryptedKeyBytes));
//use symmetric with the decrypted key
SecretKey newAesKey = new SecretKeySpec(decryptedKeyBytes, "AES");
Basically from the point this code ends i just use the SecretKey to initialize my AES cipher and go on from there. Now my question is how to distribute the keys from the RSA through sockets without losing bytes or whatever so that all clients can have unique keypairs with the server. Also is there a way to not use a keygenerator and give my own keys like Strings, since i plan on using setters so that the user can change his/her key on whish, otherwise what's the point in using keyexchange if all the clients end up having the same keys in the first place? And one last thing purely on curiosity, is it possible to modify this RSA to DH, and how are they different code-wise?
Lastly the "bytesToHex" method is as follows and was provided to me by my teacher and after tested it works fine so there are no problems there:
public static String bytesToHex(byte[] data)
{
if (data==null)
return null;
else
{
int len = data.length;
String str = "";
for (int i=0; i<len; i++)
{
if ((data[i]&0xFF)<16){
str = str + "0" + java.lang.Integer.toHexString(data[i]&0xFF);
}
else{
str = str + java.lang.Integer.toHexString(data[i]&0xFF);
}
}
return str.toUpperCase();
}
}
I know there might be answers like "look at this" and "this is a good example", but trust me i've looked at all of them i just got more confused. Just to be clear i don't want to use Files to store my keys or anything like that, i want simply the client and server to send each other their public keys so that along with their private key to create a secretKey, i know that this is basically DH but honestly i am so fed up right now that i'll take what i can get so anything you have to say is much appreciated.

Categories

Resources