001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019/* 020 * The apr_md5_encode() routine in the APR project's apr_md5.c file uses much 021 * code obtained from the FreeBSD 3.0 MD5 crypt() function, which is licenced 022 * as follows: 023 * ---------------------------------------------------------------------------- 024 * "THE BEER-WARE LICENSE" (Revision 42): 025 * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you 026 * can do whatever you want with this stuff. If we meet some day, and you think 027 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 028 * ---------------------------------------------------------------------------- 029 */ 030package org.apache.shiro.lang.codec; 031 032import java.io.IOException; 033 034/** 035 * Codec for <a href="http://en.wikipedia.org/wiki/Crypt_(Unix)">Unix Crypt</a>-style encoding. While similar to 036 * Base64, it is not compatible with Base64. 037 * <p/> 038 * This implementation is based on encoding algorithms found in the Apache Portable Runtime library's 039 * <a href="http://svn.apache.org/viewvc/apr/apr/trunk/crypto/apr_md5.c?revision=HEAD&view=markup">apr_md5.c</a> 040 * implementation for its {@code crypt}-style support. The APR team in turn received inspiration for its encoding 041 * implementation based on FreeBSD 3.0's {@code /usr/src/lib/libcrypt/crypt.c} implementation. The 042 * accompanying license headers have been retained at the top of this source file. 043 * <p/> 044 * This file and all that it contains is ASL 2.0 compatible. 045 * 046 * @since 1.2 047 */ 048@SuppressWarnings("checkstyle:MagicNumber") 049public final class H64 { 050 051 private static final byte FF = (byte) 0xff; 052 053 private static final char[] ITOA_64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray(); 054 055 private H64() { 056 057 } 058 059 private static short toShort(byte b) { 060 return (short) (b & FF); 061 } 062 063 private static int toInt(byte[] bytes, int offset, int numBytes) { 064 if (numBytes < 1 || numBytes > 4) { 065 throw new IllegalArgumentException("numBytes must be between 1 and 4."); 066 } 067 //1st byte 068 int val = toShort(bytes[offset]); 069 for (int i = 1; i < numBytes; i++) { 070 //any remaining bytes: 071 short s = toShort(bytes[offset + i]); 072 switch (i) { 073 case 1: 074 val |= s << (2 << 2); 075 break; 076 case 2: 077 val |= s << ((2 << 2) * 2); 078 break; 079 case 3: 080 val |= s << ((2 << 2) * 3); 081 break; 082 default: 083 } 084 } 085 return val; 086 } 087 088 /** 089 * Appends the specified character into the buffer, rethrowing any encountered 090 * {@link IOException} as an {@link IllegalStateException} (since this method is used for internal 091 * implementation needs and we only ever use StringBuilders, we should never encounter an IOException). 092 * 093 * @param buf the buffer to append to 094 * @param c the character to append. 095 */ 096 private static void append(Appendable buf, char c) { 097 try { 098 buf.append(c); 099 } 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}