Recently I have read an article Seriously, stop using RSA
and I was wondering what is the best way to test my implementation of the RSA keyset generation. I am not a security expert, but I have been working in Java and Software Dev. for quite some time now. However, I am not sure how I would test the strength of a key (flaws in the implementation process).
The Implementation I did for my RSA key gen is the following:
private KeyPairGenerator keyGen;
private PrivateKey privateKey;
private PublicKey publicKey;
private SecureRandom srandom;
public SecreteKeyGenerator() throws NoSuchAlgorithmException {
this.keyGen = KeyPairGenerator.getInstance("RSA");
this.keyGen.initialize(Encryptable.RSA_LENGTH);
this.srandom = new SecureRandom();
}
public void createRSAKeys() {
KeyPair pair = this.keyGen.generateKeyPair();
this.privateKey = pair.getPrivate();
this.publicKey = pair.getPublic();
}
The way I am using this implementation is in combination with AES key. I use AES key to encrypt the data and then I encrypt the key using RSA.
This is a suggested method in Java when dealing with large files.
The variable Encryptable.RSA_LENGTH has a value 2048, which is the length of a key.
After the keys are generated they are written to a files stored on clients machine.
The encryption works properly, as well as the digital signature process. However, I want to be extra sure that I've implemented everything correctly.
This is the code that is handling the encryption process of the file:
public void execute() {
OperationsLogger.getLogger().log(Level.INFO, "Encryption of file started");
AESEncryption aesEncryption = new AESEncryption();
aesEncryption.createAESKey();
aesEncryption.encryptFile(this.targetFilePath, this.publicKeyPath);
OperationsLogger.getLogger().log(Level.INFO, "File encrypted successfully.");
}
//AES Encryption
public void encryptFile(String inputFile, String publicKeyLocation) throws IOException, GeneralSecurityException {
try (FileOutputStream out = new FileOutputStream(inputFile + ".enc")) {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, new RSAEncryption().getPublic(publicKeyLocation));
byte[] b = cipher.doFinal(secretKey.getEncoded());
out.write(b);
out.write(gen.getIv());
Cipher ci = Cipher.getInstance("AES/CBC/PKCS5Padding");
ci.init(Cipher.ENCRYPT_MODE, secretKey, gen.getIvspec());
try (FileInputStream in = new FileInputStream(inputFile)) {
processFile(ci, in, out);
}
}
}
public PublicKey getPublic(String filename) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
byte[] keyBytes = Files.readAllBytes(new File(filename).toPath());
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePublic(spec);
}
Related
First of all, I'm a beginner programmer
I created a pair of keys(rsa) on the user's side(android). I intend to send the public key to the server for future operations as a base64 string, and with the public key I will encode the next data and send it to the user.I use the server side of the nodejs. Unfortunately, all the content written in The internet did not mention encrypt with the base64 public key or at least I did not see it. Can someone help me? Thank
this my java code:
public class GenKey {
private String stringPrivateKey;
private String stringPublicKey;
#RequiresApi(api = Build.VERSION_CODES.O)
public GenKey() throws NoSuchAlgorithmException {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512,new SecureRandom());//1024 or 2048
KeyPair kp = kpg.generateKeyPair();
PublicKey publicKey = kp.getPublic();
PrivateKey privateKey = kp.getPrivate();
this.stringPublicKey = Base64.encodeToString(publicKey.getEncoded(), DEFAULT);
this.stringPrivateKey =Base64.encodeToString(privateKey.getEncoded(), DEFAULT);
}
public String getPublicKey(){
return stringPublicKey;
}
public String getPrivateKey(){
return stringPrivateKey;
}
}
and:
public class EncryptDecrypt {
public static String encrypt(String publicKey, String cleartext) throws Exception {
X509EncodedKeySpec ks = new X509EncodedKeySpec(Base64.decode(publicKey,DEFAULT));
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey pub = kf.generatePublic(ks);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pub);
byte[] encMsgBinary = cipher.doFinal(cleartext.getBytes());
return Base64.encodeToString(encMsgBinary, DEFAULT);
}
public static String decrypt(String privateKey, String ciphertext) throws Exception {
PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(Base64.decode(privateKey,DEFAULT));
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey pvt = kf.generatePrivate(ks);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pvt);
byte [] encrypted = Base64.decode(ciphertext,DEFAULT);
return new String(cipher.doFinal(encrypted));
}
}
solved.use node-rsa;
var NodeRSA = require('node-rsa');
var key = new NodeRSA();
var public="-----BEGIN PUBLIC KEY-----\n"+publicKey+"\n"+"-----END PUBLIC KEY-----";
key.importKey(public,"pkcs8-public-pem");
var encrypted = key.encrypt(text, 'base64');
return encrypted;
and in java EncryptDecrypt class
Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
instead of
Cipher.getInstance("RSA");
see:
enter link description here
I am trying to RSA encrypt data on Android and send it to server(spring).
getting BadPaddingException :
Approach:
server send public key in a string, which i convert to PublicKey Object and send data from App after encryption as a string.
server has a private key string , which is converted to PublicKey object and then data is decrypted.
Any Help would be much appreciated.
generation of key :
public static KeyPair generateKeyPairRSA() {
try {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024, random);
KeyPair keyPair = keyGen.generateKeyPair();
return keyPair;
} catch (Exception e) {
Log.d(TAG,e.getLocalizedMessage());
}
return null;
}
public byte[] RSAEncrypt(final String plain, PublicKey publicKey) throws Exception {
Cipher cipher = Cipher.getInstance(ALGO_RSA);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(plain.getBytes());
return encryptedBytes;
}
public static PublicKey loadPublicKey1(String stored) throws Exception{
byte[] data = Base64.decode(stored.getBytes());
X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance(ALGO_RSA);
return fact.generatePublic(spec);
}
Server Methods :
public byte[] decryptRSA(String inputData) throws Exception {
byte[] inputBytes = Base64.decodeBase64(inputData);
PrivateKey key = loadPrivateKey(getPrivateKey());
Cipher cipher1 = Cipher.getInstance("RSA");
cipher1.init(Cipher.DECRYPT_MODE, key);
return cipher1.doFinal(inputBytes);
}
private PrivateKey loadPrivateKey(String key64) throws Exception {
byte[] pkcs8EncodedBytes = Base64.decodeBase64(key64);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(keySpec);
}
Got it Working.
So diffrent libs have differenet implementation of Cipher.
so while calling
Cipher.getInstance("RSA/ECB/PKCS1Padding");
Explicitly mention encryption mode and padding.
Hope it helps somebody.
In my android application I would like to implement the Diffie-Hellman protocol in order to generate a common secret between two users.
What I do is the following: when the first user sends a friendship request to the second one, the app generates a key pair, storing the public one in a remote database and the private in a local database.
Here is the code for this first part:
generateKeys();
localDB.insertPrivateKey(userId, entityId, privateKey);
remoteDB.insertFirstPublicKey(userId, entityId, publicKey);
The generateKeys method is the following:
private void generateKeys() {
try {
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DH");
keyPairGenerator.initialize(1024);
final KeyPair keyPair = keyPairGenerator.generateKeyPair();
privateKey = keyPair.getPrivate();
publicKey = keyPair.getPublic();
} catch (Exception e) {
e.printStackTrace();
}
}
When the second user accepts the request, another key pair is generated, once again storing the private key in the local db and the public in the remote. Then it fetches the public key of the first user from the remote db, converts it back to PublicKey and generate the common secret.
Here is the code for the second part:
generateKeys();
localDB.insertPrivateKey(userId, entityId, privateKey);
remoteDB.insertSecondPublicKey(entityId, userId, publicKey);
String stringFirstPubKey = remoteDB.fetchFirstKey(entityId, userId);
PublicKey firstPubKey = stringToPublicKey(stringFirstPubKey);
byte[] commonSecret = generateCommonSecret(firstPubKey);
The stringToPublicKey is the following:
private PublicKey stringToPublicKey(String stringPubK) throws NoSuchAlgorithmException, InvalidKeySpecException {
byte[] pubK = Base64.decodeBase64(stringPubK);
KeyFactory keyFactory = KeyFactory.getInstance("DH");
EncodedKeySpec keySpec = new X509EncodedKeySpec(pubK);
return keyFactory.generatePublic(keySpec);
}
And the generataCommonSecret:
private byte[] generateCommonSecret(PublicKey firstPubKey) {
try {
final KeyAgreement keyAgreement = KeyAgreement.getInstance("DH");
keyAgreement.init(privateKey);
keyAgreement.doPhase(firstPubKey, true);
byte[] secretKey = adjustKeyLenght(keyAgreement.generateSecret());
return secretKey;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Also when storing the keys I convert them into String with the following:
String stringPubK = Base64.encodeBase64String(publicKey.getEncoded());
When executing the following line in generateCommonSecret
keyAgreement.doPhase(firstPubKey, true);
I get this exception
java.security.InvalidKeyException: DHPublicKey not for this KeyAgreement!
at com.android.org.bouncycastle.jcajce.provider.asymmetric.dh.KeyAgreementSpi.engineDoPhase(KeyAgreementSpi.java:101)
at javax.crypto.KeyAgreement.doPhase(KeyAgreement.java:383)
Can someone help me finding the problem? What is weird is that if I try to do this in a single java class, without storing and retrieving keys, it works fine.
I created a KeyStore using KeyStore Explorer with a public/private key pair inside it of type RSA, 4096 bytes, and PKCS#8 formatting.
I get an error when my code runs and hits the cipher.init() method :
"Key for algorithm RSA not suitable for symmetric encryption."
This doesn't really make sense to me because I'm using asymmetric key encryption/decryption. I'm not sure where to go from here or what I'm doing wrong.
Here is what I have:
public TransactionData processData(TransactionData data) throws BTHException {
String keystoreFilePath = manager.getStringValue(KeyStoreFilePath);
String keystorePassword = manager.getStringValue(KeyStoreFilePassword);
String privateKeyPassword = manager.getStringValue(KeyStorePrivateKeyPassword);
String certificateAlias = manager.getStringValue(CertificateAlias);
org.apache.xml.security.Init.init();
try {
InputStream in = data.getDataStream();
byte[] dataBytes = DataUtil.readBytes(in);
String encryptedDataStr = new String(dataBytes);
PrivateKey privateKey = getPrivateKeyFromKeyStore(keystoreFilePath, keystorePassword, certificateAlias, privateKeyPassword);
decrypt(
encryptedDataStr,
privateKey
);
}catch(Exception e){
throw new BTHException(e.getMessage());
}
return data;
}
private PrivateKey getPrivateKeyFromKeyStore(String keyStoreFilePath, String keyStorePassword, String privateKeyCertAlias, String privateKeyPassword) throws BTHException {
PrivateKey privateKey = null;
try {
KeyStore keystore = KeyStore.getInstance("JKS");
BASE64Encoder encoder = new BASE64Encoder();
keystore.load(new FileInputStream(keyStoreFilePath), keyStorePassword.toCharArray());
Key key=keystore.getKey(privateKeyCertAlias,keyStorePassword.toCharArray());
if(key instanceof PrivateKey) {
Certificate cert=keystore.getCertificate(privateKeyCertAlias);
PublicKey publicKey=cert.getPublicKey();
KeyPair keyPair = new KeyPair(publicKey,(PrivateKey)key);
privateKey = keyPair.getPrivate();
}
//privateKeyEncoded = encoder.encode(privateKey.getEncoded());
} catch (Exception e) {
throw new BTHException(e.getMessage());
}
return privateKey;
}
private String decrypt(String cipherText, PrivateKey privateKey) throws IOException, GeneralSecurityException, BTHException {
String decryptedValue = null;
try {
// 1. Get the cipher ready to start doing the AES transformation
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
// 2. Start the decryption process
// THIS IS WHERE IT FAILS
cipher.init(Cipher.DECRYPT_MODE, privateKey);
// 3. Finish the decryption process
decryptedValue = new String(cipher.doFinal(Base64.decodeBase64(cipherText)), "UTF-8");
} catch (Exception e) {
throw new BTHException(e.getMessage());
}
return decryptedValue;
}
Any help would be great. Thanks in advance!
You are trying to initialize your cipher as AES/CBC/PKCS5Padding which is an symmetric encryption and that is where the exception originates.
You should use the Cipher like that:
// 1. Get the cipher ready to start doing the RSA transformation
Cipher cipher = Cipher.getInstance("RSA");
// 2. Start the decryption process
cipher.init(Cipher.DECRYPT_MODE, privateKey);
Here you can find a good example for RSA-Encryption and Decryption.
I wrote a little test case for an AES encryption and decryption. The plan is to read some text from a file, encrypt it with a key and decrypt it again. Now the problem is, that the text is always the same, a wrong password does not result in unreadable text.
Where is the problem in the code or did I make a fundamental mistake ?
Main.java
import javax.crypto.spec.SecretKeySpec;
public class Main {
public static void main(String[] args) throws Exception {
new Main();
}
public Main() throws Exception {
Reader reader = new Reader();
String text = reader.readFile("/home/benjamin/Test.txt");
System.out.println("Original text before encryption: " + text);
// User A verschlüsselt und speichert ab
Crypto crypto = new Crypto();
SecretKeySpec secretkey = crypto.generateSecretKey("123456aA");
byte[] encryptedtext = crypto.encrypt(text, secretkey);
// User B lädt Datei und kennt das Passwort
Crypto crypto2 = new Crypto();
SecretKeySpec secretkey2 = crypto2.generateSecretKey("1kkk23456aAjbhhjbhjb");
byte[] decryptedtext = crypto2.decrypt(encryptedtext, secretkey2);
System.out.println("Original text after encryption: " + new String(decryptedtext, "UTF-8"));
}
}
Crypto.java
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class Crypto {
public SecretKeySpec generateSecretKey(String password) throws Exception {
MessageDigest shahash = MessageDigest.getInstance("SHA-1");
byte[] key = shahash.digest();
key = Arrays.copyOf(key, 16);
return new SecretKeySpec(key, "AES");
}
public byte[] encrypt(String text, SecretKeySpec secretkey) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretkey);
return cipher.doFinal(text.getBytes());
}
public byte[] decrypt(byte[] encryptedtext, SecretKeySpec secretkey) throws Exception {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, secretkey);
return cipher.doFinal(encryptedtext);
}
}
This is the problem:
public SecretKeySpec generateSecretKey(String password) throws Exception {
MessageDigest shahash = MessageDigest.getInstance("SHA-1");
byte[] key = shahash.digest();
key = Arrays.copyOf(key, 16);
return new SecretKeySpec(key, "AES");
}
You don't use password anywhere within generateSecretKey, so it'll create the same secret key every time...
If you change it to:
public SecretKeySpec generateSecretKey(String password) throws Exception {
MessageDigest shahash = MessageDigest.getInstance("SHA-1");
byte[] key = shahash.digest(password.getBytes("UTF-8"));
key = Arrays.copyOf(key, 16);
return new SecretKeySpec(key, "AES");
}
then it will fail as expected when given the wrong password. That doesn't necessarily mean it's the best way of creating a secret key, or that any of the rest of the crypto code is appropriate, but I don't have enough experience to comment on that.
The generateSecretKey method is broken. It generates the key based on the digest of the empty string – the password argument is ignored.
There are also other issues. ECB mode is not secure. Key derivation is very weak here. Depending on the platform default character encoding is not a good idea.