使用密碼學技術(shù)保護敏感信息
從去年Google I/O大會上討論了“Android應(yīng)用的安全和隱私”以來,很多人對如何在Android上使用密碼學技術(shù)有些疑問。大多數(shù)問題都是圍繞在為了某一特定目的該使用哪一個API上。因此,我們在這里就是來探討一下如何在本地存儲中保護用戶的敏感信息,比如說密碼和auth token(【譯者】授權(quán)標記,這里直接用auth token更自然)。
一種反模式(錯誤的做法——譯者注)
通常我們都能意識到應(yīng)該使用SecureRandom類來產(chǎn)生密鑰,用來對本地的敏感信息進行加密,這樣的例子很容易找到,但實際上這種做法是欠妥的。
在這種方法中,不將密鑰作為一個字符串直接存儲在APK文件中,而是通過另外一個字符串來生成密鑰–有點類似于通過用戶口令生成加密密鑰。這種必要的混淆手段可以使攻擊者不容易破解加密信息,但是對于一個有經(jīng)驗的攻擊者而言,這種策略很容易被繞過,因此我們不推薦這種方法。
事實上,Android現(xiàn)有的安全機制中已經(jīng)為這種數(shù)據(jù)提供了很好的保護,敏感數(shù)據(jù)應(yīng)該標記上MODE_PRIVATE,然后存儲在內(nèi)部存儲中,請注意,千萬不能存儲在SD卡中,因為訪問控制沒法強制在外部存儲上起作用。
結(jié)合設(shè)備加密措施,這種方法可以杜絕絕大部分攻擊。
除此之外,像我們上面描述的那樣使用SecureRandom類還存在另外一個問題。從Android4.2開始,SecureRandom的默認實現(xiàn)是OpenSSL,開發(fā)者無法覆蓋SecureRandom的內(nèi)部狀態(tài)信息,例如下面這段代碼:
- SecureRandom secureRandom = new SecureRandom();
- byte[] b = new byte[]{(byte)1};
- secureRandom.setSeed(b);
- //在Android4.2上,下面這行代碼總是返回同一個數(shù)字。
- System.out.println(secureRandom.nextInt());
(【譯者注】這段代碼可以看到通過程序覆蓋了random對象中的種子,造成每次生成的隨機數(shù)序列都是一樣的)
在以前的Android版本中,SecureRandom是基于Bouncy Castle實現(xiàn)的,它允許像上面代碼這樣的操作,每個SecureRandom類的實例產(chǎn)生偽隨機數(shù)時使用的種子是從/dev/urandom獲取的。(【譯者注】/dev/urandom是類Unix系統(tǒng)中根據(jù)當前計算機混亂狀態(tài),如內(nèi)存使用,CPU占用率等信息計算出來的隨機數(shù),讀者可以在Linux下試試cat /dev/urandom,它會不停地輸出亂碼,一般用“熵”這個專業(yè)術(shù)語形容計算機的混亂狀態(tài))。那些試圖使用產(chǎn)生隨機數(shù)的開發(fā)者通常是通過替換現(xiàn)有的種子來產(chǎn)生隨機數(shù)序列的(參考相關(guān)實現(xiàn)文檔),如果種子固定,那么產(chǎn)生的隨機數(shù)列就是可預(yù)測的,這一點是不安全的?,F(xiàn)在通過OpenSSL實現(xiàn),使得這種錯誤的行為不再可能出現(xiàn)。
不幸的是,那些依賴老的SecureRandom類的應(yīng)用程序會發(fā)現(xiàn)每次程序啟動時產(chǎn)生的隨機數(shù)都不一樣了(事實上,這就應(yīng)該是隨機數(shù)發(fā)生器的期望行為)。想要通過這種方法對加密密鑰混淆已經(jīng)不可行了。
(【譯者注】原作者的意思是有些應(yīng)用對敏感信息做加密,加密的話需要密鑰,但是密鑰如果直接存起來覺得很不放心,于是通過產(chǎn)生隨機數(shù)的方式來對密鑰混淆一下,那么每次程序啟動時都要用相同的密鑰去解密數(shù)據(jù),于是通過一個固定的信息,比如一個密碼,或者記住某個隨機數(shù)字,通常很多人用當前時間,然后通過這個固定的信息作為種子產(chǎn)生隨機數(shù),用這個隨機數(shù)做密鑰,相當于對這個固定信息做了一次混淆操作。實際上這種方法還是不安全,安全性就變成了如何保證這個種子的安全性,治標不治本。下面的部分作者的意思是應(yīng)該直接將密鑰打上MODE_PRIVATE的標記存起來,通過系統(tǒng)的訪問控制機制保證密鑰的安全性。)
正確的方法
一種更加合理的解決方案很簡單,就是當應(yīng)用程序第一次啟動時產(chǎn)生一個隨機的AES算法的密鑰:
- public static SecretKey generateKey() throws NoSuchAlgorithmException
- {
- // 生成一個256位密鑰
- final int outputKeyLength = 256;
- SecureRandom secureRandom = new SecureRandom();
- // Do *not* seed secureRandom! Automatically seeded from system entropy.
- //不要給secureRandom一個固定的種子!通過系統(tǒng)熵值產(chǎn)生隨機的種子
- KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
- keyGenerator.init(outputKeyLength, secureRandom);
- SecretKey key = keyGenerator.generateKey();
- return key;
- }
注意這種方法的安全性依賴于如何保證密鑰的安全性,這可以依賴于Android系統(tǒng)的內(nèi)部存儲的安全性。將密鑰直接存放在文件中,標記為MODE_PRIVATE存在內(nèi)部存儲器。(【譯者注】我們很多人的Android手機都被Root過的,好多應(yīng)用也會取得Root權(quán)限,Root權(quán)限用戶是可以做任何事情的。。。這怎么辦呢?)
更加安全的方法
如果你的應(yīng)用還需要額外的加密操作,那么一個推薦的方法是每次進入你的應(yīng)用時需要輸入一個口令或者PIN碼。然后將這個口令傳給 PBKDF2(PBKDF2,基于口令的密鑰導(dǎo)出函數(shù)版本2,是RSA安全公司提出的密鑰導(dǎo)出算法,通常用來根據(jù)口令取得密鑰,通過一種叫做密鑰拉伸的專業(yè)技術(shù)),Android在SecretKeyFactory類中提供了一個叫做PBKDF2WithHmacSHA1的實現(xiàn):
- public static SecretKey generateKey(char[] passphraseOrPin, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException
- {
- //PBKDF2算法執(zhí)行輪數(shù),這個數(shù)字越大,計算時間越長,你應(yīng)該讓這個數(shù)字
- //足夠大,以至于這個算法執(zhí)行時間超過100毫秒以保證安全性
- final int iterations = 1000;
- // 產(chǎn)生一個256位的密鑰
- final int outputKeyLength = 256;
- SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
- KeySpec keySpec = new PBEKeySpec(passphraseOrPin, salt, iterations, outputKeyLength);
- SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);
- return secretKey;
- }
加密鹽應(yīng)該是一個通過SecureRandom產(chǎn)生的隨機字符串,和加密密文一起存放在內(nèi)部存儲器中。使用加密鹽很重要,它可以有效防止字典攻擊。
(【譯者注】看PBKDF2WithHmacSHA1這個名字也可以知道該算法是基于SHA1算法的,經(jīng)常攻擊這種單向函數(shù)方法就是字典攻擊,預(yù)先計算好大量的明文對應(yīng)的密文,就像是明文對應(yīng)密文的字典,然后再進行逐一對比,如果明文字符串在加密前和一個隨機字符串做個連接操作,那么那些預(yù)先計算的字典救沒用了。)
檢查你的應(yīng)用是不是正確的使用SecureRandom
如本文以及Jelly Bean的新安全特性所述,Android4.2的SecureRandom默認實現(xiàn)發(fā)生了變化,用它產(chǎn)生固定密鑰已經(jīng)行不通了。
如果你也用了這種錯誤方法的話,我們建議現(xiàn)在就更新你的應(yīng)用,防止當用戶升級到Android4.2或以上版本后發(fā)生一些莫名其妙的錯誤。
原文地址:http://www.importnew.com/3456.html