2013-08-16 193 views
2

我現在正在製作RSA消息身份驗證軟件。過程如下:RSA解密錯誤 - IllegalBlockSizeException:數據不得超過128字節

  1. 註冊的消息通過用A的私有密鑰(1024位)
  2. 使用A的公鑰(1024位)

#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(); 
    } 
    */ 

%> 
+0

你可以顯示堆棧跟蹤嗎?知道哪一行代碼會產生錯誤會很有幫助。 – Zhe

+0

參見#3代碼(JSP)..「try {} catch(Exception ex){out.print(」Error 2:「+ ex);}」短語收集的錯誤.. – user1349407

回答

8

您的簽名字符串包含256個字符,但是這是十六進制的,真正代表128 字節

在驗證簽名之前,您必須將其轉換回字節數組。這不是通過someString.getBytes()而是通過DatatypeConvert.parseHexBinary(someString)(或您希望從Google獲得的任何其他方法)實現的。

此外,我強烈建議您在簽名消息時使用Signature類而不是Cipher類。目前,您的代碼只能處理長度小於128個字節的消息(實際上由於填充而更小)。相反,您應該在簽名前散列消息(例如使用SHA256withRSA mechanism)。

+0

哇!對..現在它工作正常..謝謝。 – user1349407

+0

排錯** DatatypeConverter **而不是** DatatypeConvert **。節省一分鐘以備將來參考 – snAtchnAren

0

你需要鄧肯表示,受公鑰

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); 
} 
+0

@Duncan的答案是正確的解決方案。只是因爲你可以做點什麼並不意味着你應該這樣做。 – zaph

0

分割你的數據,你需要像DateTypeConverter。

我用Base64.getDecoder()。decode(encodedString),它返回128字節的數組。

當我使用encodedString.getBytes()時,它返回172字節的數組。

順便說一句,我有1024位的RSA密鑰。

相關問題