2012-03-11 21 views
5

Piszę małą aplikację do przesyłania plików, mniej więcej tak, aby dowiedzieć się więcej o programowych podstawach szyfrowania. Pomysł polega na wygenerowaniu klucza RSA, wymianie kluczy publicznych i wysłaniu AES iv and key over w celu dalszego odszyfrowania. Chcę zaszyfrowania klucza AES z kluczem publicznym odbiorcy RSA, tak:Szyfrowanie klucza AES z kluczem publicznym RSA

// encode the SecretKeySpec 
private byte[] EncryptSecretKey() 
{ 
    Cipher cipher = null; 
    byte[] key = null; 

    try 
    { 
     cipher = Cipher.getInstance("RSA/ECB/NOPADDING"); 
     // contact.getPublicKey returns a public key of type Key 
     cipher.init(Cipher.ENCRYPT_MODE, contact.getPublicKey()); 
     // skey is the SecretKey used to encrypt the AES data 
     key = cipher.doFinal(skey.getEncoded()); 
    } 
    catch(Exception e) 
    { 
     System.out.println ("exception encoding key: " + e.getMessage()); 
     e.printStackTrace(); 
    } 
    return key; 
} 

Następnie wypisać wartość klucza do odbiornika i odszyfrować go tak:

private SecretKey decryptAESKey(byte[] data) 
{ 
    SecretKey key = null; 
    PrivateKey privKey = null; 
    Cipher cipher = null; 

    System.out.println ("Data as hex: " + utility.asHex(data)); 
    System.out.println ("data length: " + data.length); 
    try 
    { 
     // assume this loads our private key 
     privKey = (PrivateKey)utility.loadLocalKey("private.key", false); 

     cipher = Cipher.getInstance("RSA/ECB/NOPADDING"); 
     cipher.init(Cipher.DECRYPT_MODE, privKey); 
     key = new SecretKeySpec(cipher.doFinal(data), "AES"); 

     System.out.println ("Key decrypted, length is " + key.getEncoded().length); 
     System.out.println ("data: " + utility.asHex(key.getEncoded())); 
    } 
    catch(Exception e) 
    { 
     System.out.println ("exception decrypting the aes key: " + e.getMessage()); 
     e.printStackTrace(); 
     return null; 
    } 

    return key; 
} 

W konsoli , z drugiej strony, mam to jako wyjście:

read_bytes for key: 16 
data length: 16 
Data as hex: <hex string> 
Key decrypted, length is 256 
java.security.InvalidKeyException: Invalid AES key length: 256 bytes 

Ponadto, jeśli tworzę tablicę bajtów o rozmiarze 16 i umieścić cipher.doFinal (dane) wyjście do niego, tablica jest pozornie przeskalowane do 256 bajty (.leng przynajmniej tak mówi). Dlaczego miałoby to być i co dalej robię niewłaściwie?

edit
Rozwiązałem to i pomyślałem, że pisać problem w przypadku, gdy ktoś prowadzi do tego. Problemem okazał się RSA/ECB/NOPADDING. Z jakiegoś dziwnego powodu zepsuło mi to stworzenie SecretKey, kiedy przekazałem go klientowi. To może mieć coś wspólnego z tym, jak generuję klawisze (używam getInstance ("RSA")), ale nie jestem do końca pewien.

+1

hi-hat, proszę zamieścić swoje ** ** pełna odpowiedź jako odpowiedź zamiast edytowania go w swoim pytaniu (możesz nam powiedzieć, co problem był, a nie odpowiedź). Następnie możesz zaakceptować własną odpowiedź po pewnym czasie. Lub zaakceptuj mój, co wyjaśnia, dlaczego powinieneś używać '' RSA/ECB/PKCS1Padding "' zamiast '" RSA/ECB/NoPadding "' ... –

+0

Pamiętaj, że powinieneś używać ochrony integralności (np. Podpisu) podczas tworzenia protokół internetowy, który wykonuje szyfrowanie. Nawet szyfrowanie RSA może być podatne na np. padding ataki oracle. Brak ochrony integralności jest częstym błędem, chociaż ten problem jest ograniczony, jeśli używasz PKCS # 1 v1.5 (implikowanego przez '" RSA/ECB/PKCS1Padding "'). –

+0

Jako siderote, EBC rzadko jest dobrym trybem dla serwera. – CodesInChaos

Odpowiedz

4

Jak wspomniano o owlstead, nie można po prostu użyć "surowego" RSA bez dopełnienia dla szyfrowania/odszyfrowywania. Dla jednego jest bardzo niepewny, a dla innych, biblioteki Java nawet go nie obsługują. Poniżej znajduje się działający kod do szyfrowania/odszyfrowywania klucza AES przy użyciu kluczy RSA.

private byte[] EncryptSecretKey() 
{ 
    Cipher cipher = null; 
    byte[] key = null; 

    try 
    { 
     // initialize the cipher with the user's public key 
     cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 
     cipher.init(Cipher.ENCRYPT_MODE, contact.getPublicKey()); 
     key = cipher.doFinal(skey.getEncoded()); 
    } 
    catch(Exception e) 
    { 
     System.out.println ("exception encoding key: " + e.getMessage()); 
     e.printStackTrace(); 
    } 
    return key; 
} 

Rozszyfrowanie z kluczowych AES wygląda jak ten:

private SecretKey decryptAESKey(byte[] data) 
{ 
    SecretKey key = null; 
    PrivateKey privKey = null; 
    Cipher cipher = null; 

    try 
    { 
     // this is OUR private key 
     privKey = (PrivateKey)utility.loadLocalKey(
           ConfigFrame.privateKeyLocation, false); 

     // initialize the cipher... 
     cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); 
     cipher.init(Cipher.DECRYPT_MODE, privKey); 

     // generate the aes key! 
     key = new SecretKeySpec (cipher.doFinal(data), "AES"); 
    } 
    catch(Exception e) 
    { 
     System.out.println ("exception decrypting the aes key: " 
               + e.getMessage()); 
     return null; 
    } 

    return key; 
} 
+1

Cześć, utworzyłem małą edycję, aby jawnie polegać na "RSA/ECB/PKCS1Padding", zamiast używać domyślnego dostawcy (który zwraca ten sam wynik w bieżącym dostawcy Oracle JCE, ale jesteś o wiele bezpieczniejszy podając go bezpośrednio). –

+0

dzięki za pomoc/wyjaśnienie, sowlstead! – hat

0

Jedną rzeczą do zapamiętania jest to, że z RSA i rzeczywiście wielu innych algorytmów, w tym AES Zwykle „Przydatne” danych, które dostarczają nie jest dosłownie dane zaszyfrowane. Zwykle należy uwzględnić niektóre dodatkowe dane , na przykład, wskazując rzeczywistą długość danych w pewnym stopniu, dane dla dowolnego sprawdzania integralności ... Dla użytkownika liczba bajtów wejściowych nie musi być równa liczba bajtów po zaszyfrowaniu.

Comment Source

Aby uzyskać odpowiedni rozmiar klucz można użyć HashAlgorithm na kluczu (SHA), które dadzą Ci stałą wielkość wyjściową. W przeciwnym razie możesz po prostu użyć pierwszych 16 bajtów jako klucza i zignorować resztę? Powodzenia.

+0

Masz rację, wygląda na to, że wypycha się do długości klucza (2048 bitowych kluczy, w moim przypadku). Nie widzę również wsparcia dla NOPADDING przy pomocy obiektu Cipher Java 7, ale nie jest to dla mnie wyjątek. Przypuszczam, że zbadam analizę prawdziwych danych, których potrzebuję. Dzięki! – hat

4

Nie można po prostu użyć "surowego" RSA do szyfrowania danych bez wypełniania. Potrzebujesz pewnego rodzaju schematu dopełnienia, jeśli tylko ze względów bezpieczeństwa. Zwykle stosuje się "RSA/ECB/PKCS1Padding". Może to szyfrować dane do 11 bajtów mniej niż rozmiar klucza. Zapewnia to, że dane mieszczą się w module i dodaje co najmniej 8 bajtów danych losowych, aby upewnić się, że szyfrowanie, np. słowo "tak" dwukrotnie nie daje dwóch identycznych tekstów szyfrów. Na koniec upewnia się, że możesz sprawdzić rozmiar w oktetach zaszyfrowanych danych, więc możesz po prostu zaszyfrować 16, 24 lub 32 bajty, które tworzą klucz AES.

+1

Oczywiście można również użyć następcy, OAEP, ale tak naprawdę nie jest to wymagane i może być mniej zgodne niż domyślny schemat dopełniania PKCS # 1 v1.5. –

Powiązane problemy