下面代码演示了对内容进行签名的过程,包括散列,加密,解密。
其中的两个类引用了如下两个博客,我这里只是做个整合工作。谢谢两位博主~
http://blog.youkuaiyun.com/zidan_2011/article/details/7708557
http://blog.youkuaiyun.com/liuhuabai100/article/details/7585879
/**
* RSA加密算法的演示验证
* RSA是一种分组加密算法
* 注意:密钥对采用的长度决定了加密块的长度,我这里取的是2048,即256byte
* 由于加密块的长度固定为256,因此明文的长度至多为256 - 11 = 245byte
* 我这里明文长度取的是128byte,密文长度为256byte,它适合于小文件加密
*/
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
/**
* 签名过程
*/
public class CipherDemo {
public static void main(String[] args) throws Exception {
// 1. 第一步: 对原始内容进行散列(MD5算法)
MD5Demo md5 = new MD5Demo();
byte[] srcContentBytes = md5.getFileBytes("d:\\test.txt"); // 获取原始内容字节流。test.txt是要签名的内容文件
String md5Str = md5.getMD5(srcContentBytes); // MD5散列
md5.saveString2File(md5Str, "d:\\test.md5.txt"); // 保存散列内容到文件
// 2. 第二步:得到私匙和公匙对
RSADemo rsa = new RSADemo();
rsa.initKey();
rsa.savePublicKey(new File("D:\\public.key"));
rsa.savePrivateKey(new File("D:\\private.key"));
// 3. 第三步:使用私匙对散列内容加密。并把加密后的内容保存到文件 “散列文件.encrypt”
// 3.1 然后,加密者把 原始内容文件+加密后的散列内容文件+公匙 发给解密者
rsa.encryptFile(new File("D:\\test.md5.txt"));
// 4. 解密者用公匙对“散列文件.encrypt”进行解密,得到解密后的散列内容,并保存到“散列文件.encrypt.decrypt”
// 4.1 机密者对元素内容文件进行同样的散列,把得到的散列值和机密得到的散列值进行比对,如果一样,这任务元素内容是经过签名的,是可信的。
rsa.decryptFile(new File("D:\\test.md5.txt.encrypt"));
}
}
/**
*
*/
class RSADemo {
/**
* 公钥
*/
private RSAPublicKey publicKey;
/**
* 私钥
*/
private RSAPrivateKey privateKey;
/**
* 用于加解密
*/
private Cipher cipher;
/**
* 明文块的长度 它必须小于密文块的长度 - 11
*/
private int originLength = 128;
/**
* 密文块的长度
*/
private int encrytLength = 256;
/**
* 得到初始化的公钥和私钥
* @throws NoSuchAlgorithmException
* @throws NoSuchPaddingException
*/
public void initKey() throws NoSuchAlgorithmException, NoSuchPaddingException {
//RSA加密算法:创建密钥对,长度采用2048
KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA");
kg.initialize(encrytLength * 8);
KeyPair keypair = kg.generateKeyPair();
//分别得到公钥和私钥
publicKey = (RSAPublicKey) keypair.getPublic();
privateKey = (RSAPrivateKey) keypair.getPrivate();
System.out.println("public key: " + publicKey);
System.out.println("private key: " + privateKey);
}
/**
* 将公钥保存至文件
* @param file 待写入的文件
* @return true 写入成功;false 写入失败
*/
public boolean savePublicKey(File file) {
return this.saveKey(publicKey, file);
}
/**
* 将私钥保持至文件
* @param file 待写入的文件
* @return true 写入成功;false 写入失败
*/
public boolean savePrivateKey(File file) {
return this.saveKey(privateKey, file);
}
//将Key文件保持到文件中
private boolean saveKey(Key key,File file) {
boolean write;
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
//System.out.println(key.getFormat());
//公钥默认使用的是X.509编码,私钥默认采用的是PKCS #8编码
byte [] encode = key.getEncoded();
//注意,此处采用writeObject方法,读取时也要采用readObject方法
oos.writeObject(encode);
write = true;
} catch (IOException e) {
write = false;
} finally {
try {
if(fos != null) fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return write;
}
/**
* 从文件中得到公钥
* @param file 保存公钥的文件
*/
public void getPublicKey(File file) {
getKey(file,0);
}
/**
* 从文件中得到私钥
* @param file 保存私钥的文件
*/
public void getPrivateKey(File file) {
getKey(file,1);
}
private void getKey(File file,int mode) {
FileInputStream fis;
try {
//读取数据
fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
byte [] keybyte = (byte[]) ois.readObject();
//关闭资源
ois.close();
//默认编码
KeyFactory keyfactory = KeyFactory.getInstance("RSA");
//得到公钥或是私钥
if(mode == 0) {
X509EncodedKeySpec x509eks= new X509EncodedKeySpec(keybyte);
publicKey = (RSAPublicKey) keyfactory.generatePublic(x509eks);
//System.out.println(pk.getAlgorithm());
} else {
PKCS8EncodedKeySpec pkcs8eks = new PKCS8EncodedKeySpec(keybyte);
privateKey = (RSAPrivateKey) keyfactory.generatePrivate(pkcs8eks);
//System.out.println(pk.getAlgorithm());
}
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 数据RSA加密 (用私匙加密)
* @param origin 明文
* @return 密文
*/
protected byte [] encrypt(byte [] origin) {
//byte [] enc = new byte [encrytLength];
byte [] enc = null;
try {
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
enc = cipher.doFinal(origin);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return enc;
}
/**
* 数据RSA解密 (用公匙解密)
* @param enc 密文
* @return 明文
*/
protected byte [] decrypt(byte [] enc) {
//byte [] origin = new byte [originLength];
byte [] origin = null;
try {
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
origin = cipher.doFinal(enc);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
return origin;
}
/**
* 加密文件
* @param origin 明文件
* @throws IOException
*/
public void encryptFile(File origin) throws IOException {
FileInputStream fis = null;
FileOutputStream fos = null;
//读入
fis = new FileInputStream(origin);
BufferedInputStream bis = new BufferedInputStream(fis);
byte [] originbyte = new byte [originLength];
//写出
fos = new FileOutputStream(new File(origin+".encrypt"));
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte [] encryptbyte;
//int k;
while(bis.read(originbyte) > 0) {
encryptbyte = this.encrypt(originbyte);
bos.write(encryptbyte);
originbyte = new byte[originLength];
}
//压入
bos.flush();
//关闭资源
if(fis != null) fis.close();
if(fos != null) fos.close();
}
/**
* 解密文件
* @param encrypt 密文件
* @throws IOException
*/
public void decryptFile(File encrypt) throws IOException {
FileInputStream fis = null;
FileOutputStream fos = null;
//读入
fis = new FileInputStream(encrypt);
BufferedInputStream bis = new BufferedInputStream(fis);
byte [] encryptbyte = new byte [encrytLength];
//写出
fos = new FileOutputStream(new File(encrypt+".decrypt"));
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte [] originbyte;
//int k;
while(bis.read(encryptbyte) > 0) {
originbyte = this.decrypt(encryptbyte);
bos.write(originbyte);
encryptbyte = new byte [encrytLength];
}
//压入
bos.flush();
//关闭资源
if(fis != null) fis.close();
if(fos != null) fos.close();
}
/**********************************
* MD5 part
**********************************/
}
/**
* MD5的算法在RFC1321 中定义
* 在RFC 1321中,给出了Test suite用来检验你的实现是否正确:
* MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
* MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
* MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
* MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
* MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
*
* @author yilee
*
* 传入:一个字节数组
* 传出:字节数组的 MD5 结果字符串
*/
class MD5Demo {
public byte[] getFileBytes(String fileName) throws Exception {
FileInputStream fis = new FileInputStream(fileName);
BufferedInputStream bis = new BufferedInputStream(fis);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] data = new byte[4096];
int count = -1;
while ((count = bis.read(data, 0, 4096)) != -1) {
outStream.write(data, 0, count);
}
data = null;
if(fis != null) fis.close();
return outStream.toByteArray();
}
public void saveString2File(String content, String fileName) throws Exception {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName)));
bw.write(content);
bw.close();
}
public String getMD5(byte[] source) {
String s = null;
char hexDigits[] = { // 用来将字节转换成 16 进制表示的字符
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
try {
java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
md.update(source);
byte tmp[] = md.digest(); // MD5 的计算结果是一个 128 位的长整数,
// 用字节表示就是 16 个字节
char str[] = new char[16 * 2]; // 每个字节用 16 进制表示的话,使用两个字符,
// 所以表示成 16 进制需要 32 个字符
int k = 0; // 表示转换结果中对应的字符位置
for (int i = 0; i < 16; i++) { // 从第一个字节开始,对 MD5 的每一个字节
// 转换成 16 进制字符的转换
byte byte0 = tmp[i]; // 取第 i 个字节
str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换,
// >>> 为逻辑右移,将符号位一起右移
str[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
}
s = new String(str); // 换后的结果转换为字符串
} catch (Exception e) {
e.printStackTrace();
}
return s;
}
}