2013-03-26 10 views
9

Mam usługę internetową w php, która generuje parę kluczy do zaszyfrowania wiadomości, oraz jedną aplikację w języku Java, która odbiera privatekey i odszyfrowuje wiadomość.Szyfrowanie PHP, deszyfrowanie Java

dla PHP używam http://phpseclib.sourceforge.net/ i mieć to dwa pliki:

keypair.php

<?php 

set_time_limit(0); 
if(file_exists('private.key')) 
{ 
    echo file_get_contents('private.key'); 
} 
else 
{ 
    include('Crypt/RSA.php'); 
    $rsa = new Crypt_RSA(); 
    $rsa->createKey(); 
    $res = $rsa->createKey(); 

    $privateKey = $res['privatekey']; 
    $publicKey = $res['publickey']; 

    file_put_contents('public.key', $publicKey); 
    file_put_contents('private.key', $privateKey); 
} 

?> 

encrypt.php

<?php 

include('Crypt/RSA.php'); 

//header("Content-type: text/plain"); 

set_time_limit(0); 
$rsa = new Crypt_RSA(); 
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_OAEP); 
$rsa->loadKey(file_get_contents('public.key')); // public key 

$plaintext = 'Hello World!'; 
$ciphertext = $rsa->encrypt($plaintext); 

echo base64_encode($ciphertext); 

?> 

w java Mam ten kod:

package com.example.app; 

import java.io.DataInputStream; 
import java.net.URL; 
import java.security.Security; 

import javax.crypto.Cipher; 
import javax.crypto.spec.SecretKeySpec; 

import sun.misc.BASE64Decoder; 

public class MainClass { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) 
    { 
     Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 

     try { 
      BASE64Decoder decoder = new BASE64Decoder(); 
      String b64PrivateKey = getContents("http://localhost/api/keypair.php").trim(); 
      String b64EncryptedStr = getContents("http://localhost/api/encrypt.php").trim(); 

      System.out.println("PrivateKey (b64): " + b64PrivateKey); 
      System.out.println(" Encrypted (b64): " + b64EncryptedStr); 

      SecretKeySpec privateKey = new SecretKeySpec(decoder.decodeBuffer(b64PrivateKey) , "AES"); 
      Cipher cipher    = Cipher.getInstance("RSA/None/OAEPWithSHA1AndMGF1Padding", "BC"); 
      cipher.init(Cipher.DECRYPT_MODE, privateKey); 

      byte[] plainText   = decoder.decodeBuffer(b64EncryptedStr); 

      System.out.println("   Message: " + plainText); 
     } 
     catch(Exception e) 
     { 
      System.out.println("   Error: " + e.getMessage()); 
     } 

    } 

    public static String getContents(String url) 
    { 
     try { 
      String result = ""; 
      String line; 
      URL u = new URL(url); 
      DataInputStream theHTML = new DataInputStream(u.openStream()); 
      while ((line = theHTML.readLine()) != null) 
       result = result + "\n" + line; 

      return result; 
     } 
     catch(Exception e){} 

     return ""; 
    } 
} 

Moje pytania są następujące:

  1. Dlaczego mam wyjątek mówiąc "nie klucz RSA!"?
  2. Jak mogę poprawić ten kod? Użyłem base64, aby uniknąć błędów kodowania i komunikacji między Javą i PHP.
  3. Ta koncepcja jest poprawna? Mam na myśli, używam go poprawnie?
+0

Czy dane pre-base64 pasują do odkodowanych danych base64? –

+0

Tak, testowałem to teraz, a suma kontrolna md5 napisu PHP i Java po dekodowaniu z base64 jest taka sama. –

+1

Może moje SecretKeySpec są błędne? Próbowałem zmienić wartość algorytmu, bez powodzenia. –

Odpowiedz

4

Przy pomocy powyższej odpowiedzi otrzymałem, że SecretKeySpec jest używany nieprawidłowo, i stwierdziłem, że plik PEM z OpenSSL nie jest "standardowym formatem", więc muszę użyć PEMReadera, aby przekonwertować go na klasę PrivateKey.

Oto moja klasa robotnicza:

package com.example.app; 

import java.io.BufferedReader; 
import java.io.DataInputStream; 
import java.io.StringReader; 
import java.net.URL; 
import java.security.KeyPair; 
import java.security.PrivateKey; 
import java.security.Security; 

import javax.crypto.Cipher; 

import org.bouncycastle.openssl.PEMReader; 

import sun.misc.BASE64Decoder; 

public class MainClass { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) 
    { 
     Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); 

     try { 
      BASE64Decoder decoder = new BASE64Decoder(); 
      String b64PrivateKey = getContents("http://localhost/api/keypair.php").trim(); 
      String b64EncryptedStr = getContents("http://localhost/api/encrypt.php").trim(); 

      System.out.println("PrivateKey (b64): " + b64PrivateKey); 
      System.out.println(" Encrypted (b64): " + b64EncryptedStr); 

      byte[] decodedKey   = decoder.decodeBuffer(b64PrivateKey); 
      byte[] decodedStr   = decoder.decodeBuffer(b64EncryptedStr); 
      PrivateKey privateKey  = strToPrivateKey(new String(decodedKey)); 

      Cipher cipher    = Cipher.getInstance("RSA/None/OAEPWithSHA1AndMGF1Padding", "BC"); 
      cipher.init(Cipher.DECRYPT_MODE, privateKey); 


      byte[] plainText   = cipher.doFinal(decodedStr); 

      System.out.println("   Message: " + new String(plainText)); 
     } 
     catch(Exception e) 
     { 
      System.out.println("   Error: " + e.getMessage()); 
     } 

    } 

    public static String getContents(String url) 
    { 
     try { 
      String result = ""; 
      String line; 
      URL u = new URL(url); 
      DataInputStream theHTML = new DataInputStream(u.openStream()); 
      while ((line = theHTML.readLine()) != null) 
       result = result + "\n" + line; 

      return result; 
     } 
     catch(Exception e){} 

     return ""; 
    } 

    public static PrivateKey strToPrivateKey(String s) 
    { 
     try { 
      BufferedReader br = new BufferedReader(new StringReader(s)); 
      PEMReader pr  = new PEMReader(br); 
      KeyPair kp   = (KeyPair)pr.readObject(); 
      pr.close(); 
      return kp.getPrivate(); 
     } 
     catch(Exception e) 
     { 

     } 

     return null; 
    } 
} 

Oto moja keypair.php

<?php 

set_time_limit(0); 
if(file_exists('private.key')) 
{ 
    echo base64_encode(file_get_contents('private.key')); 
} 
else 
{ 
    include('Crypt/RSA.php'); 

    $rsa = new Crypt_RSA(); 
    $rsa->setHash('sha1'); 
    $rsa->setMGFHash('sha1'); 
    $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_OAEP); 
    $rsa->setPrivateKeyFormat(CRYPT_RSA_PRIVATE_FORMAT_PKCS1); 
    $rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_PKCS1); 

    $res = $rsa->createKey(1024); 

    $privateKey = $res['privatekey']; 
    $publicKey = $res['publickey']; 

    file_put_contents('public.key', $publicKey); 
    file_put_contents('private.key', $privateKey); 

    echo base64_encode($privateKey); 
} 

?> 

i mój encrypt.php

<?php 
    include('Crypt/RSA.php'); 
    set_time_limit(0); 

    $rsa = new Crypt_RSA(); 
    $rsa->setHash('sha1'); 
    $rsa->setMGFHash('sha1'); 
    $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_OAEP); 

    $rsa->loadKey(file_get_contents('public.key')); // public key 

    $plaintext = 'Hello World!'; 
    $ciphertext = $rsa->encrypt($plaintext); 
    $md5  = md5($ciphertext); 
    file_put_contents('md5.txt', $md5); 
    file_put_contents('encrypted.txt', base64_encode($ciphertext)); 

    echo base64_encode($ciphertext); 

?> 

Mam nadzieję, że ktoś pomaga i dzięki.

+0

Twój kod jest najlepszy, jaki mogłem znaleźć. Problem polega na tym, że nie mogę zarejestrować BouncyCastleProvider. Pojawia się następujący błąd "Błąd: JCE nie może uwierzytelnić dostawcy BC" – MrD

3

Kilka myśli.

  1. Czy nie powinieneś również wypowiadać się za $ privatekey?

  2. Czy używasz najnowszej wersji phpseclib Git? Pytam, bo jakiś czas temu było to commit:

    https://github.com/phpseclib/phpseclib/commit/e4ccaef7bf74833891386232946d2168a9e2fce2#phpseclib/Crypt/RSA.php

    commit był inspirowany przez https://stackoverflow.com/a/13908986/569976

  3. Może być opłacalne, jeśli zmienisz swoje tagi się trochę zawierać BouncyCastle i phpseclib. Dodałabym te tagi, ale niektóre tagi będą musiały zostać usunięte, ponieważ masz już 5 lat. Pozwolę ci zdecydować, które z nich usunąć (jeśli chcesz to zrobić).

+0

Używam wersji phpseclib git i zmieniłem tagi mojego pytania. Zmieniłem moją zasadę, aby było jasne: –

1

Zrobiłem kilka wykopów do klas, których używasz i wygląda na to, że to, co napisałeś, zawiera większość domyślnych parametrów pasujących do twoich jawnych parametrów. Nie gwarantuje to jednak, że konfiguracja ma wszystkie ustawione tak, aby pasowały do ​​bieżącej dokumentacji, jeśli używasz starszych wersji.

Również wskazówka od starszych inżynierów bezpieczeństwa Facebooka, którzy niedawno omawiali podobne zagadnienia na wykładzie; różne biblioteki wdrażające te same protokoły bezpieczeństwa będą często niekompatybilne, a nawet te same biblioteki w różnych środowiskach lub językach często nie będą współpracować ze sobą. Mając to na uwadze, kilka rzeczy do wypróbowania, biorąc pod uwagę, że przykłady robocze podobne do konfiguracji istnieją w Internecie:

Upewnij się, że używasz najnowszych wersji bibliotek. Zwróć też uwagę, że niektóre funkcje i klasy javax są nieaktualne i sugerują teraz użycie java.security (nie sprawdzono, czy dotyczy to Twojego przypadku).

Wymuś wielkość klucza, aby zachować spójność (1024 lub 2048).Implementacja java może być wykonana i nie znalazłem spójnej dokumentacji dla obu bibliotek, mówiąc, jakie ustawienia domyślne będą używane (może to być przyczyną problemu nr 2, ale możesz otrzymać inny wyjątek dla nieprawidłowego rozmiaru klucza).

Upewnij się, że twoje prywatne parametry pasują do oczekiwań (długość/czytanie jest takie samo między java i php).

Wymuś domyślne ustawienie wartości domyślnych (ustaw wartość skrótu CryptRSA na sha1, długości kluczy i inne ustawienia, które można jawnie ustawić).

Spróbuj zaszyfrować tę samą wiadomość za pomocą zarówno java, jak i php, aby sprawdzić, czy możesz uzyskać te same wyniki. Zrób to po upewnieniu się, że twoje klucze są takie same i nie rzucaj wyjątków, gdy są używane w obu aplikacjach. Zaszyfrowanie pojedynczego znaku może powiedzieć, czy faktycznie używany jest ten sam schemat wypełniania (z kodu źródłowego wynika, że ​​oba używają MGF1, ale nigdy nie boli, aby sprawdzić wyjścia).

Na koniec spróbuj zrobić przykład dla php do szyfrowania java, o którym już mówiono, że działa i wykonuj jedną zmianę naraz, dopóki nie wrócisz do aktualnego schematu szyfrowania. Widziałem kilka przykładów szybkiego logowania, które używały różnych parametrów i ustawień z CryptRSA i zabezpieczeniami java, które stwierdzały, że pracują razem. Weź przykład pracę i spróbuj zamiana funkcji skrótu, a następnie szyfrowania itp

2
SecretKeySpec privateKey = new SecretKeySpec(decoder.decodeBuffer(b64PrivateKey) , "AES"); 

b64PrivateKey ma zawierać prawo klucza prywatnego? ponieważ wyszukiwanie w dokumentach wygląda tak, że SecretKeySpec jest przeznaczony tylko dla algorytmów symetrycznych (takich jak AES) - nie asymetrycznych takich jak RSA.

Powiązane problemy