Sulla base di recenti riscontri e risultati su questo problema, ho riscritto la domanda per eliminare il rumore.Java vs Python HMAC-SHA256 Mancata corrispondenza
Ho due percorsi di codice separati, uno in Java (Android), uno e Python che eseguono quanto segue ai fini della negoziazione di un accoppiamento tra un dispositivo Android e un Python/Django.
Java:
- Generare uno syncKey
- Hash una stringa concatenata di diversi valori utilizzando il PreSharedKey (tra cui lo syncKey)
- crittografare lo syncKey utilizzando un PreSharedKey
- Invia Hash, criptato syncKey, DeviceId e variabili arbitrarie sul web server
Python
- Prendi il PreSharedKey dal deviceId
- decifrare lo syncKey criptato
- Hash una stringa concatenata di diversi valori utilizzando il PreSharedKey (compreso lo syncKey decriptato)
- Assicurarsi che le partite di hash, che conferma che il syncKey è stato decodificato correttamente e il deviceId contiene la presharedKey corretta.
Ora questo processo funziona se invio syncKey non criptato. Le ultime hash match, che dimostrano che deviceId ha la chiave preshared corretta, tuttavia non appena aggiungo l'en/decryption nel processo, l'hash non corrisponde più, nonostante il fatto che sia la syncKey sia la stringa concatenata sembrano corrispondere perfettamente carattere per carattere dall'output di debug di Java/Python.
Una stranezza del processo è che una chiave a 256 bit è necessaria per l'algoritmo di crittografia AES256, quindi sto tagliando a metà la presharedKey a 512 bit. L'alternativa di utilizzare solo una chiave a 256 bit su tutta la linea richiedeva che io passassi la chiave attraverso encode('ascii')
sul lato python, oppure che stava generando errori durante l'hashing con la chiave più breve.
Ecco il codice rilevante:
Java:
String presharedKey = getKey();
// f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd0c1c8e36fddba501ef92e72c95b47e07f98f7fd9cb63da75c008a3201124ea5d
String deviceId = getDeviceId();
// 1605788742789230
SyncKey syncKey = generateSyncKey();
// 824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9
String concat = syncKey.hexString();
// 824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9
String ALGORITHM = "HmacSHA256";
String hash = null;
try {
SecretKeySpec keySpec = new SecretKeySpec(
presharedKey.getBytes(),
ALGORITHM);
Mac mac = Mac.getInstance(ALGORITHM);
mac.init(keySpec);
byte[] result = mac.doFinal(concat.getBytes());
hash = Base64.encodeToString(result, Base64.DEFAULT);
// FpDE2JLmCBr+/rW+n/jBHH13F8AV80sUM2fQAY2IpRs=
} catch (NoSuchAlgorithmException x) {
} catch (InvalidKeyException x) {
}
String encKey = presharedKey.substring(0, presharedKey.length()/2);
// f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd
int len = encKey.length();
byte[] encKeyBytes = new byte[len/2];
for (int i = 0; i < len; i += 2) {
encKeyBytes[i/2] = (byte) ((Character.digit(encKey.charAt(i), 16) << 4)
+ Character.digit(encKey.charAt(i+1), 16));
}
String encryptedSyncKey = null;
try {
byte[] iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
SecretKeySpec encKeySpec = new SecretKeySpec(encKeyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, encKeySpec, ivSpec);
byte[] encryptedSyncKeyBytes = cipher.doFinal(syncKey.hexString().getBytes());
encryptedSyncKey = Base64.encodeToString(encryptedSyncKeyBytes, Base64.DEFAULT);
/*
Yrl0/SuTUUTC6oJ8o4TCOy65EwO0JzoXfEi9kLq0AOlf6rH+nN7+BEc0s5uE7TIo1UlJb/DvR2Ca
ACmQVXXhgpZUTB4sQ0eSo+t32lg0EEb9xKI5CZ4l9QO5raw0xBn7r/tfIdVm8AIFkN9QCcthS0DF
KH3oWhpwNS+tfEuibLPgGqP/zGTozmido9U9lb4n
*/
} catch (InvalidAlgorithmParameterException e) {
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchPaddingException e) {
} catch (InvalidKeyException e) {
} catch (IllegalBlockSizeException e) {
} catch (BadPaddingException e) {
}
sendStuffToWeb(encryptedSyncKey, deviceId, hash);
Python:
hash = getHash(request)
# hash from Java: FpDE2JLmCBr+/rW+n/jBHH13F8AV80sUM2fQAY2IpRs=
encrypted_sync_key = getEncSyncKey(request)
# encryptedSyncKey from Java:
# Yrl0/SuTUUTC6oJ8o4TCOy65EwO0JzoXfEi9kLq0AOlf6rH+nN7+BEc0s5uE7TIo1UlJb/DvR2Ca
# ACmQVXXhgpZUTB4sQ0eSo+t32lg0EEb9xKI5CZ4l9QO5raw0xBn7r/tfIdVm8AIFkN9QCcthS0DF
# KH3oWhpwNS+tfEuibLPgGqP/zGTozmido9U9lb4n
device_id = getDeviceId(request)
# 1605788742789230
preshared_key = getPresharedKeyFromDevice(deviceId)
# f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd0c1c8e36fddba501ef92e72c95b47e07f98f7fd9cb63da75c008a3201124ea5d
enc_key = preshared_key[:len(preshared_key)/2]
# f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd
aes = AES.new(enc_key.decode('hex'), AES.MODE_CBC, IV="\x00"*16)
sync_key = aes.decrypt(base64.b64decode(encrypted_sync_key))
# 824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9
concat = sync_key
# 824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9
import hashlib
from hmac import new as hmac
verify_hash = hmac(preshared_key, concat, hashlib.sha256).digest().encode('base64')
# IoSc2w2sQ4/fwhJTdUQHw/Hdyjy+ranzQ1z3J5LfYbA=
Dal output di debug qui sotto potete vedere la syncKey
è codificati e decodificati con successo, e il concat
è identico. Tuttavia il risultato hash
finisce per essere diverso.
Provare a cambiare 'key.getBytes()' in 'key.getBytes (" US-ASCII ")'; se questo non funziona, prova 'key.getBytes (" ISO-8859-1 ")' –
Grazie, ho sperimentato un certo numero di codifiche differenti per la chiave come da tuo consiglio, tuttavia tutte le opzioni sembrano fornire lo stesso stringa di hash. Ho anche provato la stessa cosa sul lato python, tuttavia questo fornisce anche lo stesso hash di mismatching. – DanH
Probabilmente questo non è rilevante, ma sembra che tu stia usando uno schema di riempimento nel codice Java, e non rendendolo conto sul lato Python? PyCrypto non _non_ gestisce quel riempimento da solo. Inoltre, il codice Python come mostrato non è valido: params non contiene una chiave chiamata 'serial_number', ma ha 'device_id'. –