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 example
Text [Byte Format] : [B@187aeca
Text [Byte Format] : [B@187aeca
Text Decryted : This is an example
以及这篇博客《RSA Encryption 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 库的 org.apache.commons.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 线程不安全了。
后记
虽然《RSA Encryption Example》存在一些认识上的误区,但笔者仍然认为它是一篇很不错的入门级文章。结合本文所列内容,笔者将其代码做了一些调整以供参考:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import javax.crypto.Cipher;
import org.apache.commons.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/20150116/private.key";
* String to hold name of the public key file.
public static final String PUBLIC_KEY_FILE = "e:/defonds/work/20150116/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() {
try {
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);
if (privateKeyFile.getParentFile() != null) {
privateKeyFile.getParentFile().mkdirs();
privateKeyFile.createNewFile();
if (publicKeyFile.getParentFile() != null) {
publicKeyFile.getParentFile().mkdirs();
publicKeyFile.createNewFile();
ObjectOutputStream publicKeyOS = new ObjectOutputStream(
new FileOutputStream(publicKeyFile));
publicKeyOS.writeObject(key.getPublic());
publicKeyOS.close();
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()) {
return true;
return false;
* 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 = null;
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
final Cipher cipher = Cipher.getInstance(PADDING, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, key);
cipherText = cipher.doFinal(text.getBytes());
} catch (Exception e) {
e.printStackTrace();
return cipherText;
* 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 = null;
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
final Cipher cipher = Cipher.getInstance(PADDING, PROVIDER);
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) {
try {
if (!areKeysPresent()) {
generateKey();
final String originalText = "12345678901234567890123456789012";
ObjectInputStream inputStream = null;
inputStream = new ObjectInputStream(new FileInputStream(
PUBLIC_KEY_FILE));
final PublicKey publicKey = (PublicKey) inputStream.readObject();
final byte[] cipherText = encrypt(originalText, publicKey);
Base64 base64 = new Base64();
String cipherTextBase64 = base64.encodeToString(cipherText);
byte[] cipherTextArray = base64.decode(cipherTextBase64);
inputStream = new ObjectInputStream(new FileInputStream(
PRIVATE_KEY_FILE));
final PrivateKey privateKey = (PrivateKey) inputStream.readObject();
final String plainText = decrypt(cipherTextArray, privateKey);
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=12345678901234567890123456789012
Encrypted=GTyX3nLO9vseMJ+RB/dNrZp9XEHCzFkHpgtaZKa8aCc=
Decrypted=12345678901234567890123456789012
引用:
http://www.bouncycastle.org/wiki/display/JA1/Frequently+Asked+Questions
http://stackoverflow.com/questions/1536054/how-to-convert-byte-array-to-string-and-vice-versa
http://www.experts-exchange.com/Security/Encryption/Q_26980724.html
http://stackoverflow.com/questions/17497426/why-does-rsa-produce-different-results-with-same-key-and-message
/*1、对称加密3DES秘钥生成*/KeyGeneratorkg=KeyGenerator.getInstance("DESede");kg.init(112);//mustbeequalto112or168System.out.println("SecretKey:");System.out.println(newBASE64Encoder().encode(kg.gener...
/*1、对称加密3DES秘钥生成*/
KeyGenerator kg = KeyGenerator.getInstance("DESede");
kg.init(112);//must be equal to 112 or 168
System.out.println("SecretKey:"...
public class MyEncrypt {public void saveToFile(String fileName, BigInteger mod, BigInteger exp) throws IOException {ObjectOutputStream oout = new ObjectOutputStream(new BufferedOutputStream(new FileOu...
Java中RSA加密
一. 什么是Base64?
Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,大家可以查看RFC2045~RFC2049,上面有MIME的详细规范。
Base64要求把每三个8Bit的字节转换为四个6Bit的字节(3*8 = 4*6 = 24),然后把6Bit再添两位高位0,组成四个8Bit的字节,也就是说,转换后的字符串理论上将要比原来的长1
数据安全,不管是对于企业还是个人都是十分重要。而作为一个移动开发者,我们更需要去考虑数据传输的安全性,去保护企业及个人信息安全。在Java,Android语言中,有许多的常用加解密算法,例如:对称加密算法AES,DES,3DES,非对称加密算法RSA,经典哈希算法MD5,SHA.
对称加密算法:加密秘钥和解密秘钥相同 例:AES,DES,3DES
非对称加密算法:有公钥和秘钥,公钥加密
RSA是一种非对称加密算法,加密和解密使用不同的密钥。通信双方各握有一对密钥(称为公钥和私钥)中的一把,己方密钥加密的数据,只有对方密钥能够解密。RSA基于一个数论事实:将两个大素数相乘十分容易,但想要对其乘积进行因式分解却极其困 难,因此可以将乘积公开作为加密密钥,即公钥,而两个大素数组合成私钥。公钥是可提供给任何人使用,私钥则为自己所有,供解密之用。
android测试没有问题
java 报错(请知道解决办法的告知下)
错误:java.lang.Exception: java.security.InvalidKeyException: RSA keys must be at l
RSA加密时报错:java.lang.ArrayIndexOutOfBoundsException: too much data for RSA bloc:
按照下面顺序先后进行:
1.RSA加密
2.Base64编码:
String password_front_end = Base64.getEncoder().encodeToString(encodeData);
3.Base64解...
今天在做RSA加密的时候遇到了一个这样的错误:ArrayIndexOutOfBoundsException: too much data for RSA block
查询相关资料后得知该错误是加密数据过长导致的。
加密数据长度 <= 模长-11
解决办法:将要加密的数据截取后分段加密
下面是关于RSA算法密钥长度/密文长度/明文长度的介绍
本文转自:http://blog.si...
1. 加密的系统不要具备解密的功能,否则 RSA 可能不太合适公钥加密,私钥解密。加密的系统和解密的系统分开部署,加密的系统不应该同时具备解密的功能,这样即使黑客攻破了加密系统,他拿到的也只是一堆无法破解的密文数据。否则的话,你就要考虑你的场景是否有必要用 RSA 了。2. 可以通过修改生成密钥的长度来调整密文长度生成密文的长度等于密钥长度。密钥长度越大,生成密文的长度也就越大,加密的速度也就越慢