RSA public key generation issue (PHP/Java integration) - java

I have one server and one client.
Client might be in different technologies like java , php.
server code is written in java.
What I am doing in server is, get exponent and modulus bytes of client public key and generate public key of client.
To generate client public key I am using the code below:
RSAPublicKeySpec spec = new RSAPublicKeySpec(modulusBigInt,exponentBigInt);
keyFactory = KeyFactory.getInstance("RSA", "BC");
RSAPublicKey clientPublicKey = (RSAPublicKey) keyFactory.generatePublic(spec);
To encrypt data using client public key I am using below code:
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, clientPublickey);
scrambled = cipher.doFinal(buffer);
Server Information
I have implemented RSA-1024 to encrypt AES key.
I am using RSA/ECB/PKCS1Padding algorithm. and I have also make sure that all clients have also consider 1 padding in their code.
Client - 1 (Java)
If client is also made in java than public key is successfully generated from exponent and modulus of client public key.
I am using the code below to generate key pair of client RSA key..
keyPairGene = KeyPairGenerator.getInstance("RSA");
keyPairGene.initialize(1024);
KeyPair keyPair = keyPairGene.genKeyPair();
RSAPublicKey clientPublickey = (RSAPublicKey) keyPair.getPublic();
Client -2 (php)
Now the problem is if client is in php .. than public key is successfully generated but when I try to encrypt using that public key at that time bad padding exception occurs when I have used default provider in server.
I am using bellow code to generate key pair of client RSA key..
$options = array('private_key_bits' => 1024,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
'config' => realpath(__DIR__) . '/openssl.cnf');
#Generates New Private / Public Key Pair
$pkGenerate = openssl_pkey_new($options);
#Get Client Private Key
openssl_pkey_export($pkGenerate, $PrivateKey, NULL, $options);
#Get Client Public Key
$keyData = openssl_pkey_get_details($pkGenerate);
Than I have tried BC provider ... It gives me the exception below:
org.bouncycastle.crypto.DataLengthException: input too large for RSA cipher.
I am not getting what is the problem occurs when I am trying to generate public key from exponent and modulus when client is in php...
if client is in java than its no issues.... and works perfectly..
Any type of help is welcome...
Note:
What I have observed from debugging code is ,
client's public key modulus byte's bit length at server side is varies between 1020 to 1023... it never reaches at 1024 though we have define size as 1024.

Still don't get what exactly the problem is...
but I have implemented work around for it...
I got stuck in generating public key of client using exponent and modulus.
So now I have used one standard format of public key certificate - DER & PEM.
What I did is, generated DER or PEM from PHP side using bellow code,
$options = array('private_key_bits' => 1024,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
'config' => realpath(__DIR__) . '/openssl.cnf');
#Get Client Public Key
$keyData = openssl_pkey_get_details($pkGenerate);
$clientPublicKey = $keyData['key'];
$this->clientData['clientPublicKeyPEM'] = $keyData['key'];
And then send that generated PEM to Server (Java).
And at server side I have developed bellow code to regenerate Public key from POM string.
KeyFactory keyFactory=KeyFactory.getInstance("RSA");
byte[] pubKeyBits = Base64.decodeBase64(clientPublickeyView.getModulusBytes());
PublicKey pubKey=keyFactory.generatePublic(new X509EncodedKeySpec(pubKeyBits));

Related

Java, Android Keystore - supply unencrypted API key or secret for encrypting

As i am new to Android, i am working on hiding my API keys and found Android keystore the way to go. But when i see examples of how to use Android Keystore, one thing i am not understanding is how to supply the unencrypted original key for encryption? if i am storing in the code, wouldn't that beat the purpose of using Android Keystore?
from an article on storing secrets:
https://medium.com/#ericfu/securely-storing-secrets-in-an-android-application-501f030ae5a3
Generate a random key when the app runs the first time;
When you want to store a secret, retrieve the key from KeyStore, encrypt the data with it, and then store the encrypted data in
Preferences.
When you want to read a secret, read the encrypted data from Preferences, get the key from KeyStore and then use the key to decrypt
the data
In second point, it says encrypt the data with it. How to supply the data without exposing to the code/application?
I apologize if this has been answered.
Thanks
private static final String KEYSTORE_PROVIDER = "AndroidKeyStore";
private static final String AES_MODE = "AES/GCM/NoPadding";
private static final String KEY_ALIAS = "MyNiceKey";
Load the default AndroidKeyStore:
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER);
keyStore.load(null);
Generate AES key inside the KeyStore which in the latest verision of android, it is hardware-backed keystore; it means that it is very hard to extract the bytes of the key from it:
if (!keyStore.containsAlias(KEY_ALIAS)) {
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_PROVIDER);
keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(false)
.build());
keyGenerator.generateKey();
}
Anyway you should use .setRandomizedEncryptionRequired(true). There is no point to set up a faulty protocol. Otherwise, if you have to encrypt only few bytes(your API key) you could create an asymmetric public/private key and encrypt it with RSA so that you don't even need to provide the IV.
Haing said that, when you get the secret key from the KeyStore:
public static SecretKey getKeyStoreSecretKeyEntry(final String entryAlias)
throws GeneralSecurityException, IOException {
return ((KeyStore.SecretKeyEntry) getKeyStore().getEntry(entryAlias, null)).getSecretKey();
}
the returned SecretKey does not containt the Key Material (the real bytes of the key) but only its reference. So you can use it freely iside the Cipher to encrypt and decrypt what you want. In any case, you API key will be exposed enyway if you use it to make http request directly to you service. The best way to go in your case is to use a server like Google Firebase
P.s. there is very a simple library from google that will save you time and headache:
https://developer.android.com/jetpack/androidx/releases/security
https://developer.android.com/topic/security/data
Conclusion: The key you generate within the android Key Store is property of the user and it should be used to protect the user's private data. So it is not a good practice to encrypt an API Key, wich is the developer's private data, with the user key. Use a server to protect the API key.

Openssl encryption [Kotlin] [Android] (PHP)

Using api I have to connect to PHP project with encrypted login and password in Kotlin.
I need to use the same secret password as I use in PHP, but have problem with generating Secret Key by following line:
val sk = SecretKeySpec(secretKey.toByteArray(Charsets.UTF_8),"AES_256") //here exception is throwing
val iv = IvParameterSpec(secretKey.substring(0, 16).toByteArray(Charsets.UTF_8))
c.init(opmode, sk, iv)
That's my secret key:
ksjdg*&%$dfgh"{##!vcfkslc,.a/dcfxcsw345,45654gfdsgtrasd;fsdjf]}{O0-xfvbgdfeh=
The problem is that exception is throwing about unsupported key size. I got it.
But why using PHP and doing the same, using the same key, don't have any errors? :
$encryptionKey = `ksjdg*&%$dfgh"{##!vcfkslc,.a/dcfxcsw345,45654gfdsgtrasd;fsdjf]}{O0-xfvbgdfeh=`
$encrypted = openssl_encrypt($value, $encryptionMethod, $encryptionKey, 0, $iv);
What should do in Kotlin with my key to have it worked?

creating HMAC in Nodejs with base64 encoded secret

I'm trying to generate HMAC of a message. The algo for HMAC generation is SHA256. The issue is i have a base64 encoded key(shared secret). How can i decode this secret to get the required hmac
Sample code:
var hmac = require('crypto').createHmac('SHA256', "SOME_BASE64_ENCODED_SHARED_SECRET").update("MESSAGE").digest('base64');
This hmac is sent to a java service. The way it does hmac generation is as follows:
Mac mac = Mac.getInstance("HmacSha256");
SecretKey sharedKey = new SecretKeySpec(Base64.getDecoder().decode("SOME_BASE64_ENCODED_SHARED_SECRET"), "TlsPremasterSecret");
mac.init(sharedKey);
byte[] messageBytes = "MESSAGE".getBytes("UTF-8");
byte[] expectedHmac = mac.doFinal(messageBytes);
String hmac = Base64.getEncoder().encodeToString(expectedHmac));
Now, the HMACs generated by my nodejs code does not match with Java service code. How do i solve this problem?
The base64-encoded secret needs to be decoded before passing it to crypto.createHmac():
var secret = Buffer.from('SOME_BASE64_ENCODED_SHARED_SECRET', 'base64');
var hmac = require('crypto').createHmac('SHA256', secret)
.update('MESSAGE')
.digest('base64');
//include crypto
var crypto = require('crypto');
var yourMessage = 'your signature to be hashed using HMAC SHA256';
var sharedSecret = 'your shared secret key';
//generate hmac sha256 hash
var hmacSignature = crypto.createHmac('SHA256', new Buffer(sharedSecret, 'base64')).update(yourMessage).digest('base64');
Above worked for me too.
Note: HMAC SHA256 is hash value, it cannot be decoded. Based on unique secret and unique message (generally date-time is used) a unique hash is created. Client sends this hash value and server generates its own hash value using same algorith, if both hash value match then authorization is successful.
I spent lot of time troubleshooting this. Hope above info help others.

How to store a SecretKey within a Java KeyStore protected by a PublicKey (RSAPasswordProtection)?

I want to store a SecretKey within a Java KeyStore protected by a PublicKey. When loading the protected KeyEntry i would like to
get the protected key byte-array to manually unwrap it later on with a PrivateKey.
let the KeyStore handle the unwrapping when handing over the PrivateKey.
Using the setEntry()-Method with an already wrapped byte-Array is possible. Also getting the wrapped byte-Array back can be done by using the getEntry()-Method. To encrypt a SecretKey the setEntry()-Method supports the usage of a ProtectionParameter. The only ProtectionParameter i could find was the PasswordProtection parameter.
Does anyone know about a RsaProtection for Java KeyStore? Or is there another way around to be able to wrap SecretKeys using a PublicKey and getting it back using a PrivateKey?
The Java key stores are certainly not able to handle this; they primarily use symmetric encryption to protect the key stores. It is possible to wrap and unwrap keys though. I've shown this using OAEP instead of the less safe "RSA" (PKCS#1) encryption:
Cipher rsa = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
rsa.init(Cipher.WRAP_MODE, keyPair.getPublic());
byte[] wrapped = rsa.wrap(aesKey);
rsa.init(Cipher.UNWRAP_MODE, keyPair.getPrivate());
SecretKey unwrappedAESKey = (SecretKey) rsa.unwrap(wrapped, "RSA", Cipher.SECRET_KEY);

What is the proper way to perform authenticated encryption in Java?

Authenticated encryption requires that we use some accepted standard for encrypting and authenticating a message. So we both encrypt the message and compute a MAC on the message to verify it has not been tampered with.
This question outlines a way to perform password based key strengthening and encryption:
/* Derive the key, given password and salt. */
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal("Hello, World!".getBytes("UTF-8"));
But as far as I can tell, this does not compute any MAC on the ciphertext and so would be insecure. What is the accepted standard for performing authenticated encryption in Java?
I would recommend using GCM mode encryption. It is included in the latest JDK (1.7) by default. It uses a counter mode encryption (a stream cipher, no padding required) and adds an authentication tag. One big advantage is that it requires only a single key, whereas HMAC adds another key to the mix. Bouncy Castle has an implementation as well, which is moslty compatible with one provided by Oracle.
GCM mode encryption is also features in a TLS RFC, and in XML encrypt 1.1 (both not final). GCM mode provides all three security features: confidentiality, integrity and authenticity of the data send. The String would be "AES/GCM/NoPadding" instead of the CBC one you are now deploying. As said, make sure you have the latest JDK from Oracle, or have Bouncy Castle provider installed.
Also check out my answer here, which is mostly about String encoding, but I've succesfully tried GCM mode too - see the comment.
When transferring files from one server to another through secure ftp, I use private/public key pairs with the private key residing on the "from" server and the public key residing on the "to" server.
Using private/public key pairs is a secure standard when transferring files.
I believe it would also be a secure means in the context of a Java application.
Check out Generating and Verifying Signatures and Generate Public and Private Keys
for more details on using a private/public key pair setup for digital signatures in Java.

Categories