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 /*
20 * The apr_md5_encode() routine in the APR project's apr_md5.c file uses much
21 * code obtained from the FreeBSD 3.0 MD5 crypt() function, which is licenced
22 * as follows:
23 * ----------------------------------------------------------------------------
24 * "THE BEER-WARE LICENSE" (Revision 42):
25 * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
26 * can do whatever you want with this stuff. If we meet some day, and you think
27 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
28 * ----------------------------------------------------------------------------
29 */
30 package org.apache.shiro.lang.codec;
31
32 import java.io.IOException;
33
34 /**
35 * Codec for <a href="http://en.wikipedia.org/wiki/Crypt_(Unix)">Unix Crypt</a>-style encoding. While similar to
36 * Base64, it is not compatible with Base64.
37 * <p/>
38 * This implementation is based on encoding algorithms found in the Apache Portable Runtime library's
39 * <a href="http://svn.apache.org/viewvc/apr/apr/trunk/crypto/apr_md5.c?revision=HEAD&view=markup">apr_md5.c</a>
40 * implementation for its {@code crypt}-style support. The APR team in turn received inspiration for its encoding
41 * implementation based on FreeBSD 3.0's {@code /usr/src/lib/libcrypt/crypt.c} implementation. The
42 * accompanying license headers have been retained at the top of this source file.
43 * <p/>
44 * This file and all that it contains is ASL 2.0 compatible.
45 *
46 * @since 1.2
47 */
48 @SuppressWarnings("checkstyle:MagicNumber")
49 public final class H64 {
50
51 private static final byte FF = (byte) 0xff;
52
53 private static final char[] ITOA_64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
54
55 private H64() {
56
57 }
58
59 private static short toShort(byte b) {
60 return (short) (b & FF);
61 }
62
63 private static int toInt(byte[] bytes, int offset, int numBytes) {
64 if (numBytes < 1 || numBytes > 4) {
65 throw new IllegalArgumentException("numBytes must be between 1 and 4.");
66 }
67 //1st byte
68 int val = toShort(bytes[offset]);
69 for (int i = 1; i < numBytes; i++) {
70 //any remaining bytes:
71 short s = toShort(bytes[offset + i]);
72 switch (i) {
73 case 1:
74 val |= s << (2 << 2);
75 break;
76 case 2:
77 val |= s << ((2 << 2) * 2);
78 break;
79 case 3:
80 val |= s << ((2 << 2) * 3);
81 break;
82 default:
83 }
84 }
85 return val;
86 }
87
88 /**
89 * Appends the specified character into the buffer, rethrowing any encountered
90 * {@link IOException} as an {@link IllegalStateException} (since this method is used for internal
91 * implementation needs and we only ever use StringBuilders, we should never encounter an IOException).
92 *
93 * @param buf the buffer to append to
94 * @param c the character to append.
95 */
96 private static void append(Appendable buf, char c) {
97 try {
98 buf.append(c);
99 } catch (IOException e) {
100 throw new IllegalStateException("Unable to append character to internal buffer.", e);
101 }
102 }
103
104 /**
105 * Encodes the specified integer to {@code numChars} H64-compatible characters and appends them into {@code buf}.
106 *
107 * @param value the integer to encode to H64-compatible characters
108 * @param buf the output buffer
109 * @param numChars the number of characters the value should be converted to. 3, 2 or 1.
110 */
111 private static void encodeAndAppend(int value, Appendable buf, int numChars) {
112 for (int i = 0; i < numChars; i++) {
113 append(buf, ITOA_64[value & 0x3f]);
114 value >>= 6;
115 }
116 }
117
118 /**
119 * Encodes the specified bytes to an {@code H64}-encoded String.
120 *
121 * @param bytes
122 * @return
123 */
124 public static String encodeToString(byte[] bytes) {
125 if (bytes == null || bytes.length == 0) {
126 return null;
127 }
128
129 StringBuilder buf = new StringBuilder();
130
131 int length = bytes.length;
132 int remainder = length % 3;
133 //starting byte
134 int i = 0;
135 //last byte whose index is a multiple of 3
136 int last3ByteIndex = length - remainder;
137
138 for (; i < last3ByteIndex; i += 3) {
139 int twentyFourBit = toInt(bytes, i, 3);
140 encodeAndAppend(twentyFourBit, buf, 4);
141 }
142 if (remainder > 0) {
143 //one or two bytes that we still need to encode:
144 int a = toInt(bytes, i, remainder);
145 encodeAndAppend(a, buf, remainder + 1);
146 }
147 return buf.toString();
148 }
149 }