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.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 public class H64 { 49 50 private static final char[] itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray(); 51 52 private static short toShort(byte b) { 53 return (short) (b & 0xff); 54 } 55 56 private static int toInt(byte[] bytes, int offset, int numBytes) { 57 if (numBytes < 1 || numBytes > 4) { 58 throw new IllegalArgumentException("numBytes must be between 1 and 4."); 59 } 60 int val = toShort(bytes[offset]); //1st byte 61 for (int i = 1; i < numBytes; i++) { //any remaining bytes: 62 short s = toShort(bytes[offset + i]); 63 switch (i) { 64 case 1: val |= s << 8; break; 65 case 2: val |= s << 16; break; 66 case 3: val |= s << 24; break; 67 } 68 } 69 return val; 70 } 71 72 /** 73 * Appends the specified character into the buffer, rethrowing any encountered 74 * {@link IOException} as an {@link IllegalStateException} (since this method is used for internal 75 * implementation needs and we only ever use StringBuilders, we should never encounter an IOException). 76 * 77 * @param buf the buffer to append to 78 * @param c the character to append. 79 */ 80 private static void append(Appendable buf, char c) { 81 try { 82 buf.append(c); 83 } catch (IOException e) { 84 throw new IllegalStateException("Unable to append character to internal buffer.", e); 85 } 86 } 87 88 /** 89 * Encodes the specified integer to {@code numChars} H64-compatible characters and appends them into {@code buf}. 90 * 91 * @param value the integer to encode to H64-compatible characters 92 * @param buf the output buffer 93 * @param numChars the number of characters the value should be converted to. 3, 2 or 1. 94 */ 95 private static void encodeAndAppend(int value, Appendable buf, int numChars) { 96 for (int i = 0; i < numChars; i++) { 97 append(buf, itoa64[value & 0x3f]); 98 value >>= 6; 99 } 100 } 101 102 /** 103 * Encodes the specified bytes to an {@code H64}-encoded String. 104 * 105 * @param bytes 106 * @return 107 */ 108 public static String encodeToString(byte[] bytes) { 109 if (bytes == null || bytes.length == 0) return null; 110 111 StringBuilder buf = new StringBuilder(); 112 113 int length = bytes.length; 114 int remainder = length % 3; 115 int i = 0; //starting byte 116 int last3ByteIndex = length - remainder; //last byte whose index is a multiple of 3 117 118 for(; i < last3ByteIndex; i += 3) { 119 int twentyFourBit = toInt(bytes, i, 3); 120 encodeAndAppend(twentyFourBit, buf, 4); 121 } 122 if (remainder > 0) { 123 //one or two bytes that we still need to encode: 124 int a = toInt(bytes, i, remainder); 125 encodeAndAppend(a, buf, remainder + 1); 126 } 127 return buf.toString(); 128 } 129 }