Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
SessionIdGenerator |
|
| 3.75;3.75 |
1 | /* | |
2 | * Licensed to the Apache Software Foundation (ASF) under one | |
3 | * or more contributor license agreements. See the NOTICE file | |
4 | * distributed with this work for additional information | |
5 | * regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the | |
7 | * "License"); you may not use this file except in compliance | |
8 | * with the License. You may obtain a copy of the License at | |
9 | * | |
10 | * http://www.apache.org/licenses/LICENSE-2.0 | |
11 | * | |
12 | * Unless required by applicable law or agreed to in writing, | |
13 | * software distributed under the License is distributed on an | |
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
15 | * KIND, either express or implied. See the License for the | |
16 | * specific language governing permissions and limitations | |
17 | * under the License. | |
18 | */ | |
19 | package org.apache.myfaces.application.viewstate; | |
20 | ||
21 | import java.security.NoSuchAlgorithmException; | |
22 | import java.security.NoSuchProviderException; | |
23 | import java.security.SecureRandom; | |
24 | import java.util.Queue; | |
25 | import java.util.concurrent.ConcurrentLinkedQueue; | |
26 | import java.util.logging.Level; | |
27 | import java.util.logging.Logger; | |
28 | ||
29 | /** | |
30 | * NOTE: Class taken from tomcat 7 org.apache.catalina.util.SessionIdGenerator | |
31 | * and used here as an alternative for server side state token encryption. | |
32 | * | |
33 | */ | |
34 | 0 | class SessionIdGenerator |
35 | { | |
36 | ||
37 | 0 | private static Logger log = Logger.getLogger(SessionIdGenerator.class.getName()); |
38 | ||
39 | /** | |
40 | * Queue of random number generator objects to be used when creating session | |
41 | * identifiers. If the queue is empty when a random number generator is | |
42 | * required, a new random number generator object is created. This is | |
43 | * designed this way since random number generators use a sync to make them | |
44 | * thread-safe and the sync makes using a a single object slow(er). | |
45 | */ | |
46 | 0 | private Queue<SecureRandom> randoms = |
47 | new ConcurrentLinkedQueue<SecureRandom>(); | |
48 | /** | |
49 | * The Java class name of the secure random number generator class to be | |
50 | * used when generating session identifiers. The random number generator | |
51 | * class must be self-seeding and have a zero-argument constructor. If not | |
52 | * specified, an instance of {@link SecureRandom} will be generated. | |
53 | */ | |
54 | 0 | private String secureRandomClass = null; |
55 | /** | |
56 | * The name of the algorithm to use to create instances of | |
57 | * {@link SecureRandom} which are used to generate session IDs. If no | |
58 | * algorithm is specified, SHA1PRNG is used. To use the platform default | |
59 | * (which may be SHA1PRNG), specify the empty string. If an invalid | |
60 | * algorithm and/or provider is specified the {@link SecureRandom} instances | |
61 | * will be created using the defaults. If that fails, the {@link | |
62 | * SecureRandom} instances will be created using platform defaults. | |
63 | */ | |
64 | 0 | private String secureRandomAlgorithm = "SHA1PRNG"; |
65 | /** | |
66 | * The name of the provider to use to create instances of | |
67 | * {@link SecureRandom} which are used to generate session IDs. If no | |
68 | * algorithm is specified the of SHA1PRNG default is used. If an invalid | |
69 | * algorithm and/or provider is specified the {@link SecureRandom} instances | |
70 | * will be created using the defaults. If that fails, the {@link | |
71 | * SecureRandom} instances will be created using platform defaults. | |
72 | */ | |
73 | 0 | private String secureRandomProvider = null; |
74 | /** | |
75 | * Node identifier when in a cluster. Defaults to the empty string. | |
76 | */ | |
77 | 0 | private String jvmRoute = ""; |
78 | /** | |
79 | * Number of bytes in a session ID. Defaults to 16. | |
80 | */ | |
81 | 0 | private int sessionIdLength = 16; |
82 | ||
83 | /** | |
84 | * Specify a non-default @{link {@link SecureRandom} implementation to use. | |
85 | * | |
86 | * @param secureRandomClass The fully-qualified class name | |
87 | */ | |
88 | public void setSecureRandomClass(String secureRandomClass) | |
89 | { | |
90 | 0 | this.secureRandomClass = secureRandomClass; |
91 | 0 | } |
92 | ||
93 | /** | |
94 | * Specify a non-default algorithm to use to generate random numbers. | |
95 | * | |
96 | * @param secureRandomAlgorithm The name of the algorithm | |
97 | */ | |
98 | public void setSecureRandomAlgorithm(String secureRandomAlgorithm) | |
99 | { | |
100 | 0 | this.secureRandomAlgorithm = secureRandomAlgorithm; |
101 | 0 | } |
102 | ||
103 | /** | |
104 | * Specify a non-default provider to use to generate random numbers. | |
105 | * | |
106 | * @param secureRandomProvider The name of the provider | |
107 | */ | |
108 | public void setSecureRandomProvider(String secureRandomProvider) | |
109 | { | |
110 | 0 | this.secureRandomProvider = secureRandomProvider; |
111 | 0 | } |
112 | ||
113 | /** | |
114 | * Specify the node identifier associated with this node which will be | |
115 | * included in the generated session ID. | |
116 | * | |
117 | * @param jvmRoute The node identifier | |
118 | */ | |
119 | public void setJvmRoute(String jvmRoute) | |
120 | { | |
121 | 0 | this.jvmRoute = jvmRoute; |
122 | 0 | } |
123 | ||
124 | /** | |
125 | * Specify the number of bytes for a session ID | |
126 | * | |
127 | * @param sessionIdLength Number of bytes | |
128 | */ | |
129 | public void setSessionIdLength(int sessionIdLength) | |
130 | { | |
131 | 0 | this.sessionIdLength = sessionIdLength; |
132 | 0 | } |
133 | ||
134 | /** | |
135 | * Generate and return a new session identifier. | |
136 | */ | |
137 | public String generateSessionId() | |
138 | { | |
139 | ||
140 | 0 | byte random[] = new byte[16]; |
141 | ||
142 | // Render the result as a String of hexadecimal digits | |
143 | 0 | StringBuilder buffer = new StringBuilder(); |
144 | ||
145 | 0 | int resultLenBytes = 0; |
146 | ||
147 | 0 | while (resultLenBytes < sessionIdLength) |
148 | { | |
149 | 0 | getRandomBytes(random); |
150 | 0 | for (int j = 0; |
151 | 0 | j < random.length && resultLenBytes < sessionIdLength; |
152 | 0 | j++) |
153 | { | |
154 | 0 | byte b1 = (byte) ((random[j] & 0xf0) >> 4); |
155 | 0 | byte b2 = (byte) (random[j] & 0x0f); |
156 | 0 | if (b1 < 10) |
157 | { | |
158 | 0 | buffer.append((char) ('0' + b1)); |
159 | } | |
160 | else | |
161 | { | |
162 | 0 | buffer.append((char) ('A' + (b1 - 10))); |
163 | } | |
164 | 0 | if (b2 < 10) |
165 | { | |
166 | 0 | buffer.append((char) ('0' + b2)); |
167 | } | |
168 | else | |
169 | { | |
170 | 0 | buffer.append((char) ('A' + (b2 - 10))); |
171 | } | |
172 | 0 | resultLenBytes++; |
173 | } | |
174 | } | |
175 | ||
176 | 0 | if (jvmRoute != null && jvmRoute.length() > 0) |
177 | { | |
178 | 0 | buffer.append('.').append(jvmRoute); |
179 | } | |
180 | ||
181 | 0 | return buffer.toString(); |
182 | } | |
183 | ||
184 | public void getRandomBytes(byte bytes[]) | |
185 | { | |
186 | 0 | SecureRandom random = randoms.poll(); |
187 | 0 | if (random == null) |
188 | { | |
189 | 0 | random = createSecureRandom(); |
190 | } | |
191 | 0 | random.nextBytes(bytes); |
192 | 0 | randoms.add(random); |
193 | 0 | } |
194 | ||
195 | /** | |
196 | * Create a new random number generator instance we should use for | |
197 | * generating session identifiers. | |
198 | */ | |
199 | private SecureRandom createSecureRandom() | |
200 | { | |
201 | ||
202 | 0 | SecureRandom result = null; |
203 | ||
204 | 0 | long t1 = System.currentTimeMillis(); |
205 | 0 | if (secureRandomClass != null) |
206 | { | |
207 | try | |
208 | { | |
209 | // Construct and seed a new random number generator | |
210 | 0 | Class<?> clazz = Class.forName(secureRandomClass); |
211 | 0 | result = (SecureRandom) clazz.newInstance(); |
212 | } | |
213 | 0 | catch (Exception e) |
214 | { | |
215 | 0 | log.log(Level.SEVERE, "Exception initializing random number generator of class "+ |
216 | secureRandomClass + ". Falling back to java.secure.SecureRandom", e); | |
217 | 0 | } |
218 | } | |
219 | ||
220 | 0 | if (result == null) |
221 | { | |
222 | // No secureRandomClass or creation failed. Use SecureRandom. | |
223 | try | |
224 | { | |
225 | 0 | if (secureRandomProvider != null |
226 | && secureRandomProvider.length() > 0) | |
227 | { | |
228 | 0 | result = SecureRandom.getInstance(secureRandomAlgorithm, |
229 | secureRandomProvider); | |
230 | } | |
231 | else | |
232 | { | |
233 | 0 | if (secureRandomAlgorithm != null |
234 | && secureRandomAlgorithm.length() > 0) | |
235 | { | |
236 | 0 | result = SecureRandom.getInstance(secureRandomAlgorithm); |
237 | } | |
238 | } | |
239 | } | |
240 | 0 | catch (NoSuchAlgorithmException e) |
241 | { | |
242 | 0 | log.log(Level.SEVERE, "Exception initializing random number generator using algorithm: "+ |
243 | secureRandomAlgorithm, e); | |
244 | } | |
245 | 0 | catch (NoSuchProviderException e) |
246 | { | |
247 | 0 | log.log(Level.SEVERE, "Exception initializing random number generator using provider: " + |
248 | secureRandomProvider, e); | |
249 | 0 | } |
250 | } | |
251 | ||
252 | 0 | if (result == null) |
253 | { | |
254 | // Invalid provider / algorithm | |
255 | try | |
256 | { | |
257 | 0 | result = SecureRandom.getInstance("SHA1PRNG"); |
258 | } | |
259 | 0 | catch (NoSuchAlgorithmException e) |
260 | { | |
261 | 0 | log.log(Level.SEVERE, "Invalid provider / algoritm SHA1PRNG for generate secure random token", e); |
262 | 0 | } |
263 | } | |
264 | ||
265 | 0 | if (result == null) |
266 | { | |
267 | // Nothing works - use platform default | |
268 | 0 | result = new SecureRandom(); |
269 | } | |
270 | ||
271 | // Force seeding to take place | |
272 | 0 | result.nextInt(); |
273 | ||
274 | 0 | long t2 = System.currentTimeMillis(); |
275 | 0 | if ((t2 - t1) > 100) |
276 | { | |
277 | 0 | if (log.isLoggable(Level.FINEST)) |
278 | { | |
279 | 0 | log.info("Creation of SecureRandom instance for session ID generation using [" |
280 | +result.getAlgorithm()+"] took ["+Long.valueOf(t2 - t1)+"] milliseconds."); | |
281 | } | |
282 | } | |
283 | 0 | return result; |
284 | } | |
285 | } |