求助,关于RSA加rsa4096解密修复工具的填充问题

28303人阅读
安全防御(11)
1. 加密的系统不要具备解密的功能,否则 RSA 可能不太合适公钥加密,私钥解密。加密的系统和解密的系统分开部署,加密的系统不应该同时具备解密的功能,这样即使黑客攻破了加密系统,他拿到的也只是一堆无法破解的密文数据。否则的话,你就要考虑你的场景是否有必要用 RSA 了。2. 可以通过修改生成密钥的长度来调整密文长度生成密文的长度等于密钥长度。密钥长度越大,生成密文的长度也就越大,加密的速度也就越慢,而密文也就越难被破解掉。著名的&安全和效率总是一把双刃剑&定律,在这里展现的淋漓尽致。我们必须通过定义密钥的长度在&安全&和&加解密效率&之间做出一个平衡的选择。3. 生成密文的长度和明文长度无关,但明文长度不能超过密钥长度不管明文长度是多少,RSA 生成的密文长度总是固定的。但是明文长度不能超过密钥长度。比如 Java 默认的 RSA 加密实现不允许明文长度超过密钥长度减去 11(单位是字节,也就是 byte)。也就是说,如果我们定义的密钥(我们可以通过 java.security.KeyPairGenerator.initialize(int keysize) 来定义密钥长度)长度为 1024(单位是位,也就是 bit),生成的密钥长度就是 1024位 / 8位/字节 = 128字节,那么我们需要加密的明文长度不能超过 128字节 -11 字节 = 117字节。也就是说,我们最大能将 117 字节长度的明文进行加密,否则会出问题(抛诸如 javax.crypto.IllegalBlockSizeException: Data must not be longer than 53 bytes 的异常)。而 BC 提供的加密算法能够支持到的 RSA 明文长度最长为密钥长度。4. byte[].toString() 返回的实际上是内存地址,不是将数组的实际内容转换为 String警惕 toString 陷阱:Java 中数组的 toString() 方法返回的并非数组内容,它返回的实际上是数组存储元素的类型以及数组在内存的位置的一个标识。大部分人跌入这个误区而不自知,包括一些写了多年 Java 的老鸟。比如这篇博客《》中的代码public class TestByte
public static void main(String[] argv) {
String example = &This is an example&;
byte[] bytes = example.getBytes();
System.out.println(&Text : & + example);
System.out.println(&Text [Byte Format] : & + bytes);
System.out.println(&Text [Byte Format] : & + bytes.toString());
String s = new String(bytes);
System.out.println(&Text Decryted : & + s);
}输出:Text : This is an exampleText [Byte Format] : [B@187aecaText [Byte Format] : [B@187aecaText Decryted : This is an example以及这篇博客《》中的代码final byte[] cipherText = encrypt(originalText, publicKey);
System.out.println(&Encrypted: & +cipherText.toString());输出:[B@4c3a8ea3这些输出其实都是字节数组在内存的位置的一个标识,而不是作者所认为的字节数组转换成的字符串内容。如果我们对密钥以 byte[].toString() 进行持久化存储或者和其他一些字符串打 json 传输,那么密钥的解密者得到的将只是一串毫无意义的字符,当他解码的时候很可能会遇到 &javax.crypto.BadPaddingException& 异常。5. 字符串用以保存文本信息,字节数组用以保存二进制数据java.lang.String 保存明文,byte 数组保存二进制密文,在 java.lang.String 和 byte[] 之间不应该具备互相转换。如果你确实必须得使用 java.lang.String 来持有这些二进制数据的话,最安全的方式是使用 Base64(推荐 Apache 的 commons-codec 库的 mons.codec.binary.Base64):
// use String to hold cipher binary data
Base64 base64 = new Base64();
String cipherTextBase64 = base64.encodeToString(cipherText);
// get cipher binary data back from String
byte[] cipherTextArray = base64.decode(cipherTextBase64);6. 每次生成的密文都不一致证明你选用的加密算法很安全一个优秀的加密必须每次生成的密文都不一致,即使每次你的明文一样、使用同一个公钥。因为这样才能把明文信息更安全地隐藏起来。Java 默认的 RSA 实现是 &RSA/None/PKCS1Padding&(比如 Cipher cipher = Cipher.getInstance(&RSA&);句,这个 Cipher 生成的密文总是不一致的),Bouncy Castle 的默认 RSA 实现是 &RSA/None/NoPadding&。为什么 Java 默认的 RSA 实现每次生成的密文都不一致呢,即使每次使用同一个明文、同一个公钥?这是因为 RSA 的 PKCS #1 padding 方案在加密前对明文信息进行了随机数填充。你可以使用以下办法让同一个明文、同一个公钥每次生成同一个密文,但是你必须意识到你这么做付出的代价是什么。比如,你可能使用 RSA 来加密传输,但是由于你的同一明文每次生成的同一密文,攻击者能够据此识别到同一个信息都是何时被发送。Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
final Cipher cipher = Cipher.getInstance(&RSA/None/NoPadding&, &BC&);7. 可以通过调整算法提供者来减小密文长度Java 默认的 RSA 实现 &RSA/None/PKCS1Padding& 要求最小密钥长度为 512 位(否则会报 java.security.InvalidParameterException: RSA keys must be at least 512 bits long 异常),也就是说生成的密钥、密文长度最小为 64 个字节。如果你还嫌大,可以通过调整算法提供者来减小密文长度:Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(&RSA&, &BC&);
keyGen.initialize(128);如此这般得到的密文长度为 128 位(16 个字节)。但是这么干之前请先回顾一下本文第 2 点所述。8.&Cipher 是有状态的,而且是线程不安全的javax.crypto.Cipher 是有状态的,不要把&Cipher 当做一个静态变量,除非你的程序是单线程的,也就是说你能够保证同一时刻只有一个线程在调用 Cipher。否则你可能会像笔者似的遇到&java.lang.ArrayIndexOutOfBoundsException: too much data for RSA block 异常。遇见这个异常,你需要先确定你给&Cipher 加密的明文(或者需要解密的密文)是否过长;排除掉明文(或者密文)过长的情况,你需要考虑是不是你的&Cipher 线程不安全了。后记虽然《》存在一些认识上的误区,但笔者仍然认为它是一篇很不错的入门级文章。结合本文所列内容,笔者将其代码做了一些调整以供参考:import java.io.F
import java.io.FileInputS
import java.io.FileNotFoundE
import java.io.FileOutputS
import java.io.IOE
import java.io.ObjectInputS
import java.io.ObjectOutputS
import java.security.KeyP
import java.security.KeyPairG
import java.security.NoSuchAlgorithmE
import java.security.PrivateK
import java.security.PublicK
import java.security.S
import javax.crypto.C
import mons.codec.binary.Base64;
* @author JavaDigest
public class EncryptionUtil {
* String to hold name of the encryption algorithm.
public static final String ALGORITHM = &RSA&;
* String to hold name of the encryption padding.
public static final String PADDING = &RSA/NONE/NoPadding&;
* String to hold name of the security provider.
public static final String PROVIDER = &BC&;
* String to hold the name of the private key file.
public static final String PRIVATE_KEY_FILE = &e:/defonds/work//private.key&;
* String to hold name of the public key file.
public static final String PUBLIC_KEY_FILE = &e:/defonds/work//public.key&;
* Generate key which contains a pair of private and public key using 1024
* bytes. Store the set of keys in Prvate.key and Public.key files.
* @throws NoSuchAlgorithmException
* @throws IOException
* @throws FileNotFoundException
public static void generateKey() {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance(
ALGORITHM, PROVIDER);
keyGen.initialize(256);
final KeyPair key = keyGen.generateKeyPair();
File privateKeyFile = new File(PRIVATE_KEY_FILE);
File publicKeyFile = new File(PUBLIC_KEY_FILE);
// Create files to store public and private key
if (privateKeyFile.getParentFile() != null) {
privateKeyFile.getParentFile().mkdirs();
privateKeyFile.createNewFile();
if (publicKeyFile.getParentFile() != null) {
publicKeyFile.getParentFile().mkdirs();
publicKeyFile.createNewFile();
// Saving the Public key in a file
ObjectOutputStream publicKeyOS = new ObjectOutputStream(
new FileOutputStream(publicKeyFile));
publicKeyOS.writeObject(key.getPublic());
publicKeyOS.close();
// Saving the Private key in a file
ObjectOutputStream privateKeyOS = new ObjectOutputStream(
new FileOutputStream(privateKeyFile));
privateKeyOS.writeObject(key.getPrivate());
privateKeyOS.close();
} catch (Exception e) {
e.printStackTrace();
* The method checks if the pair of public and private key has been
* generated.
* @return flag indicating if the pair of keys were generated.
public static boolean areKeysPresent() {
File privateKey = new File(PRIVATE_KEY_FILE);
File publicKey = new File(PUBLIC_KEY_FILE);
if (privateKey.exists() && publicKey.exists()) {
* Encrypt the plain text using public key.
* @param text
: original plain text
* @param key
:The public key
* @return Encrypted text
* @throws java.lang.Exception
public static byte[] encrypt(String text, PublicKey key) {
byte[] cipherText =
// get an RSA cipher object and print the provider
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
final Cipher cipher = Cipher.getInstance(PADDING, PROVIDER);
// encrypt the plain text using the public key
cipher.init(Cipher.ENCRYPT_MODE, key);
cipherText = cipher.doFinal(text.getBytes());
} catch (Exception e) {
e.printStackTrace();
return cipherT
* Decrypt text using private key.
* @param text
:encrypted text
* @param key
:The private key
* @return plain text
* @throws java.lang.Exception
public static String decrypt(byte[] text, PrivateKey key) {
byte[] dectyptedText =
// get an RSA cipher object and print the provider
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
final Cipher cipher = Cipher.getInstance(PADDING, PROVIDER);
// decrypt the text using the private key
cipher.init(Cipher.DECRYPT_MODE, key);
dectyptedText = cipher.doFinal(text);
} catch (Exception ex) {
ex.printStackTrace();
return new String(dectyptedText);
* Test the EncryptionUtil
public static void main(String[] args) {
// Check if the pair of keys are present else generate those.
if (!areKeysPresent()) {
// Method generates a pair of keys using the RSA algorithm and
// stores it
// in their respective files
generateKey();
final String originalText = &&;
ObjectInputStream inputStream =
// Encrypt the string using the public key
inputStream = new ObjectInputStream(new FileInputStream(
PUBLIC_KEY_FILE));
final PublicKey publicKey = (PublicKey) inputStream.readObject();
final byte[] cipherText = encrypt(originalText, publicKey);
// use String to hold cipher binary data
Base64 base64 = new Base64();
String cipherTextBase64 = base64.encodeToString(cipherText);
// get cipher binary data back from String
byte[] cipherTextArray = base64.decode(cipherTextBase64);
// Decrypt the cipher text using the private key.
inputStream = new ObjectInputStream(new FileInputStream(
PRIVATE_KEY_FILE));
final PrivateKey privateKey = (PrivateKey) inputStream.readObject();
final String plainText = decrypt(cipherTextArray, privateKey);
// Printing the Original, Encrypted and Decrypted Text
System.out.println(&Original=& + originalText);
System.out.println(&Encrypted=& + cipherTextBase64);
System.out.println(&Decrypted=& + plainText);
} catch (Exception e) {
e.printStackTrace();
}先生成一对密钥,供以后加解密使用(不需要每次加解密都生成一个密钥),密钥长度为 256 位,也就是说生成密文长度都是 32 字节的,支持加密最大长度为 32 字节的明文,因为使用了 nopadding 所以对于同一密钥同一明文,本文总是生成一样的密文;然后使用生成的公钥对你提供的明文信息进行加密,生成 32 字节二进制明文,然后使用 Base64 将二进制密文转换为字符串保存;之后演示了如何把 Base64 字符串转换回二进制密文;最后把二进制密文转换成加密前的明文。以上程序输出如下:Original=Encrypted=GTyX3nLO9vseMJ+RB/dNrZp9XEHCzFkHpgtaZKa8aCc=Decrypted=参考资料
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:2526052次
积分:25751
积分:25751
排名:第159名
原创:224篇
转载:51篇
译文:123篇
评论:1261条
(1)(2)(2)(1)(1)(1)(1)(3)(7)(4)(3)(5)(10)(10)(2)(2)(5)(3)(4)(4)(7)(4)(2)(1)(1)(6)(6)(4)(3)(4)(2)(21)(10)(3)(3)(5)(4)(1)(1)(3)(5)(3)(1)(1)(1)(1)(1)(3)(5)(15)(10)(1)(1)(2)(1)(2)(4)(3)(2)(1)(2)(1)(1)(1)(1)(3)(1)(1)(2)(1)(5)(5)(4)(4)(5)(1)(3)(11)(7)(7)(13)(10)(13)(1)(4)(6)(14)(16)(25)(1)(1)(1)(1)(1)(1)(1)博客访问: 3658
博文数量: 1
博客积分: 10
博客等级: 民兵
技术积分: 15
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
原文地址: 作者:
一、单向散列算法也称为Hash(哈希)算法。是一种将任意长度的消息压缩到某一固定长度(消息摘要)的函数(该过程不可逆)。Hash函数可用于数字签名、消息的完整性检测、消息起源的认证检测等。常见的散列算法有MD5、SHA、RIPE-MD、HAVAL、N-Hash、Tiger等。1. MD5算法MD5消息摘要算法(Message Digest Algorithm)。对输入的任意长度的消息进行运算,产生一个128位的消息摘要。用到4个常量初始化数据:A=h,B=89abcdefh,C=fedcba98h,D=h。可以使用其他数据作为变形。2. SHA算法安全散列算法(Secure Hash Algorithm)简称SHA。有SHA-1、SHA-256、SHA-384、SHA-512几种,分别产生160位、256位、384位和512位的散列值。2.1 SHA-1算法是一种主流的散列加密算法,设计时基于和MD4相同的原理,并模仿了该算法。消息分组和填充方式与MD5相同。也用到了一些常量做初始化数据。总结,随着密码分析技术的发展,现有的散列算法都是不安全的。如SHA-160、MD5、RIPEMD、HAVAL、Tiger在某些条件下能构造出碰撞。建议选择SHA-256//384/512,或者Whirlpool。如果在解密时碰到Hash算法,一般只需根据每种Hash算法的特征搞清楚具体哪一种Hash算法以及是否为某种算法的变形,继而通过该Hash的源代码即可破解。二、对称加密算法加密密钥和解密密钥是完全相同的。其安全性依赖于:1.加密算法足够强;2.密钥的秘密性。常见的对称分组加密算法有DES(Data Encryption Standard)、IDEA(International Data Encryption Algorithm)、AES(Advanced Encryption Standard)、BlowFish、Twofish等。1. RC4流密码现今最为流行的流密码,应用于SSL(Secure Sockes Layer)、WEP。RC4生成一种称为密钥流的伪随机流,它同明文通过异或操作相混合以达到加密的目的。解密时,同密文进行异或操作。其密钥流的生成有两部分组成:KSA(the Key-Scheduling Algorithm)和PRGA(the Pseudo-Random Generation Algorithm)。由于RC4算法加密采用XOR,所以一旦密钥序列出现重复,密文就有可能被破解。推荐使用RC4算法时,必须对加密密钥进行测试,判断其是否为弱密钥。2. TEA算法TEA(Tiny Encryption Algorithm)算法。分组长度为64位,密钥长度为128位。采用Feistel网络。其作者推荐使用32次循环加密,即64轮。TEA算法简单易懂,容易实现。但存在很大的缺陷,如相关密钥攻击。由此提出一些改进算法,如XTEA。3. IDEA算法IDEA(International Data Encryption Algorithm)国际数据加密算法。分组密码IDEA明文和密文的分组长度为64位,密钥长度为128位。该算法的特点是使用了3种不同的代数群上的操作。IDEA共使用52个16位的子密钥,由输入的128位密钥生成。加密过程由8个相同的加密步骤(加密轮函数)和一个输出变换组成。而解密过程与加密过程相同。解密与加密唯一不同的地方就是使用不同的子密钥。首先,解密所用的52个子密钥是加密的子密钥的相应于不同操作运算的逆元,其次,解密时子密钥必须以相反的顺序使用。4. BlowFish算法BlowFish算法是一个64位分组及可变密钥长度的分组密码算法,该算法是非专利的。BlowFish算法基于Feistel网络(替换/置换网络的典型代表),加密函数迭代执行16轮。分组长度为64位(bit),密钥长度可以从32位到448位。算法由两部分组成:密钥扩展部分和数据加密部分。密钥扩展部分将最长为448位的密钥转化为共4168字节长度的子密钥数组。其中,数据加密由一个16轮的Feistel网络完成。每一轮由一个密钥相关置换和一个密钥与数据相关的替换组成。5. AES算法AES(Advanced Encryption Standard,高级加密标准),用于替代DES成为新一代的加密标准。具有128比特的分组长度,并支持128、192和256比特的密钥长度,可在全世界范围内免费得到。其前身为Rijndael(读作:Rain Doll)。Rijndael算法与AES的唯一区别在于各自所支持的分组长度和密码密钥长度的反胃不同。Rijndael是具有可变分组长度和可变密钥长度的分组密码,其分组长度和密钥长度均可独立地设定为32比特的任意倍数,最小值128bit,最大256bit。而AES将分组长度固定为128位,而且仅支持128、192和256位的密钥长度,分别称为AES-128,AES-192,AES-256。三、公开密钥加密算法又称为非对称加密算法(Asymmetric Key Cryptography),即使用公钥(Public Key)加密,使用私钥(Private Key)解密。1. RSA算法RSA是第一个既能用于数据加密也能用于数字签名的算法,易于理解和操作,应用广泛。RSA的安全性依赖于大整数因子分解。目前来看,攻击RSA算法最有效的方法便是分解模n。一般认为RSA需要1024位或更长的魔术才有安全保障。2. ElGamal公钥算法其完全依赖于在有限域上计算离散对数的困难性。ElGamal的一个不足之处是密文的长度是明文的两倍。而另一种签名算法,Schnorr签名系统的密文比较短,这是由其系统内的单向散列函数决定的。3. DSA数字签名算法是在借鉴了ElGamal及Schnorr签名算法的基础上,公布的数字签名标准(Digital Signature Standard),该标准采用的算法为DSA(Digital Signature Algorithm)。其安全性同样基于有限域的离散对数问题。目前DSA的应用越来越广泛。4. 椭圆曲线密码编码学(Elliptic Curve Cryptography)相比RSA等公钥算法,使用较短的密钥长度而能得到相同程度的安全性。预测未来ECC(Elliptic Curve Cryptography)将会取代RSA成为主流的密钥算法。四、其他算法1. CRC32算法CRC(Cyclic Redundancy Checksum 或者 Cyclic Redundancy Check),是对数据的校验值,中文为“循环冗余校验码”,常用语校验数据完整性。最常见的CRC是CRC32,即数据校验值为32位。CRC32代码量小,容易理解,所以目前应用十分广泛。但同时CRC32并不是一个安全的加密算法。如果需要更安全的完整性校验算法,建议使用数字签名技术。2. Base64编码Base64编码是将二进制数据编码为可现实的字母和数字,用于传送图形、声音和传真等非文本数据。常用于MIME电子邮件格式中。其使用含有65个字符的ASCII字符集(第65个字符为“=”,用于对字符串的特殊处理过程),并用6个进制位表示一个可显示字符。把数据编码为Base64,将第一个字节放置于24位缓冲区的高8位,第二个字节放置于中间的8位,第三个字节放置于低8位。如果是少于3个字节的数据,相应的缓冲区置0。然后对24位缓冲区以6位为一组作为索引,高位优先,从字符串“ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz+/”中取出相应的元素作为输出。如果仅有一个或两个字节输入,那么只使用输出的两个或三个字符,其余的用“=”填充。解码过程是编码的逆过程。首先得到Base64字符串的每个字符在Base64码表中的索引,然后将这些索引的二进制连接起来,重新以8位为一组进行分组,即可得到源码。Base24码表:BCDFGHJKMPQRTVWXY2346789Base32码表:ABCDEFGHIJKLMNOPQRSTUVWXYZ234567Base60码表:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxWindows产品序列号就是使用Base24编码。实际使用时,码表会和标准码表不一样,但原理相同。五、常见的加密库1. Miracl大数运算库Miracl(Multiprecision Integer and Rational Arithmetic C/C++ Library),多精度整数和有理数算术运算C/C++库。它是一个大数库,实现了设计使用大数的加密技术的最基本的函数。支持RSA公钥系统、Diffie-Hellman密钥交换、DSA数字签名系统及基于GF(p)和GF(2m)的椭圆曲线加密系统。其提供了C和C++两种接口,使用方便,速度快,开源。2. FGIntFGInt(Fast Gigantic Integers),是用于Delphi的一种可以实现常见的公钥加密系统的库。3. freeLIP最初设计用于进行RSA-129挑战大数计算的大数库,采用2的30次方进制来表示大数,速度不及miracl。4. Crypto++一个实现了相当数量的加密算法的加密库。使用了C++的高级语法,文档较少,不易上手。5. LibTomCrypt一款相当不错的加密算法库。包括了常见的散列算法、对称算法及公钥加密系统。接口友好,非常适合C程序员使用。6. GMP全称GNU Multiple Precision Arithmetic Library。其核心采用汇编实现,速度非常快,超过miracl,常用来实现大整数因子的分解。7. OpenSSL主要用于网络安全,也包含了一些加密算法的实现。如对称算法中的BlowFish、IDEA、DES、CAST,公钥中的RSA、DSA,散列中的MD5、RIPEMD、SHA等。8. DCP和DECDCP全称Delphi Cryptography Package,DEC全称Delphi Encryption Compendium,都是用于Delphi的一种加密算法库。这两个算法库实现了大部分常见的散列算法及对称算法,使用方便。9. Microsoft Crypto API是微软为了方便程序员在软件中进行数字签名、数据加密的开发而提供的一套加密系统。接口友好方便。10. NTL是一个可以用于数论相关计算的库。提供了非常友好的C++接口,用于实现有符号的、算术整数的运算,以及向量、矩阵、基于有限域和整数的多项式运算。在密码学中,有限域的应用相当广泛,如AES、twofish、ECC等都涉及有限域。
阅读(1208) | 评论(0) | 转发(0) |
上一篇:没有了
下一篇:没有了
相关热门文章
给主人留下些什么吧!~~
请登录后评论。关于RSA的解密操作。急都急死了真是有心无力啊。
[问题点数:100分,结帖人daocha]
关于RSA的解密操作。急都急死了真是有心无力啊。
[问题点数:100分,结帖人daocha]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
本帖子已过去太久远了,不再提供回复功能。RSA加解密报错问题!!!
[问题点数:20分,结帖人lw]
RSA加解密报错问题!!!
[问题点数:20分,结帖人lw]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
本帖子已过去太久远了,不再提供回复功能。}

我要回帖

更多关于 java rsa 加密解密 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信