/* * $Id$ * * Copyright (c) 2001 by Yoon Kyung Koo (yoonforh@yahoo.com), * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL YOON KYUNG KOO OR THE OTHER * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ import java.io.*; /** * fast Base64 encoder/decoder * * @version $Revision$ * created at 2002-10-23 03:35:39 * @author Yoon Kyung Koo */ public class Base64 { /** * 6ºñÆ® º¯È¯ Å×À̺í */ protected static final char[] alphabet = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0 to 7 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8 to 15 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16 to 23 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24 to 31 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32 to 39 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40 to 47 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48 to 55 '4', '5', '6', '7', '8', '9', '+', '/' // 56 to 63 }; /** * ¾ËÆĺª ¿ªº¯È¯ Å×À̺í */ protected static final int[] decodeTable = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 to 9 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10 to 19 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 20 to 29 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 30 to 39 -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, // 40 to 49 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, // 50 to 59 -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, // 60 to 69 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 70 to 79 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 80 to 89 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, // 90 to 99 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // 100 to 109 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, // 110 to 119 49, 50, 51 // 120 to 122 }; public static char[] encode(String s) { return encode(s.getBytes()); } /** * convert bytes into a BASE64 encoded string */ public static char[] encode(byte[] bytes) { int sixbit; // Àüü ¹®ÀÚ¿­ ±æÀÌ´Â 3¹ÙÀÌÆ®¸¦ 6ºñÆ®¾¿ Ç¥ÇöÇϹǷΠ3À¸·Î ³ª´« ´ÙÀ½ // 4¸¦ °öÇÏ¸é µÈ´Ù. 3À¸·Î ³ª´©¾î ¶³¾îÁöÁö ¾ÊÀ¸¸é 4 ¹ÙÀÌÆ®°¡ Ãß°¡µÈ´Ù. // (Base64´Â µÞ ºÎºÐÀ» =À¸·Î ä¿ö Àüü ±æÀ̸¦ 4ÀÇ ¹è¼ö·Î ¸¸µç´Ù.) char[] output = new char[((bytes.length - 1) / 3 + 1) * 4]; int outIndex = 0; int i = 0; while ((i + 3) <= bytes.length) { // 3 ¹ÙÀÌÆ®(24ºñÆ®)¸¦ 6ºñÆ®¾¿ ÀÚ¸¥ ´ÙÀ½ // ÇØ´çÇÏ´Â °ªÀ» ¾ËÆĺª Å×ÀÌºí¿¡¼­ ã´Â´Ù. // ù¹ø° 6ºñÆ® (6ºñÆ®) sixbit = (bytes[i] & 0xFC) >> 2; output[outIndex++] = alphabet[sixbit]; // µÎ¹ø° 6ºñÆ® (2ºñÆ®, 4ºñÆ®) sixbit = ((bytes[i] & 0x3) << 4) + ((bytes[i + 1] & 0xF0) >> 4); output[outIndex++] = alphabet[sixbit]; // ¼¼¹ø° 6ºñÆ® (4ºñÆ®, 2ºñÆ®) sixbit = ((bytes[i + 1] & 0xF) << 2) + ((bytes[i + 2] & 0xC0) >> 6); output[outIndex++] = alphabet[sixbit]; // ³×¹ø° 6ºñÆ® (6ºñÆ®) sixbit = bytes[i + 2] & 0x3F; output[outIndex++] = alphabet[sixbit]; i += 3; } if (bytes.length - i == 2) { // 2 ¹ÙÀÌÆ®°¡ ³²Àº °æ¿ì ó¸® // 2 ¹ÙÀÌÆ®(16ºñÆ®)¸¦ 6ºñÆ®¾¿ ÀÚ¸¥ ´ÙÀ½ // ÇØ´çÇÏ´Â °ªÀ» ¾ËÆĺª Å×ÀÌºí¿¡¼­ ã´Â´Ù. // ù¹ø° 6ºñÆ® (6ºñÆ®) sixbit = (bytes[i] & 0xFC) >> 2; output[outIndex++] = alphabet[sixbit]; // µÎ¹ø° 6ºñÆ® (2ºñÆ®, 4ºñÆ®) sixbit = ((bytes[i] & 0x3) << 4) + ((bytes[i + 1] & 0xF0) >> 4); output[outIndex++] = alphabet[sixbit]; // ¼¼¹ø° 6ºñÆ® (4ºñÆ®, 0ºñÆ®) sixbit = (bytes[i + 1] & 0xF) << 2; output[outIndex++] = alphabet[sixbit]; // ³×¹ø° 6ºñÆ® (³²´Â ºÎºÐÀº =À¸·Î ä¿î´Ù.) output[outIndex++] = '='; } else if (bytes.length - i == 1) { // 1¹ÙÀÌÆ®°¡ ³²Àº °æ¿ì ó¸® // 1 ¹ÙÀÌÆ®(8ºñÆ®)¸¦ 6ºñÆ®¾¿ ÀÚ¸¥ ´ÙÀ½ // ÇØ´çÇÏ´Â °ªÀ» ¾ËÆĺª Å×ÀÌºí¿¡¼­ ã´Â´Ù. // ù¹ø° 6ºñÆ® (6ºñÆ®) sixbit = (bytes[i] & 0xFC) >> 2; output[outIndex++] = alphabet[sixbit]; // µÎ¹ø° 6ºñÆ® (2ºñÆ®, 0ºñÆ®) sixbit = (bytes[i] & 0x3) << 4; output[outIndex++] = alphabet[sixbit]; // ¼¼¹ø° 6ºñÆ® (³²´Â ºÎºÐÀº =À¸·Î ä¿î´Ù.) output[outIndex++] = '='; // ³×¹ø° 6ºñÆ® (³²´Â ºÎºÐÀº =À¸·Î ä¿î´Ù.) output[outIndex++] = '='; } return output; } /** * Base64·Î ÀÎÄÚµùµÈ ¹®ÀÚ¿­À» ¿ø·¡ÀÇ ¹ÙÀÌÆ® ¹è¿­·Î µÇµ¹¸°´Ù. */ public static byte[] decode(String encoded) { byte[] decoded = null; int decodedLength = (encoded.length() / 4 * 3); int invalid = 0; if (encoded.length() % 4 != 0) { System.err.println("It's not BASE64 encoded string."); return null; } if (encoded.charAt(encoded.length() - 2) == '=') { // ¸¶Áö¸· 2 ¹ÙÀÌÆ®°¡ ¹«È¿ invalid = 2; } else if (encoded.charAt(encoded.length() - 1) == '=') { // ¸¶Áö¸· 1 ¹ÙÀÌÆ®°¡ ¹«È¿ invalid = 1; } decodedLength -= invalid; decoded = new byte[decodedLength]; int i = 0, di = 0; int sixbit0, sixbit1, sixbit2, sixbit3; for (; i < encoded.length() - 4; i += 4) { sixbit0 = decodeTable[encoded.charAt(i)]; sixbit1 = decodeTable[encoded.charAt(i + 1)]; sixbit2 = decodeTable[encoded.charAt(i + 2)]; sixbit3 = decodeTable[encoded.charAt(i + 3)]; decoded[di++] = (byte) ((sixbit0 << 2) + ((sixbit1 & 0x30) >> 4)); decoded[di++] = (byte) (((sixbit1 & 0xF) << 4) + ((sixbit2 & 0x3C) >> 2)); decoded[di++] = (byte) (((sixbit2 & 0x3) << 6) + sixbit3); } // ¸¶Áö¸· »çÀÌŬ switch (invalid) { case 0 : // 3 ¹ÙÀÌÆ® ¸ðµÎ À¯È¿ sixbit0 = decodeTable[encoded.charAt(i)]; sixbit1 = decodeTable[encoded.charAt(i + 1)]; sixbit2 = decodeTable[encoded.charAt(i + 2)]; sixbit3 = decodeTable[encoded.charAt(i + 3)]; decoded[di++] = (byte) ((sixbit0 << 2) + ((sixbit1 & 0x30) >> 4)); decoded[di++] = (byte) (((sixbit1 & 0xF) << 4) + ((sixbit2 & 0x3C) >> 2)); decoded[di++] = (byte) (((sixbit2 & 0x3) << 6) + sixbit3); break; case 1 : // 2 ¹ÙÀÌÆ®¸¸ À¯È¿ sixbit0 = decodeTable[encoded.charAt(i)]; sixbit1 = decodeTable[encoded.charAt(i + 1)]; sixbit2 = decodeTable[encoded.charAt(i + 2)]; decoded[di++] = (byte) ((sixbit0 << 2) + ((sixbit1 & 0x30) >> 4)); decoded[di++] = (byte) (((sixbit1 & 0xF) << 4) + ((sixbit2 & 0x3C) >> 2)); break; case 2 : // 1 ¹ÙÀÌÆ®¸¸ À¯È¿ sixbit0 = decodeTable[encoded.charAt(i)]; sixbit1 = decodeTable[encoded.charAt(i + 1)]; decoded[di++] = (byte) ((sixbit0 << 2) + ((sixbit1 & 0x30) >> 4)); break; } // assert decoded.length == di; return decoded; } /* performance test */ public static void main(String[] args) throws IOException { if (args.length < 1) { System.err.println("Usage : java Base64 "); System.exit(1); } byte[] toEncode = null; char[] encoded = null; String toDecode = null; long now = 0L; BufferedInputStream in = new BufferedInputStream(new FileInputStream(args[0])); ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buffer = new byte[2048]; int nRead = -1; while ((nRead = in.read(buffer)) >= 0) { out.write(buffer, 0, nRead); } toEncode = out.toByteArray(); now = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) { encoded = Base64.encode(toEncode); } now = System.currentTimeMillis() - now; System.out.println("encoding took " + now + " (ms)"); toDecode = new String(encoded); now = System.currentTimeMillis(); byte[] decoded = null; for (int i = 0; i < 1000; i++) { decoded = Base64.decode(toDecode); } now = System.currentTimeMillis() - now; System.out.println("decoding took " + now + " (ms)"); System.out.println("decode ok? " + bytesEquals(decoded, toEncode)); } /** * µÎ ¹ÙÀÌÆ® ¹è¿­ÀÇ ³»¿ëÀÌ °°ÀºÁö ¿©ºÎ¸¦ ºñ±³ */ static boolean bytesEquals(byte[] b1, byte[] b2) { if (b1.length != b2.length) { return false; } for (int i = b1.length - 1; i >= 0; i--) { if (b1[i] != b2[i]) { return false; } } return true; } }