3

秘密RSAキーを使用して、デフォルトのJavaRSA実装でランダムなAESキーを暗号化しています。

Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] cipherBytes = cipher.doFinal(plainText.getBytes());

とにかく公開鍵が必要なので、これは鍵を偽装し、秘密鍵で暗号化されていることを確認するための便利な方法です。復号化も同様に行われます。

Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] plainBytes = cipher.doFinal(cipherBytes);

これはOracleのJDKで正常に機能しますが、IBMでは暗号化に秘密鍵を使用することは有効なユースケースではないと考えているため、これは失敗します。残念ながら、両方のJDKをサポートする必要があるため、RSA復号化を自分で再実装しようとしています。

これは私がこれまでに持っているコードです:

BigInteger big = new BigInteger(cipherBytes);
big = big.modPow(pub.getPublicExponent(), pub.getModulus()); 
System.out.println(new String(big.toByteArray()));

ほぼ機能しますが、パディングの問題があるようです。ほとんどの場合、前にドットのような記号の文字列が付いた元のテキストを取得していますが、ランダムなバイトしかない場合もあります。

残念ながら、デフォルトでどのパディングスキームが使用されているかを理解できませんでした。誰かが私のコードに何が欠けているか知っていますか、または少なくともパディングが処理されるアルゴリズムのヒントを与えることができますか?

要求に応じて、入力値と出力値の例を次に示します。数字が大きすぎるのを避けるために、512ビットのキーを使用しました。

Public modulus :  8117919732251191237549784557538073836207094968952416063837701691514861428726690140363567956265691836505266266364256892197254736023284927189008247933889303
Public exponent:  65537
Plaintext:        teststring
Plaintext as BN:  549665952565679142563431
Ciphertext as BN: 6304229782339071167863563708554898540621778162930150363326921290545577949349781053660336996882823758722402137580193903457839924005473545992074817339077456
"Decrypted" BN:   409173825987017733751648712103449894027080255755383098685411421012016724550584319360408761540738019643860835515945008876151848132891805352276483731047
Resultstring: ˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇteststring

議論に対処するために、なぜ私はこれをしているのですか?

公開鍵は私のソフトウェアにハードコードされています。秘密鍵を使用して、AESの別の鍵を暗号化します。したがって、実際にAESで何かをデコードするには、最初にAESキーが必要です。この鍵を取得するには、最初に公開鍵で復号化する必要があります。公開鍵は深刻な操作なしでは変更できないため、秘密鍵で暗号化されたAES鍵のみが機能します。何らかの方法で公開鍵を抽出してAES鍵を復号化することもできますが、それは複雑であり、保護されたコンテンツを復号化するためのAES鍵のみを取得します。秘密鍵で計算された署名もあり、公開鍵でも検証されます。したがって、操作はできません。

そうです、コンテンツを読み取る方法があるので、技術的には署名で十分です。しかし、それらは手の込んだものであり、誰かが本当にすべての問題を抱えていてもかまいませんが、私は物事を簡単にしたくありません。

4

3 に答える 3

1

公開鍵は、署名を暗号化および検証するためのものです。秘密鍵は、復号化および署名用です。公開鍵は、それだけを目的としています。公開です。正しく実行している場合は、公開鍵を非表示にする理由はありません。

于 2012-12-19T21:35:04.150 に答える
0

あなたがやろうとしていることは、暗号化というよりは署名のように見えます。暗号化とまったく同じではないため、署名には別のキーペアを使用してください。

于 2012-12-19T21:33:29.893 に答える
0

OK、RSA仕様を読んで理解しました。セキュリティを強化するために、暗号化の前にパディングが追加され、次の「文字列」が作成されます。

0x00 + BT + Padding + 0x00 + Data

ブロックタイプ(BT)は、パディングの種類を示します。BT = 0x01の場合、パディングは0xffであり、BT = 0x02の場合、パディングはランダムですがゼロではありません。次に、連結された文字列が暗号化されます。

復号化する場合、フォーマットを検証できますが、データを読み取るには、先頭のバイトを削除する必要があります。データの直前の0x00までは、すべてゼロ以外です。したがって、パディング後の0x00までのすべてを削除できます。残っているのはメッセージです。

このコードは現在機能しています:

// Decrypt
byte[] decryptedBytes = (new BigInteger(1, cipherBytes)).modPow(pub.getPublicExponent(), pub.getModulus()).toByteArray();

// Extract msg
int dataStart;
for (dataStart = 0; decryptedBytes[msgStart] != 0; dataStart++);
dataStart++;

byte finalBytes[] = new byte[decryptedBytes.length - msgStart];
System.arraycopy(decryptedBytes, msgStart, finalBytes, 0, finalBytes.length);

これは、以前の試みでの「^」の文字列についても説明しています。それらはパディングバイトであり、BT=0x01で0xffです。

復号化だけが必要でしたが、完全を期すために、これは暗号化のコードです。

int bitLength = 512;
String plainText = "teststring";

// Convert to bytes
byte plainBytes[] = plainText.getBytes(); 

byte encryptionBytes[] = new byte[bitLength / 8];

encryptionBytes[0] = 0; // Leading 0
encryptionBytes[1] = 1; // Block type

// Padding String
int paddingEnd = (bitLength / 8) - plainBytes.length - 2;
for (int i = 2; i < paddingEnd; i++) {
    encryptionBytes[i] = (byte) 0xff;
}
encryptionBytes[paddingEnd + 1] = 0;

// Actual data
System.arraycopy(plainBytes, 0, encryptionBytes, paddingEnd + 1, plainBytes.length);

// Encrypt
byte[] cipherBytes = (new BigInteger(1, encryptionBytes)).modPow(priv.getPrivateExponent(), priv.getModulus()).toByteArray();

これが誰かに役立つことを願っています:)

于 2012-12-20T00:34:17.773 に答える