【问题标题】:Basic Program for encrypt/Decrypt : javax.crypto.BadPaddingException: Decryption error加密/解密的基本程序:javax.crypto.BadPaddingException:解密错误
【发布时间】:2016-09-25 01:35:22
【问题描述】:

我已经为加密/解密编写了一个非常基本的程序,并且现在尝试只加密单个字符串。 加密工作正常,但在解密中却抛出错误

javax.crypto.BadPaddingException:解密错误。

下面一行报错

byte[] decodedData = (rsa.doFinal(decodedValue));

我尝试了多种方法并经历了很多线程,但无法找到解决方案。有人可以帮我解决这个问题吗?

这个类很简单,只有4个方法,第一个是测试方法,第二个是keystore加载方法,剩下的两个方法是加密和解密。

package XXXX;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;


public class EncryptDecryptUtil {
    private String publicKeyStoreFileName = "C:\\Program Files\\Java\\jdk1.8.0_51\\jre\\lib\\security\\cacerts";
    private String pubKeyStorePwd = "XXX";
    private String pubKeyAlias="XXXX";
    private static final String JKS = "JKS";
    private static final int CONST_16 = 16;    

    public String TestMethod(final String clearText) throws InvalidKeyException, NoSuchAlgorithmException,
            NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, KeyStoreException,
            CertificateException, IOException, UnrecoverableKeyException {    
        byte[] ecryptedAESKey = generateEncryptedData("TEST");
        System.out.println("Encrypted Key = " + ecryptedAESKey);
        System.out.println("Decrypted Key = " + generateDecryptedData(ecryptedAESKey));
        return generateDecryptedData(ecryptedAESKey);
    }

    private KeyStore loadKeyStore() throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
            IOException {
        KeyStore keystore = KeyStore.getInstance(JKS);
        FileInputStream tmp = new FileInputStream(publicKeyStoreFileName);
        keystore.load(tmp, pubKeyStorePwd.toCharArray());
        tmp.close();
        return keystore;
    } 

    private byte[] generateEncryptedData(final String data) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
            KeyStoreException, CertificateException, IOException, UnrecoverableKeyException {
            Base64 base64 = new Base64();
            X509Certificate cert;
            KeyStore keystore = loadKeyStore();
            cert = (java.security.cert.X509Certificate) keystore.getCertificate(pubKeyAlias);
            Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            rsa.init(Cipher.ENCRYPT_MODE, cert);

            byte[] ecrypteddata = (base64.encode(rsa.doFinal(data.getBytes(StandardCharsets.UTF_8))));
            return ecrypteddata;
    }

    public String generateDecryptedData(final byte[] encryptedData) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
            KeyStoreException, CertificateException, IOException, UnrecoverableKeyException {
            Base64 base64 = new Base64();
            X509Certificate cert;
            KeyStore keystore = loadKeyStore();
            cert = (java.security.cert.X509Certificate) keystore.getCertificate(pubKeyAlias);
            Cipher rsa = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            rsa.init(Cipher.DECRYPT_MODE, cert);
            byte[] decodedValue = base64.decode(encryptedData);
            byte[] decodedData = (rsa.doFinal(decodedValue));
            return new String(decodedData);
    }    
}

【问题讨论】:

  • 您是否使用 UTF-8 作为系统编码?也许不是......只需尝试运行 byte[] ecryptedAESKey = generateEncryptedData(new String("TEST", "UTF-8"));
  • 感谢 Jurgen 的回复..!!我已经尝试过不使用 UTF-8(只是 data.getBytes())以及使用 UTF-8。还尝试将数据从加密器传递到解密器作为字节数组,作为字符串作为十六进制数据但没有成功,同样的错误

标签: java encryption cryptography


【解决方案1】:

Java API 在证书和 (RSA) 私钥之间有所区别。私钥虽然与证书相关联,但不是证书的一部分。 Java(幸运的是)在这方面与微软的 .NET API 不同,.NET API 充满了这样糟糕的设计选择。

当你尝试时:

rsa.init(Cipher.DECRYPT_MODE, cert);

您应该预料到会出现错误。然而,问题是这种结构有时用于使用公钥原始“解密”基于 RSA 的签名。因此,尽管您应该始终使用私钥解密,但并未明确禁止使用公钥解密(对于 Sun 提供商)。

因此请尝试使用KeyStore#getKey 或(更现代但更复杂)KeyStore#getEntry 来检索私钥。也就是说:如果您确实在密钥库中确实有一个私钥和用于检索它的密码。

【讨论】:

  • 感谢 Maarten 的回复。我也已经尝试过了。 ~ rsa.init(Cipher.DECRYPT_MODE, keystore.getKey(pubKeyAlias, pubKeyStorePwd.toCharArray())); ~ 即使上面的代码也给出了同样的错误。也试过没有base64.decode和base64.encode,只是直接传递加密数据,仍然给出相同的错误:(
  • 你找回私钥了吗?尝试将返回的密钥转换为 PrivateKey 看看会发生什么......还有一些方法可以检查条目是否(或更准确地说,还包含)私钥。
  • 是的,它确实返回了我 sun.security.rsa.RSAPrivateCrtKeyImpl@ffd74a7d。无法获取实际值,因为 lib 中没有打印它的方法。
  • 您应该能够将值转换为RSAPrivateCrtKey。然后,您可以在强制转换后再次将该密钥的模数与证书中的公钥进行比较(证书应该有获取它的方法)。你能试试吗?
  • 现在终于成功了。我正在使用公钥加密,如下所示 ** Key key = cert.getPublicKey(); rsa.init(Cipher.ENCRYPT_MODE, key);** 并按照下面的方法使用 PrivateKey 解密,现在可以正常工作了。 ** 密钥密钥 = keystore.getKey(pubKeyAlias, pubKeyStorePwd.toCharArray()); rsa.init(Cipher.DECRYPT_MODE, key); ** 对不起,我无法弄清楚如何格式化注释中的代码,稍后会找到它,稍后会编辑 cmets。
【解决方案2】:

下面是最终的工作代码:

import org.apache.commons.codec.binary.Base64;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

/**
 * Created by Suchit Pandya on 9/16/2016.
 */
public class EncryptLogic2 {

    private String publicKeyStoreFileName = "C:\\Program Files\\Java\\jdk1.8.0_51\\jre\\lib\\security\\cacerts";
    private String pubKeyStorePwd = "password";
    private String pubKeyAlias="certName";
    private static final String JKS = "JKS";
    private static final String SHA1PRNG = "SHA1PRNG";
    private static final String RSA = "RSA/ECB/PKCS1Padding";
    private static final int CONST_16 = 16;
    private static byte[] asKey;


    public String TestMethod(final String clearText) throws InvalidKeyException, NoSuchAlgorithmException,
            NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, KeyStoreException,
            CertificateException, IOException, UnrecoverableKeyException {

        String ecryptedData = generateEncryptedData(clearText);
        String decryptedData = generateDecryptedData(ecryptedData);
        System.out.println("*********** Decrypted Data = " + decryptedData);
        return decryptedData;
    }

    private KeyStore loadKeyStore() throws KeyStoreException, NoSuchAlgorithmException, CertificateException,
            IOException {
        KeyStore keystore = KeyStore.getInstance(JKS);
        FileInputStream tmp = new FileInputStream(publicKeyStoreFileName);
        keystore.load(tmp, pubKeyStorePwd.toCharArray());
        tmp.close();
        return keystore;
    }

    private String generateEncryptedData(final String data) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
            KeyStoreException, CertificateException, IOException, UnrecoverableKeyException {
        Base64 base64 = new Base64();
        X509Certificate cert;
        KeyStore keystore = loadKeyStore();
        cert = (java.security.cert.X509Certificate) keystore.getCertificate(pubKeyAlias);
        Cipher rsa = Cipher.getInstance(RSA);
        Key key = cert.getPublicKey();
        rsa.init(Cipher.ENCRYPT_MODE, key);

        byte[] ecrypteddata = rsa.doFinal(data.getBytes(StandardCharsets.UTF_8));
        return Base64.encodeBase64String(ecrypteddata);
    }

    public String generateDecryptedData(final String encryptedData) throws NoSuchAlgorithmException,
            NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
            KeyStoreException, CertificateException, IOException, UnrecoverableKeyException {
        Base64 base64 = new Base64();
        X509Certificate cert;
        KeyStore keystore = loadKeyStore();
        Cipher rsa = Cipher.getInstance(RSA);
        Key key = keystore.getKey(pubKeyAlias, pubKeyStorePwd.toCharArray());
        rsa.init(Cipher.DECRYPT_MODE, key);
        byte[] decodedValue = base64.decode(encryptedData);
        byte[] decodedData = (rsa.doFinal(decodedValue));
        return new String(decodedData, StandardCharsets.UTF_8);
    }
}

【讨论】:

  • 只是给你一个警告,对任何其他精心制作的会话密钥数据或花哨的组算术数据使用 RSA 加密是非常危险的。围绕它的数学很难理解,事情很慢,更不用说尺寸限制了。你最好像最初尝试的那样使用混合模式。
  • @eckes:我已经完成了研究,但 RSA 仍然符合我的要求。我只将它用于精心制作的数据。我的数据仅在同一台服务器上加密和解密,并在出现错误情况时临时存储在磁盘上。它永远不会超过 16 个字节。我了解这里的所有风险。我刚刚发布了这个解决方案,所以如果有其他人在寻找有效的 RSA 代码,他们可以找到它。请不要将答案标记为否定。
  • @eckes:如果您愿意关注对话,这篇文章的重点是使用 RSA 获得有效的解决方案,这是 RSA 解决方案的有效答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-10
相关资源
最近更新 更多