Java 使用RSA解密字符串会在开头返回额外的零

Java 使用RSA解密字符串会在开头返回额外的零,java,encryption,cryptography,rsa,Java,Encryption,Cryptography,Rsa,我正在尝试生成一个AES密钥,使用RSA对其进行加密和解密。 这是可行的,只是在解密数据并使用Base64编码之后,我在实际字符串(Base64编码的AES密钥)之前得到了一堆“a”字母。我猜这些是字节中的零 “RSA/ECB/NOPANDING”参数是必需的。我做错了什么?我需要它返回原始字符串/字节 package szyfrator; import java.io.BufferedInputStream; import java.io.File; import java.io.FileI

我正在尝试生成一个AES密钥,使用RSA对其进行加密和解密。 这是可行的,只是在解密数据并使用Base64编码之后,我在实际字符串(Base64编码的AES密钥)之前得到了一堆“a”字母。我猜这些是字节中的零

“RSA/ECB/NOPANDING”参数是必需的。我做错了什么?我需要它返回原始字符串/字节

package szyfrator;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.tools.bzip2.CBZip2OutputStream;

import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;
import com.google.common.io.Files;
import com.sun.org.apache.xml.internal.security.utils.Base64;

public class Cryptography {

    private static byte[] aesKey;
    private static String base64AESKey;
    private static byte[] encryptedAESKey;
    private static String base64AESEncryptedKey;
    private static byte[] aesKeyTransformed;

    public static void main(String args[]){

        Cryptography.generateAESkey();
        Cryptography.encryptAESKey(new File("G:\\HASHBABYHASH\\public.txt"));
        Cryptography.decryptAESKey(new File("G:\\HASHBABYHASH\\private.txt"));

        System.out.println("String: " + Base64.encode(Cryptography.getAesKey()) + "\r\n");
        System.out.println("Encrypted string: " + Cryptography.getBase64EncryptedKey() + "\r\n");
        System.out.println("Decrypted String: " + Base64.encode(Cryptography.getAesKeyTransformed()) + "\r\n");

    }

    public static void generateAESkey(){

        try {
            KeyGenerator    keyGen = KeyGenerator.getInstance("AES");

            keyGen.init(256); 
            SecretKey secretKey = keyGen.generateKey();

            byte[] keyBytes = secretKey.getEncoded(); 
            base64AESKey = Base64.encode(keyBytes); 

            aesKey = keyBytes;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    public static void encryptAESKey(File publicKeyFile){

        try {       
            FileInputStream input = new FileInputStream(publicKeyFile);

            byte[] decoded = Base64.decode(IOUtils.toByteArray(input));     

            X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(decoded);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = keyFactory.generatePublic(publicSpec);   

            Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);  

            encryptedAESKey = cipher.doFinal(aesKey);
            base64AESEncryptedKey = Base64.encode(encryptedAESKey);

            input.close();
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void decryptAESKey(File privateKeyFile){

        try {
            FileInputStream input = new FileInputStream(privateKeyFile);

            byte[] decoded = Base64.decode(IOUtils.toByteArray(input));

            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

            Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);  

            aesKeyTransformed = cipher.doFinal(encryptedAESKey);
            input.close();  
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}
结果如下:

String: xVwH7Nbz84emVoH0J31sRHC+B669T9wCUVlTDhYgXiI=

Encrypted string: INTA8rx46hX6bZbDIl4iiWsUGO4ywCW0Aee1reqQ3wR5X7He5ztLHvyZoa0WZmUGYbYwprNGffRI
OVJFxczMHkxUfHU1WWCTzcfNylD+sWObIYrbyc13aZi9OL/r1GXuaGtkIgTJyqv0QPHfIri7iaH3
Lr/F4EIcyphJM3E2reQ=

Decrypted String: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxVwH7Nbz84emVoH0J31sRHC+
B669T9wCUVlTDhYgXiI=

在RSA中,一些数据被编码成一个大的数字并根据它进行计算
NoPadding
(unpadded或RSA)表示您对消息的正确编码负有全部责任。所有计算都是针对大模数进行的(目前至少应为2048位)。由于Java采用大端数字,因此消息会自动编码为最低有效字节,但解密会以相同的模大小返回解码后的消息,因为它无法知道前导的零字节是否是有意的

为了使此计算正确和安全,有必要应用填充。旧式PKCS#1 v1.5填充现在被认为是不安全的,但它只有11字节的开销(只有2048/8-11=245字节可以用2048位的密钥加密)。较新的PKCS#1 v2.1 padding(OAEP)被认为是安全的,应该在这里使用。如果使用SHA-1,它确实有42字节的开销

“RSA/ECB/NOPANDING”参数是必需的

这真的很糟糕,因为它非常不安全:

如果您不愿意简单地将密码字符串更改为
cipher.getInstance(“RSA/ECB/OAEPWithSHA-256和mgf1padding”),您必须自己删除前导零。当然,问题是这种“零填充”模式是不明确的,如果明文以0x00字节开始,您将无法将其与填充字节区分开来,必须将其删除,从而破坏明文如果明文是AES密钥,则有0.3%的几率它以0x00字节开始,从而断开密钥。您必须确保密钥实际上是正确的,如果长度不正确,则填充零字节

以下是如何删除前导零字节:

byte[] unpadZeros(byte[] in) {
    int i = 0;
    while(in[i] == 0) i++;
    return Arrays.copyOfRange(in, i, in.length);
}
如果您知道您正在解密AES密钥,则可能会使未加密密钥生成错误数据:

byte[] unpadZerosToGetAesKey(byte[] in) {
    int i = 0;
    while(in[i] == 0) i++;
    int len = in.length - i;
    if (len <= 16) len = 16;
    else if (len <= 24) len = 24;
    else len = 32;
    return Arrays.copyOfRange(in, in.length - len, in.length);
}
byte[]unpadZerosToGetAesKey(字节[]in){
int i=0;
而(在[i]==0)i++;
int len=in.length-i;

如果(len是
PKCS8EncodedKeySpec
所需的解密密钥规范?应该是X509,对吗?关于这些参数的有趣(或悲伤)的事情是,它们是由我国的税务局强加的=)。第二种方法正是我所需要的,谢谢。