久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

循序漸進(jìn)學(xué)加密

 西北望msm66g9f 2019-06-25
作者:張京
鏈接:https://segmentfault.com/a/1190000019437132

還記得上初二的那年夏天,,班里來了一個新同學(xué),他就住在我家對面的樓里,,于是我們一起上學(xué)放學(xué),,很快便成了最要好的朋友。我們決定發(fā)明一套神秘的溝通方式,,任何人看到都不可能猜到它的真實含義,。我們第一個想到的就是漢語拼音,但很顯然光把一個句子變成漢語拼音是不夠的,,于是我們把26個英文字母用簡譜的方式從低音到高音排起來,,就得到了一個簡單的密碼本:

把“ 我們都是好朋友”用這個密碼本變換之后就得到了這樣的結(jié)果:

小時候玩這個游戲樂此不疲,覺得非常有趣,。上大學(xué)后,,有幸聽盧開澄教授講《計算機密碼學(xué)》,才知道原來我們小時候玩的這個游戲遠(yuǎn)遠(yuǎn)不能稱之為加密,。那么到底什么是加密呢,?

什么是加密,?

把字符串 123456經(jīng)過 base64變換之后,得到了 MTIzNDU2,,有人說這是 base64加密,。

把字符串 123456經(jīng)過 md5變換之后,得到了 E10ADC3949BA59ABBE56E057F20F883E,,有人說這是 md5加密,。

從嚴(yán)格意義上來說,,不管是 base64還是 md5甚至更復(fù)雜一些的 sha256都不能稱之為加密,。

一句話,沒有密鑰的算法都不能叫加密,。

編碼(Encoding)是把字符集中的字符編碼為指定集合中某一對象(例如:比特模式,、自然數(shù)序列、8位字節(jié)或者電脈沖),,以便文本在計算機中存儲和通過通信網(wǎng)絡(luò)的傳遞的方法,,常見的例子包括將拉丁字母表編碼成摩爾斯電碼和 ASCII。 base64只是一種編碼方式,。

雜湊(Hashing)是電腦科學(xué)中一種對資料的處理方法,,通過某種特定的函數(shù)/算法(稱為雜湊函數(shù)/算法)將要檢索的項與用來檢索的索引(稱為雜湊,或者雜湊值)關(guān)聯(lián)起來,,生成一種便于搜索的資料結(jié)構(gòu)(稱為雜湊表),。雜湊算法常被用來保護(hù)存在資料庫中的密碼字符串,由于雜湊算法所計算出來的雜湊值具有不可逆(無法逆向演算回原本的數(shù)值)的性質(zhì),,因此可有效的保護(hù)密碼,。常用的雜湊算法包括 md5sha1sha256等。

加密(Encryption)是將明文信息改變?yōu)殡y以讀取的密文內(nèi)容,,使之不可讀的過程,。只有擁有解密方法的對象,經(jīng)由解密過程,,才能將密文還原為正??勺x的內(nèi)容。加密分為對稱加密和非對稱加密,,對稱加密的常用算法包括 DESAES等,,非對稱加密算法包括 RSA,橢圓曲線算法等,。

在古典加密算法當(dāng)中,,加密算法和密鑰都是不能公開的,一旦泄露就有被破解的風(fēng)險,,我們可以用詞頻推算等方法獲知明文,。 1972年美國 IBM公司研制的 DES算法( Data Encryption Standard)是人類歷史上第一個公開加密算法但不公開密鑰的加密方法,,后來成為美國軍方和政府機構(gòu)的標(biāo)準(zhǔn)加密算法。 2002年升級成為 AES算法( AdvancedEncryption Standard),,我們今天就從 AES開始入手學(xué)習(xí)加密和解密,。

準(zhǔn)備工具

通常情況下,加解密都只需要在服務(wù)端完成就夠了,,這也是網(wǎng)上大多數(shù)教程和樣例代碼的情況,,但在某種特殊情況下,你需要用一種語言加密而用另一種語言解密的時候,,最好有一個中立的公正的第三方結(jié)果集來驗證你的加密結(jié)果,,否則一旦出錯,你都不知道是加密算法出錯了,,還是解密算法出錯了,,對此我們是有慘痛教訓(xùn)的,特別是如果一個公司里,,寫加密的是前端,,用的是 js語言,而寫解密的是后端,,用的是 java語言或者 php語言或者 go語言,,則雙方更需要有這樣一個客觀公正的平臺,否則你們之間必然會陷入永無休止的互相指責(zé)的境地,,前端說自己沒有錯,,是后端解密解錯了,后端說解密沒有錯,,是前端加密寫錯了,,而事實上是雙方都是菜鳥,對密碼學(xué)一知半解,,在這種情況下浪費的時間就更多,。

在線AES加密解密就是這樣的一個工具網(wǎng)站,你可以在上面驗證你的加密結(jié)果,,如果你加密得到的結(jié)果和它的結(jié)果完全一致,,就說明你的加密算法沒有問題,否則你就去調(diào)整,,直到和它的結(jié)果完全一致為止,。反之亦然,如果它能從一個密文解密解出來,,而你的代碼解不出來,,那么一定是你的算法有問題,而不可能是數(shù)據(jù)的問題。

我們先在這個網(wǎng)站上對一個簡單的字符串 123456進(jìn)行加密,。

下面我們對網(wǎng)站上的所有選項逐個解釋一下:

  1. AES加密模式:這里我們選擇的是 ECBee cc block)模式,。這是 AES所有模式中最簡單也是最不被人推薦的一種模式,因為它的固定的明文對應(yīng)的是固定的密文,,很容易被破解,。但是既然是練習(xí)的話,就讓我們先從最簡單的開始,。

  2. 填充:在這里我們選擇 pkcs標(biāo)準(zhǔn)的 pkcs7padding,。

  3. 數(shù)據(jù)塊:我們選擇 128位,因為 java端解密算法目前只支持 AES128,,所以我們先從 128位開始,。

  4. 密鑰:因為我們前面選擇了 128位的數(shù)據(jù)塊,所以這里我們用 128 / 8 = 16個字節(jié)來處理,,我們先簡單地填入 160,,其實你也可以填寫任意字符,,比如 abcdefg1234567ab或者其它,,只要是 16個字節(jié)即可。理論上來說,,不是16個字節(jié)也可以用來當(dāng)密鑰,,優(yōu)秀的算法會自動補齊,但是為了簡單起見,,我們先填入 16個 0,。

  5. 偏移量:置空。因為是 ECB模式,,不需要 iv偏移量,。

  6. 輸出:我們選擇 base64編碼方式。

  7. 字符集:這里因為我們只加密英文字母和阿拉伯?dāng)?shù)字,,所以選擇 utf-8和 gb2312都是一樣的,。

好了,現(xiàn)在我們知道按照以上選項設(shè)置好之后的代碼如果加密 123456的話,,應(yīng)該輸出 DoxDHHOjfol/2WxpaXAXgQ==,,如果不是這個結(jié)果,那就是加密端的問題,。

AES-ECB

AES-ECB的Javascript加密

為了完成 AES加密,,我們并不需要自己手寫一個 AES算法,不需要去重復(fù)造輪子,。但如何選擇 js的加密庫是個很有意思的挑戰(zhàn),。我們嘗試了很多方法,一開始我們嘗試了aes-js這個庫,但它不支持 RSA算法,,后來我們看到Web Crypto API這種瀏覽器自帶的加密庫,,原生支持 AES和 RSA,但它的 RSA實現(xiàn)和 Java不兼容,,最終我們還是選擇了Forge這個庫,,它天生支持 AES的各種子集,并且它的 RSA也能和 Java完美配合,。

使用 forge編寫的 js代碼實現(xiàn) AES-ECB加密的代碼就是下面這些:

const cipher = forge.cipher.createCipher('AES-ECB''這里是16字節(jié)密鑰');
cipher.start();
cipher.update(forge.util.createBuffer('這里是明文'));
cipher.finish();
const result = forge.util.encode64(cipher.output.getBytes())

forge的 AES缺省就是 pkcs7padding,,所以不用特別設(shè)置。運行它之后你就會得到正確的加密結(jié)果,。

AES-ECB的Java解密

接下來我們看看Java端的解密代碼該如何寫:

try {
    Cipher cipher = Cipher.getInstance('AES/ECB/PKCS5Padding');
    cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec('這里是16字節(jié)密鑰'.getBytes(), 'AES'));
    String plaintext = new String(cipher.doFinal(Base64.getDecoder().decode('這里是明文'.getBytes())), 'UTF-8');
    System.out.println(plaintext);
catch (Exception e) {
    System.out.println('解密出錯:' + e.toString());
}

注意這里我們用到的是 PKCS5Padding,,上面加密的時候不是用的是 pkcs7padding嗎?怎么這里變成 5了呢,?

我們先來了解一下什么是 pkcs,。 pkcs的全稱是 Public Key Cryptography Standards公鑰加密標(biāo)準(zhǔn)),這是 RSA實驗室制定的一系列的公鑰密碼編譯標(biāo)準(zhǔn),,比較著名的有 pkcs1pkcs5pkcs7pkcs8這四個,,它們分別管理的是不同的內(nèi)容。在這里我們只是用它來填充,,所以我們只關(guān)注 pkcs5和 pkcs7就夠了,。那么 pkcs5和 pkcs7有什么區(qū)別呢?其實在填充方面它們兩個的算法是一樣的,, pkcs5是 pkcs7的一個子集,,區(qū)別在于 pkcs5是 8字節(jié)固定的,而 pkcs7可以是 1到 255之間的任意字節(jié),。但用在 AES算法上,,因為 AES標(biāo)準(zhǔn)規(guī)定塊大小必須是 16字節(jié)或者 24字節(jié)或者 32字節(jié),不可能用 pkcs5的 8字節(jié),,所以 AES算法只能用 pkcs7填充,。但是由于 java早期工程師犯的一個命名上的錯誤,他們把 AES填充算法的名稱設(shè)定為 pkcs5,,而實際實現(xiàn)中實現(xiàn)的是 pkcs7,,所以我們在 java端開發(fā)解密的時候需要使用 pkcs5

AES-CBC

談完了不安全的 AES-ECB,,我們來做一下相對安全一些的 AES-CBC模式,。

AES-CBC的Javascript加密

直接上代碼:

const cipher = forge.cipher.createCipher('AES-CBC''這里是16字節(jié)密鑰');
cipher.start({ iv: '這里是16字節(jié)偏移量' });
cipher.update(forge.util.createBuffer('這里是明文'));
cipher.finish();
const result = forge.util.encode64(cipher.output.getBytes());

跟上面的 AES-ECB差不多,唯一區(qū)別只是在 start函數(shù)里定義了一個 iv,。

AES-CBC的Java解密

下面是 Java代碼:

try {
    Cipher cipher = Cipher.getInstance('AES/CBC/PKCS5Padding');
    cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec('這里是16字節(jié)密鑰'.getBytes(), 'AES'), new IvParameterSpec('這里是16字節(jié)偏移量'.getBytes()));
    String plaintext = new String(cipher.doFinal(Base64.getDecoder().decode('這里是明文'.getBytes())), 'UTF-8');
    System.out.println(plaintext);
catch (Exception e) {
    System.out.println('解密出錯:' + e.toString());
}

也是同樣,,跟上面用 AES-ECB時的模式幾乎一模一樣,只是增加了一個 IvParameterSpec,用來生成 iv,,在 cipher.init里面增加了一個 iv參數(shù),,除此之外完全相同,就這樣我們就已經(jīng)實現(xiàn)了一個簡單的 CBC模式,。

RSA

但是以上兩種做法都明顯是非常不安全的,,因為我們把加密用的密鑰和 iv參數(shù)都直接暴露在了前端,為此我們需要一種更加安全的加密方法—— RSA,。因為 RSA是非對稱加密,,即使我們把加密用的公鑰完全暴露在前端也不必?fù)?dān)心,別人即使截獲了我們的密文,,但因為他們沒有解密密鑰,,是無法解出我們的明文的。

生成密鑰對

要用 RSA加密,,首先我們需要生成一個公鑰和一個私鑰,,我們可以直接執(zhí)行命令 ssh-keygen。它會問我們密鑰文件保存的文件夾,,注意一定要單獨找一個文件夾存放,,不要放在缺省文件夾下,否則你日常使用的 ssh公鑰和私鑰就都被覆蓋了,。

得到公鑰文件之后,,由于這個公鑰文件是 rfc4716格式的,而我們的 forge庫要求一個 pkcs1格式的公鑰,,所以這里我們需要把它轉(zhuǎn)換成 pem格式(也就是 pkcs1格式):

ssh-keygen -f 公鑰文件名 -m pem -e

RSA的Javascript加密

得到 pem格式的公鑰之后,我們來看一下 js的代碼:

forge.util.encode64(forge.pki.publicKeyFromPem('-----BEGIN RSA PUBLIC KEY-----MIIBCfdsafasfasfafsdaafdsaAB-----END RSA PUBLIC KEY-----').encrypt('這里是明文''RSA-OAEP', { md: forge.md.sha256.create(), mgf1: { md: forge.md.sha1.create() } });

一句話就完成整個加密過程了,,這就是 forge的強大之處,。

RSA的Java解密

接下來我們看解密。

對于私鑰,,因為 Java只支持 PKCS8,,而我們用 ssh-keygen生成的私鑰是 pkcs1的,所以還需要用以下命令把 pkcs1的私鑰轉(zhuǎn)換為 pkcs8的私鑰:

openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in 私鑰文件名 -out 導(dǎo)出文件名

得到 pkcs8格式的私鑰之后,,我們把這個文件的頭和尾去掉,,然后放入以下 Java代碼:

try {
    Cipher cipher = Cipher.getInstance('RSA/ECB/OAEPWithSHA-256AndMGF1Padding');
    cipher.init(Cipher.DECRYPT_MODE, KeyFactory.getInstance('RSA').generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode('這里是私鑰'))));
    String plaintext = new String(cipher.doFinal(Base64.getDecoder().decode('這里是密文'.getBytes())), 'UTF-8');
    System.out.println(plaintext);
catch (Exception e) {
    System.out.println('解密出錯:' + e.toString());
}

和上面的 AES解密類似,只是增加了 KeyFactory讀取 PKCS8格式私鑰的部分,,這樣我們就完成了 Java端的 RSA解密,。

以上我們用最簡單的方式實現(xiàn)了 js端加密, java端解密的過程,,感興趣的朋友可以在這里下載完整的代碼親自驗證一下:

https://github.com/fengerzh/encdec


●編號891,,輸入編號直達(dá)本文

●輸入m獲取文章目錄

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多