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