hmac message encryption but using our own key - java

try { // Generate a key for the HMAC-MD5 keyed-hashing algorithm
KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5");
SecretKey key = keyGen.generateKey();
// Generate a key for the HMAC-SHA1 keyed-hashing algorithm
keyGen = KeyGenerator.getInstance("HmacSHA1");
key = keyGen.generateKey(); }
catch (java.security.NoSuchAlgorithmException e) { }
Above code would give us keys to digest a message using HMAC. Now i wanted to implement a HMAC logic in java where the key would be given by user.
Any ideas?

Construct a KeySpec using the provided user key (either a SecretKeySpec if it's a byte array, or a PBEKeySpec if it's a password), then use a SecretKeyFactory to turn that into a SecretKey.

Related

Android: Check if Key is in Secure Hardware

For my application, I create an AES key and want to check whether said key is stored inside the Secure Hardware. I googled and found an example for RSA, but figured it shouldn't matter. Below is the RSA example I found:
final KeyGenerator keyGenerator = KeyGenerator
.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
final KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder("key_alias",
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build();
keyGenerator.init(keyGenParameterSpec);
final SecretKey secretKey = keyGenerator.generateKey();
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
KeyFactory keyFactory = KeyFactory.getInstance(secretKey.getAlgorithm(), "AndroidKeyStore");
KeyInfo keyInfo = keyFactory.getKeySpec(secretKey, KeyInfo.class);
keyInfo.isInsideSecureHardware();
However, the very first line returns a no such algorithm: AES for provider AndroidKeyStore exception. But shouldn't it be possible to check if an AES key is inside the secure hardware for AES as well?
Theoretically I could use asymmetric encryption, since it is only a small snippet of data I want to en/decrypt but I would still prefer if I could use symmetric encryption.
Do you guys have an idea?
Edit: Added further implementation details.
In order to get KeyInfo for a symmetric key, the following code is needed:
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(secretKey.getAlgorithm(), "AndroidKeyStore");
KeyInfo keyInfo = (KeyInfo) secretKeyFactory.getKeySpec(secretKey, KeyInfo.class);

Java SecretKeyFactory generated key is same as input password

I'm trying to generate a secret key using PBE but the secret key generated by the SecretKeyFactory is exactly the same as the input password. I've tried different algorithms, iteration counts etc. and it is still the same so I feel I'm missing a step here.
public SecretKey generateKey(String password, String salt) {
char[] passChars = password.toCharArray();
byte[] saltBytes = salt.getBytes();
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithHmacSHA256AndAES_128");
PBEKeySpec keySpec = new PBEKeySpec(passChars, saltBytes, 2048, 128);
SecretKey secretKey = keyFactory.generateSecret(keySpec);
byte[] encodedKey = secretKey.getEncoded();
System.out.println("key: " + new String(encodedKey));
return new SecretKeySpec(encodedKey, "AES");
}
EDIT: if I use the algorithm "PBKDF2WithHmacSHA1" then the key generated is different from the password, but how come the algorithm I'm using is generating a key that is exactly the same as the input password?
When you generate a SecretKey using the SecretKeyFactory PBEWithHmacSHA256AndAES_128 you will get an instance of com.sun.crypto.provider.PBEKey and this class has the "special feature" that it returns the original "key" (aka password) when calling getEncoded() and not the cryptographic key material. If I understand it correctly the key derivation will not be made by the KeyFactory but by the Cipher itself.
Therefore you should not try to convert the SecretKey instance into a SecretKeySpec instance; instead just use the generated SecretKey instance in the correct cipher instance:
Cipher c = Cipher.getInstance("PBEWithHmacSHA256AndAES_128");
c.init(Cipher.ENCRYPT_MODE, secretKey);

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

Encrypting a AES key using RSA Key

I have written a code to encrypt a aes key and decrypt it but it dosent seem to be happening.Why is this so?
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair pair = keyGen.generateKeyPair();
PublicKey pubKey= pair.getPublic();
PrivateKey privateKey = pair.getPrivate();
Cipher c1 = Cipher.getInstance("RSA/ECB/PKCS1Padding");
c1.init(Cipher.ENCRYPT_MODE, pubKey);
KeyGenerator aesKeyGenerator = KeyGenerator.getInstance("AES");
aesKeyGenerator.init(256);
Key aesKey = rijndaelKeyGenerator.generateKey();
Cipher symmetricCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] encodedKeyBytes = c1.doFinal(aeslKey.getEncoded());
SecretKey aesKey1 = new SecretKeySpec(encodedKeyBytes, "aes1");
Cipher dec = Cipher.getInstance("RSA/ECB/PKCS1Padding");
dec.init(Cipher.DECRYPT_MODE, privateKey);
symmetricCipher.init(Cipher.DECRYPT_MODE, aesKey1, spec);
if(aesKey.getEncoded() == dec.doFinal(c1.doFinal(aesKey.getEncoded())) )
{
// Not reaching here but is supposed to
}
On the line:
SecretKey aesKey1 = new SecretKeySpec(encodedKeyBytes, "aes1");
You are converting the still (RSA) encrypted aesKey to a SecretKey. At that spot you should have decrypted the key first. "aes1" is not any known type of key either.
Please try and separate the various wrapping (key encryption) and encryption statements into methods, and make separate methods for the unwrapping and decryption. Just throwing statements around is not going to get you anywhere. Try to make a methodical attempt to solve the problem at hand.

AES KeyPairGenerator Not Recognised

I have an issue with my java code. I'm trying to encrypt a file. However, when I run my java code I get "java.security.InvalidKeyException: Invalid AES key length: 162 bytes".
Here is the code:
byte[] rawFile;
File f = new File("./src/wonkybox.stl");
FileInputStream fileReader = new FileInputStream(f);
rawFile = new byte[(int)f.length()];
fileReader.read(rawFile);
/***** Encrypt the file (CAN DO THIS ONCE!) ***********/
//Generate the public/private keys
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("AES");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG","SUN");
keyGen.initialize(1024, random);
KeyPair key = keyGen.generateKeyPair();
PrivateKey privKey = key.getPrivate();
PublicKey pubKey = key.getPublic();
//Store the keys
byte[] pkey = pubKey.getEncoded();
FileOutputStream keyfos = new FileOutputStream("./CloudStore/keys/pubkey");
keyfos.write(pkey);
keyfos.close();
pkey = privKey.getEncoded();
keyfos = new FileOutputStream("./CloudStore/keys/privkey");
keyfos.write(pkey);
keyfos.close();
//Read public/private keys
KeyFactory keyFactory = KeyFactory.getInstance("AES");
FileInputStream keyfis = new FileInputStream("./CloudStore/keys/pubkey");
byte[] encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encKey);
PublicKey pub1Key = keyFactory.generatePublic(pubKeySpec);
keyfis = new FileInputStream("./CloudStore/keys/privkey");
encKey = new byte[keyfis.available()];
keyfis.read(encKey);
keyfis.close();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(encKey);
PrivateKey priv1key = keyFactory.generatePrivate(privKeySpec);
//Encrypt file using public key
Cipher cipher = Cipher.getInstance("AES");
System.out.println("provider= " + cipher.getProvider());
cipher.init(Cipher.ENCRYPT_MODE, pub1Key);
byte[] encryptedFile;
encryptedFile = cipher.doFinal(rawFile);
//Write encrypted file to 'CloudStore' folder
FileOutputStream fileEncryptOutput = new FileOutputStream(new File("./CloudStore/encrypted.txt"));
fileEncryptOutput.write(encryptedFile);
fileEncryptOutput.close();
The error occurs at the line "KeyPairGenerator keyGen = KeyPairGenerator.getInstance("AES");".
AES is a symmetric algorithm, hence they use of KeyPairGenerator is not supported. To generate a key with AES you call KeyGenerator
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128); //set keysize, can be 128, 192, and 256
By looking at the rest of your code, it looks like you are trying to achive asymmetric encryption (since you call getPublic() and getPrivate() etc), so I advice you to switch to using RSA or any other asymmetric algorithm that java supports. You will most likley only need to replace AES with RSA in your getInstance(); calls, and pherhaps some fine-tuning. Good luck
As far as I know, AES is symmetric encryption algorithm i.e. it needs only one key for encryption/decryption.
From the JavaDoc of java.security.KeyPairGenerator:
The KeyPairGenerator class is used to generate pairs of public and private keys.
Meaning that it should be used for asymmetric encryption algorithms. For symmetric encryption algorithms one should use javax.crypto.KeyGenerator.
However, I advise simply mimicking some tutorial on how to encrypt / decrypt byte array in Java using AES like this one.
It uses sun.misc.Base64Encoder / Base64Decoder classes to encode / decode byte array to / from String, however you may skip this step.
Hope this helps
How can you use a keypair generator for AES? AES is a symmetric key algorithm. Refer this link. That means if you encrypt data using a key "k", then you will have to decrypt it also using the same key "k". But when you generate key pair, as the name suggests, two keys are generated and if you encrypt using one of the keys, you can decrypt only using the other key. This is the base for PKI.
If you want to use keypair generator use an algorithm like "rsa" or "dsa" in the getInstance() method like this :
KeyPairGenerator keygen=KeyPairGenerator.getInstance("rsa");
I think your code should now work fine after making the above change.

Categories