For over a week, I've been using this to implement a RSA secured communication with my server that is in python.
However, I can not for the life of me figure out the proper imports for all the jars.
I have included all the jars found on bouncy castles website and still no dice!
I read that they moved stuff around. If this code is old or broken, what other implementation of RSA with pkcs1 padding is put there?
EDIT:
pub key is in a file named key.pub. how do I read that file in to be used as the key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2B0wo+QJ6tCqeyTzhZ3
AtPLgAHEQ/fRYDcR0BkQ+lXEhD277P2fPZwla5AW6szqsjR1olkZEF7IuoI27Hxm
tQHJU0ROhrzstHgK42emz5Ya3BWcm+oq5pLDZnsNDnNlrPncaCT7gHQQJn3YjH8q
aibtB1WCoy7ZJ127QxoKoLfeonBDtt7Qw6P5iXE57IbQ63oLq1EaYUfg8ZpADvJF
b2H3MASJSSDrSDgrtCcKAUYuu3cZw16XShuKCNb5QLsj3tR0QC++7qjM3VcG311K
7gHVjB6zybw+5vX2UWTgZuL6WVtCvRK+WY7nhL3cc5fmXZhkW1Jbx6wLPK3K/JcR
NQIDAQAB
-----END PUBLIC KEY-----
EDIT 2: Adding Broken Code based on an answer
package Main;
import org.bouncycastle.util.encoders.Base64;
import javax.crypto.Cipher;
import javax.xml.bind.DatatypeConverter;
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Security;
import java.security.spec.X509EncodedKeySpec;
public class EncDecRSA {
public static byte[] pemToDer(String pemKey) throws GeneralSecurityException {
String[] parts = pemKey.split("-----");
return DatatypeConverter.parseBase64Binary(parts[parts.length / 2]);
}
public static PublicKey derToPublicKey(byte[] asn1key) throws GeneralSecurityException {
X509EncodedKeySpec spec = new X509EncodedKeySpec(asn1key);
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
return keyFactory.generatePublic(spec);
}
public static byte[] encrypt(PublicKey publicKey, String text) throws GeneralSecurityException {
Cipher rsa = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "BC");//PKCS1-OAEP
rsa.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] cipher = rsa.doFinal(text.getBytes());
String s = new String(cipher);
System.out.print(s);
// return cipher;
// return Base64.encode(rsa.doFinal(text.getBytes()));
cipher = Base64.encode(cipher);
return cipher;
}
static String readFile(String path)
throws IOException
{
String line = null;
BufferedReader br = new BufferedReader(new FileReader(path));
try {
StringBuilder sb = new StringBuilder();
line = br.readLine();
while (line != null) {
sb.append(line);
sb.append("\n");
line = br.readLine();
}
return sb.toString();
} finally {
br.close();
}
}
public static void main(String[] args) throws IOException, GeneralSecurityException {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
System.out.println("Working Directory = " +
System.getProperty("user.dir"));
String publicKey = readFile("key.public");
byte[] pem = pemToDer(publicKey);
PublicKey myKey = derToPublicKey(pem);
String sendMessage = "{'vid_hash': '917ef7e7be4a84e279b74a257953307f1cff4a2e3d221e363ead528c6b556edb', 'state': 'ballot_response', 'userInfo': {'ssn': '700-33-6870', 'pin': '1234', 'vid': '265jMeges'}}";
byte[] encryptedDATA = encrypt(myKey, sendMessage);
Socket smtpSocket = null;
DataOutputStream os = null;
DataInputStream is = null;
try {
smtpSocket = new Socket("192.168.1.124", 9999);
os = new DataOutputStream(smtpSocket.getOutputStream());
is = new DataInputStream(smtpSocket.getInputStream());
} catch (UnknownHostException e) {
System.err.println("Don't know about host: hostname");
} catch (IOException e) {
System.err.println("Couldn't get I/O for the connection to: hostname");
}
if (smtpSocket != null && os != null && is != null) {
try {
System.out.println("sending message");
os.writeBytes(encryptedDATA+"\n");
os.close();
is.close();
smtpSocket.close();
} catch (UnknownHostException e) {
System.err.println("Trying to connect to unknown host: " + e);
} catch (IOException e) {
System.err.println("IOException: " + e);
}
}
}
}
Which has the following error:
Here is the bit of python code that does that:
def _decrypt_RSA(self, private_key_loc, package):
'''
param: public_key_loc Path to your private key
param: package String to be decrypted
return decrypted string
'''
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from base64 import b64decode
key = open('key.private', "r").read()
rsakey = RSA.importKey(key)
rsakey = PKCS1_OAEP.new(rsakey)
decrypted = rsakey.decrypt(package)
# decrypted = rsakey.decrypt(b64decode(package))
return decrypted
Lastly:
Is this padding scheme with PKCS1-OAEP.
That is a major requirement for this to work.
Note that I have tried it with both base64 encode and without
This example is for SpongyCastle - Android repackage of BouncyCastle. They differ as you noticed. However, making encryption in Java is as simple as this
Convert Base64 encoded key string to ASN.1 DER encoded byte array
public static byte[] pemToDer(String pemKey) throws GeneralSecurityException {
String[] parts = pemKey.split("-----");
return DatatypeConverter.parseBase64Binary(parts[parts.length / 2]);
}
Convert ASN.1 DER encoded public key to PublicKey object
public static PublicKey derToPublicKey(byte[] asn1key) throws GeneralSecurityException {
X509EncodedKeySpec spec = new X509EncodedKeySpec(asn1key);
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
return keyFactory.generatePublic(spec);
}
Encrypt data
public static byte[] encrypt(PublicKey publicKey, String text) throws GeneralSecurityException {
Cipher rsa = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding", "BC");
rsa.init(Cipher.ENCRYPT_MODE, publicKey);
return rsa.doFinal(text.getBytes());
}
bcprov-jdk15on-150.jar is the only jar needed.
Related
i'm trying to do the 7° challeng of cryptopals and i have to decrypt a file Base64 that was encrypted with the AES in ECB mode. So, i found this code that work with the tutoria stirngs, but when i try with the given data in exercise it give me back the error:
Error while decrypting: javax.crypto.BadPaddingException: Given final
block not properly padded. Such issues can arise if a bad key is used
during decryption.
Process finished with exit code 0
this is the code:
public class ES7 {
private static SecretKeySpec secretKey;
private static byte[] key;
public static void main(String[] args) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, IOException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
String file = readBase64("C:\\Users\\Huawei\\OneDrive\\Desktop\\Lavoro\\Esercizi\\src\\ES7.txt");
String res = decrypt(file,"YELLOW SUBMARINE"); --> this gave error of key
// final String secretKey = "ssshhhhhhhhhhh!!!!";
// String encryptedString= "Tg2Nn7wUZOQ6Xc+1lenkZTQ9ZDf9a2/RBRiqJBCIX6o=";
// String decryptedString = decrypt(encryptedString, secretKey) ; --> this work
}
public static void setKey(final String myKey) {
MessageDigest sha = null;
try {
key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, "AES");
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public static String decrypt(final String strToDecrypt, final String secret) {
try {
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
} catch (Exception e) {
System.out.println("Error while decrypting: " + e.toString());
}
return null;
}
private static String readBase64(String file) {
String filetext = "";
String line;
try
{
BufferedReader input = new BufferedReader(new FileReader(new File(file)));
while ((line = input.readLine()) != null)
{
filetext += line;
}
input.close();
} catch (IOException e){
e.printStackTrace();
}
return filetext;
}
}
I want to be able to encrypt and decrypt strings over a simple client and server using an echo model architecture with a specified port.
The way the client and server communicate is as follows:
The client is run first and generates a key pair, prints out its public key to the console, and waits for an input (which will be server's public key via copy and paste)
The server is then run and does the same also. Printing out its public key and waits for the client's public key (also copy and paste.)
Copy and paste (client public key) is done to the server first, which then starts the server listening on port 4444.
The client is then also started with copy and paste (server public key), sending a string over port 4444.
encrypted with the server’s public key.
The strings are to be encrypted by the client, sent, and received by the server who will decrypt the strings. An additional feature I am thinking to add should allow multiple strings to be encrypted and decrypted (done by a while loop).
My console for the client seems to be able to send the strings (converted to bytes, then sent) just fine by using out.write() and out.flush(), but has trouble reading the encrypted bytes using (while data is not empty... in.read().
The exception thrown is Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
My client code:
public class EchoClient {
private Socket clientSocket;
private DataOutputStream out;
private DataInputStream in;
private PrivateKey generateKeyPairClient() throws NoSuchAlgorithmException {
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
final KeyPair kp = kpg.generateKeyPair();
final PublicKey clientPublicKey = kp.getPublic();
System.out.println(clientPublicKey);
System.out.println("Client Public Key (Encoded) is " + Base64.getEncoder().encodeToString(clientPublicKey.getEncoded()));
return kp.getPrivate();
}
private PublicKey obtainServerPublicKey() {
System.out.println("Please enter the servers's public key below:");
Scanner sc = new Scanner(System.in);
String key = sc.next();
sc.close();
PublicKey serverPublicKey = null;
try{
byte[] byteKey = Base64.getDecoder().decode(key.getBytes());
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
serverPublicKey = kf.generatePublic(X509publicKey);
}
catch(Exception e){
System.out.println("The public key you entered for the server is not valid, please try again");
}
return serverPublicKey;
}
private void startConnection(String ip, int port) {
try {
clientSocket = new Socket(ip, port);
out = new DataOutputStream(clientSocket.getOutputStream());
in = new DataInputStream(clientSocket.getInputStream());
} catch (IOException e) {
System.out.println("Error when initializing connection");
}
}
private void sendPlainTextToEncryptedText(String original, PublicKey serverPublicKey, PrivateKey clientPrivateKey) {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, serverPublicKey);
byte[] originalBytes = original.getBytes(StandardCharsets.UTF_8);
byte[] cipherTextBytes = cipher.doFinal(originalBytes);
System.out.println("Client will send the ciphertext: "+ Util.bytesToHex(cipherTextBytes));
out.write(cipherTextBytes);
out.flush();
} catch (Exception e) {
System.out.println("There was a problem when trying to encrypt/decrypt, please try again");
e.printStackTrace();
return;
}
}
private void stopConnection() {
try {
in.close();
out.close();
clientSocket.close();
} catch (IOException e) {
System.out.println("error when closing");
}
}
public static void main(String[] args) throws NoSuchAlgorithmException {
EchoClient client = new EchoClient();
PrivateKey clientPrivateKey = client.generateKeyPairClient();
PublicKey serverPublicKey = client.obtainServerPublicKey();
System.out.println("Key Exchange Complete for Client");
client.startConnection("127.0.0.1", 4444);
client.sendPlainTextToEncryptedText("everyone", serverPublicKey, clientPrivateKey);
client.stopConnection();
}
}
My server code:
public class EchoServer {
private ServerSocket serverSocket;
private Socket clientSocket;
private DataOutputStream out;
private DataInputStream in;
private PrivateKey generateKeyPairServer() throws NoSuchAlgorithmException {
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
final KeyPair kp = kpg.generateKeyPair();
final PublicKey serverPublicKey = kp.getPublic();
System.out.println(serverPublicKey);
System.out.println("Server Public Key (Encoded) is " + Base64.getEncoder().encodeToString(serverPublicKey.getEncoded()));
return kp.getPrivate();
}
private PublicKey obtainClientPublicKey() {
System.out.println("Please enter the clients's public key below:");
Scanner sc = new Scanner(System.in);
String key = sc.next();
sc.close();
PublicKey clientPublicKey = null;
try{
byte[] byteKey = Base64.getDecoder().decode(key.getBytes());
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(byteKey);
KeyFactory kf = KeyFactory.getInstance("RSA");
clientPublicKey = kf.generatePublic(X509publicKey);
}
catch(Exception e){
System.out.println("The public key you entered for the client is not valid, please try again");
}
return clientPublicKey;
}
public void start(int port) {
try {
serverSocket = new ServerSocket(port);
clientSocket = serverSocket.accept();
out = new DataOutputStream(clientSocket.getOutputStream());
in = new DataInputStream(clientSocket.getInputStream());
} catch (IOException e) {
System.out.println("Error when establishing/accepting server connection");
}
}
private void receiveEncryptedTextToPlainText(PrivateKey serverPrivateKey) throws UnsupportedEncodingException, IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] data = new byte[8];
#SuppressWarnings("unused")
int numBytes;
while ((numBytes = in.read(data)) != -1) {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, serverPrivateKey);
byte[] decryptedBytes = cipher.doFinal(data);
String text = new String(decryptedBytes, StandardCharsets.UTF_8);
System.out.println("Server has recieved the plain text: "+ text);
}
stop();
}
public void stop() {
try {
in.close();
out.close();
clientSocket.close();
serverSocket.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, UnsupportedEncodingException, IOException, IllegalBlockSizeException, BadPaddingException {
EchoServer server = new EchoServer();
PrivateKey serverPrivateKey = server.generateKeyPairServer();
//Sorry for the mis-named var, will rename in code
PublicKey serverPublicKey = server.obtainClientPublicKey();
System.out.println("Key Exchange Complete for Server");
System.out.println("Now waiting for incoming connection...");
server.start(4444);
server.receiveEncryptedTextToPlainText(serverPrivateKey);
server.stop();
}
}
The Util assist class
import java.io.UnsupportedEncodingException;
public class Util {
public static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
return sb.toString();
}
public static String strToHex(String s) {
s = "failed decoding";
try {
s = Util.bytesToHex(s.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
System.out.println("Unsupported Encoding Exception");
}
return s;
}
}
The console outputs:
I'm not sure at to what might be wrong here, any advice?
I have noted two things which I think may be the cause:
Note: I am copying and pasting public keys (converting them to strings and back to public key objects - may be a problem...)
Note 2: The transfer of bytes over the socket in a size of 8 bytes might be problematic for longer strings
You must read all cipher-data before decrypt it. At EchoServer:
private void receiveEncryptedTextToPlainText(PrivateKey serverPrivateKey) throws UnsupportedEncodingException, IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] data = new byte[8];
ByteArrayOutputStream cipherData = new ByteArrayOutputStream();
#SuppressWarnings("unused")
int numBytes;
while ((numBytes = in.read(data)) != -1) {
cipherData.write(data, 0, numBytes);
}
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, serverPrivateKey);
byte[] decryptedBytes = cipher.doFinal(cipherData.toByteArray());
String text = new String(decryptedBytes, StandardCharsets.UTF_8);
System.out.println("Server has recieved the plain text: " + text);
stop();
}
Requirements:
Encrypt string using AES Encryption(with GCMParameterSpec and nonce).
Append nonce to Encrypted String to use it later for decryption.
Decrypt string by extracting nonce and the string to decrypt.
I have written the code to encrypt and decrypt the string as required but i am facing "Error while decrypting Tag mismatch!"
Could someone please help me identify the problem. Thanks!
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AES {
private static SecretKeySpec secretKey;
private static byte[] key;
private static final String ENCODING_TYPE = "UTF-8";
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION_VALUE = "AES/GCM/NoPadding";
public static void setKey(String myKey, String hashingType) {
MessageDigest sha = null;
try {
key = myKey.getBytes(ENCODING_TYPE);
sha = MessageDigest.getInstance(hashingType);
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, ALGORITHM);
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgorithmException "+e.getMessage());
} catch (UnsupportedEncodingException e) {
System.out.println("UnsupportedEncodingException "+e.getMessage());
} catch (Exception e) {
System.out.println("Exception Occured! "+e.getMessage());
}
}
public static String encrypt(String strToEncrypt, String secret, String hashingType) {
String encryptedString = null;
try {
byte[] nonce = new byte[12];
setKey(secret,hashingType);
SecureRandom random = SecureRandom.getInstanceStrong();
random.nextBytes(nonce);
final Cipher encryptCipher = Cipher.getInstance(TRANSFORMATION_VALUE);
encryptCipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(16 * 8, nonce));
byte[] cipheredString = encryptCipher.doFinal(strToEncrypt.getBytes());
ByteBuffer byteBuffer = ByteBuffer.allocate(nonce.length + cipheredString.length);
byteBuffer.put(nonce);
byteBuffer.put(cipheredString);
encryptedString = Base64.getEncoder().encodeToString(byteBuffer.array()); //Encoding the ByteArrayOutputStream result object to Base64 format
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgorithmException "+e.getMessage());
} catch (Exception e) {
System.out.println("Error while encrypting "+e.getMessage());
}
return encryptedString;
}
public static String decrypt(String strToDecrypt, String secret, String hashingType) {
String decryptedString = null;
try {
byte[] nonce = new byte[12];
setKey(secret,hashingType);
final Cipher decryptCipher = Cipher.getInstance(TRANSFORMATION_VALUE);
ByteBuffer byteBuffer = ByteBuffer.wrap(Base64.getDecoder().decode(strToDecrypt));
byteBuffer.get(nonce);
byte[] cipheredString = new byte[byteBuffer.remaining()];
byteBuffer.get(cipheredString);
decryptCipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(16 * 8, nonce));
decryptedString = new String(decryptCipher.doFinal(cipheredString));
} catch (Exception e) {
System.out.println("Error while decrypting "+e.getMessage());
}
return decryptedString;
}
}
public class TestAESEncrypt {
public static void main(String[] args) {
final String secretKey = "NEWTEST#Key123";
String originalString = "Gamer_123.aa01#gmail.com";
String encryptedString = AESEncryption.encrypt(originalString, secretKey, "SHA-256");
System.out.println("String to Encrypt : "+originalString);
System.out.println("Encrypted String : "+encryptedString);
}
}
public class TestAESDecryption {
public static void main(String[] args) {
final String secretKey = "NEWTEST#Key123";
String encryptedData = "ey4E+5zNPx4WLx5q0Srf7d1TN/sAzsdYuVmnihXQoA/yoYoxAO/ygrtuh+Zr9rZQ"; //Paste the encrypted string here
String decryptedString = AESEncryption.decrypt(encryptedData, secretKey, "SHA-256") ;
System.out.println("Encrypted String : "+encryptedData);
System.out.println("Decrypted String: "+decryptedString);
}
}
This will never work:
encryptedString = new String(cipher.doFinal(strToEncrypt.getBytes())); //Creating a ciphered string
encryptedString += new String(nonce); //Appending nonce to the encrypted string
encryptedString = new String(Base64.getEncoder().encode(encryptedString.getBytes())); //Encoding ciphered string to Base64 format
Once you attempt to convert the raw bytes to a string you have likely corrupted the data. Base64 encoding it afterward will not save you. Instead, you must do something like:
ByteArrayOutputStream result = new ByteArrayOutputStream();
result.write(nonce);
result.write(cipher.doFinal(strToEncrypt.getBytes()));
String encryptedString = Base64.getEncoder().encodeToString(result.toByteArray());
Also, I noticed your nonce is only 8 bytes. That's probably too short, you should make it 12 bytes.
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AES {
private static SecretKeySpec secretKey;
private static byte[] key;
private static final String ENCODING_TYPE = "UTF-8";
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION_VALUE = "AES/GCM/NoPadding";
public static void setKey(String myKey, String hashingType) {
MessageDigest sha = null;
try {
key = myKey.getBytes(ENCODING_TYPE);
sha = MessageDigest.getInstance(hashingType);
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, ALGORITHM);
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgorithmException "+e.getMessage());
} catch (UnsupportedEncodingException e) {
System.out.println("UnsupportedEncodingException "+e.getMessage());
} catch (Exception e) {
System.out.println("Exception Occured! "+e.getMessage());
}
}
public static String encrypt(String strToEncrypt, String secret, String hashingType) {
String encryptedString = null;
try {
byte[] nonce = new byte[12];
setKey(secret,hashingType);
SecureRandom random = SecureRandom.getInstanceStrong();
random.nextBytes(nonce);
final Cipher encryptCipher = Cipher.getInstance(TRANSFORMATION_VALUE);
encryptCipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(16 * 8, nonce));
byte[] cipheredString = encryptCipher.doFinal(strToEncrypt.getBytes());
ByteBuffer byteBuffer = ByteBuffer.allocate(nonce.length + cipheredString.length);
byteBuffer.put(nonce);
byteBuffer.put(cipheredString);
encryptedString = Base64.getEncoder().encodeToString(byteBuffer.array()); //Encoding the ByteArrayOutputStream result object to Base64 format
} catch (NoSuchAlgorithmException e) {
System.out.println("NoSuchAlgorithmException "+e.getMessage());
} catch (Exception e) {
System.out.println("Error while encrypting "+e.getMessage());
}
return encryptedString;
}
public static String decrypt(String strToDecrypt, String secret, String hashingType) {
String decryptedString = null;
try {
byte[] nonce = new byte[12];
setKey(secret,hashingType);
final Cipher decryptCipher = Cipher.getInstance(TRANSFORMATION_VALUE);
ByteBuffer byteBuffer = ByteBuffer.wrap(Base64.getDecoder().decode(strToDecrypt));
byteBuffer.get(nonce);
byte[] cipheredString = new byte[byteBuffer.remaining()];
byteBuffer.get(cipheredString);
decryptCipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(16 * 8, nonce));
decryptedString = new String(decryptCipher.doFinal(cipheredString));
} catch (Exception e) {
System.out.println("Error while decrypting "+e.getMessage());
}
return decryptedString;
}
}
So I have generated a key pair in keytool and generated a symmetric key and the encrypted a String with the symmetric key and then encrypted the symmetric key. Now I have to decrypt the symmetric key and I am having some trouble. The code I am using for decryption is not throwing back any errors but it is not actually doing anything either and I am not sure what I am doing wrong.
package ReadFileExample;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.security.Key;
import java.security.KeyException;
import java.security.KeyPair;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.security.cert.Certificate;
import java.security.KeyStore;
import java.security.Key;
import java.io.FileInputStream;
public class generatekey {
static Cipher cipher;
public static void main(String[] args) throws Exception {
// generating a symmetric key using the AES algorithm
KeyGenerator generator = KeyGenerator.getInstance("AES");
// 128 bit key
generator.init(128);
//generates a secret key
SecretKey secretkey = generator.generateKey();
// returns an AES cipher
cipher = Cipher.getInstance("AES");
//print key
System.out.println("Key: " + cipher);
String plainText = "Hello World";
// call to method encrypt
String encryptedText = encrypt(plainText, secretkey);
// print orignial text and encrypted text
System.out.println("Plain Text: " + plainText);
System.out.println("Encrypted Text: " + encryptedText);
String publicKey = "C:/Users/girgich/public.cert";
// allows to write data to a file
FileOutputStream fos = null;
// write bytes to file
BufferedOutputStream bos = null;
// create file to which data needs to be written
String fileName = "C:/Users/girgich/newFile.txt";
try{
// allows written data to go into the written path
fos = new FileOutputStream(fileName);
// converts written data into bytes
bos = new BufferedOutputStream(fos);
// writes the encrypted text into file
bos.write(encryptedText.getBytes());
System.out.println("encryptedText has been written successfully in "
+fileName);
// allows to catch bug in code
} catch (IOException e) {
e.printStackTrace();
} finally {
try{
// check for null exception
if (bos != null){
bos.close();
}
// check for null exception
if (fos != null){
fos.close();
}
} catch (IOException e){
e.printStackTrace();
}
}
// creates a file input stream by opening a path to the file needed
FileInputStream fin = new
FileInputStream("C:/Users/girgich/public.cert");
// implements the X509 certificate type
CertificateFactory f = CertificateFactory.getInstance("X.509");
// initalizes data found in the file
X509Certificate certificate =
(X509Certificate)f.generateCertificate(fin);
// gets public key from this certificate
PublicKey pk = certificate.getPublicKey();
System.out.println(pk);
String encryptedTextKey = encryptedKey(pk, secretkey);
System.out.println("Encrypted Key: " + encryptedTextKey);
// allows to write data to a file
FileOutputStream newFos = null;
// write bytes to file
BufferedOutputStream newBos = null;
// create file to which data needs to be written
String fileNameKey = "C:/Users/girgich/symmetric.txt";
try{
// allows written data to go into the written path
newFos = new FileOutputStream(fileNameKey);
// converts written data into bytes
newBos = new BufferedOutputStream(newFos);
// writes the encrypted text into file
newBos.write(encryptedTextKey.getBytes());
System.out.println("encryptedKey has been written successfully in "
+fileNameKey);
// allows to catch bug in code
} catch (IOException e) {
e.printStackTrace();
} finally {
try{
// check for null exception
if (newBos != null){
newBos.close();
}
// check for null exception
if (newFos != null){
newFos.close();
}
} catch (IOException e){
e.printStackTrace();
}
}
String decrypt = (encryptedTextKey);
}
public static String encrypt(String plainText, SecretKey secretkey) throws
Exception {
//Encodes the string into a sequence of bytes
byte[] plainTextByte = plainText.getBytes();
//intialize cipher to encryption mode
cipher.init(Cipher.ENCRYPT_MODE, secretkey);
//data is encrypted
byte[] encryptedByte = cipher.doFinal(plainTextByte);
Base64.Encoder encoder = Base64.getEncoder();
//encodes bytes into a string using Base64
String encryptedText = encoder.encodeToString(encryptedByte);
// return the string encrypted text to the main method
return encryptedText;
}
public static String encryptedKey(PublicKey pk, SecretKey secretkey) throws Exception {
// data written to byte array
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// writes data types to the output stream
ObjectOutputStream writter = new ObjectOutputStream(baos);
//specific object of secretkey is written to the output stream
writter.writeObject(secretkey);
//creates a byte array
byte[] plainTextByteKey = baos.toByteArray();
//creates a cipher using the RSA algorithm
Cipher cipher = Cipher.getInstance("RSA");
// initalizes cipher for encryption using the public key
cipher.init(Cipher.ENCRYPT_MODE, pk);
//encrypts data
byte[] encryptedByteKey = cipher.doFinal(plainTextByteKey);
Base64.Encoder encoderKey = Base64.getEncoder();
// encodes the byte array into a string.
String encryptedTextKey = encoderKey.encodeToString(encryptedByteKey);
return encryptedTextKey;
}
public void decrypt(String encryptedTextKey) {
byte[] decryptedData = null;
String password = "******";
try {
FileInputStream is = new FileInputStream("C:/Users/girgich/keystore.jks");
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(is, password.toCharArray());
String alias = "mykey";
Key key = keystore.getKey(alias, password.toCharArray());
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
decryptedData = cipher.doFinal(encryptedTextKey.getBytes());
System.out.println("Decrypted Key: " + decryptedData);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Hi I am trying to decrypt the stuff from my public key on Android:
public String decrypt(String basetext) {
try {
FileInputStream iR = new FileInputStream("/sdcard/publickkey");
ObjectInputStream inputStream = new ObjectInputStream(iR);
final PublicKey key = (PublicKey) inputStream.readObject();
byte[] text = Base64.decode(basetext, Base64.DEFAULT);
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance("RSA");
// decrypt the text using the public key
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] dectyptedText = cipher.doFinal(text);
iR.close();
return new String(dectyptedText,"UTF-8");
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
It works fine on my marshmallow, tried to run on emulator 4.2.2 and it throws below error:
caused by java.lang.noclassdeffounderror com/android/org/constcrypt/OpenSSLRSAPublicKey android
If I see my imports there is no imports like above error
import javax.crypto.Cipher;
import java.security.PublicKey;
import java.io.ObjectInputStream;
import java.io.FileInputStream;
This works fine on real device android marshmallow,4.2.2 on emulator it crashes
full class :-
import android.util.Base64;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import javax.crypto.Cipher;
public class EncryptionUtils {
public static final String ALGORITHM = "RSA";
public static final String PRIVATE_KEY_FILE = "/sdcard/private.key";
public static final String PUBLIC_KEY_FILE = "/sdcard/public.key";
public static void generateKey() {
try {
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM);
keyGen.initialize(2048);
final KeyPair key = keyGen.generateKeyPair();
File privateKeyFile = new File(PRIVATE_KEY_FILE);
File publicKeyFile = new File(PUBLIC_KEY_FILE);
// Create files to store public and private key
if (privateKeyFile.getParentFile() != null) {
privateKeyFile.getParentFile().mkdirs();
}
privateKeyFile.createNewFile();
if (publicKeyFile.getParentFile() != null) {
publicKeyFile.getParentFile().mkdirs();
}
publicKeyFile.createNewFile();
// Saving the Public key in a file
ObjectOutputStream publicKeyOS = new ObjectOutputStream(
new FileOutputStream(publicKeyFile));
publicKeyOS.writeObject(key.getPublic());
publicKeyOS.close();
// Saving the Private key in a file
ObjectOutputStream privateKeyOS = new ObjectOutputStream(
new FileOutputStream(privateKeyFile));
privateKeyOS.writeObject(key.getPrivate());
privateKeyOS.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String encrypt(String text, PrivateKey key) {
try {
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance(ALGORITHM);
// encrypt the plain text using the private key
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = cipher.doFinal(text.getBytes("UTF-8"));
String base64 = Base64.encodeToString(cipherText, Base64.DEFAULT);
return base64;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String decrypt(String basetext, PublicKey key) {
try {
byte[] text = Base64.decode(basetext, Base64.DEFAULT);
// get an RSA cipher object and print the provider
final Cipher cipher = Cipher.getInstance(ALGORITHM);
// decrypt the text using the public key
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] dectyptedText = cipher.doFinal(text);
return new String(dectyptedText,"UTF-8");
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
}
It pays to be specific when defining the cipher you intend to use. Instead of using:
final Cipher cipher = Cipher.getInstance("RSA");
You should try:
final Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
I believe there were some inconsistencies between plain Java and Android, I don't recall what versions, but in most cases the adjustment above fixes the problem.
well the only thing is that real cryptography is added in android 4.3 and up,so on android 4.2 and below it will cause problem because openssl class was made default in api 18,so using this rsa encrypting/decryption is fine on 4.3+ else to support below use some other techniques.
thats whay is the solution from my side,if anybody else get this porblem you can see the solution here,
or if someone managed to make it work without using third party libraries then he is most welcome to write it here
You should not use ObjectOutputStream / ObjectInputStream to store the keys. The underlying implementation class of PublicKey vary between versions and the serialization of an object using an internal class will fail if the class it is not present in a upper version.
I suggest to store the public key data directly
byte publicKeyData[] = publicKey.getEncoded();
And load it using
X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKeyData);
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey publicKey = kf.generatePublic(spec);
Note also that in android previous to 4.3 the cryptographic provider is not fully implemented and non conventional operations like decryption with RSA public key and encryption with private key can be used with guarantees.
UPDATED - Spongycastle
Alternatively you can use spongycastle cryptographic provider to generate and load the keys
Gradle dependencies
compile 'com.madgag.spongycastle:core:1.51.0.0'
compile 'com.madgag.spongycastle:prov:1.51.0.0'
compile 'com.madgag.spongycastle:pkix:1.51.0.0'
Adding the provider
static {
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
}
USAGE
Add SC to the provider parameter. For example
KeyPairGenerator keyGen= KeyPairGenerator.getInstance("RSA", "SC");
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "SC");
See more info here