2013-03-26 34 views
9

私は、メッセージを暗号化するための鍵ペアを生成するphpのWebサービスと、秘密鍵を取得してメッセージの復号化を行うjavaの1つのアプリケーションを持っています。暗号化PHP、復号化Java

私は http://phpseclib.sourceforge.net/を使用して、この二つのファイル持ってるPHP用の

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); 

?> 

とJavaで、私はこのコードを持っている:

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 ""; 
    } 
} 

私の質問は次のとおりです:

  1. 「私はRSAキーではありません!
  2. このコードを改善するにはどうすればよいですか?私はbase64を使って、JavaとPHPの間のエンコーディングやコミニケーションのエラーを避けました。
  3. このコンセプトは正しいですか?私はそれを正しく使っていますか?
+0

pre-base64データがデコードされたbase64データと一致していますか? –

+0

はい、私はそれを今テストしました。また、base64でデコードされた後のPHPとJava文字列のmd5チェックサムは同じです。 –

+1

おそらく私のSecretKeySpecが間違っていますか?私はアルゴリズムの価値を変えようとしましたが、成功しませんでした。 –

答えて

4

上記の答えの助けを借りて、私はSecretKeySpecが間違って使用されていることを知りました。そしてOpenSSLからのPEMファイルは 'standart形式'ではないことがわかりましたので、PEMReaderを使用してPrivateKeyクラスに変換する必要があります。ここで

は私の労働者階級である:ここで

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; 
    } 
} 

は私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); 
} 

?> 

と私の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); 

?> 

私はそれが誰と感謝に役立ちます願っています。

+0

あなたのコードは私が見つけることができた最高です。問題は、BouncyCastleProviderを登録できないことです。次のエラーが表示される "エラー:JCEがプロバイダBCを認証できません" – MrD

3

いくつかの考えがあります。

  1. elseでも同様に$ privatekeyをエコーし​​ないでください。

  2. 最新のGitバージョンのphpseclibを使用していますか?しばらく前にこのコミットがあったので、私は尋ねる:

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

    あなたははBouncyCastleとphpseclibが含まれるようにビットをあなたのタグを変更する場合は価値があるかもしれませんhttps://stackoverflow.com/a/13908986/569976

  3. に触発されたコミット。私はこれらのタグを追加しますが、あなたがすでに5の限界にあるので、いくつかのタグを削除する必要があります。削除するものを決めることができます。

+0

私はphpseclibのgitバージョンを使用しています。私は自分の質問のタグを変更しました。私は少し明確に私のメインを変更しました: –

1

あなたが使用しているクラスを掘り下げました。あなたの明示的なパラメータと一致するほとんどのデフォルトパラメータが投稿されたようです。ただし、古い実装を使用している場合は、現在のドキュメントと一致するように設定されていることはありません。

また、近年講演で同様の問題を話したシニアFacebookのセキュリティエンジニアのヒントもあります。同じセキュリティプロトコルを実装している別のライブラリはしばしば互換性がなく、異なる環境や言語の同じライブラリであっても頻繁に連携することはありません。これを念頭において、あなたの設定に似た作業例がオンラインで存在することを考えてみてください:

最新バージョンのライブラリを使用していることを確認してください。また、いくつかのjavax関数とクラスは非推奨であり、java.securityを使用することを推奨します(これはあなたのケースに該当するかどうかをチェックしませんでした)。

キーのサイズを一貫させる(1024または2048)ようにします。Javaの実装はどちらかを行うことができますし、どちらのライブラリでも設定のデフォルトが使用されていることを示す一貫したドキュメントが見つかりませんでした(無効なキーサイズに対して異なる例外が発生する可能性がありますが問題#2が発生する可能性があります)。

privatekeyが期待通りに一致することを確認してください(長さ/ javaとphpの間で同じ長さが読み取られます)。

強制的にデフォルト値を明示的に定義する(CryptRSAハッシュをsha1に設定する、キーの長さ、明示的に設定できるその他のもの)。

javaとphpの両方で同じメッセージを暗号化して、同じ出力が得られるかどうかを確認してください。キーを同じように読み込んだ後でこれを行い、両方のアプリケーションで使用されている間に例外をスローしないでください。単一の文字を暗号化することで、同じパディングスキームが実際に使用されているかどうかを知ることができます(MGF1を使用するソースコードのように見えますが、出力をチェックすることは決してありません)。

最後に、現在の暗号化方式に戻るまで、すでに動作していると言われている、一度に1つの変更を行うJava暗号化のPHPを使用してみてください。私はCryptRSAと一緒に働いていると述べたJavaセキュリティと、さまざまなパラメータと設定を使ったいくつかの例題をすばやく見つけました。

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

など、作業の例を取り、ハッシュ関数を交換してみてください、その後、暗号化b64PrivateKeyは、秘密鍵権利が含まれていることになっていますか? SecretKeySpecは、RSAのような非対称なアルゴリズムではなく、AESのような対称アルゴリズムのみを対象としています。