RSA解密错误 - IllegalBlockSizeException:数据不得超过128个字节

时间:2013-08-16 17:54:47

标签: java key rsa jce

我现在正在制作RSA消息认证软件。过程如下:

  1. 使用A的私钥(1024位)
  2. 对邮件进行签名
  3. 使用A的公钥(1024位)
  4. 验证消息

    #1代码(如下)工作正常并产生以下结果:

      

    5554c9a9f6838b6cf40d9dbfbab3d90ea27aa6434ed095d289c13c2624617993ad99161ac265276d150510c176341d8ab8600d08b7353286d465e6bd3370a6fd8dd3ffb82916f612fd6dcee5e654ed801cfca6b6d2d5d6dc99ff7921b615abdf62eb67db1f71e6a6ea70012fd35e7cefa1a8d3aab7614c47746cfe1fc2bc875b

    然而,#2代码显示以下错误:

      

    javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes

    我认为#1 Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");中的一行 生成2048位(256字节)的结果。也许这就是问题......记住我使用1024位私钥。

    那么#1代码如何生成128字节的结果呢?

    1。 SignMail.java

    public class SignMail {
    
        static {
            Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider());
        }
    
        public static String sign(String userOriginalMessage) throws Exception {
    
            PEMReader userPrivateKey = new PEMReader(
              new InputStreamReader(
                 new FileInputStream(Environment.getExternalStorageDirectory()+"/pkcs10priv.key")));
    
            KeyPair keyPair = (KeyPair)userPrivateKey.readObject();
    
            byte[] cipherText;
            //modified by JEON 20130817
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            //encrypt the message using private key
            cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate());
            cipherText = cipher.doFinal(userOriginalMessage.getBytes());
            return new String(Hex.encode(cipherText));
    
        }
    
    
    }
    

    2。 UserSMSVerifier.java

    public class UserSMSVerifier {
    
    static String signedMail;
    
    static {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    }
    
    
    public static String messageGenarator(
            String UserCert,
            String origninalMessage         
            ) throws Exception{
    
        InputStream userCertStream = new ByteArrayInputStream(UserCert.getBytes("UTF-8"));
    
        PEMReader userCerti = new PEMReader(
                  new InputStreamReader(
                          userCertStream));
    
    
    
        //KeyPair userPrivate = (KeyPair)userPrivateKey.readObject();
        X509Certificate userCert = (X509Certificate)userCerti.readObject();
    
    
        byte[] dectyptedText = null;
        // decrypt the text using the private key
        //modified by JEON 20130817
        //Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.DECRYPT_MODE, userCert.getPublicKey());
        dectyptedText = cipher.doFinal(origninalMessage.getBytes());
    
        String result = new String(dectyptedText, Charset.forName("UTF-8"));
        return result;
    
    }
    
    
    }
    

    #2代码由以下JSP

    执行

    #3 messageVeri.JSP

    <%@ page language="java" contentType="text/html; charset=euc-kr" %>
    
    <%@ page session = "true" %>
    
    <%@ page import="java.sql.DriverManager" %>
    <%@ page import="java.sql.Connection" %>
    <%@ page import="java.sql.PreparedStatement" %>
    <%@ page import="java.sql.Statement" %>
    <%@ page import="java.sql.SQLException" %>
    <%@ page import="java.sql.ResultSet" %>
    
    <%@ page import="myPackage.UserSMSVerifier" %>
    
    
    <%
        request.setCharacterEncoding("euc-kr");
    
        String userID = request.getParameter("sender");
        String encryptedSMS = request.getParameter("encryptedSMS");
    
        //String sql = "select user_id, user_pw from testca.testca_init where user_id=? and user_pw=?";
        //String sql = "update testca.testca_init set pkcs10request = '"+pkcs10request_new+"' where user_id='user1'";
        String sql = "select * from testca.testca_init where user_id='" + userID + "'";
    
        Class.forName("com.mysql.jdbc.Driver");
    
        Connection conn = null;
        PreparedStatement pstmt = null;
    
        Statement stmt = null;
        ResultSet rs = null;
    
        String jdbcDriver = "jdbc:mysql://localhost:3306/";
        String dbUser = "root";
        String dbPass = "fo.......t";
    
    
        try{
            conn = DriverManager.getConnection(jdbcDriver, dbUser, dbPass);
            stmt = conn.createStatement();
            //stmt.executeUpdate(sql);
            rs=stmt.executeQuery(sql);
            while(rs.next()){
            //rs=stmt.executeQuery(sql);
            String userCertificate=rs.getString("certificate");
            UserSMSVerifier.messageGenarator(userCertificate, encryptedSMS);
            }
    
    
    
        }catch(Exception ex){out.print("Error 2: " +ex);}
        /*
        if(rs.next())
        {
            //session.setAttribute("userID", userID);
            out.print("Insert Succeed!");
            out.println();
            //out.print("Welcome!" + " " + session.getAttribute("userID"));
        }
        else
        {
            out.print("failed to login!");
            //session.invalidate();
        }
        */
    
    %>
    

5 个答案:

答案 0 :(得分:12)

您的签名字符串包含256个字符,但这是十六进制的,实际上代表128个字节

在验证签名之前,必须将其转换回字节数组。这不是通过someString.getBytes()实现的,而是通过DatatypeConverter.parseHexBinary(someString)(或您偏爱Google的任何其他方法)实现的。

此外,我强烈建议您在签名邮件时使用Signature类而不是Cipher类。目前,您的代码只能处理长度小于128字节的消息(实际上,由于填充较小)。相反,您应该在签名之前对邮件进行哈希处理(例如,使用SHA256withRSA mechanism)。

答案 1 :(得分:0)

您需要通过publicKey分割数据

int keyLength = publicKey.getModulus().bitLength() / 16;
String[] datas = splitString(data, keyLength - 11);
String mi = "";
        //如果明文长度大于模长-11则要分组加密
for (String s : datas) {
            mi += bcd2Str(cipher.doFinal(s.getBytes()));
}
return mi;


public static String bcd2Str(byte[] bytes) {
    char temp[] = new char[bytes.length * 2], val;

    for (int i = 0; i < bytes.length; i++) {
        val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
        temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');

        val = (char) (bytes[i] & 0x0f);
        temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
    }
    return new String(temp);
}

答案 2 :(得分:0)

正如Duncan所说,你需要像DateTypeConverter这样的东西。

我使用 Base64.getDecoder()。decode(encodedString),它返回128字节数组。

当我使用encodedString.getBytes()时,它返回了172字节数组。

顺便说一下,我有1024位RSA密钥。

答案 3 :(得分:0)

首先将文本转换为十六进制然后加密,加密后可以从十六进制转换为字符串。

public static String toHex(String text)
{
    return String.format("%040x", new BigInteger(1, text.getBytes()));
}

public static byte[] hexToBytes(String hex)
{
    int l = hex.length();
    byte[] data = new byte[l/2];

    for (int i = 0; i < l; i += 2)
    {
        data[i/2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i+1), 16));
    }

    return data;
}

public static String hexToStringA(String hex)
{
    return new String(hexToBytes(hex));
}

答案 4 :(得分:0)

这是代码:

     EncDecProcessor ac = new EncDecProcessor();
     PublicKey publicKey = ac.getPublic(keyPath);
     this.cipher.init(Cipher.DECRYPT_MODE, publicKey);
     byte[] b = new byte[2048];
     b = msg.getBytes("UTF-8");
     byte[] byteStr = Base64.decodeBase64(b);
     String Str = new String(cipher.doFinal(byteStr), "UTF-8");

     return Str;