1 /*
2 * JBoss, Home of Professional Open Source.
3 * Copyright 2014 Red Hat, Inc., and individual contributors
4 * as indicated by the @author tags.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * 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, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 package io.undertow.server.session;
20
21 import java.security.SecureRandom;
22
23 /**
24 * A {@link SessionIdGenerator} that uses a secure random to generate a
25 * session ID.
26 *
27 * On some systems this may perform poorly if not enough entropy is available,
28 * depending on the algorithm in use.
29 *
30 *
31 * @author Stuart Douglas
32 */
33 public class SecureRandomSessionIdGenerator implements SessionIdGenerator {
34
35 private final SecureRandom random = new SecureRandom();
36
37 private volatile int length = 30;
38
39 private static final char[] SESSION_ID_ALPHABET;
40
41 private static final String ALPHABET_PROPERTY = "io.undertow.server.session.SecureRandomSessionIdGenerator.ALPHABET";
42
43 static {
44 String alphabet = System.getProperty(ALPHABET_PROPERTY, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_");
45 if(alphabet.length() != 64) {
46 throw new RuntimeException("io.undertow.server.session.SecureRandomSessionIdGenerator must be exactly 64 characters long");
47 }
48 SESSION_ID_ALPHABET = alphabet.toCharArray();
49 }
50
51 @Override
52 public String createSessionId() {
53 final byte[] bytes = new byte[length];
54 random.nextBytes(bytes);
55 return new String(encode(bytes));
56 }
57
58
59 public int getLength() {
60 return length;
61 }
62
63 public void setLength(final int length) {
64 this.length = length;
65 }
66
67 /**
68 * Encode the bytes into a String with a slightly modified Base64-algorithm
69 * This code was written by Kevin Kelley <kelley@ruralnet.net>
70 * and adapted by Thomas Peuss <jboss@peuss.de>
71 *
72 * @param data The bytes you want to encode
73 * @return the encoded String
74 */
75 private char[] encode(byte[] data) {
76 char[] out = new char[((data.length + 2) / 3) * 4];
77 char[] alphabet = SESSION_ID_ALPHABET;
78 //
79 // 3 bytes encode to 4 chars. Output is always an even
80 // multiple of 4 characters.
81 //
82 for (int i = 0, index = 0; i < data.length; i += 3, index += 4) {
83 boolean quad = false;
84 boolean trip = false;
85
86 int val = (0xFF & (int) data[i]);
87 val <<= 8;
88 if ((i + 1) < data.length) {
89 val |= (0xFF & (int) data[i + 1]);
90 trip = true;
91 }
92 val <<= 8;
93 if ((i + 2) < data.length) {
94 val |= (0xFF & (int) data[i + 2]);
95 quad = true;
96 }
97 out[index + 3] = alphabet[(quad ? (val & 0x3F) : 63)];
98 val >>= 6;
99 out[index + 2] = alphabet[(trip ? (val & 0x3F) : 63)];
100 val >>= 6;
101 out[index + 1] = alphabet[val & 0x3F];
102 val >>= 6;
103 out[index] = alphabet[val & 0x3F];
104 }
105 return out;
106 }
107 }
108