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