9

メッセージを暗号化するためのキーペアを生成するPHPのWebサービスと、秘密鍵を取得してメッセージを復号化するJavaのアプリケーションがあります。

phpの場合、私はhttp://phpseclib.sourceforge.net/を使用しており、次の2つのファイルがあります。

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

?>

encode.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. この概念は正しいですか?つまり、私はそれを正しく使用していますか?
4

4 に答える 4

4

上記の回答の助けを借りて、SecretKeySpecが間違って使用されていることがわかりました。また、OpenSSLのPEMファイルは「標準形式」ではないことがわかりました。そのため、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);

?>

それが誰にでも役立つことを願っています。

于 2013-03-27T13:34:14.077 に答える
3

いくつかの考え。

  1. 他の場所でも$privatekeyをエコーアウトするべきではありませんか?

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

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

    コミットはhttps://stackoverflow.com/a/13908986/569976に触発されました

  3. タグを少し変更してbouncycastleとphpseclibを含めると、価値があるかもしれません。これらのタグを追加しますが、すでに5の制限に達しているため、一部のタグを削除する必要があります。削除するタグを決定します(削除する場合もあります)。

于 2013-03-26T17:38:16.613 に答える
2
SecretKeySpec privateKey    = new SecretKeySpec( decoder.decodeBuffer(b64PrivateKey) , "AES");

b64PrivateKeyには秘密鍵が含まれているはずですよね?ドキュメントで調べてみると、SecretKeySpecは対称アルゴリズム(AESなど)のみを対象としているように見えます。RSAのような非対称アルゴリズムは対象としていません。

于 2013-03-27T05:03:18.230 に答える
1

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

また、最近の講義で同様の問題について話し合ったFacebookの上級セキュリティエンジニアからのヒント。同じセキュリティプロトコルを実装する異なるライブラリは、多くの場合互換性がなく、異なる環境または言語の同じライブラリでさえ、しばしば連携に失敗します。そのことを念頭に置いて、セットアップと同様の実用的な例がオンラインに存在することを前提として、いくつか試してみてください。

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

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

秘密鍵が期待値と一致することを確認します(長さ/読み取り値はjavaとphpの間で同じです)。

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

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

最後に、phpからjavaへの暗号化の例を見てみてください。これはすでに機能していると言われており、現在の暗号化スキームに戻るまで一度に1つの変更を行います。CryptRSAとJavaセキュリティでさまざまなパラメータと設定を使用して一緒に動作していると述べたいくつかの例をすばやくグーグルで確認しました。実用的な例を見て、ハッシュ関数を交換してから暗号化などを試してください。

于 2013-03-26T17:58:03.073 に答える