Java 密碼學算法

Java 密碼學算法

候捷老師在《 深刻淺出MFC 2e(電子版)》中引用林語堂先生的一句話:html

只用同樣東西,不明白它的道理,實在不高明java

只知道How,不知道Why,出了一點小問題時就無能爲力了。咱們課上鼓勵你們在Linux下學習編程,儘可能在命令行中編輯/編譯/調試程序,Git的使用,數據庫的管理都先會命令方式下使用,這樣在IDE中,在GUI界面中出了問題,咱們有更好的方法查找。程序員

如今咱們遇到另一個極端,不會用同樣東西,卻想要明白它的道理,這實在太難了。好比有的同窗連Linux都沒用過,卻想弄明白Linux內核設計原理,沒有用過相似Java的Collection API就學《數據結構》,沒有相似Java 密碼算法API的經驗,沒有相似Java 多線程的使用經驗就學《操做系統》,沒有Java JDBC的相似的經驗就學數據庫...web

結果是:算法

  • 學了《密碼學》卻不會使用密碼技術
  • 學了《數據結構》卻不會用數據結構解決問題
  • 學了《操做系統》也不知道併發是怎麼回事
  • 學了《數據庫原理》卻不會開發數據庫應用
  • ...

候捷老師在《 大局觀:泛型程序設計與STL》中認爲學習泛型程序設計以及 STL 有三個境界:數據庫

  • 第一個境界是使用 STL。
  • 第二個境界是瞭解泛型程序設計的內涵與 STL 的實做原理。
  • 第三個境界是擴充 STL。

這個「會用,明理,擴展」方法論能夠用到不少技術的學習上。明理以前要會用編程

程序開發中,程序員有兩種角色:數組

  • 類的建立者:建立者的目標是構建一系列的類,這些類向使用者暴露必要的功能服務,並隱藏其它部分
  • 類的使用者:使用者的目標是調用這些類的對象提供的服務來實現特定功能。

在不少課程中,同窗們學習是一人分飾兩種角色,但不少人不清楚這兩種角色。安全

《Think in Java》中說:服務器

開發一個程序時,最好的方法就是將對象想象爲「服務提供者」,程序經過調用若干對象提供的服務來實現預期目標。

Java的一個好處是API極其豐富,想學習一門技術能夠先用Java寫點程序,再深刻學習就能明理了。

好比學習《密碼學》課程前,能夠先用用相關的Java API。Java API支持多種加密算法。如MessageDigest類,能夠構建MD五、 SHA等摘要算法;Mac類能夠構建HMAC算法; Cipher類能夠構建多種加密算法, 如DES、 AES、Blowfish對稱加密算法, 以及RSA、 DSA、 DH等多種非對稱加密算法; Signature類能夠用於數字簽名和簽名驗證; Certificate類可用於操做證書;...

密碼學基礎

咱們每一個人都有本身的祕密, 所謂祕密就是不但願被別人知道的信息。

例如,你確定不想讓別人知道你的銀行卡口令。 還有信用卡號、 貸款金額、 異性關係、 犯罪履歷、 病歷、 電子郵箱口令等, 這些敏感信息恐怕誰都不但願泄漏給他人。 別說這些敏感信息了, 有些人就連年齡、身高和體重都想保密, 某些狀況下甚至不但願對方知道本身的姓名。

在現代社會中, 不少信息都存儲在計算機裏, 這讓信息的使用變得很是方便,不過, 也正是由於如此, 在現代社會中要保護好本身的祕密信息已經變得很是困難。即使別人複製了你的祕密信息, 你也不會有所察覺, 由於你手上的信息並無丟失;正是由於信息能夠很容易地被修改,因此你的重要文件也存在被他人篡改的風險; 此外, 若是有人將你的祕密信息經過郵件發送給第三者或者是發佈在網頁上, 也會給你帶來大麻煩。
爲了解決這些問題, 人們開發出形形色色的密碼技術。

咱們遇到的安全問題能夠歸結爲安全的三個屬性(CIA金三角):

  • 機密性(Confidentiality):確保數據僅能被合法的用戶訪問, 即數據不能被未受權的第三方使用。
  • 完整性(Integrity):主要確保數據只能由受權方或以受權的方式進行修改,即數據在傳輸過程當中不能被未受權方修改。
  • 可用性(Availability):主要確保全部數據僅在適當的時候能夠由受權方訪問。

其餘安全技術目標:

  • 可靠性: 主要確保系統能在規定條件下、 規定時間內、 完成規定功能時具備穩定的機率。
  • 抗否定性: 又稱抗抵賴性, 主要確保發送方與接收方在執行各自操做後, 對所作的操做不能否認。
  • 可控性: 主要是對信息及信息系統實施安全監控。
  • 可審查性: 主要是經過審計、 監控、 抗否定性等安全機制, 確保數據訪問者( 包括合法用戶、 攻擊者、
    破壞者、 抵賴者) 的行爲有證可查, 當網絡出現安全問
    題時, 提供調查依據和手段。
  • 認證( 鑑別) : 主要確保數據訪問者和信息服務者的身份真實有效。
  • 訪問控制: 主要確保數據不被非受權方或以未受權方式使用。

密碼學逐步發展成爲一門學科,那麼什麼是密碼學呢?

密碼學: 主要是研究保密通訊和信息保密的學科, 包括信息保密傳輸和信息加密存儲等。

密碼學包含密碼編碼學( Cryptography) 和密碼分析學(Cryptanalyst) 兩個分支。 編碼學與分析學相互促
進, 又相互制約。 一方面, 二者在增強密碼分析的安全上相互促進; 另外一方面,二者在實施更爲有效的攻擊方面也相互影響。

  • 密碼編碼學: 主要研究如何對信息進行編碼, 如何實現對信息的隱蔽, 是密碼學理論的基礎, 也是保密系統設計的基礎。
  • 密碼分析學: 主要研究加密消息的破譯或消息的僞造, 是檢驗密碼體制安全性最爲直接的手段, 只有經過實際密碼分析考驗的密碼體制,纔是真正可用的。

讓咱們來看一下密碼學經常使用術語, 以下所示:

  • 明文( Plaintext) : 指待加密信息。 明文能夠是文本文件、 圖片文件、 二進制數據等。
  • 密文( Ciphertext) : 指通過加密後的明文。 密文一般以文本、 二進制數據等形式存在。
  • 發送者( Sender) : 指發送消息的人。
  • 接收者( Receiver) : 指接收消息的人。
  • 加密( Encryption) : 指將明文轉換爲密文的過程。
  • 加密算法( Encryption Algorithm) : 指將明文轉換爲密文的算法。
  • 加密密鑰( Encryption Key) : 指經過加密算法進行加密操做用的密鑰。
  • 解密( Decryption) : 指將密文轉換成明文的過程。
  • 解密算法( Decryption Algorithm) : 指將密文轉換爲明文的算法。
  • 解密密鑰( Decryption Key) : 指經過解密算法進行解密操做用的密鑰。
  • 密碼分析( Cryptanalysis) : 指截獲密文者試圖經過分析截獲的密文從而推斷出原來的明文或密鑰的
    過程。
  • 密碼分析者( Cryptanalyst) : 等同於密碼破譯者, 指從事密碼分析的人。
  • 被動攻擊( Passive Attack) : 指對一個保密系統採起截獲密文並對密文進行分析和攻擊的行爲。 這種
    攻擊對密文沒有破壞做用。
  • 主動攻擊( Active Attack) : 指攻擊者非法入侵密碼系統, 採用僞造、 修改、 刪除等手段向系統注入
    假消息進行欺騙的行爲。 這種攻擊對密文具備破壞做
    用。
  • 密碼體制( Cipher System) : 由明文空間、 密文空間、 密鑰空間、 加密算法和解密算法五部分構成。
  • 密碼協議( Cryptographic Protocol) : 有時又稱安全協議, 是指以密碼學爲基礎的消息交換的通訊協議, 其目的是在網絡環境中提供各類安全服務。 密碼協議與密碼算法同等重要, 是當今密碼學研究的兩大課題。 密碼學是網絡安全的基礎, 但網絡安全不能單純依靠安全的密碼算法。密碼協議是網絡安全的一個重要組成部分, 經過密碼協議能夠進行實體之間的認證、 在實體之間安全地分配密鑰或其餘各類祕密、確認發送和接收的消息的不能否認性等。
  • 密碼系統( Cryptography) : 指用於加密和解密的系統。 加密時, 密碼系統輸入明文和加密密鑰, 進行
    加密變換後, 輸出密文; 解密時, 密碼系統輸入密文和解密密鑰, 進行解密變換後, 輸入明文。 一個密碼系統由信源、 加密變換、 解密變換、 信宿和攻擊者組成。 密碼系統強調密碼方案的實際應用, 一般應當是一個包含軟、 硬件的系統。
  • 柯克霍夫原則( Kerckhoff's rinciple) : 又稱柯克霍夫假說、 公理或定律, 是由奧古斯特·柯克霍夫
    ( Auguste Kerckhoffs) 在19世紀提出的密碼理論, 即數據的安全基於密鑰而不是算法的保密。 換句話說, 系統的安全性取決於密鑰, 對密鑰保密, 對算法公開。 信息論始祖克勞德·艾爾伍德·香農( Claude Elwood
    Shannon) 將其改成「 敵人瞭解系統」 , 這樣的說法稱爲香農箴言。 柯克霍夫原則是現代密碼學設計的基本原
    則。
    • 即便非數學上不可破解, 系統也應在實質( 實用) 程度上沒法破解系統內不該含任何機密物, 即便落入敵人手中也不會形成困擾。
    • 密匙必須易於溝通和記憶, 而無須寫下, 且雙方能夠很容易地改變密匙。
    • 系統應能夠用於電訊。
    • 系統應能夠攜帶, 不該須要兩我的或兩我的以上才能使用( 應只要一我的就能使用) 。
    • 系統應容易使用, 不致讓使用者的腦力過度操勞, 也無須記得長串的規則。

密碼學並非孤立存在的, 它須要有一個環境——保密通訊模型。密碼學的目的在於確保信息的保密傳送。 一般這樣理解這層含義: 信息的發送者和接收者在不安全的信道上進行通訊,而破譯者不能理解他們通訊的內容。 用保密通訊模型來詮釋這種信息傳送方式, 以下圖所示:

在保密通訊模型中,密碼技術在保證信息傳輸安全中發揮着重要的做用:

  • 對稱密碼
  • 公鑰密碼
  • 單向散列函數
  • 消息認證碼
  • 數字簽名
  • 僞隨機數生成器

咱們將上述六種技術統稱爲密碼學家的工具箱。咱們將信息安全所面臨的威脅與用來應對這些威脅的密碼技術
之間的關係用一張圖表來表示出來:

根據密鑰的使用方法, 能夠將密碼分爲對稱密碼和公鑰密碼兩種。

  • 對稱密碼(symmetric cryptography)是指在加密和解密時使用同一密鑰的方式。
  • 公鑰密碼(public-key cryptography)則是指在加密和解密時使用不一樣密鑰的方式,公鑰密碼又稱爲非對稱密碼(asymmetric cryptography)。

將對稱密碼和公鑰密碼結合起來的密碼方式稱爲混合密碼系統(hybrid cryptosystem),這種系統結合了對稱密碼和公鑰密碼二者的優點。

混合密碼系統加密:

混合密碼系統解密:

咱們在各類系統中輸入的「密碼」,咱們應該叫口令(Password),主要用來身份驗證的,加密過程當中能夠用PBE(Password Based Encryption,基於口令加密)算法,使用口令產生密鑰,而口令由用戶本身掌管。

密碼與信息安全常識

  • 不要使用保密的密碼算法
  • 使用低強度的密碼比不進行任何加密更危險
  • 任何密碼總有一天都會被破解
  • 密碼只是信息安全的一部分

Java與密碼學

Java安全體系結構總共分爲4個部分:

  • JCA( Java Cryptography Architecture, Java加密體系結構):JCA提供基本的加密框架, 如證書、 數字簽名、消息摘要和密鑰對產生器。
  • JCE( Java Cryptography Extension, Java加密擴展包):JCE在JCA的基礎上做了擴展, 提供了各類加密算法、 消息摘要算法和密鑰管理等功能。JCE的實現主要在javax.crypto包( 及其子包) 中
  • JSSE( Java Secure Sockets Extension, Java安全套接字擴展包):JSSE提供了基於SSL( Secure Sockets Layer,安全套接字層) 的加密功能。 在網絡的傳輸過程當中, 信息會通過多個主機(頗有可能其中一臺就被竊聽) , 最終傳送給接收者, 這是不安全的。這種確保網絡通訊安全的服務就是由JSSE來提供的。
  • JAAS( Java Authentication and Authentication Service, Java鑑別與安全服務):JAAS提供了在Java平臺上進行用戶身份鑑別的功能。

JCA和JCE是Java平臺提供的用於安全和加密服務的兩組API。它們並不執行任何算法,它們只是鏈接應用和實際算法實現程序的一組接口。 軟件開發商能夠根據JCE接口( 又稱安全提供者接口) 將各類算法實現後,打包成一個Provider( 安全提供者) , 動態地加載到Java運行環境中。
根據美國出口限制規定, JCA可出口, 但JCE對部分國家是限制出口的。 所以, 要實現一個完整的安全結構,
就須要一個或多個第三方廠商提供的JCE產品, 稱爲安全提供者。 BouncyCastle JCE就是其中的一個安全提供
者。

安全提供者是承擔特定安全機制實現的第三方。 有些提供者是徹底免費的, 而另外一些提供者則須要付費。
提供安全提供者的公司有Sun、 Bouncy Castle等, Sun(Oracle)提供瞭如何開發安全提供者的細節。 Bouncy Castle提供了能夠在J2ME/J2EE/J2SE平臺獲得支持的API, 並且Bouncy Castle的API是免費的。JDK 1.4版本及其後續版本中包含了上述擴展包,無須進行配置。

體驗加解密---凱撒密碼

密碼學是在戰爭中發家的,即便最簡單的凱撒密碼也不例外。凱撒密碼是羅馬擴張時期朱利斯•凱撒(Julius Caesar)創造的,用於加密經過信使傳遞的做戰命令。它將字母表中的字母移動必定位置而實現加密。

在密碼學中存在着各類各樣的置換方式,但全部不一樣的置換方式都包含2個相同的元素。密鑰和協議(算法)。凱撒密碼的密鑰是3,算法是將普通字母表中的字母用密鑰對應的字母替換。置換加密的優勢就在於它易於實施卻難於破解. 發送方和接收方很容易事先商量好一個密鑰,而後經過密鑰從明文中生成密文,便是敵人若獲取密文,經過密文直接猜想其表明的意義,在實踐中是不可能的。

凱撒密碼的加密算法極其簡單。其加密過程以下:

在這裏,咱們作此約定:明文記爲m,密文記爲c,加密變換記爲E(k1,m)(其中k1爲密鑰),解密變換記爲D(k2,m)(k2爲解密密鑰)(在這裏k1=k2,不妨記爲k)。凱撒密碼的加密過程可記爲以下一個變換:

c≡m+k mod n (其中n爲基本字符個數)

一樣,解密過程可表示爲:
m≡c+k mod n (其中n爲基本字符個數)

public static void main(String args[]) throws Exception{
String s=args[0];
        int key=Integer.parseInt(args[1]);
        String es="";
        for(int i=0;i<s.length( );i++)
{  char c=s.charAt(i);
               if(c>='a' && c<='z') // 是小寫字母
                  { c+=key%26;  //移動key%26位
                    if(c<'a') c+=26;  //向左超界
                    if(c>'z') c-=26;  //向右超界
                  }
               else if(c>='A' && c<='Z') // 是大寫字母
{  c+=key%26;
                    if(c<'A') c+=26;
                    if(c>'Z') c-=26;
                  }
               es+=c;
           }
       System.out.println(es);
     }

該程序既可用於加密又可用於解密。只要執行:

java Caesar 明文(要加密的字符串) 密鑰(移動的位數)

便可加密。
在密鑰前面加上負號,將運行

java Caesar 明文(要加密的字符串) -密鑰(移動的位數)

便可解密。

如爲了加密字符串「Hello World!」,可隨意取一個密鑰如4,運行:

java Caesar "Hello World!" 4

將輸出「Lipps Asvph!」。這裏「Hello World!」是明文,「Lipps Asvph!」是密文。

若是密鑰大於26,程序中移位前會和26取模而將其調整到26如下。所以運行:

java Caesar "Hello World!" 30

一樣將輸出「Lipps Asvph!」。

爲了將密文「Lipps Asvph!」解密,須要知道加密該密文所用的密鑰4,這樣,執行:

java Caesar "Lipps Asvph!" -4

將獲得明文「Hello World!」。

若是密鑰和加密時所用的不一樣,則解密時將獲得無心義的輸出,如運行

java Caesar "Lipps Asvph!" –3

程序將輸出「Ifmmp Xpsme!」。這樣,只有知道密鑰才能獲得原來的密文。

通常人見到"Lipps Asvph!",可否猜出是「Hello World!」?咱們能夠經過暴力破解把0-25都試一遍,也能夠經過統計手段進行破解。

Java對稱加密-DES算法

  • 對稱密鑰的生成和保存

本實例給出Java中建立對稱密鑰的步驟,並經過對象序列化方式保存在文件中。

  • 編程思路:

(1) 獲取密鑰生成器
KeyGenerator kg=KeyGenerator.getInstance("DESede");
分析:Java中KeyGenerator類中提供了建立對稱密鑰的方法。Java中的類通常使用new操做符經過構造器建立對象,但KeyGenerator類不是這樣,它預約義了一個靜態方法getInstance( ),經過它得到KeyGenerator類型的對象。這種類成爲工廠類或工廠。
方法getInstance( )的參數爲字符串類型,指定加密算法的名稱。能夠是 「Blowfish」、「DES」、「DESede」、「HmacMD5」或「HmacSHA1」等。這些算法均可以實現加密,這裏咱們不關心這些算法的細節,只要知道其使用上的特色便可。其中「DES」是目前最經常使用的對稱加密算法,但安全性較差。針對DES安全性的改進產生了能知足當前安全須要的TripleDES算法,即「DESede」。「Blowfish」的密鑰長度可達448位,安全性很好。「AES」是一種替代DES算法的新算法,可提供很好的安全性。

(2) 初始化密鑰生成器
kg.init(168);
分析:該步驟通常指定密鑰的長度。若是該步驟省略的話,會根據算法自動使用默認的密鑰長度。指定長度時,若第一步密鑰生成器使用的是「DES」算法,則密鑰長度必須是56位;如果「DESede」,則能夠是112或168位,其中112位有效;如果「AES」,能夠是128, 192或256位;如果「Blowfish」,則能夠是32至448之間能夠被8整除的數;「HmacMD5」和「HmacSHA1」默認的密鑰長度都是64個字節。

(3) 生成密鑰
SecretKey k=kg.generateKey( );
分析:使用第一步得到的KeyGenerator類型的對象中generateKey( )方法能夠得到密鑰。其類型爲SecretKey類型,可用於之後的加密和解密。

(4) 經過對象序列化方式將密鑰保存在文件中
FileOutputStream f=new FileOutputStream("key1.dat");
ObjectOutputStream b=new ObjectOutputStream(f);
b.writeObject(k);
分析:ObjectOutputStream類中提供的writeObject方法能夠將對象序列化,以流的方式進行處理。這裏將文件輸出流做爲參數傳遞給ObjectOutputStream類的構造器,這樣建立好的密鑰將保存在文件key1.data中。

import java.io.*;
import javax.crypto.*;
public class Skey_DES{ 
 public static void main(String args[])
 throws Exception{ 
KeyGenerator kg=KeyGenerator.getInstance("DESede");
            kg.init(168); 
            SecretKey k=kg.generateKey( );
            FileOutputStream  f=new FileOutputStream("key1.dat");
            ObjectOutputStream b=new  ObjectOutputStream(f);
            b.writeObject(k);
         }
}

運行java Skey_DES,在當前目錄下將生成文件key1.dat,其中包含的密鑰能夠用於使用Triple-DES算法的加密和解密。

上面的示例將密鑰經過對象序列化方式保存在文件中,在文件中保存的是對象,本實例以另外一種方式保存在文件中,即以字節保存在文件中。

  • 編程思路:

Java中全部的密鑰類都有一個getEncoded( )方法,經過它能夠從密鑰對象中獲取主要編碼格式,其返回值是字節數組。其主要步驟爲:

(1) 獲取密鑰

FileInputStream f=new FileInputStream("key1.dat");
ObjectInputStream b=new ObjectInputStream(f);
Key k=(Key)b.readObject( );

分析:首先建立文件輸入流,而後將其做爲參數傳遞給對象輸入流,最後執行對象輸入流的readObject( )方法讀取密鑰對象。因爲readObject( )返回的是Object類型,所以須要強制轉換成Key類型。

(2) 獲取主要編碼格式

byte[ ] kb=k.getEncoded( );

分析:執行SecretKey類型的對象k的getEncoded( )方法,返回的編碼放在byte類型的數組中。

(3) 保存密鑰編碼格式

FileOutputStream f2=new FileOutputStream("keykb1.dat");
f2.write(kb);

分析:先建立文件輸出流對象,在其參數中指定文件名,如keykb1.dat。而後執行文件輸出流的write( )方法將第2步中獲得的字節數組中的內容寫入文件。

import java.io.*;
import java.security.*;
public class Skey_kb{
    public static void main(String args[]) throws Exception{
        FileInputStream f=new FileInputStream("key1.dat");
        ObjectInputStream b=new ObjectInputStream(f);
        Key k=(Key)b.readObject( );
        byte[ ] kb=k.getEncoded( );
        FileOutputStream  f2=new FileOutputStream("keykb1.dat");
       f2.write(kb);
        // 打印密鑰編碼中的內容
        for(int i=0;i<kb.length;i++){
                 System.out.print(kb[i]+",");
        }
   }
}

程序中在保存了密鑰編碼後,又使用循環語句將字節數組中的內容打印出來。這樣能夠較爲直觀地看到密鑰編碼的內容。

運行程序

輸入java Skey_kb 運行程序,在程序的當前目錄中將產生文件名爲keykb1.dat的文件,屏幕輸出以下:

11,-105,-119,50,4,-105,16,38,-14,-111,21,-95,70,-15,76,-74,67,-88,59,-71,55,-125,104,42,

此即程序中建立的密鑰的編碼內容,若是用文本編輯器打開keykb1.dat,看到的不是上面的數字而是相似下面的字符:

棄2 ?&驊 馤禖??僪*

這是由於keykb1.dat是一個二進制文件,存放的是任意二進制數。

讀者運行時確定結果和上面會有所不一樣,每次運行時生成的密鑰都不會相同,這就保證了密鑰的惟一性。做爲對稱密鑰,只要保證若加密某段文字用的是某個密鑰,則解密這段密文時用一樣的密鑰便可。

  • 使用對稱密鑰進行加密和解密

  • 本實例的輸入是面生成並以對象方式保存在文件key1.dat中的密鑰,以及須要加密的一段最簡單的字符串"Hello World!",使用密鑰對"Hello World!"進行加密,加密後的信息保存在文件中。

  • 編程思路:

首先要從文件中獲取已經生成的密鑰,而後考慮如何使用密鑰進行加密。這涉及到各類算法。Java中已經提供了經常使用的加密算法,咱們執行Java中Cipher類的各個方法就能夠完成加密過程,其主要步驟爲:

(1) 從文件中獲取密鑰
FileInputStream f=new FileInputStream("key1.dat");
ObjectInputStream b=new ObjectInputStream(f);
Key k=(Key)b.readObject( );

(2) 建立密碼器(Cipher對象)
Cipher cp=Cipher.getInstance("DESede");

分析:和KeyGenerator類同樣,Cipher類是一個工廠類,它不是經過new方法建立對象,而是經過其中預約義的一個靜態方法getInstance( )獲取Cipher對象。

getInstance( )方法的參數是一個字符串,該字符串給出Cipher對象應該執行哪些操做,所以把傳入的字符串稱爲轉換(transformation)。一般經過它指定加密算法或解密所用的算法的名字,如本例的"DESede"。此外還能夠同時指定反饋模式及填充方案等,如"DESede/ECB/PKCS5Padding"。

(3) 初始化密碼器
cp.init(Cipher.ENCRYPT_MODE, k);
分析:該步驟執行Cipher對象的init()方法對Cipher對象進行初始化。該方法包括兩個參數,第一個參數指定密碼器準備進行加密仍是解密,若傳入Cipher.ENCRYPT_MODE則進入加密模式。第二個參數則傳入加密或解密所使用的密鑰,即第1步從文件中讀取的密鑰對象k。

(4) 獲取等待加密的明文

String s="Hello World!";
byte ptext[]=s.getBytes("UTF8");

分析:Cipher對象所做的操做是針對字節數組的,所以須要將要加密的內容轉換成字節數組。本例中要加密的是一個字符串s,可使用字符串的getBytes( )方法得到對應的字節數組。getBytes( )方法中必須使用參數"UTF8"指定…,不然…

(5) 執行加密

byte ctext[]=cp.doFinal(ptext);

分析:執行Cipher對象的doFinal( )方法,該方法的參數中傳入待加密的明文,從而按照前面幾步設置的算法及各類模式對所傳入的明文進行加密操做,該方法返回加密的結果。

(6) 處理加密結果

FileOutputStream f2=new FileOutputStream("SEnc.dat");
f2.write(ctext);

分析:第5步獲得的加密結果是字節數組,對其可做各類處理,如在網上傳遞、保存在文件中等。這裏將其保存在文件Senc.dat中。

import java.io.*;
import java.security.*;
import javax.crypto.*;
public class SEnc{
   public static void main(String args[]) throws Exception{
     String s="Hello World!";
    FileInputStream f=new FileInputStream("key1.dat");
    ObjectInputStream b=new ObjectInputStream(f);
    Key k=(Key)b.readObject( );
        Cipher cp=Cipher.getInstance("DESede");
        cp.init(Cipher.ENCRYPT_MODE, k);
        byte ptext[]=s.getBytes("UTF8");
        for(int i=0;i<ptext.length;i++){
            System.out.print(ptext[i]+",");
        }
        System.out.println("");
        byte ctext[]=cp.doFinal(ptext);
        for(int i=0;i<ctext.length;i++){
             System.out.print(ctext[i] +",");
        }
        FileOutputStream f2=new FileOutputStream("SEnc.dat");
        f2.write(ctext);
   }
}

程序中使用兩個循環語句將字節數組加密先後加密後的內容打印出來,可做爲對比。

當前目錄下必須有前面生成的密鑰文件key1.dat,

輸入java SEnc運行程序,在程序的當前目錄中將產生文件名爲SEnc.dat的文件,屏幕輸出以下:

72,101,108,108,111,32,87,111,114,108,100,33,
-57,119,0,-45,-9,23,37,-56,-60,-34,-99,105,99,113,-17,76,

其中第一行爲字符串"Hello World!"的字節數組編碼方式,第二行爲加密後的內容,第二行的內容會隨着密鑰的不一樣而不一樣。

第一行的內容沒有加過密,任何人若獲得第一行數據,只要將其用二進制方式寫入文本文件,用文本編輯器打開文件就能夠看到對應的字符串「Hello World!」。而第二行的內容因爲是加密過的,沒有密鑰的人即便獲得第二行的內容也沒法知道其內容。

密文同時保存在SEnc.dat文件中,將其提供給須要的人時,須要同時提供加密時使用的密鑰(key1.dat,或keykb1.dat),這樣收到SEnc.dat中密文的人才可以解密文件中的內容。

前面加密後的密文SEnc.dat,以及加密時所使用的密鑰key1.dat或keykb1.dat,本實例對SEnc.dat中的密文進行解密,獲得明文。

首先要從文件中獲取加密時使用的密鑰,而後考慮如何使用密鑰進行解密。其主要步驟爲:

(1) 獲取密文

FileInputStream f=new FileInputStream("SEnc.dat");
        int num=f.available();
        byte[ ] ctext=new byte[num];          
        f.read(ctext);

分析:密文存放在文件SEnc.dat中,因爲解密是針對字節數組進行操做的,所以要先將密文從文件中讀入字節數組。首先建立文件輸入流,而後使用文件輸入流的available( )方法判斷密文將佔用多少字節,從而建立相應大小的字節數組ctext,最後使用文件輸入流的read( )方法一次性讀入數組ctext。

(2) 獲取密鑰

FileInputStream  f2=new FileInputStream("keykb1.dat");
int num2=f2.available();
byte[ ] keykb=new byte[num2];          
f2.read(keykb);
SecretKeySpec k=new  SecretKeySpec(keykb,"DESede");

SecretKeySpec類的構造器中第2個參數則指定加密算法。因爲keykb1.dat中的密鑰原來使用的是DESede算法,所以這裏仍舊使用字符串「DESede」做爲參數。

(3) 建立密碼器(Cipher對象)

Cipher cp=Cipher.getInstance("DESede");

(4) 初始化密碼器
cp.init(Cipher.DECRYPT_MODE, k);

(5) 執行解密

byte []ptext=cp.doFinal(ctext);

import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
public class SDec{
   public static void main(String args[]) throws Exception{
        // 獲取密文
        FileInputStream f=new FileInputStream("SEnc.dat");
        int num=f.available();
        byte[ ] ctext=new byte[num];          
        f.read(ctext);
        // 獲取密鑰
        FileInputStream  f2=new FileInputStream("keykb1.dat");
        int num2=f2.available();
        byte[ ] keykb=new byte[num2];          
        f2.read(keykb);
        SecretKeySpec k=new  SecretKeySpec(keykb,"DESede");
        // 解密
        Cipher cp=Cipher.getInstance("DESede");
        cp.init(Cipher.DECRYPT_MODE, k);
        byte []ptext=cp.doFinal(ctext);
         // 顯示明文
        String p=new String(ptext,"UTF8");
        System.out.println(p);
   }
}

程序中最後將明文生成字符串加以顯示。

運行程序

當前目錄下必須有前面生成的密鑰文件keykb1.dat,以及密文文件SEnc.dat。

輸入java SDec運行程序,將輸出明文字符串「Hello World!」。

Java非對稱加密-RSA算法

下面演示如何使用Java中定義好的類建立RSA公鑰和私鑰。

Java的KeyPairGenerator類提供了一些方法來建立密鑰對以便用於非對稱加密,密鑰對建立好後封裝在KeyPair類型的對象中,在KeyPair類中提供了獲取公鑰和私鑰的方法。具體步驟以下:

(1) 建立密鑰對生成器
KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");
分析:密鑰對生成器即KeyPairGenerator類型的對象,和2.2.1小節的第1步中介紹的KeyGenerator類同樣,KeyPairGenerator類是一個工廠類,它經過其中預約義的一個靜態方法getInstance( )獲取KeyPairGenerator類型的對象。getInstance( )方法的參數是一個字符串,指定非對稱加密所使用的算法,經常使用的有RSA,DSA等。

(2) 初始化密鑰生成器
kpg.initialize(1024);
分析:對於密鑰長度。對於RSA算法,這裏指定的實際上是RSA算法中所用的模的位數。能夠在512到2048之間。

(3) 生成密鑰對
KeyPair kp=kpg.genKeyPair( );
分析:使用KeyPairGenerator類的genKeyPair( )方法生成密鑰對,其中包含了一對公鑰和私鑰的信息。

(4) 獲取公鑰和私鑰
PublicKey pbkey=kp.getPublic( );
PrivateKey prkey=kp.getPrivate( );
分析:使用KeyPair類的getPublic( )和getPrivate( )方法得到公鑰和私鑰對象。

import java.io.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;

public class Skey_RSA{
   public static void main(String args[]) throws Exception{
        KeyPairGenerator kpg=KeyPairGenerator.getInstance("RSA");
        kpg.initialize(1024);
        KeyPair kp=kpg.genKeyPair();
        PublicKey pbkey=kp.getPublic();
        PrivateKey prkey=kp.getPrivate();
        //  保存公鑰        
        FileOutputStream  f1=new FileOutputStream("Skey_RSA_pub.dat");
        ObjectOutputStream b1=new  ObjectOutputStream(f1);
b1.writeObject(pbkey);
        //  保存私鑰
        FileOutputStream  f2=new FileOutputStream("Skey_RSA_priv.dat");
        ObjectOutputStream b2=new  ObjectOutputStream(f2);
b2.writeObject(prkey);
   }
}

分析:使用對象流將密鑰保存在文件中,加密所用的公鑰和解密所用的私鑰分開保存。將公鑰對外公佈,供其餘人加密使用,而把私鑰祕密保存,在須要解密時使用。

運行程序

輸入java Skey_RSA運行程序,當前目錄下將生成兩個文件:Skey_RSA_pub.dat和Skey_RSA_priv.dat,前者保存着公鑰,後者保存着私鑰。將文件Skey_RSA_pub.dat對外公佈(如放在Web服務器上給你們下載,或者直接拷貝給全部須要的人),而Skey_RSA_priv.dat祕密保存。

以加密一串最簡單的字符串「Hello World!」爲例,演示瞭如何使用上面生成的RSA公鑰文件Skey_RSA_pub.dat進行加密。

編程思路:

使用RSA公鑰進行加密的代碼和使用DESede進行加密其實沒什麼大的區別,只是Cipher類的getInstance( )方法的參數中應該指定使用RSA。但因爲J2SDK1.4中只實現了RSA密鑰的建立,沒有實現RSA算法,所以須要安裝其餘加密提供者軟件才能直接使用Cipher類執行加密解密。其實有了RSA公鑰和私鑰後,本身編寫程序從底層實現RSA算法也並不複雜。本實例給出簡單的例子實現了RSA加密,使讀者只使用J2SDK1.4便能直觀地瞭解非對稱加密算法。

RSA算法是使用整數進行加密運算的,在RSA公鑰中包含了兩個信息:公鑰對應的整數e和用於取模的整數n。對於明文數字m,計算密文的公式是:me mod n。所以,編程步驟以下:

(1) 獲取公鑰

FileInputStream f=new FileInputStream("Skey_RSA_pub.dat");
      ObjectInputStream b=new ObjectInputStream(f);
      RSAPublicKey  pbk=(RSAPublicKey)b.readObject( );

分析: 從公鑰文件Skey_RSA_pub.dat中讀取公鑰,因爲生成使用的是RSA算法,所以從文件讀取公鑰對象後強制轉換爲RSAPublicKey類型,以便後面讀取RSA算法所須要的參數。

(2) 獲取公鑰的參數(e, n)
BigInteger e=pbk.getPublicExponent();
BigInteger n=pbk.getModulus();
分析:使用RSAPublicKey類的getPublicExponent( )和getModulus( )方法能夠分別得到公始中e和n的值。因爲密鑰很長,所以對應的整數值很是大,沒法使用通常的整型來存儲,Java中定義了BigInteger類來存儲這類很大的整數並可進行各類運算。

(3) 獲取明文整數(m)
String s="Hello World!";
byte ptext[]=s.getBytes("UTF8");
BigInteger m=new BigInteger(ptext);

分析:明文是一個字符串,爲了用整數表達這個字符串,先使用字符串的getBytes( )方法將其轉換爲byte類型數組,它實際上是字符串中各個字符的二進制表達方式,這一串二進制數轉換爲一個整數將很是大,所以仍舊使用BigInteger類將這個二進制串轉換爲整型。
本實例中出於簡化,將整個字符串轉換爲一個整數。實際使用中,應該對明文進行分組,由於RSA算法要求整型數m的值必須小於n。

(4) 執行計算
BigInteger c=m.modPow(e,n);
分析:計算前面的公式:me mod n。BigInteger類中已經提供了方法modPow( )來執行這個計算。底數m執行這個方法,方法modPow( )的第一個參數即指數e,第二個參數即模n。方法返回的結果即公式me mod n的計算結果,即密文。

import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.interfaces.*;
import java.security.interfaces.*;
import java.math.*;
import java.io.*;
public class Enc_RSA{
   public static void main(String args[]) throws Exception{
        String s="Hello World!";
        // 獲取公鑰及參數e,n
        FileInputStream f=new FileInputStream("Skey_RSA_pub.dat");
        ObjectInputStream b=new ObjectInputStream(f);
        RSAPublicKey  pbk=(RSAPublicKey)b.readObject( );
        BigInteger e=pbk.getPublicExponent();
        BigInteger n=pbk.getModulus();
        System.out.println("e= "+e);
        System.out.println("n= "+n);
        // 明文 m
        byte ptext[]=s.getBytes("UTF8");
        BigInteger m=new BigInteger(ptext);
        // 計算密文c,打印
        BigInteger c=m.modPow(e,n);
        System.out.println("c= "+c);
       // 保存密文
        String cs=c.toString( );
        BufferedWriter out= 
             new BufferedWriter(new OutputStreamWriter(
                new FileOutputStream("Enc_RSA.dat")));
        out.write(cs,0,cs.length( ));
        out.close( );
       
   }
}

程序最後將密文c打印出來,並以字符串形式保存在文件中。

運行程序

輸入java Enc_RSA運行程序,獲得以下結果:

其中顯示了公鑰中的參數以及加密的結果c,這些都是很大的整數,n和c多達上百位。程序運行後密文c以字符串形式保存在文件Enc_RSA.dat中。

下面實例使用私鑰文件Skey_RSA_priv.dat,對密文文件Enc_RSA.dat進行解密。

  • 編程思路:

使用RSA私鑰進行解密的代碼也能夠在Cipher類的getInstance( )方法的參數中指定使用RSA,使用解密模式進行解密。但須要安裝其餘加密提供者軟件才能直接使用Cipher類執行加密解密。本實例給出簡單的例子從底層實現RSA解密,以便只使用J2SDK1.4便能直觀地瞭解非對稱加密算法。
RSA算法的解密和加密相似,在RSA私鑰中包含了兩個信息:私鑰對應的整數d和用於取模的整數n。其中的n和加密時的n徹底相同。對於密文數字c,計算明文的公式是:cd mod n,之因此加密時由公式me mod n獲得的密文c經過這個公式計算一下就能夠反過來獲得原來的明文m,有其自己的數學規律決定。從編程角度只須要知道這個結果就好了。編程步驟以下:

(1) 讀取密文

BufferedReader in= 
          new BufferedReader(new InputStreamReader(
new FileInputStream("Enc_RSA.dat")));
String ctext=in.readLine();
BigInteger c=new BigInteger(ctext);

分析: 從密文文件Enc_RSA.dat中讀取密文,因爲保存的只是一行字符串,所以只要一條readLine( )語句便可。因爲這一行字符串表示的是一個很大的整型數,所以使用BigInteger類來表示這個整型數。

(2) 獲取私鑰

FileInputStream f=new FileInputStream("Skey_RSA_priv.dat");
ObjectInputStream b=new ObjectInputStream(f);
RSAPrivateKey prk=(RSAPrivateKey)b.readObject( );

分析: 從私鑰文件Skey_RSA_priv.dat中讀取公鑰,因爲使用的是RSA算法,所以從文件讀取公鑰對象後強制轉換爲RSAPrivateKey類型,以便後面讀取RSA算法所須要的參數。

(3) 獲取私鑰的參數(d, n)

BigInteger d=prk.getPrivateExponent( );
BigInteger n=prk.getModulus( );

分析:使用RSAPrivateKey類的getPrivateExponent( )和getModulus( )方法能夠分別得到公始中d和n的值。

(4) 執行計算

BigInteger m=c.modPow(d,n);

分析:使用BigInteger的modPow( )方法計算前面的公式:cd mod n。方法返回的結果即公式cd mod n的計算結果,即明文對應的整型數m。

(5) 計算明文整型數對應的字符串

byte[] mt=m.toByteArray();
for(int i=0;i<mt.length;i++){
       System.out.print((char) mt[i]);
}

分析:RSA算法解密的結果m是一個很大的整數,爲了計算出其對應的字符串的值,先使用BigInteger類的toByteArray( )方法獲得表明該整型數的字節數組,而後將數組中每一個元素轉換爲字符,組成字符串。

import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.interfaces.*;
import java.security.interfaces.*;
import java.math.*;
import java.io.*;
public class Dec_RSA{
   public static void main(String args[]) throws Exception{
       //讀取密文
        BufferedReader in= 
                new BufferedReader(new InputStreamReader(
new FileInputStream("Enc_RSA.dat")));
        String ctext=in.readLine();
        BigInteger c=new BigInteger(ctext);
       //讀取私鑰
        FileInputStream f=new FileInputStream("Skey_RSA_priv.dat");
        ObjectInputStream b=new ObjectInputStream(f);
        RSAPrivateKey prk=(RSAPrivateKey)b.readObject( );
        BigInteger d=prk.getPrivateExponent();
       //獲取私鑰參數及解密 
        BigInteger n=prk.getModulus();
        System.out.println("d= "+d);
        System.out.println("n= "+n);
        BigInteger m=c.modPow(d,n);
       //顯示解密結果
        System.out.println("m= "+m);
        byte[] mt=m.toByteArray();
        System.out.println("PlainText is ");
        for(int i=0;i<mt.length;i++){
             System.out.print((char) mt[i]);
       }
    }
}

運行程序輸入java Dec_RSA運行程序,獲得以下結果:

其中顯示了私鑰中的參數以及解密的結果,其中整型的明文轉換後顯示出字符串「Hello World!」。

使用密鑰協定建立共享密鑰

非對稱加密解決了密鑰分發的難題,但其計算量比對稱密鑰大,所以通常並不使用非對稱加密加密大量數據。常見的作法是:主要數據經過對稱密鑰加密,而使用非對稱加密來分發對稱密鑰,這樣就將二者的優點結合了起來。

例如若A和B之間想祕密傳送大量數據,一方(如A)先建立公私鑰對,公鑰對外公佈,另外一方(如B)建立對稱密鑰,而後使用A的公鑰加密對稱密鑰,傳遞給A,A收到後用本身的私鑰解密,獲得對稱密鑰,之後A和B之間就可使用對稱密鑰加密通訊了。

除了這種方式之外,還可使用密鑰協定來交換對稱密鑰。執行密鑰協定的標準算法是DH算法(Diffie-Hellman算法),本節介紹在Java中如何使用DH算法來交換共享密鑰。

  • 建立DH公鑰和私鑰

DH算法是創建在DH公鑰和私鑰的基礎上的, A須要和B共享密鑰時,A和B各自生成DH公鑰和私鑰,公鑰對外公佈而私鑰各自祕密保存。本實例將介紹Java中如何建立並部署DH公鑰和私鑰,以便後面一小節利用它建立共享密鑰。

程思路:

和上面使用KeyPairGenerator類建立RSA公鑰和私鑰相似,只是其參數中指定「DH」,此外在初始化時須要爲DH指定特定的參數。

代碼與分析:

import java.io.*;
import java.math.*;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.interfaces.*;

public class Key_DH{
       //三個靜態變量的定義從
// C:\j2sdk-1_4_0-doc\docs\guide\security\jce\JCERefGuide.html
// 拷貝而來
// The 1024 bit Diffie-Hellman modulus values used by SKIP
    private static final byte skip1024ModulusBytes[] = {
        (byte)0xF4, (byte)0x88, (byte)0xFD, (byte)0x58,
        (byte)0x4E, (byte)0x49, (byte)0xDB, (byte)0xCD,
        (byte)0x20, (byte)0xB4, (byte)0x9D, (byte)0xE4,
        (byte)0x91, (byte)0x07, (byte)0x36, (byte)0x6B,
        (byte)0x33, (byte)0x6C, (byte)0x38, (byte)0x0D,
        (byte)0x45, (byte)0x1D, (byte)0x0F, (byte)0x7C,
        (byte)0x88, (byte)0xB3, (byte)0x1C, (byte)0x7C,
        (byte)0x5B, (byte)0x2D, (byte)0x8E, (byte)0xF6,
        (byte)0xF3, (byte)0xC9, (byte)0x23, (byte)0xC0,
        (byte)0x43, (byte)0xF0, (byte)0xA5, (byte)0x5B,
        (byte)0x18, (byte)0x8D, (byte)0x8E, (byte)0xBB,
        (byte)0x55, (byte)0x8C, (byte)0xB8, (byte)0x5D,
        (byte)0x38, (byte)0xD3, (byte)0x34, (byte)0xFD,
        (byte)0x7C, (byte)0x17, (byte)0x57, (byte)0x43,
        (byte)0xA3, (byte)0x1D, (byte)0x18, (byte)0x6C,
        (byte)0xDE, (byte)0x33, (byte)0x21, (byte)0x2C,
        (byte)0xB5, (byte)0x2A, (byte)0xFF, (byte)0x3C,
        (byte)0xE1, (byte)0xB1, (byte)0x29, (byte)0x40,
        (byte)0x18, (byte)0x11, (byte)0x8D, (byte)0x7C,
        (byte)0x84, (byte)0xA7, (byte)0x0A, (byte)0x72,
        (byte)0xD6, (byte)0x86, (byte)0xC4, (byte)0x03,
        (byte)0x19, (byte)0xC8, (byte)0x07, (byte)0x29,
        (byte)0x7A, (byte)0xCA, (byte)0x95, (byte)0x0C,
        (byte)0xD9, (byte)0x96, (byte)0x9F, (byte)0xAB,
        (byte)0xD0, (byte)0x0A, (byte)0x50, (byte)0x9B,
        (byte)0x02, (byte)0x46, (byte)0xD3, (byte)0x08,
        (byte)0x3D, (byte)0x66, (byte)0xA4, (byte)0x5D,
        (byte)0x41, (byte)0x9F, (byte)0x9C, (byte)0x7C,
        (byte)0xBD, (byte)0x89, (byte)0x4B, (byte)0x22,
        (byte)0x19, (byte)0x26, (byte)0xBA, (byte)0xAB,
        (byte)0xA2, (byte)0x5E, (byte)0xC3, (byte)0x55,
        (byte)0xE9, (byte)0x2F, (byte)0x78, (byte)0xC7
    };
    // The SKIP 1024 bit modulus
    private static final BigInteger skip1024Modulus
              = new BigInteger(1, skip1024ModulusBytes);
    // The base used with the SKIP 1024 bit modulus
    private static final BigInteger skip1024Base = BigInteger.valueOf(2);
public static void main(String args[ ]) throws Exception{
    DHParameterSpec DHP=
new DHParameterSpec(skip1024Modulus,skip1024Base);

     KeyPairGenerator kpg= KeyPairGenerator.getInstance("DH");
     kpg.initialize(DHP);
     KeyPair kp=kpg.genKeyPair();

     PublicKey pbk=kp.getPublic();
     PrivateKey prk=kp.getPrivate();
     // 保存公鑰
     FileOutputStream  f1=new FileOutputStream(args[0]);
     ObjectOutputStream b1=new  ObjectOutputStream(f1);
     b1.writeObject(pbk);
     // 保存私鑰
     FileOutputStream  f2=new FileOutputStream(args[1]);
     ObjectOutputStream b2=new  ObjectOutputStream(f2);
     b2.writeObject(prk);
   }      
}

程序最後將公鑰和私鑰以對象流的形式保存在文件中,文件名經過命令行參數指定,第一個命令行參數對應的文件保存公鑰,第二個命令行參數對應的文件保存私鑰。

運行程序:

創建兩個目錄A和B,模擬須要祕密通訊的A、B雙方,因爲DH算法須要A和B各自生成DH公鑰和私鑰,所以在這兩個目錄下都拷貝編譯後文件Key_DH。

首先由A建立本身的公鑰和私鑰,即在A目錄下輸入「java Key_DH Apub.dat Apri.dat」運行程序,這時在目錄A下將產生文件Apub.dat和Apri.dat,前者保存着A的公鑰,後者保存着A的私鑰。
而後由B建立本身的公鑰和私鑰,即在B目錄下輸入「java Key_DH Bpub.dat Bpri.dat」運行程序,這時在目錄B下將產生文件Bpub.dat和Bpri.dat,前者保存着B的公鑰,後者保存着B的私鑰。
最後發佈公鑰,A將Apub.dat拷貝到B目錄,B將Bpub.dat拷貝到A的目錄。
這樣,A、B雙方的DH公鑰和私鑰已經建立並部署完畢。

  • 建立共享密鑰

DH算法中,A能夠用本身的密鑰和B的公鑰按照必定方法生成一個密鑰,B也能夠用本身的密鑰和A的公鑰按照必定方法生成一個密鑰,因爲一些數學規律,這兩個密鑰徹底相同。這樣,A和B間就有了一個共同的密鑰能夠用於各類加密。本實例介紹Java中在上一小節的基礎上如何利用DH公鑰和私鑰各自建立共享密鑰。

編程思路:

Java中KeyAgreement類實現了密鑰協定,它使用init( )方法傳入本身的私鑰,使用doPhase( )方法傳入對方的公鑰,進而可使用generateSecret( )方法生成共享的信息具體步驟以下:

(1) 讀取本身的DH私鑰和對方的DH公鑰

FileInputStream f1=new FileInputStream(args[0]);
ObjectInputStream b1=new ObjectInputStream(f1);
PublicKey  pbk=(PublicKey)b1.readObject( );
FileInputStream f2=new FileInputStream(args[1]);
ObjectInputStream b2=new ObjectInputStream(f2);
PrivateKey  prk=(PrivateKey)b2.readObject( );

分析:從文件中獲取密鑰。只是分爲公鑰和私鑰兩個文件,經過命令行參數傳入公鑰和私鑰文件名,第一個命令行參數爲對方的公鑰文件名,第二個命令行參數爲本身的私鑰文件名。

(2) 建立密鑰協定對象

KeyAgreement ka=KeyAgreement.getInstance("DH");

分析:密鑰協定對象即KeyAgreement類型的對象,和KeyPairGenerator類相似,KeyAgreement類是一個工廠類,經過其中預約義的一個靜態方法getInstance( )獲取KeyAgreement類型的對象。getInstance( )方法的參數指定爲「DH」。

(3) 初始化密鑰協定對象

ka.init(prk);

分析:執行密鑰協定對象的init()方法,傳入第1步得到的本身的私鑰,它在第1步中經過第2個命令行參數提供。

(4) 執行密鑰協定

ka.doPhase(pbk,true);

分析:執行密鑰協定對象的doPhase()方法,其第一個參數中傳入對方的公鑰。在本實例中,只有A、B兩方須要共享密鑰,所以對方只有一個,所以第二個參數設置爲true。若是有A、B、C三方須要共享密鑰,則對方有兩個,doPhase()方法要寫兩次,每次在第1個參數中傳入一個公鑰,第2個參數最初設置爲false,最後一次設置爲true。例如C方應該執行ka.doPhase(pbk_of_A,false); ka.doPhase(pbk_of_B,true);。一次類推,能夠用密鑰協定實現多方共享一個密鑰。

(5) 生成共享信息

byte[ ] sb=ka.generateSecret();

分析:執行密鑰協定對象的generateSecret()方法,返回字節類型的數組。A、B雙方獲得的該數組的內容徹底相同,用它建立密鑰也各方徹底相同。如可以使用SecretKeySpec k=new SecretKeySpec(sb,"DESede");建立密鑰。

代碼與分析:

import java.io.*;
import java.math.*;
import java.security.*;
import java.security.spec.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import javax.crypto.interfaces.*;

public class KeyAgree{
   public static void main(String args[ ]) throws Exception{
      // 讀取對方的DH公鑰
      FileInputStream f1=new FileInputStream(args[0]);
      ObjectInputStream b1=new ObjectInputStream(f1);
      PublicKey  pbk=(PublicKey)b1.readObject( );
//讀取本身的DH私鑰
      FileInputStream f2=new FileInputStream(args[1]);
      ObjectInputStream b2=new ObjectInputStream(f2);
      PrivateKey  prk=(PrivateKey)b2.readObject( );
      // 執行密鑰協定
     KeyAgreement ka=KeyAgreement.getInstance("DH");
     ka.init(prk);
     ka.doPhase(pbk,true);
     //生成共享信息
     byte[ ] sb=ka.generateSecret();
     for(int i=0;i<sb.length;i++){
        System.out.print(sb[i]+",");
     }
    SecretKeySpec k=new  SecretKeySpec(sb,"DESede");
  }
}

程序最後將共享信息打印了出來,以便直觀地對比A和B獲得的信息是否相同。

將程序KeyAgree編譯後分別拷貝在A和B兩個目錄,首先在A目錄輸入「java KeyAgree Bpub.dat Apri.dat」運行程序,它使用文件Bpub.dat中對方的公鑰和文件Apri.dat中本身的私鑰建立了一段共享的字節數組。

Java摘要算法- MD5

使用Java計算指定字符串的消息摘要。
java.security包中的MessageDigest類提供了計算消息摘要的方法,

首先生成對象,執行其update()方法能夠將原始數據傳遞給該對象,而後執行其digest( )方法便可獲得消息摘要。具體步驟以下:

(1) 生成MessageDigest對象
MessageDigest m=MessageDigest.getInstance("MD5");
分析:和2.2.1小節的KeyGenerator類同樣。MessageDigest類也是一個工廠類,其構造器是受保護的,不容許直接使用new MessageDigist( )來建立對象,而必須經過其靜態方法getInstance( )生成MessageDigest對象。其中傳入的參數指定計算消息摘要所使用的算法,經常使用的有"MD5","SHA"等。若對MD5算法的細節感興趣可參考http://www.ietf.org/rfc/rfc1321.txt。

(2) 傳入須要計算的字符串
m.update(x.getBytes("UTF8" ));
分析:x爲須要計算的字符串,update傳入的參數是字節類型或字節類型數組,對於字符串,須要先使用getBytes( )方法生成字符串數組。

(3) 計算消息摘要
byte s[ ]=m.digest( );
分析:執行MessageDigest對象的digest( )方法完成計算,計算的結果經過字節類型的數組返回。

(4) 處理計算結果
必要的話可使用以下代碼將計算結果s轉換爲字符串。

String result="";
for (int i=0; i<s.length; i++){
       result+=Integer.toHexString((0x000000ff & s[i]) | 0xffffff00).substring(6);
  }

代碼以下:

import java.security.*;
public class DigestPass{
     public static void main(String args[ ]) throws Exception{
         String x=args[0];
         MessageDigest m=MessageDigest.getInstance("MD5");
         m.update(x.getBytes("UTF8"));
         byte s[ ]=m.digest( );
         String result="";
         for (int i=0; i<s.length; i++){
            result+=Integer.toHexString((0x000000ff & s[i]) | 
0xffffff00).substring(6);
         }
         System.out.println(result);
      }   
}

運行程序

輸入java DigestCalc abc來運行程序,其中命令行參數abc是原始數據,屏幕輸出計算後的消息摘要:900150983cd24fb0d6963f7d28e17f72。

Java混合密碼系統

參考資料


歡迎關注「rocedu」微信公衆號(手機上長按二維碼)

作中教,作中學,實踐中共同進步!

rocedu


相關文章
相關標籤/搜索