I need to read an encrypted message sent from a device with ATMEL chip.
The Communication flow is attached here:
I did everything about socket communication and ECDSA signature verifying.
Now I am stuck at the decrypt phase.
The data needed for an example decryption is the following:
// 0 - private key (prv-h, android host) : "308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104206cb80a311272dae8a6bf01273c865a7d2890c16436330b88cb53d2bc55133f23a00a06082a8648ce3d030107a144034200046342f6b12beab7dd829bb95cee5073c8968e1cb406c185ab6dfd8889df0aec1f7ed1a31af57a0df00454cd683d83ee490e6747adbb6d6d68ab345149e7de1366"
// 1 / public ephemereal key (device pub-x): "04F0442494445A67606873A943F228A11FEE28B56502B3753E1FD4B5A369BA62B9DF0965C27E069E4997893CDBCC06B72A39907632FF6E4740597970F3DBEC9A17"
// 2 / encrypted message: "5A92B516C7F6C29168879D913F4D6210EC689419C16B67FB17365CB8C2363D80" "C263D7389158EB0ADF4470FC689CA6726F87EC82C3DCEAE49005C90230034DF7" (2 blocks of 64 bytes. I still don't know if i can collate them or decrypt one by one)
// 3 / iv (generated by device and sent) : "28395B5C43A82E3951B153A33B10B01C"
// 4 / randNum1 (generated by android and previously sent to device): "9f7ed0f97069b01b097f1e6b7e28465678a0b7cd45cfb354de0632b308879f94"
I try to decrypt using the flow found here:
but i found some diferences, overall being it verify-then-decrypy, while i need a decrypt-then-mac solution.
Also, that solution relies on a "tag" that I think I don't have.
And also, that solution doesn't use a randNum previously sent by the host.
At the moment, I can do this:
public static String DeCrypt(String privateKey_s, String ephemeralPublicKey_s, String message_s, String tag_s, String iv_s, String randNum1_s) throws Exception {
// premasterkey = ecdh(private, public)
PrivateKey privateKey = FromHexStringToPrivateKey(privateKey_s);
PublicKey ephemeralPublicKey = FromCompactStringToPublicKey(ephemeralPublicKey_s);
KeyAgreement ka = KeyAgreement.getInstance("ECDH");
ka.init(privateKey);
ka.doPhase(ephemeralPublicKey, true);
byte[] preMasterKey = ka.generateSecret();
Here, I get a 16 bytes (32 hex chars) preMasterKey. I think it is correct. But from this point, I don't know how to do well. I see that the reference code mixes the public key with the shared secret (preMasterKey) to get the session key (encryption key), but my flow requires that the session key is derived from randNum1 and preMasterKey
byte[] randNum1 = hexStringToByteArray(randNum1_s);
byte[] message = hexStringToByteArray(message_s);
byte[] iv = hexStringToByteArray(iv_s);
// Deriving encryption and mac keys.
HKDFBytesGenerator hkdfBytesGenerator = new HKDFBytesGenerator(new SHA256Digest());
byte[] khdfInput = ByteUtils.concatenate(randNum1, preMasterKey);
hkdfBytesGenerator.init(new HKDFParameters(khdfInput, null, "Android".getBytes(Charset.forName("UTF-8"))));
byte[] encryptionKey = new byte[16];
hkdfBytesGenerator.generateBytes(encryptionKey, 0, 16);
byte[] macKey = new byte[16];
hkdfBytesGenerator.generateBytes(macKey, 0, 16);
// Verifying Message Authentication Code (aka mac/tag)
Mac macGenerator = Mac.getInstance("HmacSHA256", "BC");
macGenerator.init(new SecretKeySpec(macKey, "HmacSHA256"));
byte[] expectedTag = macGenerator.doFinal(messaggio);
// if (!isArrayEqual(tag, expectedTag)) {
// throw new RuntimeException("Bad Message Authentication Code!");
// }
// Decrypting the message.
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
cipher.init(
Cipher.DECRYPT_MODE,
new SecretKeySpec(encryptionKey, "AES"),
new IvParameterSpec(iv));
return new String(cipher.doFinal(messaggio));
}
Addendum
Maybe my private key is badly formatted.
I generated the pair through this piece of code:
public String GenKeysHex() throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(256);
KeyPair keyPair = kpg.generateKeyPair();
PrivateKey prv = keyPair.getPrivate();
PublicKey pub = keyPair.getPublic();
byte[] prvBytes = prv.getEncoded();
byte[] pubBytes = pub.getEncoded();
String result1 = new BigInteger(1, prvBytes).toString(16);
String result2 = new BigInteger(1, pubBytes).toString(16);
String result3 = FromLongToCompactPublic(result2);
return result3 + "," + result1 + "," + result2 ;
}
public static PrivateKey FromHexToPrivateKey(String Hex) throws Exception {
byte[] hardcodedPrivate = hexStringToByteArray(Hex);
PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(hardcodedPrivate);
KeyFactory factory = KeyFactory.getInstance("EC");
PrivateKey privateKey = factory.generatePrivate(privateSpec);
return privateKey;
}
public static String FromPrivateKeyToHex(PrivateKey key) throws Exception {
byte[] prvBytes = key.getEncoded();
String result1 = new BigInteger(1, prvBytes).toString(16);
return result1;
}
This is because i need to create a library and I need to exchange import/export data in String format, so I checked that the text/Hex format of the private key would be consistent through the other two routines.
I wonder how to to get the private key in a more compact form.