android decrypt file using fingerprint - java

With the code below I can easily encrypt a file and decrypt it, using the password that the user enters. I am now imploding the use of the fingerprint to enter my app. When the fingerprint is correct, I want to decrypt the file. But how do I do if the user has not entered the password? is there a relatively safe way to do this? Thanks
public class CryptoUtils {
private static final String ALGORITHM = "AES";
private static final String ENCRYPTION_IV = "4e5Wa71fYoT7MFEX";
private static String ENCRYPTION_KEY = "";
public static void encrypt(String key, File inputFile, File outputFile)
throws CryptoException {
doCrypto(Cipher.ENCRYPT_MODE, key, inputFile, outputFile);
}
public static void decrypt(String key, File inputFile, File outputFile)
throws CryptoException {
doCrypto(Cipher.DECRYPT_MODE, key, inputFile, outputFile);
}
private static void doCrypto(int cipherMode, String key, File inputFile, File outputFile)
throws CryptoException {
try {
ENCRYPTION_KEY = key;
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(cipherMode, makeKey(), makeIv());
FileInputStream inputStream = new FileInputStream(inputFile);
byte[] inputBytes = new byte[(int) inputFile.length()];
inputStream.read(inputBytes);
byte[] outputBytes = cipher.doFinal(inputBytes);
FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(outputBytes);
inputStream.close();
outputStream.close();
//Log.d("cur__", "Encryption/Decryption Completed Succesfully");
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException | IOException ex) {
throw new CryptoException("Error encrypting/decrypting file " + ex.getMessage(), ex);
} catch (InvalidAlgorithmParameterException ex) {
Logger.getLogger(CryptoUtils.class.getName()).log(Level.SEVERE, null, ex);
}
}
private static AlgorithmParameterSpec makeIv() {
return new IvParameterSpec(ENCRYPTION_IV.getBytes(StandardCharsets.UTF_8));
}
private static Key makeKey() {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] key = md.digest(ENCRYPTION_KEY.getBytes(StandardCharsets.UTF_8));
//Log.d("Lenghtis", new SecretKeySpec(key, ALGORITHM).getEncoded().length + "");
return new SecretKeySpec(key, ALGORITHM);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}

You first need to generate a Key. You can use the following function
fun generateKey(storeKey: StoreKey): SecretKey {
val keyGenerator =
KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "MyKeyStore")
val keyGenParameterSpec = KeyGenParameterSpec.Builder(storeKey.Alias,
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
.setRandomizedEncryptionRequired(false)
.build()
keyGenerator.init(keyGenParameterSpec)
return keyGenerator.generateKey()
}
You need setUserAuthenticationRequired(true) for biometric authentication.
Then you can provide this key to your biometric authenticator to authenticate the user and in return you will receive cipher from it.
Then you can use that cipher (encrypt cipher OR decrypt cipher) to perform encrypt/decrypt data directly using this cipher.
androidx.biometric:biometric:1.0.0-beta01
like this
val biometricCallbacks = object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
result.cryptoObject?.cipher?.let { cipher ->
val encryptedData = cipherUtils.encryptAES(dataToEncrypt, cipher)
dismiss()
}
}
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
if (errorCode == BiometricPrompt.ERROR_CANCELED || errorCode == BiometricPrompt.ERROR_USER_CANCELED) {
dismiss()
}
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
Log.e("BIOMETRIC", "FAILED")
}
}
val biometricPrompt = BiometricPrompt(this, biometricExecutor, biometricCallbacks)
val biometricPromptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("APP")
.setSubtitle("Enable Biometric for Encryption")
.setDescription("")
.setNegativeButtonText("Cancel")
.build()
biometricPrompt.authenticate(
biometricPromptInfo,
BiometricPrompt.CryptoObject(
cipherUtils.getBioEncryptCipher(keyStoreUtils.generateNewKey(StoreKey.Pass, true))
)
)
You can encrypt users password and keep it securely in database somewhere and use the biometric authentication. Since the key you are generating is strictly bind to users biometrics, users encrypted password will not get compromised.

Here's the androidx.biometric demo app for using biometric-bound keys (key can only be unlocked by strong biometric sensors).
Basically
Generate a key that's only usable after being authenticated via biometrics, with the associated capabilities you'd like the key to be able to perform (encrypt, decrypt, etc)
Wrap the cryptographic operation you wish to perform in a CryptoObject. The key will only become usable after a user has authenticated with biometrics
Invoke BiometricPrompt#authenticate(CryptoObject)
Use the key after onAuthenticationSucceeded
For completeness, there's also authentication-bound keys (key can be unlocked by biometrics OR device credential), which become available for use by your app whenever the user has authenticated within t seconds, where t is specified in KeyGenParameterSpec.Builder#setUserAuthenticationValidityDurationSeconds. You can see an example usage here.

Related

BadPaddingException when trying to decrypt a text in Base64 encrypted in AES in ECB

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;
}
}

java.security.InvalidKeyException thrown during Java AES key exchange using RSA

I'm trying to make a client/server program in Java that allows the server to send messages encrypted using AES to the client. Right now, I'm having problems while creating the key exchange protocol. The way that this key exchange current works is:
Client generates RSA public/private key pair
Client sends out his RSA public key to the server
Server generates and encrypts AES key with client's RSA public key
Server sends encrypted AES key to client
Both parties now have the correct AES key and all messages can be encrypted using AES
However, every time I get to step three, I am unable to encrypt the generated AES key with the client's RSA public key because I get this error:
java.security.InvalidKeyException: No installed provider supports this key: javax.crypto.spec.SecretKeySpec
at java.base/javax.crypto.Cipher.chooseProvider(Cipher.java:919)
at java.base/javax.crypto.Cipher.init(Cipher.java:1275)
at java.base/javax.crypto.Cipher.init(Cipher.java:1212)
at test.Server.<init>(Server.java:50)
at test.Start.main(Start.java:11)
As a result, I am unable to complete the AES key exchange that I'm trying to do.
Server.java is used to do things on the server side, and Client.java is used to do everything on the client side. My Server.java file looks like this:
public class Server {
private ServerSocket serverSocket; // Server socket
private Socket socket; // Socket
private BufferedReader in; // Reading from stream
private PrintWriter out; // Writing to stream
private Key key; // AES key used for encryption
// Constructor
public Server() {
// Initialize the server socket
try {
// Setup connections
serverSocket = new ServerSocket(12345);
socket = serverSocket.accept();
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(newInputStreamReader(socket.getInputStream()));
// Receive the client's public RSA key
byte[] encodedClientKey = Base64.getDecoder().decode(in.readLine());
Key clientRSAKey = new SecretKeySpec(encodedClientKey, 0, encodedClientKey.length, "RSA");
// Generate AES key
KeyGenerator aesKeyGen = KeyGenerator.getInstance("AES");
aesKeyGen.init(256);
key = aesKeyGen.generateKey();
// Encrypt the AES key using the client's RSA public key
Cipher c = Cipher.getInstance("RSA");
c.init(Cipher.ENCRYPT_MODE, clientRSAKey);
byte[] encryptedAESKey = c.doFinal(key.getEncoded());
// Send the encrypted AES key to the client
sendUnencrypted(Base64.getEncoder().encodeToString(encryptedAESKey));
} catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
}
// Receive an unencrypted message
public String receiveUnencrypted() {
try {
// Wait until the stream is ready to be read
while (true)
if (in.ready())
break;
return in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
// Send an unencrypted message
public void sendUnencrypted(String message) {
out.println(message);
out.flush();
}
// Send an encrypted message
public void send(String message) {
try {
// Encrypt the message
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.ENCRYPT_MODE, key);
String encoded = Base64.getEncoder().encodeToString(message.getBytes("utf-8"));
byte[] encrypted = c.doFinal(encoded.getBytes());
String encryptedString = Base64.getEncoder().encodeToString(encrypted);
// Send the encrypted message
out.println(encryptedString);
out.flush();
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
| BadPaddingException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
My Client.java file looks like this:
public class Client {
private Socket socket; // Socket
private BufferedReader in; // Reading from stream
private PrintWriter out; // Writing to stream
private Key key; // AES key
// Constructor
public Client() {
try {
// Create streams to server
socket = new Socket("127.0.0.1", 12345);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// Generate an RSA key pair
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair kp = keyGen.generateKeyPair();
// Send out our public key to the server
byte[] publicKey = kp.getPublic().getEncoded();
sendUnencrypted(Base64.getEncoder().encodeToString(publicKey));
// Recieve and decrypt the AES key sent from the server
String encryptedKey = receiveUnencrypted();
Cipher c = Cipher.getInstance("RSA");
c.init(Cipher.DECRYPT_MODE, kp.getPrivate());
byte[] AESKey = c.doFinal(encryptedKey.getBytes());
key = new SecretKeySpec(AESKey, 0, AESKey.length, "AES");
} catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
| IllegalBlockSizeException | BadPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// Receive an unencrypted message
public String receiveUnencrypted() {
try {
// Wait until the stream is ready to be read
while (true)
if (in.ready())
break;
return in.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
// Send an unencrypted message
public void sendUnencrypted(String message) {
out.println(message);
out.flush();
}
// Receive an encrypted message
public String receive() {
try {
// Wait until the stream is ready to be read
while (true)
if (in.ready())
break;
// Obtain the encrypted message
String encrypted = in.readLine();
// Decrypt and return the message
Cipher c = Cipher.getInstance("AES");
c.init(Cipher.DECRYPT_MODE, key);
byte[] decoded = Base64.getDecoder().decode(encrypted);
String utf8 = new String(c.doFinal(decoded));
String plaintext = new String(Base64.getDecoder().decode(utf8));
// Return the message
return plaintext;
} catch (IOException | InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException
| IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
return null;
}
}
Start.java is used to initial both the server and client.
package test;
import java.util.Scanner;
public class Start {
public static void main(String args[]) {
Scanner scan = new Scanner(System.in);
System.out.println("1.) Create data server.\n2.) Connect to data server.\nPlease select an option: ");
int option = scan.nextInt();
if (option == 1) { // Setup a server if they choose option one
Server s = new Server();
s.send("Hello");
} else if (option == 2) { // Setup a client if they choose option two
Client c = new Client();
System.out.println(c.receive());
}
// Close scanner
scan.close();
}
}
First, you can't use SecretKeySpec to restore an RSA public key. In your Server's constructor, change
Key clientRSAKey = new SecretKeySpec(encodedClientKey, 0, encodedClientKey.length, "RSA");
to
Key clientRSAKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(encodedClientKey));
Second, you need to decode the base64 encoded encrypted key. In your Client constructor, change
String encryptedKey = receiveUnencrypted();
to
byte[] encryptedKey = Base64.getDecoder().decode(receiveUnencrypted());
Finally, in your Client constructor, change
byte[] AESKey = c.doFinal(encryptedKey.getBytes());
to
byte[] AESKey = c.doFinal(encryptedKey);

BadPaddingException RSA Encrypt then decrypt in java

I have been given an RSA public Key and an RSA private key with OAEP SHA-256 padding. I am attempting to simply encrypt a random string and then decrypt it to assert the result is equalt to the original.
This is the public key i have been given:
-----BEGIN RSA PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq8tiBtDmkeS/ruY3rrkq
dz6Lc6XWFRbI/GjPtIokrtpM+Ujyv6wX8TBqY8e03gzh+eE7VUyEVPapDnceAqgJ
ZQah2h+N5AEQKqNDM4/1to1V0F+m1ISR7CIUU8dvU+bPk67DU5VkEtLxf+mW8/es
hy0u6oSB04WCDPNnh+9GDF7tN7lOzBH1FxKPWIb5Gqg6GoXS+KgvQhYEk0TajIBk
+mefzTv1D4HJlhFYybgY+/p3k4P2kM5HnbEtoVpz/PL9+94YVp5RBHTQHmk/3SAX
RkPP34IjY6LZqTJGWQGxc64Qci54ZJsq4wSTXvfdHgUWz7eX+v1jA+GLjFExNcYZ
nQIDAQAB
-----END RSA PUBLIC KEY-----
This is the private key I have been given:
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDAUQiHdHuMBCnF
P/7RQoRWrp62dGWQLDLP9l+3KUMlSPheN2R5jHDZ/WEGJGF2WYKxaQvv5XCocdnt
uxVZTNM1PVyduokJaJzrSIj+jGWDd4hTWvVoS3vhds8u6W0jD3M4ayrF6c7NYuHc
tE5YLLIgK1DTn+ZZCr3VGbScISDJ/WPx3+1YTNQDvm+T7MfjhetJqqzWIsunMUiw
nQOQvkdjCVWpk+L10SaMrezixtIS5wfpOTjGIZ0w5VunvtsPxg1TkuvGURa9S3rS
7D3uhDPY+V8UDMpBPso3j7TGD5axlBJcpavWeF8qde3sWztmqUFQ3JDypZWGSAe2
d8e4mY65AgMBAAECggEAal8nwYxbHZnb5L892U7aVfuly7Nbzb+0pzRVwsBu5DuV
LL+kslpMvTYZqUUMJ2LhF/HLaXhVtMWsTYLSDx+gHu1+wbtAOtUDHlxzcaAEMhA2
dix0WqiNr6qAdCkmdWMBTu5vrSJigVW1KdcNElY+e+6ZeUQTK6L2Vt0t+cGVGkM4
K5PdHcGLR1BiAf3k4BguHE4TGGnqN9dF5Rn9nPP6C7QihWzGn15efD2dAXn3Kbho
dMIRgyYiO7uXPm2LrIjtldYb8tPus+V9SWT4hcgnJZrtd9atkqZrJ3dhQf9ZetGs
UwTCzHJ169NNbVyrjCbcy4Uht8Na+t/94Im9hI8n0QKBgQDO6tYnwh1hPg3E+4DY
xCiDAp7v3afJIvC9a1sBtif8KkzZTYH31ww+PRcfDPQUlk/MIDie0u6xIawnBYkC
oN1u9erojcvQbBP0GFylP5hNxYZbI7gPA/7AiGRxyezZPOiVMwdd5fCM7Wv4kucr
c1JCcvtPoDQcFFZOfI+wqqdWnQKBgQDt745Pvckt7/XL7wbfv4BV+cwpLNjc4/3k
ajOLtasgpT8mc0gUH2ejHhpkuhjSUpSTrlFB8EN1kAwmBYy0GOoO9967hm6twmqk
Q5L3OHJ96pYkf1rbyXNE1N7inh5Z3M1H5ONIaliAYLHXOzKsZvI5eUdKAJdSqFLo
uvVCwr4PzQKBgG6W3rzDJ9a4Rr24OgYg2RIkTXQgALQko4xpm2tPwxEoPoiJv2QK
ILYHCpuC3dU+/Qk5U2m3jPFI8OyuLask9RSABPwkBQGxMfztJF8BnVI7tvJxJceI
uBiJDT4v0RHOVvSfIFnUMnvvzRw+z6TObvGq6JyHIDK9v98U/etLWkKVAoGAIIxF
lmjqzUrm/8ep1A+5OYmbQQKug8D4aTeR54mpaCTSt6rLcF0/axPiHmdKn/LF+lG9
MdzxDXLwBn952OUTl4qWwGZKW6Cdv+yyfPkOyGS/tyxovGoZR5ArESr6Eebfefc4
lB5gDuerTDr/2o+WkQAjHV9pU9hMxyNUC5biMv0CgYEAiDlw8lBf3cQs6FxNXs9t
whWpfL0yY7WAONvhfFB0Dpsz52gGDCYRvJekGRz6jOlKDuXJ+Mm1AX4BaufMETI5
QseZxtVPIn+BXm0A1x8w/DifmE1JqQZmPCQPOh3eLx5nSn9LKGIbMgd17mfH3HhU
8WX2mzWjmRA3C/CzdGfCKSk=
-----END PRIVATE KEY-----
I am reading this key from a pem file "crypt_pkcs8.pem" but recive the public key as a byte array from a gRPC call.
Here is my Java code:
public class Encryption{
public static void main(String args){
ByteString publicKey = client.getPublicKey(nonce).getRSAEncryptionKey();
String strKeyPEM = "";
BufferedReader br = new BufferedReader(new FileReader("crypt_pkcs8.pem"));
String line;
while ((line = br.readLine()) != null) {
strKeyPEM += line+"\n";
}
br.close();
byte[] pt;
try {
pt = "h".getBytes("UTF-8");
byte[] ct =encrypt(publicKey, pt);
byte[] response= decrypt(strKeyPEM, ct);
assertEquals(pt, response);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static byte[] encrypt(ByteString rawKey ,byte[] message){
String strippedKey=stripKey(rawKey.toStringUtf8());
byte[] keyBytes = Base64.getDecoder().decode(strippedKey);
System.out.println(keyBytes.length);
Cipher cipher_RSA;
try {
cipher_RSA = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
PublicKey pk = keyFactory.generatePublic(spec);
System.out.println(pk.getAlgorithm()+" format : "+pk.getFormat());
cipher_RSA.init(Cipher.ENCRYPT_MODE, pk);
return cipher_RSA.doFinal(message);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException |
BadPaddingException | InvalidKeyException | InvalidKeySpecException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
public static byte[] decrypt(String rawKey ,byte[] message){
String strippedKey=stripPrivateKey(rawKey);
byte[] keyBytes = Base64.getDecoder().decode(strippedKey);
System.out.println(keyBytes.length);
Cipher cipher_RSA;
try {
cipher_RSA = Cipher.getInstance("RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
PrivateKey pk = keyFactory.generatePrivate(spec);
System.out.println(pk.getAlgorithm()+" format : "+pk.getFormat());
cipher_RSA.init(Cipher.DECRYPT_MODE, pk);
return cipher_RSA.doFinal(message);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException |
BadPaddingException | InvalidKeyException | InvalidKeySpecException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
public static String stripKey(String key){
key = key.replace("-----BEGIN RSA PUBLIC KEY-----\n", "");
key = key.replace("-----END RSA PUBLIC KEY-----", "");
key = key.replace("\n", "");
return key;
}
public static String stripPrivateKey(String key){
key = key.replace("-----BEGIN PRIVATE KEY-----", "");
key = key.replace("-----END PRIVATE KEY-----", "");
key = key.replace("\n", "");
return key;
}
}
I cannot find anything obviously wrong with my actaul code so I believe this is to do with the keys not matching.
I also notice the header/footer is different on the public key and private key. From looking online this is because the public key is in PKCS#8 format.
Will I need to change the public key to pkcs#8 format as well? If so what is the easiest way to do this?
I have also been told I should be able to extract the public key (in pkcs8 format) from the private key given above. Can this be done easily?
If you can get a valid public key then the key is not in PKCS#8 format, it will be in X.509 (SubjectPublicKeyInfo) format as Java expects. However, your public and private keys do indeed not match. You can use the answer here to create the correct public key file from the private key file.
Java doesn't directly contain code to retrieve the public exponent from an RSAPrivateKey without CRT parameters. However, if you want a Java only solution you could cast the PrivateKey even further to RSAPrivateCrtKey which does contain a getPublicExponent method to retrieve the public exponent. Then you can use an RSA KeyFactory to generate a public key using RSAPublicKeySpec.
You could also parse the PEM file to retrieve the public key in Java using e.g. the Bouncy Castle API. But expect a steep learning curve if you do.

How do I encrypt a message and then I decryot it on another device with javax.crypto.cipher using AES

I have a problem with my code. I want to encrypt a message using a key made from passphrase. Then I want to send it (or use it somewhere, whatever) in String form (the encrypted one), and after that I want to decrypt it only by knowing the passphrase.
I wrote the code below, but I receive either IllegalBlockSizeException or BadPaddingException. I thought that badding would be took care by padding.
Here is my code:
Constructor and initialization:
public class AES_Cipher {
private String keyString;
private byte[] byteKey;
private SecretKey key;
Cipher c;
public AES_Cipher(String keyString){
this.keyString = keyString.toString();
}
public void init() throws InitializtionFailedException{
try{
c = Cipher.getInstance("AES/ECB/PKCS5Padding");
byteKey = keyString.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
byteKey = sha.digest(byteKey);
byteKey = Arrays.copyOf(byteKey, 16);
key = new SecretKeySpec(byteKey, "AES");
}catch(NoSuchAlgorithmException e){
throw new InitializtionFailedException();
}
}
Encrypt:
public String encrypt(String text) throws EncryptionException{
try{
c.init(Cipher.ENCRYPT_MODE, key);
byte[] tmp = c.doFinal( text.getBytes("UTF-8"));
return new String(tmp, "UTF-8");
}catch(IllegalBlockSizeException e){
throw new EncryptionException();
}
}
Decrypt:
public String decrypt(String text) throws DecryptionException{
try{
//byte[] decordedValue = new BASE64Decoder().decodeBuffer(text);
// tried too use it but with poor outcome
//c = Cipher.getInstance("AES/ECB/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE, key);
byte[] tmp1 = text.getBytes("UTF-8");
//byte[] tmp = c.doFinal("aaaaaaaaaaaaaaaa".getBytes());
//this returns BadPaddingException
byte[] tmp = c.doFinal(text.getBytes());
return new String(tmp, "UTF-8");
}catch(IllegalBlockSizeException e){
throw new DecryptionException();
}
}
Of course there are some more exceptions.
Also, I would like to later use this code on android, and It doesn't need to be AES if there is some other Ciphers that are maybe less effective but less troublesome. But not simple XOR if that's not too much too ask.
Thank you in advance.
Ok. Seems to work now thanks to Artjom B.
Constructor and inicialization:
public class AES_Cipher {
private String keyString;
private byte[] byteKey;
private SecretKey key;
Cipher c;
public AES_Cipher(String keyString){
this.keyString = keyString.toString();
}
public void init() throws InitializtionFailedException{
try{
c = Cipher.getInstance("AES/ECB/PKCS5Padding");
byteKey = keyString.getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-1");
byteKey = sha.digest(byteKey);
byteKey = Arrays.copyOf(byteKey, 16);
key = new SecretKeySpec(byteKey, "AES");
}catch(NoSuchAlgorithmException e){
throw new InitializtionFailedException();
}
}
Encryption:
public String encrypt(String text) throws EncryptionException{
try{
c.init(Cipher.ENCRYPT_MODE, key);
byte[] textByte = text.getBytes("UTF-8");
byte[] tmp = c.doFinal(textByte);
String return1 = Base64.encodeToString(tmp, Base64.DEFAULT);
return return1;
} // of course there is some exceptions catched down there
Decryption:
public String decrypt(String text) throws DecryptionException{
try{
c.init(Cipher.DECRYPT_MODE, key);
byte[] textByte = Base64.decode(text, Base64.DEFAULT);
byte[] tmp = c.doFinal(textByte);
return new String(tmp, "UTF-8");
}catch(IllegalBlockSizeException e){
Log.d("Exception", "IllegalBlockSizeException");
throw new DecryptionException();
} // wrote DecryptionException myself.
// also there is more Exceptions below
}

JAVA: Socket override to use CipherInputStream and CipherOutputStream

Let me explain my problem.
I want to override Socket and ServerSocket classes in order to encrypt my messages in this way:
1) Client sends a random generated symmetric key (AES algorithm) to the Server
2) After that, client and server can communicate by encrypting their messages with this key
3) To exchange the symmetric key the client encrypts it using the public key of the server (RSA algorithm)
I override Socket and ServerSocket, so automatically, when the client opens a Socket, this will send the symmetric key encrypted by the server's public key. The server reads the first 128 byte in the stream, decodes them, and builds the symmetric key.
This part seems work. I check the communication using Wireshark: packets are encrypted and received symmetric key is correctly delivered.
In order to guarantee a transparent use of my Sockets I override the getInputStream and getOutputStream methods, returning a CipheredInputStream and a ChiperedOutputStream.
It doesn't work for now.. When I try to get OutputStream to send data, the program goes through the instruction but it doesn't matter (I check via Wireshark and no packets are sent).
This is the code of the ServerSocket:
public class SecureServerSocket extends ServerSocket {
public SecureServerSocket(int port) throws IOException {
super(port);
}
public Socket accept() throws IOException {
SecureSocket s = new SecureSocket();
implAccept(s);
SecretKey seckey;
InputStream is = s.getInputStream();
byte[] tmp = new byte[128]; //128: length of the key
int i = 0;
while (i < 128) {
tmp[i] = (byte) (is.read() & 0x000000FF);
++i;
}
byte[] mess = EncryptionManager.rsaDecryptPrivate(tmp);
seckey = new SecretKeySpec(mess, "AES");
try {
s.setkey(seckey);
} catch (InvalidKeyException | NoSuchAlgorithmException
| NoSuchPaddingException e) {
e.printStackTrace();
}
return s;
}
}
This is the code of the Socket:
public class SecureSocket extends Socket {
private SecretKey seckey;
private InputStream in = null;
private OutputStream out = null;
private CipherInputStream cin = null;
private CipherOutputStream cout = null;
public SecureSocket() throws IOException {
}
public SecureSocket(String address, int port) throws UnknownHostException,
IOException, NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException {
super(address, port);
if (out == null) {
this.out = super.getOutputStream();
}
if (in == null) {
this.in = super.getInputStream();
}
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
SecureRandom random = new SecureRandom();
keyGen.init(random);
seckey = keyGen.generateKey();
byte[] mess = EncryptionManager.rsaEncryptPublic(seckey.getEncoded());
// writing the initial message with the AES encryption key
out.write(mess);
// Initialization of the Cipher streams
Cipher cipherEn = Cipher.getInstance("AES");
cipherEn.init(Cipher.ENCRYPT_MODE, seckey);
Cipher cipherDc = Cipher.getInstance("AES");
cipherDc.init(Cipher.DECRYPT_MODE, seckey);
cout = new CipherOutputStream(out, cipherEn);
cin = new CipherInputStream(in, cipherDc);
}
public InputStream getInputStream() throws IOException {
if (cin == null)
return super.getInputStream();
return cin;
}
public OutputStream getOutputStream() throws IOException {
if (cout == null)
return super.getOutputStream();
return cout;
}
public synchronized void close() throws IOException {
OutputStream o = getOutputStream();
o.flush();
super.close();
}
public void setkey(SecretKey seckey) throws NoSuchAlgorithmException,
NoSuchPaddingException, InvalidKeyException, IOException {
this.seckey = seckey;
Cipher cipherEn = Cipher.getInstance("AES");
cipherEn.init(Cipher.ENCRYPT_MODE, seckey);
cout = new CipherOutputStream(super.getOutputStream(), cipherEn);
Cipher cipherDc = Cipher.getInstance("AES");
cipherDc.init(Cipher.DECRYPT_MODE, seckey);
cin = new CipherInputStream(super.getInputStream(), cipherDc);
}
}
I can't figure out where is the problem. Thank you!
The problem is that a Cipher requires a specific amount of data (for example 128 bits) to be able to encrypt it correctly. If you send a file, that's no problem because the last few bits of the file will be sent when you close the stream.
However, you need a padding for network communication. You can specify one for your Cipher instances:
Cipher cipherEnOrDe = Cipher.getInstance("AES/CBC/PKCS5Padding"); //for example, check documentation for more
Using a padding, the Cipher will be able to send your data once you call the flush() method (which you should do whenever you want something to be sent anyway).
Note: Your application is only safe if your client is distributed with the public key. Otherwise, you cannot be sure that you are connecting to the right server in the first place. Anyone can create a public key.

Categories