web-dev-qa-db-ja.com

Android:ファイルに保存されている公開鍵を使用してRSAテキストを復号化します

私は成功せずにそれをやろうとして数日経ちました。

StackOverflowには同様の質問がたくさんあり、そのうちの2つでも私のものとまったく同じですが、未回答で未解決です:1) PHP RSAPublicKeyをAndroid PublicKey 2) Android:RSAキーでopenssl暗号化ファイルを復号化する方法は?

私のシナリオ:RSAを使用して暗号化されたテキストがあります(私は暗号化していません)。 res/rawフォルダーに「public.key」ファイルがあり、復号化に必要な公開鍵(メッセージの暗号化に使用される秘密鍵に関連する公開鍵)が次の例のような形式で含まれています。 enter image description here

次のように、RSAテキストを復号化する方法の例がたくさんあります。

public static byte[] decryptRSA( PublicKey key, byte[] text) throws Exception
      { 
          byte[] dectyptedText = null;

          Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
          cipher.init(Cipher.DECRYPT_MODE, key);
          dectyptedText = cipher.doFinal(text);
          return dectyptedText;
      }

しかし、私の質問は、ファイルから適切なPublicKeyインスタンスを取得する方法です。この例はありません。

私が単に試してみると:

    InputStream is = getResources().openRawResource(R.raw.public);
    DataInputStream dis = new DataInputStream(is);
    byte [] keyBytes = new byte [(int) is.available()];
    dis.readFully(keyBytes);
    dis.close();
    X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    return keyFactory.generatePublic(spec);

戻り文でInvalidKeyExceptionが発生します。 HexまたはBase64をデコードする必要がありますか?公開鍵ファイルの最初と最後の行(「---- BEGIN PUBLICKEY ----」など)は問題ではありませんか?

たぶん、StackOverflowで初めてこれの答えを正しく得ることができました:-)

17
thelawnmowerman

ついに解決!!!ドラム、トランペット、そして魅惑的な音のシンフォニー!!!

public static byte[] decryptRSA(Context mContext, byte[] message) throws Exception { 

    // reads the public key stored in a file
    InputStream is = mContext.getResources().openRawResource(R.raw.sm_public);
    BufferedReader br = new BufferedReader(new InputStreamReader(is));
    List<String> lines = new ArrayList<String>();
    String line = null;
    while ((line = br.readLine()) != null)
        lines.add(line);

    // removes the first and last lines of the file (comments)
    if (lines.size() > 1 && lines.get(0).startsWith("-----") && lines.get(lines.size()-1).startsWith("-----")) {
        lines.remove(0);
        lines.remove(lines.size()-1);
    }

    // concats the remaining lines to a single String
    StringBuilder sb = new StringBuilder();
    for (String aLine: lines)
        sb.append(aLine);
    String keyString = sb.toString();
    Log.d("log", "keyString:"+keyString);

    // converts the String to a PublicKey instance
    byte[] keyBytes = Base64.decodeBase64(keyString.getBytes("utf-8"));
    X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PublicKey key = keyFactory.generatePublic(spec);

    // decrypts the message
    byte[] dectyptedText = null;
    Cipher cipher = Cipher.getInstance("RSA");
    cipher.init(Cipher.DECRYPT_MODE, key);
    dectyptedText = cipher.doFinal(Base64.decodeBase64(message));
    return dectyptedText;
}

解決策は、ファイルから読み取られた公開鍵だけでなく、暗号化されたメッセージ自体もBase64でデコードすることでした。

ちなみに、@ Nikolayが提案した方法でファイルから公開鍵を読み取りました(tnx再びman)。

どうもありがとうございました。 StackOverflowは素晴らしいです!

14
thelawnmowerman

重要なポイントがありません。公開鍵と秘密鍵は別々であり、一方を他方に基づいて計算することはできません。それが公開鍵暗号化のポイントの一種です。生のRSAの使用に関する問題はさておき、公開鍵で暗号化されたものがある場合、それを復号化するには、対応するprivate鍵が必要です。およびその逆。したがって、公開鍵ファイルがある場合は、そこからpublic鍵しか取得できません。これは、データが対応するprivateキーで暗号化されている場合にのみ役立ちます。

実際の例外については、最初と最後の「---」行を削除し、Base64.decode()を使用してバイト配列を取得し、これを使用してX509EncodedKeySpecを作成します。これを行う1つの方法は、BufferedReaderのようなものを使用して行ごとに読み取り、 '---'行を無視して、残りを1つの大きなStringに連結します。

2
Nikolay Elenkov

公開鍵はデータの暗号化のみが可能です。公開鍵はデータを復号化できません。秘密鍵を使用してのみデータを復号化できます。重要なのは、公開鍵を誰にでも配布でき、秘密鍵の所有者だけが見ることができる暗号化されたメッセージを送信できるということです。

暗号化技術の使用には本当に注意する必要があります。秘密鍵をすべてのデバイスに配布するだけで、全員が同じ秘密鍵を使用するため、セキュリティが弱くなるのではないかと心配しています。したがって、セキュリティを破りたい場合は、Google Playにアクセスしてアプリをダウンロードし、アプリから秘密鍵を引き出します。ヴィオラ私はすべてを見ることができます。

それで、なぜそれが機能しないのかあなたはあなたの答えを持っています、しかしあなたはあなたが暗号化を使用している理由を知ることであなたに与えることができないデザインについて今アドバイスが必要です。何を隠していますか?

更新:

RSAのしくみのように暗号化と署名の検証を実行しようとしているように聞こえますが、実際にどのように機能するのか混乱しています。そのためには、2セットの秘密/公開鍵が必要です。クライアント用の1セットのキーとサーバー用の1セットのキー。

Webサーバーは、その公開鍵をクライアントに送信します。クライアントは、サーバーの公開鍵を使用して認証および暗号化されたメッセージをサーバーに送信し、クライアントの秘密鍵を使用してそのメッセージに署名することができます。サーバーの場合はその逆です。サーバーは、クライアントの公開鍵を使用してメッセージを暗号化し、秘密鍵で署名してクライアントにメッセージを送信します。次に、クライアントはクライアントの秘密鍵を使用してメッセージを復号化し、サーバーの公開鍵を使用して署名を検証できます。

今、SSLを再実装していますか?やめて。 SSLを使用します。

SSLが安全なチャネルを実現する方法は次のとおりです。クライアントは、WebサーバーからPUBLICキーと、対称暗号化アルゴリズムの1つ以上の名前を受け取ります。共通のアルゴリズムを選択し、今後のすべてのメッセージに使用する秘密鍵を生成します。その秘密鍵をWebサーバーの公開鍵で暗号化し、選択したアルゴリズムとともに送信します。 Webサーバーは、PRIVATEキーを使用してDECRYPTSを実行し、共有秘密キーを取得します。その後、すべての暗号化は、非対称暗号化よりもはるかに高速な共有秘密を使用した対称暗号化です。

1
chubbsondubs

指定したようなPEM形式からRSA公開鍵を生成するには(opensslgen。rsakey)

-----BEGIN PUBLIC KEY-----
SOMEDES3UNREADABLETEXT+PADDING==
-----END PUBLIC KEY

それで署名されたコンテンツを読むためにそれを使用しますか?

-ここで同様の質問で私の答えを見てください: https://stackoverflow.com/a/12101100/546054

1
TouchBoarder