/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.security; import java.nio.ByteOrder; import java.util.Random; import libcore.io.Memory; import libcore.io.SizeOf; import org.apache.harmony.security.fortress.Engine; import org.apache.harmony.security.fortress.Services; import org.apache.harmony.security.provider.crypto.SHA1PRNG_SecureRandomImpl; /** * This class generates cryptographically secure pseudo-random numbers. * *

Supported Algorithms

* * *

The default algorithm is defined by the first {@code SecureRandomSpi} * provider found in the VM's installed security providers. Use {@link * Security} to install custom {@link SecureRandomSpi} providers. * *

Seeding {@code SecureRandom} may be * insecure

* A seed is an array of bytes used to bootstrap random number generation. * To produce cryptographically secure random numbers, both the seed and the * algorithm must be secure. * *

By default, instances of this class will generate an initial seed using * an internal entropy source, such as {@code /dev/urandom}. This seed is * unpredictable and appropriate for secure use. * *

You may alternatively specify the initial seed explicitly with the * {@link #SecureRandom(byte[]) seeded constructor} or by calling {@link * #setSeed} before any random numbers have been generated. Specifying a fixed * seed will cause the instance to return a predictable sequence of numbers. * This may be useful for testing but it is not appropriate for secure use. * *

It is dangerous to seed {@code SecureRandom} with the current time because * that value is more predictable to an attacker than the default seed. * *

Calling {@link #setSeed} on a {@code SecureRandom} after it has * been used to generate random numbers (ie. calling {@link #nextBytes}) will * supplement the existing seed. This does not cause the instance to return a * predictable numbers, nor does it harm the security of the numbers generated. */ public class SecureRandom extends Random { private static final long serialVersionUID = 4940670005562187L; // The service name. private static final String SERVICE = "SecureRandom"; // Used to access common engine functionality private static final Engine ENGINE = new Engine(SERVICE); private final Provider provider; private final SecureRandomSpi secureRandomSpi; private final String algorithm; // Internal SecureRandom used for getSeed(int) private static volatile SecureRandom internalSecureRandom; /** * Constructs a new {@code SecureRandom} that uses the default algorithm. */ public SecureRandom() { super(0); Services.refresh(); Provider.Service service = Services.getSecureRandomService(); if (service == null) { this.provider = null; this.secureRandomSpi = new SHA1PRNG_SecureRandomImpl(); this.algorithm = "SHA1PRNG"; } else { try { this.provider = service.getProvider(); this.secureRandomSpi = (SecureRandomSpi)service.newInstance(null); this.algorithm = service.getAlgorithm(); } catch (Exception e) { throw new RuntimeException(e); } } } /** * Constructs a new seeded {@code SecureRandom} that uses the default * algorithm. Seeding {@code SecureRandom} may be * insecure. */ public SecureRandom(byte[] seed) { this(); setSeed(seed); } /** * Constructs a new instance of {@code SecureRandom} using the given * implementation from the specified provider. * * @param secureRandomSpi * the implementation. * @param provider * the security provider. */ protected SecureRandom(SecureRandomSpi secureRandomSpi, Provider provider) { this(secureRandomSpi, provider, "unknown"); } // Constructor private SecureRandom(SecureRandomSpi secureRandomSpi, Provider provider, String algorithm) { super(0); this.provider = provider; this.algorithm = algorithm; this.secureRandomSpi = secureRandomSpi; } /** * Returns a new instance of {@code SecureRandom} that utilizes the * specified algorithm. * * @param algorithm * the name of the algorithm to use. * @return a new instance of {@code SecureRandom} that utilizes the * specified algorithm. * @throws NoSuchAlgorithmException * if the specified algorithm is not available. * @throws NullPointerException * if {@code algorithm} is {@code null}. */ public static SecureRandom getInstance(String algorithm) throws NoSuchAlgorithmException { if (algorithm == null) { throw new NullPointerException(); } Engine.SpiAndProvider sap = ENGINE.getInstance(algorithm, null); return new SecureRandom((SecureRandomSpi) sap.spi, sap.provider, algorithm); } /** * Returns a new instance of {@code SecureRandom} that utilizes the * specified algorithm from the specified provider. * * @param algorithm * the name of the algorithm to use. * @param provider * the name of the provider. * @return a new instance of {@code SecureRandom} that utilizes the * specified algorithm from the specified provider. * @throws NoSuchAlgorithmException * if the specified algorithm is not available. * @throws NoSuchProviderException * if the specified provider is not available. * @throws NullPointerException * if {@code algorithm} is {@code null}. * @throws IllegalArgumentException if {@code provider == null || provider.isEmpty()} */ public static SecureRandom getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException { if (provider == null || provider.isEmpty()) { throw new IllegalArgumentException(); } Provider p = Security.getProvider(provider); if (p == null) { throw new NoSuchProviderException(provider); } return getInstance(algorithm, p); } /** * Returns a new instance of {@code SecureRandom} that utilizes the * specified algorithm from the specified provider. * * @param algorithm * the name of the algorithm to use. * @param provider * the security provider. * @return a new instance of {@code SecureRandom} that utilizes the * specified algorithm from the specified provider. * @throws NoSuchAlgorithmException * if the specified algorithm is not available. * @throws NullPointerException * if {@code algorithm} is {@code null}. * @throws IllegalArgumentException if {@code provider == null} */ public static SecureRandom getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { if (provider == null) { throw new IllegalArgumentException(); } if (algorithm == null) { throw new NullPointerException(); } Object spi = ENGINE.getInstance(algorithm, provider, null); return new SecureRandom((SecureRandomSpi) spi, provider, algorithm); } /** * Returns the provider associated with this {@code SecureRandom}. * * @return the provider associated with this {@code SecureRandom}. */ public final Provider getProvider() { return provider; } /** * Returns the name of the algorithm of this {@code SecureRandom}. * * @return the name of the algorithm of this {@code SecureRandom}. */ public String getAlgorithm() { return algorithm; } /** * Seeds this {@code SecureRandom} instance with the specified {@code * seed}. Seeding {@code SecureRandom} may be * insecure. */ public synchronized void setSeed(byte[] seed) { secureRandomSpi.engineSetSeed(seed); } /** * Seeds this {@code SecureRandom} instance with the specified eight-byte * {@code seed}. Seeding {@code SecureRandom} may * be insecure. */ @Override public void setSeed(long seed) { if (seed == 0) { // skip call from Random return; } byte[] byteSeed = new byte[SizeOf.LONG]; Memory.pokeLong(byteSeed, 0, seed, ByteOrder.BIG_ENDIAN); setSeed(byteSeed); } /** * Generates and stores random bytes in the given {@code byte[]} for each * array element. * * @param bytes * the {@code byte[]} to be filled with random bytes. */ @Override public synchronized void nextBytes(byte[] bytes) { secureRandomSpi.engineNextBytes(bytes); } /** * Generates and returns an {@code int} containing the specified number of * random bits (right justified, with leading zeros). * * @param numBits * number of bits to be generated. An input value should be in * the range [0, 32]. * @return an {@code int} containing the specified number of random bits. */ @Override protected final int next(int numBits) { if (numBits < 0) { numBits = 0; } else { if (numBits > 32) { numBits = 32; } } int bytes = (numBits+7)/8; byte[] next = new byte[bytes]; int ret = 0; nextBytes(next); for (int i = 0; i < bytes; i++) { ret = (next[i] & 0xFF) | (ret << 8); } ret = ret >>> (bytes*8 - numBits); return ret; } /** * Generates and returns the specified number of seed bytes, computed using * the seed generation algorithm used by this {@code SecureRandom}. * * @param numBytes * the number of seed bytes. * @return the seed bytes */ public static byte[] getSeed(int numBytes) { SecureRandom result = internalSecureRandom; if (result == null) { // single-check idiom internalSecureRandom = result = new SecureRandom(); } return result.generateSeed(numBytes); } /** * Generates and returns the specified number of seed bytes, computed using * the seed generation algorithm used by this {@code SecureRandom}. * * @param numBytes * the number of seed bytes. * @return the seed bytes. */ public byte[] generateSeed(int numBytes) { return secureRandomSpi.engineGenerateSeed(numBytes); } }