2013-07-05 43 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