2013-05-10 12 views
11

Na podstawie ostatnich opinii i ustaleń dotyczących tego problemu, przepisałem to pytanie, aby pozbyć się szumu.Java vs Python HMAC-SHA256 Niezgodność

Mam 2 oddzielne ścieżki kodu, jedną w Javie (Android), jedną i Python, które spełniają następujące warunki w celu negocjowania parowania między urządzeniem z systemem Android i Python/Django.

Java:

  • wygenerować syncKey
  • Hash konkatenacyjnego ciąg różnych wartościach wykorzystaniem presharedKey (w tym syncKey)
  • zaszyfrować syncKey użyciu presharedKey
  • Wyślij Hash, szyfrowane syncKey, DeviceId i dowolne zmienne do serwera WWW

Python

  • Pobierz presharedKey z Deviceid
  • odszyfrować zaszyfrowane syncKey
  • Hash konkatenacyjnego ciąg różnych wartości z wykorzystaniem presharedKey (w tym rozszyfrowanej syncKey)
  • Upewnij mecze hash, który potwierdza, że syncKey został odszyfrowany pomyślnie, a identyfikator urządzenia trzyma poprawny klucz wstępny.

Teraz ten proces działa, jeśli wyślę syncKey niezaszyfrowany. Końcowe wyniki mieszające, które dowodzą, że identyfikator urządzenia ma poprawny klucz wstępny, ale zaraz po dodaniu en/deszyfracji do procesu, hasz już nie pasuje, pomimo tego, że oba klucze syncKey i łączone wydają się idealnie pasować znak dla postaci z wyjścia debugowania obu programów Java/Python.

Jednym z dziwactw tego procesu jest to, że klucz 256-bitowy jest niezbędny dla algorytmu szyfrowania AES256, więc przecinam 5-bitowy klucz wstępny o wartości 512 bitów na pół. Alternatywa użycia tylko klucza 256-bitowego na całej płycie wymagała, aby przekazać klucz przez encode('ascii') po stronie Pythona, albo też powodował on błędy podczas mieszania za pomocą krótszego klawisza.

Oto odpowiedni kod:

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= 

Z wyjścia debugowania poniżej widać syncKey są szyfrowane i odszyfrowywane powodzeniem, a concat jest identyczny. Jednak wynik hash kończy się inaczej.

+1

Spróbuj zmienić 'key.getBytes()' na 'key.getBytes (" US-ASCII ")'; jeśli to nie działa, spróbuj 'key.getBytes (" ISO-8859-1 ")' –

+0

Dzięki, eksperymentowałem z wieloma różnymi kodowaniami dla klucza, jak na twoją radę, jednak wszystkie opcje wydają się zapewniać takie same ciąg skrótów. Próbowałem również to samo po stronie Pythona, jednak zapewnia to również ten sam niedopasowany hash. – DanH

+0

To prawdopodobnie nie ma znaczenia, ale wygląda na to, że używasz schematu dopełnienia w kodzie Java i nie rozliczasz go po stronie Pythona? PyCrypto nie _nie zajmuje się tym dopełnieniem sam. Ponadto przedstawiony kod Pythona jest nieprawidłowy: params nie zawiera klucza o nazwie "serial_number", ale ma "device_id". –

Odpowiedz

8

Twój kod Pythona jest nieprawidłowy. Mogę odtworzyć w Pythonie odpowiedź, którą otrzymałeś w Javie.

Gdybym użyć wejścia:

>>> preshared_key_hex 
b'f8250b0d5960444e4de6ecc3a78900bb941246a1dece7848fc72b90092ab3ecd0c1c8e36fddba501ef92e72c95b47e07f98f7fd9cb63da75c008a3201124ea5d' 
>>> concat_hex 
b'824C1EE9EF507B52EA28362C71BD4AD512A5F82ACFAE80DEF531F73AC124CA814BA30CE805A157D6ADB9EC04FC99AAE6FDC4238FCD76B87CE22BC2FE33B2E5C9' 

uzyskać taką samą wartość można dostać w Java:

>>> base64.b64encode(hmac.new(preshared_key_hex, concat_hex, hashlib.sha256).digest()) 
b'FpDE2JLmCBr+/rW+n/jBHH13F8AV80sUM2fQAY2IpRs=' 

jednak, że wartość ta jest prawdopodobnie błędne. Powinieneś prawie na pewno zdekodować wartości wejściowe.

Nie mogę odtworzyć tego, co otrzymałeś w Pythonie; jedna z wartości, które przechodzisz na hmac.new, nie jest taka, jak myślisz. print je bezpośrednio przed wywołaniem hmac.new i powinieneś zobaczyć, co nie pasuje.

+0

Niestety nie widzę, gdzie w moim Pythonie dekodowania 'preshared_key' lub' concat'? Dekodowałem tylko dla odszyfrowania. Dla rekordu używam Pythona 2.7, domyślam się, że używasz 3+ biorąc pod uwagę notację 'b'''? Czy to ma znaczenie? Przepraszam, jeśli jestem noobem, po prostu go nie widzę :) – DanH

+3

@DanH Nie mogę odtworzyć tego, co masz w ogóle w Pythonie. Właśnie wspomniałem, że te wartości powinny być prawdopodobnie zdekodowane w systemie szesnastkowym. Tutaj nie ma żadnych różnic w Pythonie 2/3. Bez względu na kodowanie, gdzieś popełnisz błąd w Pythonie. Jeśli wydrukujesz wartości przed wywołaniem 'hmac.new' zobaczysz, że nie pasują one do tego, co przekazujesz w Javie - ponieważ jeśli przekażesz te wartości, tak jak ja, otrzymasz taką samą odpowiedź jak w Jawa. Więc twoim bezpośrednim problemem jest Python. – agf

+0

OK Jeszcze raz zobaczę, kod już nie ma dla mnie sensu! To tylko piksele D: – DanH