Java RSA 解密错误 - IllegalBlockSizeException: 数据不得超过 128 字节
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18279378/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
RSA decryption error - IllegalBlockSizeException: Data must not be longer than 128 bytes
提问by user1349407
I am now on making RSA message authentication software. The process is as follows:
我现在正在制作RSA消息认证软件。过程如下:
- Sign the message by using A's private key (1024 bit)
- Verify the message by using A's public key (1024 bit)
- 使用 A 的私钥(1024 位)对消息进行签名
- 使用 A 的公钥(1024 位)验证消息
The #1 code (below) works fine and generates following result:
#1 代码(如下)工作正常并生成以下结果:
5554c9a9f6838b6cf40d9dbfbab3d90ea27aa6434ed095d289c13c2624617993ad99161ac265276d150510c176341d8ab8600d08b7353286d465e6bd3370a6fd8dd3ffb82916f612fd6dcee5e654ed801cfca6b6d2d5d6dc99ff7921b615abdf62eb67db1f71e6a6ea70012fd35e7cefa1a8d3aab7614c47746cfe1fc2bc875b
5554c9a9f6838b6cf40d9dbfbab3d90ea27aa6434ed095d289c13c2624617993ad99161ac265276d150510c176341d8ab8600d08b7353286d465e6bd3370a6fd8dd3ffb82916f612fd6dcee5e654ed801cfca6b6d2d5d6dc99ff7921b615abdf62eb67db1f71e6a6ea70012fd35e7cefa1a8d3aab7614c47746cfe1fc2bc875b
However the #2 code shows following error:
然而,#2 代码显示以下错误:
javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes
javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes
I think the line in #1 Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
generates 2048 bits (256 bytes) result. Perhaps this is the problem... remember that I use 1024 bit private key.
我认为 #1 中的行Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
生成 2048 位(256 字节)结果。也许这就是问题所在……请记住,我使用的是 1024 位私钥。
So how can the #1 code generate 128-byte result?
那么#1 代码如何生成 128 字节的结果呢?
1. SignMail.java
1. 签名邮件.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
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;
}
}
the #2 code was executed by the following JSP
#2 代码由以下 JSP 执行
#3 messageVeri.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();
}
*/
%>
采纳答案by Duncan Jones
Your signature string contains 256 characters, however this is hexadecimal and really represents 128 bytes.
您的签名字符串包含 256 个字符,但这是十六进制的,实际表示 128个字节。
Before you verify the signature, you must convert it back to a byte array. This is not achieved through someString.getBytes()
but rather via DatatypeConverter.parseHexBinary(someString)
(or any other method you prefer from Google).
在验证签名之前,您必须将其转换回字节数组。这不是通过someString.getBytes()
而是通过DatatypeConverter.parseHexBinary(someString)
(或您喜欢的任何其他 Google 方法)实现的。
Also, I would strongly recommend you use the Signature
class rather than the Cipher
class when signing messages. Currently your code can only handle messages that are smaller than 128 bytes in length (smaller, in fact, due to padding). Instead, you should be hashing the message prior to signing (e.g. using the SHA256withRSA
mechanism).
此外,我强烈建议您在签署消息时使用Signature
类而不是Cipher
类。目前,您的代码只能处理长度小于 128 字节的消息(实际上由于填充而更小)。相反,您应该在签名之前对消息进行哈希处理(例如使用SHA256withRSA
机制)。
回答by Carl
you need split your data by the publicKey
您需要通过 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);
}
回答by Vova Perebykivskyi
As Duncan said, you need something like DateTypeConverter.
正如邓肯所说,你需要像 DateTypeConverter 这样的东西。
I used Base64.getDecoder().decode(encodedString), it returned 128 byte array.
我使用Base64.getDecoder().decode(encodedString),它返回 128 字节数组。
When I had been using encodedString.getBytes(), it returned 172 byte array.
当我使用encodedString.getBytes() 时,它返回了172 字节的数组。
By the way I had 1024 bits RSA key.
顺便说一下,我有 1024 位 RSA 密钥。
回答by Ali Bagheri
first convert text to Hex and then encrypt it, after encrypt you can convert from hex to string.
首先将文本转换为十六进制然后加密它,加密后您可以从十六进制转换为字符串。
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));
}
回答by vipin kumar
Here is code:
这是代码:
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;