001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.shiro.crypto;
020
021import org.apache.shiro.util.ByteSource;
022
023import java.security.SecureRandom;
024
025/**
026 * Default implementation of the {@link RandomNumberGenerator RandomNumberGenerator} interface, backed by a
027 * {@link SecureRandom SecureRandom} instance.
028 * <p/>
029 * This class is a little easier to use than using the JDK's {@code SecureRandom} class directly.  It also
030 * allows for JavaBeans-style of customization, convenient for Shiro's INI configuration or other IoC configuration
031 * mechanism.
032 *
033 * @since 1.1
034 */
035public class SecureRandomNumberGenerator implements RandomNumberGenerator {
036
037    protected static final int DEFAULT_NEXT_BYTES_SIZE = 16; //16 bytes == 128 bits (a common number in crypto)
038
039    private int defaultNextBytesSize;
040    private SecureRandom secureRandom;
041
042    /**
043     * Creates a new instance with a default backing {@link SecureRandom SecureRandom} and a
044     * {@link #getDefaultNextBytesSize() defaultNextBytesSize} of {@code 16}, which equals 128 bits, a size commonly
045     * used in cryptographic algorithms.
046     */
047    public SecureRandomNumberGenerator() {
048        this.defaultNextBytesSize = DEFAULT_NEXT_BYTES_SIZE;
049        this.secureRandom = new SecureRandom();
050    }
051
052    /**
053     * Seeds the backing {@link SecureRandom SecureRandom} instance with additional seed data.
054     *
055     * @param bytes the seed bytes
056     * @see SecureRandom#setSeed(byte[])
057     */
058    public void setSeed(byte[] bytes) {
059        this.secureRandom.setSeed(bytes);
060    }
061
062    /**
063     * Returns the {@link SecureRandom SecureRandom} backing this instance.
064     *
065     * @return the {@link SecureRandom SecureRandom} backing this instance.
066     */
067    public SecureRandom getSecureRandom() {
068        return secureRandom;
069    }
070
071    /**
072     * Sets the {@link SecureRandom SecureRandom} to back this instance.
073     *
074     * @param random the {@link SecureRandom SecureRandom} to back this instance.
075     * @throws NullPointerException if the method argument is null
076     */
077    public void setSecureRandom(SecureRandom random) throws NullPointerException {
078        if (random == null) {
079            throw new NullPointerException("SecureRandom argument cannot be null.");
080        }
081        this.secureRandom = random;
082    }
083
084    /**
085     * Returns the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}.  Defaults to
086     * {@code 16}, which equals 128 bits, a size commonly used in cryptographic algorithms.
087     *
088     * @return the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}.
089     */
090    public int getDefaultNextBytesSize() {
091        return defaultNextBytesSize;
092    }
093
094    /**
095     * Sets the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}. Defaults to
096     * {@code 16}, which equals 128 bits, a size commonly used in cryptographic algorithms.
097     *
098     * @param defaultNextBytesSize the size of the generated byte array for calls to {@link #nextBytes() nextBytes()}.
099     * @throws IllegalArgumentException if the argument is 0 or negative
100     */
101    public void setDefaultNextBytesSize(int defaultNextBytesSize) throws IllegalArgumentException {
102        if ( defaultNextBytesSize <= 0) {
103            throw new IllegalArgumentException("size value must be a positive integer (1 or larger)");
104        }
105        this.defaultNextBytesSize = defaultNextBytesSize;
106    }
107
108    public ByteSource nextBytes() {
109        return nextBytes(getDefaultNextBytesSize());
110    }
111
112    public ByteSource nextBytes(int numBytes) {
113        if (numBytes <= 0) {
114            throw new IllegalArgumentException("numBytes argument must be a positive integer (1 or larger)");
115        }
116        byte[] bytes = new byte[numBytes];
117        this.secureRandom.nextBytes(bytes);
118        return ByteSource.Util.bytes(bytes);
119    }
120}