AES加密算法在不同平臺(tái)上引起的“血案”

產(chǎn)品經(jīng)理:小凌好爬,這里有個(gè)簡(jiǎn)單的需求,將用戶的敏感信息加密保存起來(lái)甥啄,需要盡快實(shí)現(xiàn)存炮。

程序猿:好,沒(méi)有問(wèn)題蜈漓,半個(gè)小時(shí)就搞定穆桂。

說(shuō)完以后,小凌就動(dòng)手起來(lái)了融虽,打開百度搜索“Java加密算法”享完,復(fù)制了如下代碼:

//加密
public static byte[] encrypt(String content, String password) {
        try {
            //構(gòu)造密鑰生成器,指定為AES算法
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            //初始化密鑰生成器有额,指定隨機(jī)源
            kgen.init(128, new SecureRandom(password.getBytes()));
            //產(chǎn)生原始對(duì)稱密鑰
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            //根據(jù)字節(jié)數(shù)組生成AES密鑰
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
            //根據(jù)指定算法AES自成密碼器
            Cipher cipher = Cipher.getInstance("AES");
            byte[] byteContent = content.getBytes("utf-8");
            //初始化密碼器
            cipher.init(Cipher.ENCRYPT_MODE, key);
            //加密
            byte[] result = cipher.doFinal(byteContent);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
}

加密寫好了般又,哦不,是復(fù)制好了巍佑,既然有加密茴迁,那必須有解密,總不能將加密的信息直接顯示出來(lái)萤衰,解密如下:

//解密
public static byte[] decrypt(byte[] content, String password) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128, new SecureRandom(password.getBytes()));
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] result = cipher.doFinal(content);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
 }

加密和解密的代碼實(shí)現(xiàn)沒(méi)有太大的不同堕义,嗯.....代碼復(fù)制好,就是這么簡(jiǎn)單.



接下來(lái)就是進(jìn)行測(cè)試了:

public static void main(String[] args) throws Exception {
        String content = "linghenzeng";
        String password = "12345678";
        //加密
        System.out.println("加密前1:" + content);
        byte[] encryptResult = encrypt(content, password); 
        String strEncryptResult = parseByte2HexStr(encryptResult);
        System.out.println("加密后1:" + strEncryptResult);
        //解密
        byte[] byteDecryptResult = parseHexStr2Byte(strEncryptResult);
        byte[] decryptResult = decrypt(byteDecryptResult, password);    
        System.out.println("解密后1:" + new String(decryptResult));
}

為了加密和解密顯示正常脆栋,將加密生成的字節(jié)轉(zhuǎn)換成為十六進(jìn)制倦卖,再將十六進(jìn)制轉(zhuǎn)換為字符串洒擦,解密之前,將字符串轉(zhuǎn)換為二進(jìn)制的字節(jié)怕膛,二進(jìn)制和十六進(jìn)制的轉(zhuǎn)換函數(shù)如下:

// 二進(jìn)制轉(zhuǎn)十六進(jìn)制
public static String parseByte2HexStr(byte buf[]) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
}
//十六進(jìn)制轉(zhuǎn)二進(jìn)制
public static byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1)
            return null;
        byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2),
                    16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
}

在Window上測(cè)試一切正常熟嫩,加密解密都好使,剛好半個(gè)小時(shí)就完成了這個(gè)小需求褐捻,跟產(chǎn)品確認(rèn)下邦危,發(fā)布上線。

燃鵝舍扰,現(xiàn)實(shí)和理想存在巨大的鴻溝倦蚪,服務(wù)器報(bào)異常:

javax.crypto.BadPaddingException: Given final block not properly padded
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
    at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
    at javax.crypto.Cipher.doFinal(Cipher.java:2165)
    at test.decrypt(file.java:48)
    at test.main(file.java:94)
Exception in thread "main" java.lang.NullPointerException
    at java.lang.String.<init>(String.java:554)
    at test.main(file.java:95)

晴天霹靂,線上差不多三千條的用戶敏感信息加密了边苹,但解密不了......陵且,將版本回滾回來(lái),聯(lián)系DBA將數(shù)據(jù)恢復(fù)个束,在DAB深厚技術(shù)基礎(chǔ)上慕购,數(shù)據(jù)恢復(fù)回來(lái)了。

遇到問(wèn)題茬底,首先是Ctrl + C沪悲、Ctrl + V,然后Enter阱表,最后在搜索出來(lái)的內(nèi)容去找出解決問(wèn)題的方法殿如,根據(jù)廣大網(wǎng)友的智慧提供的一系列方法中提煉出來(lái)的答案如下:
密鑰生成器指定的隨機(jī)源是操作系統(tǒng)本身的內(nèi)部狀態(tài)的,即SecureRandom 類在源碼上實(shí)現(xiàn)是不一樣的最爬,在windows平臺(tái)上每次生成的key都相同涉馁,但是在linux平臺(tái)上則不同。
具體的解決辦法為將如下代碼:

KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128, new SecureRandom(password.getBytes()));

換成

KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(key.getBytes());
kgen.init(128, secureRandom);

新舊代碼不同的地方是舊代碼直接將解密密鑰的字節(jié)初始化SecureRandom爱致,而新代碼則是指定“SHA1PRNG”隨機(jī)生成種子算法初始化SecureRandom烤送,然后再將解密密鑰的字節(jié)設(shè)置為隨機(jī)種子。

問(wèn)題解決了糠悯,代碼在Linux上可以正常加密解密帮坚。



但是為什么指定“SHA1PRNG”,代碼就可以在window平臺(tái)和Linux平臺(tái)上運(yùn)行互艾?難道在Linux平臺(tái)默認(rèn)指定的是其它算法试和?

在程序員界中,女程序員以為男程序員忘朝,什么都會(huì)灰署。男程序員中判帮,初級(jí)程序員以為高級(jí)程序員局嘁,什么都會(huì)溉箕。而高級(jí)程序員,每次都在網(wǎng)上苦苦查找答案悦昵。

為了更進(jìn)一步接近問(wèn)題的本質(zhì)肴茄,打算從源碼層次上尋找答案,探究SecureRandom類在不同平臺(tái)上采取的隨機(jī)種子算法有什么不同但指。


上圖是window平臺(tái)上調(diào)試的代碼截圖寡痰,以字節(jié)數(shù)組初始化的SecureRandom的構(gòu)造函數(shù)中,默認(rèn)采用的是“SHA1PRNG”算法進(jìn)行初始化對(duì)象棋凳。下圖的代碼截圖是在Linux上調(diào)試的結(jié)果:

在Linux平臺(tái)上拦坠,以字節(jié)數(shù)組初始化的SecureRandom的構(gòu)造函數(shù)中,默認(rèn)采用的是“NativePRNG”算法進(jìn)行初始化對(duì)象剩岳。

經(jīng)過(guò)一系列的分析贞滨,終于在源碼層面上找到產(chǎn)生問(wèn)題的原因了,但引發(fā)的疑問(wèn)更多了拍棕,什么是“SHA1PRNG”晓铆?什么是“NativePRNG”?它們之間有什么不同绰播?.......

在java文檔中找到了“SHA1PRNG”的解釋:
https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html

The name of the pseudo-random number generation (PRNG) algorithm supplied by the SUN provider. This algorithm uses SHA-1 as the foundation of the PRNG. It computes the SHA-1 hash over a true-random seed value concatenated with a 64-bit counter which is incremented by 1 for each operation. From the 160-bit SHA-1 output, only 64 bits are used.

翻譯為:

SUN提供的偽隨機(jī)數(shù)生成(PRNG)算法的名稱骄噪。該算法以SHA-1作為PRNG的生成函數(shù)。它通過(guò)一個(gè)真隨機(jī)種子值和一個(gè)64位計(jì)數(shù)器連接來(lái)計(jì)算SHA-1散列蠢箩,每個(gè)操作增加1链蕊。在160位SHA-1輸出中,只使用64位谬泌。

而在另外一篇博文中示弓,找到有關(guān)“NativePRNG”的信息:
https://metebalci.com/blog/everything-about-javas-securerandom/

As you expect, NativePRNG is platform specific:
1、For Solaris/Linux/MacOS, it obtains seed and random numbers from /dev/random and /dev/urandom and reads securerandom.source Security property and java.security.egd System property. The default is to obtain seed from /dev/random and obtain random numbers from /dev/urandom.
2呵萨、For Windows, NativePRNG is not implemented, but Windows native implemetation is provided using SunMSCAPI provider.

總結(jié)上面的意思:

1奏属、在Solaris/Linux/MacOS上,“NativePRNG”底層是從從/dev/random和/dev/urandom獲取種子和隨機(jī)數(shù)潮峦。默認(rèn)是從/dev/random獲取種子囱皿,從/dev/urandom獲取隨機(jī)數(shù)。

2忱嘹、在window上嘱腥,沒(méi)有實(shí)現(xiàn)“NativePRNG”,但是Windows的實(shí)現(xiàn)是使用SunMSCAPI provider提供的拘悦。

至此齿兔,所有疑問(wèn)都解決了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市分苇,隨后出現(xiàn)的幾起案子添诉,更是在濱河造成了極大的恐慌,老刑警劉巖医寿,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件栏赴,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡靖秩,警方通過(guò)查閱死者的電腦和手機(jī)须眷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門为黎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嘴脾,“玉大人迅涮,你說(shuō)我怎么就攤上這事仲智∪矗” “怎么了洲拇?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵棉饶,是天一觀的道長(zhǎng)拥褂。 經(jīng)常有香客問(wèn)我求橄,道長(zhǎng)今野,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任罐农,我火速辦了婚禮条霜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涵亏。我一直安慰自己宰睡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布气筋。 她就那樣靜靜地躺著拆内,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宠默。 梳的紋絲不亂的頭發(fā)上麸恍,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音搀矫,去河邊找鬼抹沪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瓤球,可吹牛的內(nèi)容都是我干的融欧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼卦羡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼噪馏!你這毒婦竟也來(lái)了麦到?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤欠肾,失蹤者是張志新(化名)和其女友劉穎瓶颠,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體董济,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年要门,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了虏肾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡欢搜,死狀恐怖封豪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情炒瘟,我是刑警寧澤吹埠,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站疮装,受9級(jí)特大地震影響缘琅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜廓推,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一刷袍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧樊展,春花似錦呻纹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至涝婉,卻和暖如春哥力,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背墩弯。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工省骂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人最住。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓钞澳,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親涨缚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子轧粟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1策治、問(wèn)題現(xiàn)象 tomcat啟動(dòng)時(shí)候,日志顯示如下兰吟,生成sessionid耗費(fèi)了72S通惫,整個(gè)tomcat部署完成耗費(fèi)...
    Lavanda_yang閱讀 366評(píng)論 0 0
  • 偽隨機(jī)性(英語(yǔ):Pseudorandomness)是一個(gè)過(guò)程似乎是隨機(jī)的履腋,但實(shí)際上并不是。偽隨機(jī)數(shù)是看似隨機(jī)實(shí)質(zhì)是...
    殷俊杰閱讀 15,704評(píng)論 0 3
  • 一. 隨機(jī)數(shù) 隨機(jī)算法的起源數(shù)字稱為種子數(shù)(seed)惭嚣,在種子數(shù)的基礎(chǔ)上進(jìn)行一定的變換遵湖,從而產(chǎn)生需要的隨機(jī)數(shù)字。...
    AndroidMaster閱讀 4,819評(píng)論 2 3
  • 概述 之前一直對(duì)加密相關(guān)的算法知之甚少晚吞,只知道類似DES延旧、RSA等加密算法能對(duì)數(shù)據(jù)傳輸進(jìn)行加密,且各種加密算法各有...
    Henryzhu閱讀 3,019評(píng)論 0 14
  • 好久沒(méi)來(lái)了槽地,女人愛(ài)花迁沫,是天性!捌蚊!
    夏未知閱讀 420評(píng)論 0 1