コンソールからの入力として、Zipファイルの名前、最初のZipから生成された(復号化/暗号化)暗号化されたファイルを含むZipファイルの名前、および公開キーを含むファイルを入力するプログラムを作成しています。 。復号化すると例外が発生します:
exception Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
at Sun.security.rsa.RSAPadding.unpadV15(RSAPadding.Java:380)
at Sun.security.rsa.RSAPadding.unpad(RSAPadding.Java:291)
at com.Sun.crypto.provider.RSACipher.doFinal(RSACipher.Java:363)
at com.Sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.Java:389)
at javax.crypto.Cipher.doFinal(Cipher.Java:2165)
at com.Main.decrypt(Main.Java:67)
at com.Main.main(Main.Java:201)
この例外が発生する理由がわかりませんか?
公開鍵:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCE3pA746UfpC8sFk8ZJp0yupyJqj5jy6cjdxUYoP7mCm7c0mqQDeCcDNBYW2eSozCioPrH/9L+CDQEPLYakoem+jFnUKDH5+pru/0PJTJJF8Xh/ZT9eJlvsYBr1/qSfICf6RTs7kzwq9IuSZBw7/tfNEF9i0A8FVox6HOopXod1QIDAQAB
秘密鍵:
MIICXQIBAAKBgQCE3pA746UfpC8sFk8ZJp0yupyJqj5jy6cjdxUYoP7mCm7c0mqQDeCcDNBYW2eSozCioPrH/9L+CDQEPLYakoem+jFnUKDH5+pru/0PJTJJF8Xh/ZT9eJlvsYBr1/qSfICf6RTs7kzwq9IuSZBw7/tfNEF9i0A8FVox6HOopXod1QIDAQABAoGANOFrYBqK5lvu1koOswDWQZFZqcSSzh8IZyoGwGWa7S0r0EECXlDXmuPSq8e9IfRG8ALHrH+ZlrbnFOSgyVSWHfpj3aH+qknoSX5TW2rMQHih8865xuqheMQ+RTZ7+BRDqNsYkzxB/Z8mqzpoJQSYf+H7nWxdDCgAJVYZzxl3DmUCQQD32iEjnwiwUjii8slcmvCEZl+z84DWNdvJOg6Z38sI4AvrfpKc1WAcDg1rNZCKrRgokh54wpLt08cpFcrD04c3AkEAiTzDmc0bdgfg5wj6xHFZpYlBwiGm/bjOR2PS57P0GNU5PsDllRbFqIuzArITutO5lvZZImzuYz7Lf+cQ73pxUwJBAOdEwmdaneDo17A0m2+to3/nhqWDMVSwLMU3RyiNigZeCMFU+bkd4PBMrHi9IoJDwacZsRU9eZwxYEUV8H2Jg0ECQEEkOqRSm2pXKwX/WSjNtQPCNxhy6NUeV6vDUmTxIjh3XYjP/ynZeVEbnoj1BjB0N2/U11Jj6nPpZqb7gyppMEkCQQCoGdVYDipU+hMMnvxa0zOIyQc/a+HE0lESqn+2ZPafYi9Z1RldRMvUXhP8U7s+OuhRwprdw2ivvOFrnWyz9lL2
プログラムのコードは次のとおりです。どんな助けも歓迎です:)
package com;
import Java.io.BufferedReader;
import Java.io.FileOutputStream;
import Java.io.FileReader;
import Java.io.IOException;
import Java.io.InputStream;
import Java.security.GeneralSecurityException;
import Java.security.KeyFactory;
import Java.security.PrivateKey;
import Java.security.PublicKey;
import Java.security.spec.PKCS8EncodedKeySpec;
import Java.security.spec.X509EncodedKeySpec;
import Java.util.Base64;
import Java.util.Enumeration;
import Java.util.Scanner;
import Java.util.Zip.ZipEntry;
import Java.util.Zip.ZipFile;
import Java.util.Zip.ZipOutputStream;
import javax.crypto.Cipher;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
public class Main {
public final static int BUFFER_SIZE = 117;
public static void decrypt(String originalZipFileName, String newZipFileName, String privateKeyFileName) throws Exception {
byte[] buffer = new byte[128];
ZipFile originalZipFile = new ZipFile(originalZipFileName);
ZipOutputStream newZipFile = new ZipOutputStream(new FileOutputStream(newZipFileName));
Enumeration<? extends ZipEntry> zipEntries = originalZipFile.entries();
String privateKey = getKeyString(privateKeyFileName);
PrivateKey key = makePrivateKey(privateKey);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
while(zipEntries.hasMoreElements()){
ZipEntry entry = zipEntries.nextElement();
ZipEntry copy = new ZipEntry(entry.getName());
newZipFile.putNextEntry(copy);
InputStream inputEntry = originalZipFile.getInputStream(entry);
while(inputEntry.read(buffer) != -1){
newZipFile.write(cipher.doFinal(buffer));
}
newZipFile.closeEntry();
inputEntry.close();
}
newZipFile.close();
originalZipFile.close();
}
public static void encrypt(String originalZipFileName, String newZipFileName, String publicKeyFileName) throws Exception{
byte[] buffer = new byte[BUFFER_SIZE];
ZipFile originalZipFile = new ZipFile(originalZipFileName);
ZipOutputStream newZipFile = new ZipOutputStream(new FileOutputStream(newZipFileName));
Enumeration<? extends ZipEntry> zipEntries = originalZipFile.entries();
String publicKey = getKeyString(publicKeyFileName);
PublicKey key = makePublicKey(publicKey);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, key);
while(zipEntries.hasMoreElements()){
ZipEntry entry = zipEntries.nextElement();
ZipEntry copy = new ZipEntry(entry.getName());
newZipFile.putNextEntry(copy);
InputStream inputEntry = originalZipFile.getInputStream(entry);
while(inputEntry.read(buffer) != -1){
newZipFile.write(cipher.doFinal(buffer));
}
newZipFile.closeEntry();
inputEntry.close();
}
newZipFile.close();
originalZipFile.close();
}
public static String getKeyString(String fileName){
String key = new String();
try {
BufferedReader buf = new BufferedReader(new FileReader(fileName));
key = buf.readLine();
} catch ( IOException e) {
e.printStackTrace();
}
return key.trim();
}
public static PublicKey makePublicKey(String stored) throws GeneralSecurityException {
byte[] data = Base64.getDecoder().decode(stored);
X509EncodedKeySpec spec = new X509EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance("RSA");
return fact.generatePublic(spec);
}
public static PrivateKey makePrivateKey(String stored) throws GeneralSecurityException, Exception {
/*byte[] data = Base64.getDecoder().decode(stored);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(data);
KeyFactory fact = KeyFactory.getInstance("RSA");
return fact.generatePrivate(spec);*/
byte[] data = Base64.getDecoder().decode(stored);
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(0));
ASN1EncodableVector v2 = new ASN1EncodableVector();
v2.add(new ASN1ObjectIdentifier(PKCSObjectIdentifiers.rsaEncryption.getId()));
v2.add(DERNull.INSTANCE);
v.add(new DERSequence(v2));
v.add(new DEROctetString(data));
ASN1Sequence seq = new DERSequence(v);
byte[] privKey = seq.getEncoded("DER");
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privKey);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey key = fact.generatePrivate(spec);
return key;
}
public static void main(String[] args) throws Exception {
Scanner scan = new Scanner(System.in);
System.out.println("Enter type of operation:");
String line = scan.nextLine();
if(line.equals("encrypt")){
System.out.println("Enter name of original Zip file:");
String originalZipFileName = scan.nextLine();
System.out.println("Enter name of new Zip file:");
String newZipFileName = scan.nextLine();
System.out.println("Enter name of file containg public key:");
String publicKeyFileName = scan.nextLine();
encrypt(originalZipFileName, newZipFileName, publicKeyFileName);
}
if(line.equals("decrypt")){
System.out.println("Enter name of original Zip file:");
String originalZipFileName = scan.nextLine();
System.out.println("Enter name of new Zip file:");
String newZipFileName = scan.nextLine();
System.out.println("Enter name of file containg private key:");
String privateKeyFileName = scan.nextLine();
decrypt(originalZipFileName, newZipFileName, privateKeyFileName);
}
}
}
PS:decrypt
メソッドを更新しました。それでも同じエラーが発生します。
public static void decrypt(String originalZipFileName, String newZipFileName, String privateKeyFileName) throws Exception {
byte[] buffer = new byte[128];
ZipFile originalZipFile = new ZipFile(originalZipFileName);
ZipOutputStream newZipFile = new ZipOutputStream(new FileOutputStream(newZipFileName));
Enumeration<? extends ZipEntry> zipEntries = originalZipFile.entries();
String privateKey = getKeyString(privateKeyFileName);
PrivateKey key = makePrivateKey(privateKey);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, key);
while(zipEntries.hasMoreElements()){
ZipEntry entry = zipEntries.nextElement();
ZipEntry copy = new ZipEntry(entry.getName());
newZipFile.putNextEntry(copy);
InputStream inputEntry = originalZipFile.getInputStream(entry);
while(inputEntry.read(buffer) != -1){
newZipFile.write(cipher.doFinal(buffer));
}
newZipFile.closeEntry();
inputEntry.close();
}
newZipFile.close();
originalZipFile.close();
}
ジョゼフは正しい。
デフォルトパラメータで暗号を作成すると、デフォルトは「RSA/ECB/PKCS1Padding」になります。厄介な驚きが気に入らない場合は、明示的にパディングを指定する必要があります。他のセキュリティプロバイダーには異なるデフォルトパラメータがある場合があるためです。また、特定のJREがどのセキュリティ設定を持っているかを事前に知ることはできません。
そのため、PKCS1パディングは、元のデータに11バイトを追加し、117バイトから128バイトに増やします。これらの数値は1024ビットRSAキー(わずかに安全)に固有のものであり、長いキーでは異なることを考慮する必要があります。ファイルからキーをロードしているため、その長さを確認することを検討してください。
@Test
public void testPadding() throws Exception {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024, random);
KeyPair keyPair = keyGen.generateKeyPair();
/* constant 117 is a public key size - 11 */
byte[] plaintext = new byte[117];
random.nextBytes(plaintext);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] ciphertext = cipher.doFinal(plaintext);
System.out.println(plaintext.length + " becomes " + ciphertext.length);
}
これは印刷します
117 becomes 128
最後に、ファイルの暗号化にRSAではなくAESを使用することを検討してください。
そのため、問題を修正するには、暗号化に公開キー長-11(117)、復号化に公開キーサイズ(128)のバッファを使用する必要があります。
変化する
outputFile.write(cipher.doFinal(buffer), 0, read);
に
outputFile.write(cipher.doFinal(buffer));
バッファの読み取りは117バイトで、doFinalの結果のサイズは128バイトです。
また、入力ストリームをバッファリングする必要があります。ファイルから読み取るときは、時間がかかることがあり、InputStreamが読み取るデータの量は、バッファーに含まれる量よりも少なくなります。 BufferedInputStreamを使用することにより、読み取り呼び出しが戻る前に十分なデータがあることを確認できます。ただし、復号化にはデータの完全なブロックが重要です
InputStream inputEntry = new BufferedInputStream(originalZipFile.getInputStream(entry));
while((read = inputEntry.read(buffer)) != -1){
outputFile.write(cipher.doFinal(buffer), 0, read);
}
ここに問題があります。 read
は、暗号文ではなく、読み取られた平文のサイズです。 2番目と3番目のパラメーターを完全に削除する必要があります。
また、暗号文を中間ファイルに書き込むのは時間とスペースの無駄です。 Zipストリームに直接書き込むだけです。
復号化メソッドのバイト配列は、アルゴリズムのデフォルトの出力サイズであるため、長さが256バイトである必要があります(余分なバイトはこの長さになります)。変化する byte[] buffer = new byte[128];
からbyte[] buffer = new byte[256];
。