2013-07-05 45 views
3

我目前正在玩的涉及加密的Android應用程序的想法。我打算在ctr模式下使用aes,在鍵盤上使用帶有漩渦的PBKDF2。所有Android版本的安全隨機數生成

我將實施一個新的彈性城堡實現,而不是在舊實現中構建的機器人。確保它在任何Android版本上按預期工作。

但我有一些問題找出一個可靠的方法來產生鹽和關鍵的隨機數。我已經閱讀過某些地方,內置安全隨機在Android是不安全的一些舊的Android版本,我也聽說,大多數Android手機很難保持高熵在開發/隨機和塊經常。這不應該對dev/urandom的安全性產生巨大影響嗎? 因此,我正在尋找使用手機上的傳感器來收集更多熵的好方法。

+0

使用傳感器是昂貴的(如果我沒有記錯的話)並且不受影響。你最好使自己的隨機數字發生器遠離使用手機的傳感器。有很多統計算法可以幫助您製作一個。 –

+0

關於這個問題的有趣的博客文章:http://blog.cryptographyengineering.com/2012/03/surviving-bad-rng.html – ntoskrnl

+0

對不起。我不確定你的技能水平。我之前實際上實現了一個簡化的DES,所以我只是假設其他人做了類似的事情......如果你是新人,我會建議現在用Java隨機數生成創建你想要的項目。一旦這個工作,你應該嘗試改進(和實驗)你的隨機數gen。 –

回答

2

以下課程可幫助您減輕Android SecureRandom類的問題。這個代碼是創建的,而不是一個文本,因爲否則的小細節。

/** 
* A strengthener that can be used to generate and re-seed random number 
* generators that do not seed themselves appropriately. 
* 
* @author owlstead 
*/ 
public class SecureRandomStrengthener { 
    private static final String DEFAULT_PSEUDO_RANDOM_NUMBER_GENERATOR = "SHA1PRNG"; 

    private static final EntropySource TIME_ENTROPY_SOURCE = new EntropySource() { 

     final ByteBuffer timeBuffer = ByteBuffer.allocate(Long.SIZE/Byte.SIZE 
       * 2); 

     @Override 
     public ByteBuffer provideEntropy() { 
      this.timeBuffer.clear(); 
      this.timeBuffer.putLong(System.currentTimeMillis()); 
      this.timeBuffer.putLong(System.nanoTime()); 
      this.timeBuffer.flip(); 
      return this.timeBuffer; 
     } 
    }; 

    private final String algorithm; 
    private final List<EntropySource> entropySources = new LinkedList<EntropySource>(); 
    private final MessageDigest digest; 
    private final ByteBuffer seedBuffer; 

    /** 
    * Generates an instance of a {@link SecureRandomStrengthener} that 
    * generates and re-seeds instances of {@code "SHA1PRNG"}. 
    * 
    * @return the strengthener, never null 
    */ 
    public static SecureRandomStrengthener getInstance() { 
     return new SecureRandomStrengthener(
       DEFAULT_PSEUDO_RANDOM_NUMBER_GENERATOR); 
    } 

    /** 
    * Generates an instance of a {@link SecureRandomStrengthener} that 
    * generates instances of the given argument. Note that the availability of 
    * the given algorithm arguments in not tested until generation. 
    * 
    * @param algorithm 
    *   the algorithm indicating the {@link SecureRandom} instance to 
    *   use 
    * @return the strengthener, never null 
    */ 
    public static SecureRandomStrengthener getInstance(final String algorithm) { 
     return new SecureRandomStrengthener(algorithm); 
    } 

    private SecureRandomStrengthener(final String algorithm) { 
     if (algorithm == null || algorithm.length() == 0) { 
      throw new IllegalArgumentException(
        "Please provide a PRNG algorithm string such as SHA1PRNG"); 
     } 

     this.algorithm = algorithm; 
     try { 
      this.digest = MessageDigest.getInstance("SHA1"); 
     } catch (final NoSuchAlgorithmException e) { 
      throw new IllegalStateException(
        "MessageDigest to create seed not available", e); 
     } 
     this.seedBuffer = ByteBuffer.allocate(this.digest.getDigestLength()); 
    } 

    /** 
    * Add an entropy source, which will be called for each generation and 
    * re-seeding of the given random number generator. 
    * 
    * @param source 
    *   the source of entropy 
    */ 
    public void addEntropySource(final EntropySource source) { 
     if (source == null) { 
      throw new IllegalArgumentException(
        "EntropySource should not be null"); 
     } 
     this.entropySources.add(source); 
    } 

    /** 
    * Generates and seeds a random number generator of the configured 
    * algorithm. Calls the {@link EntropySource#provideEntropy()} method of all 
    * added sources of entropy. 
    * 
    * @return the random number generator 
    */ 
    public SecureRandom generateAndSeedRandomNumberGenerator() { 
     final SecureRandom secureRandom; 
     try { 
      secureRandom = SecureRandom.getInstance(this.algorithm); 
     } catch (final NoSuchAlgorithmException e) { 
      throw new IllegalStateException("PRNG is not available", e); 
     } 

     reseed(secureRandom); 
     return secureRandom; 
    } 

    /** 
    * Re-seeds the random number generator. Calls the 
    * {@link EntropySource#provideEntropy()} method of all added sources of 
    * entropy. 
    * 
    * @param secureRandom 
    *   the random number generator to re-seed 
    */ 
    public void reseed(final SecureRandom secureRandom) { 
     this.seedBuffer.clear(); 
     secureRandom.nextBytes(this.seedBuffer.array()); 

     for (final EntropySource source : this.entropySources) { 
      final ByteBuffer entropy = source.provideEntropy(); 
      if (entropy == null) { 
       continue; 
      } 

      final ByteBuffer wipeBuffer = entropy.duplicate(); 
      this.digest.update(entropy); 
      wipe(wipeBuffer); 
     } 

     this.digest.update(TIME_ENTROPY_SOURCE.provideEntropy()); 
     this.digest.update(this.seedBuffer); 
     this.seedBuffer.clear(); 
     // remove data from seedBuffer so it won't be retrievable 

     // reuse 

     try { 
      this.digest.digest(this.seedBuffer.array(), 0, 
        this.seedBuffer.capacity()); 
     } catch (final DigestException e) { 
      throw new IllegalStateException(
        "DigestException should not be thrown", e); 
     } 
     secureRandom.setSeed(this.seedBuffer.array()); 

     wipe(this.seedBuffer); 
    } 

    private void wipe(final ByteBuffer buf) { 
     while (buf.hasRemaining()) { 
      buf.put((byte) 0); 
     } 
    } 
} 

這是小界面是EntropySource

/** 
* A simple interface that can be used to retrieve entropy from any source. 
* 
* @author owlstead 
*/ 
public interface EntropySource { 
    /** 
    * Retrieves the entropy. 
    * The position of the ByteBuffer must be advanced to the limit by any users calling this method. 
    * The values of the bytes between the position and limit should be set to zero by any users calling this method. 
    * 
    * @return entropy within the position and limit of the given buffer 
    */ 
    ByteBuffer provideEntropy(); 
} 

注意的是,類的輸出尚未隨機性測試(但這主要依賴於返回SecureRandom類因此應該沒事的)。

最後,由於我沒有準備好Android 1.6運行環境,因此有人應該針對此兼容性(!)或更低版本進行測試。

+0

在將熵放入它們之後,請小心'將'你的'ByteBuffer'翻轉過來。 –

+0

我的答案Hampus有什麼遺漏? –

+1

[Google的CSPRNG補丁](http://android-developers.blogspot.nl/2013/08/some-securerandom-thoughts.html)會拋出SHA1PRNG,以便直接從所有Android版本的/ dev/urandom中讀取,除了已經這麼做的4.2/4.3(但是搞亂了最初的種子)。他們使用更多的熵源而不僅僅是時間。不幸的是,他們通過*寫入'/ dev/urandom'來播種,這在一些CyanogenMod和最新的Galaxy S4固件上失敗了,因爲他們否認寫入。在你的代碼和'/ dev/urandom'之間,內核隨着時間的推移重新編譯,我不知道該使用什麼。 – Barend