当某两方需要进行加密传输消息时,大致经过如下步骤。
/*规定所有要发送的文件及加密后的文件均存于D:\FileBox\. 密钥存于D:\ */
/*发送方A:
* 1.对文件进行摘要计算,预防内容被修改* 2.对消息摘要进行数字签名(A私钥),预防文件内容与摘要同时被修改
* 3.对文件采用密码流对称加密,预防文件被捕获
* 4.对称密钥上附加B的公钥加密,预防被破解对称加密
* 5.发送加密后的文件、对称密钥、A的公钥 密钥均以对象形式写入到文件中
*
*接收方B:
* 1.用A的公钥对消息摘要验证签名,确定消息来自A
* 2.用B的私钥解密对称密钥
* 3.用对称密钥解密文件内容
* 4.重新计算文件摘要,并与接收到的对比,确认无误。
*
* 非对称 RSA 对称 AES 消息摘要 SHA1 安全消息摘要Mac HmacSHA1 数字签名 SHA1withRSA
*
* 另外涉及的知识点: FileChannel MapByteBuffer 快速映射读取操作文件
*
* /
<strong><span style="font-size:24px;">public class java非证书加密 {
/*准备工作,生成各密钥*/
final static String Boxpath="D:\\FileBox\\";
public static void crypt(InputStream in,OutputStream out,Cipher cipher)throws
IOException,GeneralSecurityException{
int blockSize=cipher.getBlockSize();
int outputSize=cipher.getOutputSize(blockSize);
byte[] inBytes=new byte[blockSize];
byte[] outBytes=new byte[outputSize];
int len=0,outLenth;
boolean more=true;
while(more){
len=in.read(inBytes);
if(len==blockSize)
{ outLenth=cipher.update(inBytes, 0, blockSize, outBytes);
out.write(outBytes,0,outLenth);
}else
more=false;
}
if(len>0) outBytes=cipher.doFinal(inBytes,0,len);
else outBytes=cipher.doFinal();
out.write(outBytes);
}
public static void func1(){
//1.生成对称密钥
KeyGenerator keygen=null;
FileOutputStream Fileout=null;
ObjectOutputStream out=null;
try {
keygen=KeyGenerator.getInstance("AES");
SecureRandom random=new SecureRandom();
keygen.init(random);
SecretKey secretKey=keygen.generateKey();
Fileout=new FileOutputStream("D:\\AES.dat");
out=new ObjectOutputStream(Fileout);
out.writeObject(secretKey);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(Fileout!=null)
Fileout.close();
if(out!=null)
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void func2(){
//2.非对称密钥
KeyPairGenerator keypairgen=null;
FileOutputStream Fileout1=null,Fileout2=null;
try {
keypairgen = KeyPairGenerator.getInstance("RSA");
SecureRandom random=new SecureRandom();
keypairgen.initialize(512, random);
KeyPair keyPair=keypairgen.genKeyPair();
Fileout1=new FileOutputStream("D:\\RSApublic.dat");
Fileout2=new FileOutputStream("D:\\RSAprivate.dat");
ObjectOutputStream out1=new ObjectOutputStream(Fileout1);
ObjectOutputStream out2=new ObjectOutputStream(Fileout2);
out1.writeObject(keyPair.getPublic());
out2.writeObject(keyPair.getPrivate());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(Fileout1!=null)
Fileout1.close();
if(Fileout2!=null)
Fileout2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static String func3(String FileName){
//3.生成消息摘要并返回
FileChannel fc=null;
MessageDigest sha=null;
MappedByteBuffer mbbf=null;
String msgDigest="";
try {
fc=new FileInputStream(Boxpath+FileName).getChannel();
int length=(int)fc.size();
mbbf=fc.map(FileChannel.MapMode.READ_ONLY, 0, length);
sha=MessageDigest.getInstance("SHA1");
sha.update(mbbf);
msgDigest=new BigInteger(1,sha.digest()).toString(16);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fc!=null){
try {
fc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return msgDigest;
}
public static byte[] func4(String msgDigest){
//4.对摘要进行数字签名 返回数字签名
byte[] signs=new byte[1024];
Signature signature=null;
FileInputStream fileIn=null;
ObjectInputStream Keyin=null;
try {
fileIn=new FileInputStream("D:\\RSAprivate.dat");
Keyin=new ObjectInputStream(fileIn);
PrivateKey privateKey=(PrivateKey)Keyin.readObject();
signature=Signature.getInstance("SHA1withRSA");
signature.initSign(privateKey);
signature.update(msgDigest.getBytes());
signs=signature.sign();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(fileIn!=null)
fileIn.close();
if(Keyin!=null)
Keyin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return signs;
}
public static String func5(String fileName){
//用非对称密钥对文件内容加密 并加密对称密钥,返回加密文件路径
String secertFileName="";
Cipher cipher=null;
FileInputStream RSAKey=null,DesKey=null,fileIn=null;
FileOutputStream fileOut=null,secretFileout=null;
ObjectInputStream DESKeyIn=null,RSAKeyIn=null;
ObjectOutputStream secretDES=null;
SecretKey secretKey=null;
try {
fileIn=new FileInputStream(Boxpath+fileName);
//读入代加密文件↑
DesKey=new FileInputStream("D:\\AES.dat");
DESKeyIn=new ObjectInputStream(DesKey);
secretKey=(SecretKey)DESKeyIn.readObject();
//读入AES密钥↑
cipher=Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
secertFileName="SECRET"+fileName;
secretFileout = new FileOutputStream(Boxpath+"SECRET"+fileName);
try {
<span style="white-space:pre"> </span>crypt(fileIn, secretFileout, cipher);
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
//对文件加密并生成加密后的文件↑
RSAKey=new FileInputStream("D:\\RSApublic.dat");
RSAKeyIn=new ObjectInputStream(RSAKey);
PublicKey publicKey=(PublicKey)RSAKeyIn.readObject();
cipher=Cipher.getInstance("RSA");
cipher.init(Cipher.WRAP_MODE,publicKey );
byte[] wrappedKey=cipher.wrap(secretKey);
fileOut=new FileOutputStream(Boxpath+"secretAES.dat");
secretDES=new ObjectOutputStream(fileOut);
secretDES.writeObject(wrappedKey);
//用公钥对AES密钥加密并存入文件↑
}catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(DesKey!=null)
DesKey.close();
if(RSAKey!=null)
RSAKey.close();
if(fileOut!=null)
fileOut.close();
if(DESKeyIn!=null)
DESKeyIn.close();
if(RSAKeyIn!=null)
RSAKeyIn.close();
if(secretDES!=null)
secretDES.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return secertFileName;
}
public static boolean func6(String msgDigest,byte[] Sign){
//验证签名
boolean flag=false;
Signature signature=null;
FileInputStream fileIn=null;
ObjectInputStream Keyin=null;
try {
fileIn=new FileInputStream("D:\\RSApublic.dat");
Keyin=new ObjectInputStream(fileIn);
PublicKey publicKey=(PublicKey)Keyin.readObject();
signature=Signature.getInstance("SHA1withRSA");
signature.initVerify(publicKey);
signature.update(msgDigest.getBytes());
flag=signature.verify(Sign);
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(fileIn!=null)
fileIn.close();
if(Keyin!=null)
Keyin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return flag;
}
public static String func7(String secretDESfile,String secretFile){
//解密对称密钥 及文件 返回解密后的文件名
String secertFileName="";
FileChannel fc=null;
Cipher cipher=null;
FileInputStream secretedKey=null,privateKeyFile=null;
ObjectInputStream secretKeyIn=null,privateKeyIn=null;
FileInputStream secretFileIN=null;
FileOutputStream DEsecretFileout=null;
byte[] wrappedKey=null;
try {
secretedKey=new FileInputStream(Boxpath+secretDESfile);
secretKeyIn=new ObjectInputStream(secretedKey);
wrappedKey=(byte[])secretKeyIn.readObject();
//读取经加密后的对称密钥字节数组
privateKeyFile=new FileInputStream("D:\\RSAprivate.dat");
privateKeyIn=new ObjectInputStream(privateKeyFile);
PrivateKey privateKey=(PrivateKey)privateKeyIn.readObject();
cipher=Cipher.getInstance("RSA");
cipher.init(Cipher.UNWRAP_MODE, privateKey);
Key AesKey=cipher.unwrap(wrappedKey, "AES", Cipher.SECRET_KEY);
//读取私钥解密对称密钥
cipher=Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, AesKey);
secretFileIN=new FileInputStream(Boxpath+secretFile);
DEsecretFileout=new FileOutputStream(Boxpath+"DE"+secretFile);
secertFileName="DE"+secretFile;
try {
crypt(secretFileIN, DEsecretFileout, cipher);
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
//用解密后的对称密钥解密文件,并保存
}catch (InvalidKeyException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(fc!=null)
fc.close();
if(secretedKey!=null)
secretedKey.close();
if(privateKeyFile!=null)
privateKeyFile.close();
if(secretKeyIn!=null)
secretKeyIn.close();
if(privateKeyIn!=null)
privateKeyIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return secertFileName;
}
public static void main(String[] args) {
func1();
func2();
String msgDigest=func3("MyRecord.txt");
System.out.println("文件消息摘要: "+msgDigest);
byte[] msgSign=func4(msgDigest);
String Sign=new BigInteger(1,msgSign).toString(16);
System.out.println("消息摘要数字签名: "+Sign);
//注意 传输数字签名应该是byte[] 的形式若以String形式会更改对象内容
String secertFileName=func5("MyRecord.txt");
boolean flag=func6(msgDigest, msgSign);
System.out.println("数字签名验证: "+flag);
String desecretFileName=func7("secretAES.dat", secertFileName);
String msgDigest2=func3(desecretFileName);
System.out.println("解密后的文件消息摘要: "+msgDigest2);
System.out.println("文件是否完整: "+msgDigest.equals(msgDigest2));
}
}
输出结果如下:(具体摘要内容由使用的算法决定)
文件消息摘要: 79c641104ec87d86d111738e825bc0d33c733d27
消息摘要数字签名: 66f0059b54f5b058bfa7904a7cd3de4028b5f22296e69d04ab9d4bc54870c17cb014cfa774cb45989368a84f7ea568390692edd58cab14884b44357660310624
数字签名验证: true
解密后的文件消息摘要: 79c641104ec87d86d111738e825bc0d33c733d27
文件是否完整: true
///
博主原先是打算使用 CipherInputStream CipherOutputStream 来操作文件的,再配合使用MapByteBuffer 来进行文件的快速映射读写,或许是对加密算法的本质还不够了解,导致使用过程出错,总是报 DESGiven final block not properly padded (原先使用DES对称加密) 原因可能是因为分组加密的读入和写入问题,经多番测试,发现映射的文件原文是正确,但是利用密钥流加密输出到另一文件时,只写入了8位, (代码循环过程应该是没问题的,字节数组的空间也足够),这又让博主开始怀疑是否是加密过程出错。
曾以为是利用map映射的时候没能读入完整,后经过测试,排除此因素。查阅网上大量例子及他人所遇到的问题,有的讲到改为无填充方式,有的认为在生成对称密钥的时候要控制好长度及利用base64,不过我是直接以对象方式存进文件再读出的,并不存在因为转为字符串而填补空余长度元素的问题。
这个问题就等整理一番思绪以及再深入了解一些加密算法本质的时候 来修改吧。