當某兩方需要進行加密傳輸消息時,大致經過如下步驟。
/*規定所有要發送的文件及加密後的文件均存於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,不過我是直接以對象方式存進文件再讀出的,並不存在因爲轉爲字符串而填補空餘長度元素的問題。
這個問題就等整理一番思緒以及再深入瞭解一些加密算法本質的時候 來修改吧。