001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk.auth;
019
020
021import java.nio.charset.Charset;
022import java.security.SecureRandom;
023import java.util.Arrays;
024import java.util.Date;
025
026import com.nimbusds.jose.util.Base64URL;
027import net.jcip.annotations.Immutable;
028
029
030/**
031 * Secret. The secret value should be {@link #erase erased} when no longer in
032 * use.
033 */
034@Immutable
035public class Secret {
036        
037        
038        /**
039         * The default byte length of generated secrets.
040         */
041        public static final int DEFAULT_BYTE_LENGTH = 32;
042        
043        
044        /**
045         * The secure random generator.
046         */
047        private static final SecureRandom SECURE_RANDOM = new SecureRandom();
048
049
050        /**
051         * The secret value.
052         */
053        private byte[] value;
054
055
056        /**
057         * Optional expiration date.
058         */
059        private final Date expDate;
060
061
062        /**
063         * Creates a new secret with the specified value.
064         *
065         * @param value The secret value. May be an empty string. Must be
066         *              UTF-8 encoded and not {@code null}.
067         */
068        public Secret(final String value) {
069
070                this(value, null);
071        }
072
073
074        /**
075         * Creates a new secret with the specified value and expiration date.
076         *
077         * @param value   The secret value. May be an empty string. Must be
078         *                UTF-8 encoded and not {@code null}.
079         * @param expDate The expiration date, {@code null} if not specified.
080         */
081        public Secret(final String value, final Date expDate) {
082
083                this.value = value.getBytes(Charset.forName("utf-8"));
084                this.expDate = expDate;
085        }
086        
087        
088        /**
089         * Generates a new secret with a cryptographic random value of the
090         * specified byte length, Base64URL-encoded.
091         *
092         * @param byteLength The byte length of the secret value to generate. 
093         *                   Must be greater than one.
094         */
095        public Secret(final int byteLength) {
096
097                this(byteLength, null);
098        }
099
100
101        /**
102         * Generates a new secret with a cryptographic random value of the
103         * specified byte length, Base64URL-encoded, and the specified 
104         * expiration date.
105         *
106         * @param byteLength The byte length of the secret value to generate. 
107         *                   Must be greater than one.
108         * @param expDate    The expiration date, {@code null} if not 
109         *                   specified.
110         */
111        public Secret(final int byteLength, final Date expDate) {
112        
113                if (byteLength < 1)
114                        throw new IllegalArgumentException("The byte length must be a positive integer");
115                
116                byte[] n = new byte[byteLength];
117                
118                SECURE_RANDOM.nextBytes(n);
119
120                value = Base64URL.encode(n).toString().getBytes(Charset.forName("UTF-8"));
121                
122                this.expDate = expDate;
123        }
124        
125        
126        /**
127         * Generates a new secret with a cryptographic 256-bit (32-byte) random
128         * value, Base64URL-encoded.
129         */
130        public Secret() {
131
132                this(DEFAULT_BYTE_LENGTH);
133        }
134
135
136        /**
137         * Gets the value of this secret.
138         *
139         * @return The value as a UTF-8 encoded string, {@code null} if it has 
140         *         been erased.
141         */
142        public String getValue() {
143
144                if (value == null) {
145                        return null; // value has been erased
146                }
147
148                return new String(value, Charset.forName("utf-8"));
149        }
150        
151        
152        /**
153         * Gets the value of this secret.
154         *
155         * @return The value as a byte array, {@code null} if it has 
156         *         been erased.
157         */
158        public byte[] getValueBytes() {
159
160                return value;
161        }
162
163
164        /**
165         * Erases of the value of this secret.
166         */
167        public void erase() {
168
169                if (value == null) {
170                        return; // Already erased
171                }
172
173                for (int i=0; i < value.length; i++) {
174                        value[i] = 0;
175                }
176                
177                value = null;
178        }
179
180
181        /**
182         * Gets the expiration date of this secret.
183         *
184         * @return The expiration date, {@code null} if not specified.
185         */
186        public Date getExpirationDate() {
187
188                return expDate;
189        }
190
191
192        /**
193         * Checks is this secret has expired.
194         *
195         * @return {@code true} if the secret has an associated expiration date
196         *         which is in the past (according to the current system time), 
197         *         else returns {@code false}.
198         */
199        public boolean expired() {
200
201                if (expDate == null) {
202                        return false; // never expires
203                }
204
205                final Date now = new Date();
206
207                return expDate.before(now);
208        }
209
210        
211        @Override
212        public boolean equals(Object o) {
213                if (this == o) return true;
214                if (!(o instanceof Secret)) return false;
215
216                Secret secret = (Secret) o;
217
218                return Arrays.equals(value, secret.value);
219
220        }
221
222
223        @Override
224        public int hashCode() {
225                return Arrays.hashCode(value);
226        }
227}