JavaScript encryption based on Java generated RSA key - java

I'm trying to implement a solution for encryption between Java and JavaScript.
on the Java end I have the following static block:
public class Manager {
public static KeyPairGenerator keyPairGenerator;
public static KeyPair keyPair;
static{
try {
keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
keyPair = keyPairGenerator.genKeyPair();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
...
}
This basically generates a fresh KeyPair once my server is up and running...
then I give the public key in a JSON format:
<%
JSONObject json = new JSONObject();
json.put("publicKey", "-----BEGIN PUBLIC KEY-----" + Base64.encodeBase64URLSafeString(Manager.keyPair.getPublic().getEncoded()) + "-----END PUBLIC KEY-----");
%>
and I want to use that key (be it 1024 or 2048 bit) to encode information coming from client's forms...
anyone knows how can I encode the information using an RSA 1024 bit, base64 encoded public key?
I tried jCryption and severel other libraries to no avail...

If you don't send your public key as a certificate, you are better off just sending the modulus and the public exponent separately (e.g. base 64 encoded in separate fields). The default encoding will result in a X509 SubjectPublicKeyInfo ASN.1 structure, which you would need to parse in your JavaScript libraries.
Note that you are protecting only against eavesdroppers; man-in-the-middle attacks are still viable as they can replace your public key with their own. RSA 1024 is of course outdated by now. Fortunately you still have TLS/SSL to protect you.

Related

RSA public key to base64 String

I have written a socket server. The server-side is Java and the client-side is in Python. The server uses TCP communication and since TCP is not safe I use asymmetric encryption. The server has a private-public key pair and every time a client connects it sends the public key and requests clients public key.
In order to send the PublicKey object over TCP, I convert it to string with this function.
public String publicKeyToString(PublicKey publicKey){
byte[] pKBytes = publicKey.getEncoded(); // Get the public key to bytes
return Base64.getEncoder().encodeToString(pKBytes); // Convert the public key to String
}
On the other side to create a PublicKey object from string I use this function
public PublicKey publicKeyFromString(String key) throws NoSuchAlgorithmException, InvalidKeySpecException {
byte[] keyBytes = Base64.getDecoder().decode(key);
KeyFactory kf = KeyFactory.getInstance("RSA");
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
return kf.generatePublic(spec);
}
The above code works very well with java clients. Now I would like to do the same thing in python. I create the private-public key with this block of code
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
client_private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
client_public_key = client_private_key.public_key()
The problem now is that I don't know how to extract a string from the public key so I can send it to the server. Also, the string key will be decoded from public String publicKeyToString(PublicKey publicKey) so it should be encoded with base64.
"Specs":
Java 8 with java.security library
Python 3 with cryptography library
Encryption algorithm RSA
If you have a better library for python I am open to suggestions, java is somewhat fixed because by this point to much code is written.

Java - Storing Keypairs in KeyStore

I am implementing an Hybrid Encryption project and I have generated 2 key pairs for sender and receiver keys (private and public). I have these keys in a file.
KeyPair Generation Code
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import Decoder.BASE64Encoder;
public class GenerateRSAKeys{
public static void main(String[] args)
{
String publicKeyFilename = null;
String privateKeyFilename = null;
publicKeyFilename = "C:\\Users\\imjme1\\Desktop\\Work_backup\\FMS\\EPM_FILE_ENCRYPTION\\NIFT_SOLUTION\\sender_keys\\receiver_publicKey";
privateKeyFilename = "C:\\Users\\imjme1\\Desktop\\Work_backup\\FMS\\EPM_FILE_ENCRYPTION\\NIFT_SOLUTION\\sender_keys\\receiver_privateKey";
GenerateRSAKeys generateRSAKeys = new GenerateRSAKeys();
generateRSAKeys.generate(publicKeyFilename, privateKeyFilename);
}
private void generate (String publicKeyFilename, String privateFilename){
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// Create the public and private keys
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
BASE64Encoder b64 = new BASE64Encoder();
SecureRandom random = createFixedRandom();
generator.initialize(1024, random);
KeyPair pair = generator.generateKeyPair();
Key pubKey = pair.getPublic();
Key privKey = pair.getPrivate();
System.out.println("publicKey : " + b64.encode(pubKey.getEncoded()));
System.out.println("privateKey : " + b64.encode(privKey.getEncoded()));
BufferedWriter out = new BufferedWriter(new FileWriter(publicKeyFilename));
out.write(b64.encode(pubKey.getEncoded()));
out.close();
out = new BufferedWriter(new FileWriter(privateFilename));
out.write(b64.encode(privKey.getEncoded()));
out.close();
}
catch (Exception e) {
System.out.println(e);
}
}
public static SecureRandom createFixedRandom()
{
return new FixedRand();
}
private static class FixedRand extends SecureRandom {
MessageDigest sha;
byte[] state;
FixedRand() {
try
{
this.sha = MessageDigest.getInstance("SHA-1");
this.state = sha.digest();
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException("can't find SHA-1!");
}
}
public void nextBytes(byte[] bytes){
int off = 0;
sha.update(state);
while (off < bytes.length)
{
state = sha.digest();
if (bytes.length - off > state.length)
{
System.arraycopy(state, 0, bytes, off, state.length);
}
else
{
System.arraycopy(state, 0, bytes, off, bytes.length - off);
}
off += state.length;
sha.update(state);
}
}
}
}
Now, I need to secure these keys (probably not on any disk directly).
I have searched for this case on the internet; saving keys into key stores is the way to secure keys and use the same keystore when reading keys for the use in encryption and decryption.
Can someone suggest me how to save public and private key in key store and how to read it in java?
The Java KeyStore API (and underlying providers) does not support storing a keypair -- that is, a privatekey and publickey (which can then be used for publickey operations like encrypt and verify). It does support storing a privatekey and a certificate chain for the publickey. The certificate chain can consist of only one certificate, especially if that certificate is a self-signed certificate (which by definition is a chain by itself). That is exactly what commandline keytool -genkeypair does: it generates a keypair and stores the privatekey plus a self-signed certificate for the publickey in the (specified or defaulted) keystore file.
If you were using only 'standard' JRE, creating a self-signed (or other) certificate in code is not very easy.
However, if you have BouncyCastle added, as you obviously do or the code you posted wouldn't work, that has numerous applicable capabilities. For a simple example of generating a cert with only bcprov (the old, deprecated X509V3CertificateGenerator) see my JAVA API to create a keystore and attaching a csr and keypair to it -- but do as I recommended there but didn't show: don't create the unneeded CSR first, instead create the cert using the desired name and publickey.
Better, if you also have or can get bcpkix, use X509v3CertificateBuilder. See examples at Self signed X509 Certificate with Bouncy Castle in Java and Generating X509 Certificate using Bouncy Castle Java .
For writing out, and reading back in, a keystore file containing this data, just see the Javadoc linked above. Note Java traditionally defaulted to JKS-format files, and you will find many older answers here and elsewhere on the web which assume that, but since Java 9 in 2017 it defaults to PKCS12-format, which is not only standard and thus (mostly) interoperable but also more secure (uses a much better PBKDF algorithm).
However, your code uses a completely bogus random generator, and as a result generates the same keypair every time. Given this, there is no real need to store it, since you can always re-generate it any time you want. In fact, there is no benefit to even having this keypairs since it provides no security at all.

Android RSA encryption from public string

I'm working on an android application where I'd like the user to be able to encrypt messages using other's public keys. The system would generate a public/private keypair and then messages can be sent to other users secretly.
I'm creating an Encryption class which will handle the encryption/decryption of messages. Unfortunately I'm having some problems.
In this method, I'd like to pass the user's secret (private key) as well as the message they want to encrypt. I'd like the secret to be user-defined (like "MySecretPassword").
public static void lock(String secret, String textToEncrypt) {
try {
//Convert the public key string into a key
byte[] encodedPublicKey = Base64.decode(secret.getBytes("utf-8"),Base64.DEFAULT);
X509EncodedKeySpec spec = new X509EncodedKeySpec(encodedPublicKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publickey = keyFactory.generatePublic(spec); //Crash Here
PrivateKey privateKey = keyFactory.generatePrivate(spec);
//Encrypt Message
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publickey);
byte[] encryptedBytes = cipher.doFinal(textToEncrypt.getBytes());
Log.d(TAG,"Encrypted: "+new String(encryptedBytes));
} catch (Exception e) {
e.printStackTrace();
}
}
The exception is as follows:
java.security.spec.InvalidKeySpecException: java.lang.RuntimeException: error:0c0740b0:ASN.1 encoding routines:ASN1_get_object:TOO_LONG
What am I missing here? Am I missing something obvious or am I misunderstanding how these tools work? I've used this javascript library for public/private key encryption before and am trying to do something similar here. I'd appreciate it if somebody could point me in the right direction :)
A secret is not a public key.
You encrypt with the public key of the recipient. That value is public, which means that anybody can look it up. You need to get the value of the other party's public key and feed it into your code, not send in your own private key. The proper way to do this does not involve any secrets!
Normally one does not directly encrypt a message with RSA, instead they encrypt an AES key (or other symmetric key) with RSA and use the AES key to encrypt the message. If your messages are really short, you could use RSA directly, but it won't work for long messages.
Here are a couple links showing how to implement RSA on Android:
RSA using SpongyCastle
RSA encryption in Android and Java

IllegalBlockSizeException in RSA encryption on Android only

I'm currently working on an Java-Cyrpto-API which I want to include in an Android app later on. I tested every function of my Crypto-API and after all unit test succeeded I decided to include my jar into an Android project.
In the project I started generating a 4096-bit key pair in order to add it to an object in my class.
RSA.RSAKeyPair keyPair = null;
try {
keyPair = RSA.generateKeyPair(4096);
} catch (IOException e) {
e.printStackTrace();
}
self.setPrivateKey(keyPair.getPrivateKey());
self.setPublicKey(keyPair.getPublicKey());
Afterwards I call a function in my API which uses data from the "self" object to encrypt some data.
The app throws me the following exception when it tries to encrypt some data with RSA.
03-15 02:39:16.769 2394-2414/de.ifasec.instari E/Testīš• javax.crypto.IllegalBlockSizeException: input must be under 512 bytes
at com.android.org.conscrypt.OpenSSLCipherRSA.engineDoFinal(OpenSSLCipherRSA.java:245)
at javax.crypto.Cipher.doFinal(Cipher.java:1340)
at com.instari.encryption.RSA.encryptWithPublic(RSA.java:91)
I used Google to find out whats going wrong here an only found posts about invalid key lengths. I used the debugger to get all keys and values I generated in the app to test them directly in my API. My API tests succeeded without any errors.
Does Android have any restrictions or problems with RSA-Encryption?
Edit:
private static final String CIPHER_ALGORITHM = "RSA/ECB/PKCS1Padding";
This is my encryptWithPublic() Method:
// initialize
byte[] byteData = data.getBytes(); // convert string to byte array
PublicKey keyObject = extractPublicKey(publicKey);
// encrypt
Cipher cipher = null; // create conversion processing object
try {
cipher = Cipher.getInstance(CIPHER_ALGORITHM);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
return null;
}
cipher.init(Cipher.ENCRYPT_MODE, keyObject); // initialize object's mode and key
byte[] encryptedByteData = cipher.doFinal(byteData); // use object for encryption
return Base64.encode(encryptedByteData);
RSA.RSAKeyPair is a simple class i added to store the key:
public static class RSAKeyPair{
private String privateKey;
private String publicKey;
private RSAKeyPair(String privateKey, String publicKey) {
this.privateKey = privateKey;
this.publicKey = publicKey;
}
public String getPrivateKey() {
return privateKey;
}
public String getPublicKey() {
return publicKey;
}
}
The object self is similar to this. It just returns the keys I added before.
It seems you are just trying to encrypt too much data. The amount of data that can be encrypted using RSA with PKCS#1 padding is the key size (512 bytes) minus the padding overhead of 11 bytes, making for a total of 501 bytes. This is true for both Android, but also for Java SE.
With Java, the "ECB" part is a bit of a misnomer. RSA doesn't use any mode of operation, so it should have been "None". Only one block of plaintext will be encrypted. If you want to encrypt more, you can first generate a random AES key, use that to encrypt the message, and subsequently encrypt the random key using RSA. This is called hybrid encryption.

How to Generate an AES Key using a Local Private Key and a Remote Public Key in Java?

Hi I was reading the following Wikipedia article on Public Key Cryptography
Public Key Cryptography
I saw this picture too which shows how to generate a symmetric key cipher using your Private Key and the OTHER persons Public Key.
Generate Symmetric Key/Shared Secret
I already know how to exchange the public keys between the parties however I was wondering If it were possible to implement the procedure in the picture using the Java Programming Language.
The Private and Public Keys used would be generated using RSA and the Key/Shared Secret to be generated would be a Symmetric Key for a Symmetric Cipher (I want to use AES-128)
I understand the theory behind this but am unsure how to implement it in Java properly, any ideas or help would be much appreciated :)
Try KeyAgreement and algorithm "DiffieHellman". Note that you need Java 8 for 2048 bit key sizes, Java 7 and below are stuck on 1024 (unless you install the Bouncy Castle provider).
Or you can go for the "ECDH" algorithm, but beware that takes a bit of a learning curve.
Thanks owlstead using that I was able to write the following code which generates RSA keys and then Tries to link the private keys together with the opposing public key using DiffieHellman
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class AESKeGenFromRSA
{
public static void main(String[] args)
{
try
{
// Generate RSA KeyPair for Alice
Cipher alice_rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// Get RSA KeyPairGenerator Object Instance
KeyPairGenerator alice_gen = KeyPairGenerator.getInstance("RSA");
// Generate RSA Assymetric KeyPair
KeyPair alice_pair = alice_gen.generateKeyPair();
// Extract Public Key
PublicKey alice_pub = alice_pair.getPublic();
// Extract Private Key
PrivateKey alice_pvt = alice_pair.getPrivate();
System.out.println();
System.out.println("Alice Public: " + alice_pub);
System.out.println();
System.out.println("Alice Private: " + alice_pvt);
// Generate RSA KeyPair for Bob
Cipher bob_rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");
// Get RSA KeyPairGenerator Object Instance
KeyPairGenerator bob_gen = KeyPairGenerator.getInstance("RSA");
// Generate RSA Assymetric KeyPair
KeyPair bob_pair = bob_gen.generateKeyPair();
// Extract Public Key
PublicKey bob_pub = bob_pair.getPublic();
// Extract Private Key
PrivateKey bob_pvt = bob_pair.getPrivate();
System.out.println();
System.out.println("Bob Public: " + bob_pub);
System.out.println();
System.out.println("Bob Private: " + bob_pvt);
// Create KeyAgreement for Alice
KeyAgreement alice_agreement = KeyAgreement.getInstance("DiffieHellman");
alice_agreement.init(alice_pvt);
alice_agreement.doPhase(bob_pub, true);
byte[] alice_secret = alice_agreement.generateSecret();
SecretKeySpec alice_aes = new SecretKeySpec(alice_secret, "AES");
// Create KeyAgreement for Bob
KeyAgreement bob_agreement = KeyAgreement.getInstance("DiffieHellman");
bob_agreement.init(bob_pvt);
bob_agreement.doPhase(alice_pub, true);
byte[] bob_secret = bob_agreement.generateSecret();
SecretKeySpec bob_aes = new SecretKeySpec(bob_secret, "AES");
System.out.println();
System.out.println(alice_aes.equals(bob_aes));
}
catch (NoSuchAlgorithmException e)
{e.printStackTrace();}
catch (NoSuchPaddingException e)
{e.printStackTrace();}
catch (InvalidKeyException e)
{e.printStackTrace();}
}
}
This is the exception that gets thrown when I try to run the program. I understand why this is happening but I'm a little unsure how to fix it.
java.security.InvalidKeyException: No installed provider supports this key: sun.security.rsa.RSAPrivateCrtKeyImpl
at javax.crypto.KeyAgreement.chooseProvider(KeyAgreement.java:398)
at javax.crypto.KeyAgreement.init(KeyAgreement.java:464)
at javax.crypto.KeyAgreement.init(KeyAgreement.java:435)
at AESKeGenFromRSA.main(AESKeGenFromRSA.java:45)
It seems that PrivateKey objects are not a valid argument to the KeyAgreement.init(Key key) function, Any ideas would be appreciated ...

Categories