所有Android版本的安全随机数生成
我目前正在考虑为android开发一个涉及加密的应用程序。我计划在ctr模式下使用aes,在惠而浦模式下使用PBKDF2进行按键拉伸 我将实现一个新的bouncy castle实现,而不是旧实现中构建的androids。确保它在任何android版本上都能正常工作 但我在想出一个可靠的方法来为salt和key生成随机数时遇到了一些问题。我在某个地方读到过安卓内置的安全随机在一些旧安卓版本中是不安全的,我还听说大多数安卓手机很难在dev/random中保持高熵,并且经常阻塞。这难道不应该对dev/uradom的安全性产生巨大影响吗?所有Android版本的安全随机数生成,android,security,random,cryptography,Android,Security,Random,Cryptography,我目前正在考虑为android开发一个涉及加密的应用程序。我计划在ctr模式下使用aes,在惠而浦模式下使用PBKDF2进行按键拉伸 我将实现一个新的bouncy castle实现,而不是旧实现中构建的androids。确保它在任何android版本上都能正常工作 但我在想出一个可靠的方法来为salt和key生成随机数时遇到了一些问题。我在某个地方读到过安卓内置的安全随机在一些旧安卓版本中是不安全的,我还听说大多数安卓手机很难在dev/random中保持高熵,并且经常阻塞。这难道不应该对dev/
因此,我正在寻找使用手机上的传感器收集更多熵的好方法。以下课程应该可以帮助您缓解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);
}
}
}
请注意,尚未对类的输出进行随机性测试(但这主要依赖于返回的SecureRandom
类,因此应该可以)
最后,由于我没有准备好Android 1.6运行时,应该有人测试它与此或更低版本的兼容性(!)。使用传感器既昂贵(如果我没记错的话)又不碍事。你最好自己制作一个随机数发生器,避免使用手机的传感器。有很多统计算法可以帮你做一个。关于这个主题的有趣的博客文章:对不起。我不确定你的技术水平。实际上我以前实现过一个简化的DES,所以我假设其他人也做过类似的事情。。。如果您是新手,我建议您现在就用Java random number gen创建您想要的项目。一旦这种方法奏效,你应该尝试改进(并实验)你的随机数gen.@Mohammad你说的传感器昂贵是什么意思?昂贵的意思是使用它需要大量内存和电源。在Android手机应用程序中解析字符串也很昂贵。这项费用会大大降低应用程序的速度。此外,这与设计有关,但如果有人的手机传感器坏了怎么办?在这种情况下你会怎么做?在你的
ByteBuffer
s中加入熵后,当心翻转()s。我的答案有什么遗漏吗,汉普斯?抛弃了SHA1PRNG,支持在所有Android版本上直接读取/dev/uradom
,除了已经这样做的4.2/4.3版本(但打乱了最初的种子)。他们使用了比时间更多的熵源。不幸的是,他们正在通过写入/dev/uradom
进行种子设定,而这在某些CyanogenMod和最新的Galaxy S4固件上失败,因为他们拒绝写入。在你的代码和/dev/uradom
之间,随着时间的推移,内核会重新设定种子,我不知道该用什么了。
/**
* 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();
}