添加Gator aes加解密工具类
authorSnickerSun <scy@incartech.cn>
Sat, 26 Jun 2021 10:49:57 +0000 (18:49 +0800)
committerSnickerSun <scy@incartech.cn>
Sat, 26 Jun 2021 10:49:57 +0000 (18:49 +0800)
app/src/main/java/com/enqualcomm/support/utils/AESCipher.java [new file with mode: 0644]
app/src/main/java/com/ic/Base64/Base64Decoder.java [new file with mode: 0644]
app/src/main/java/com/ic/Base64/Base64Encoder.java [new file with mode: 0644]

diff --git a/app/src/main/java/com/enqualcomm/support/utils/AESCipher.java b/app/src/main/java/com/enqualcomm/support/utils/AESCipher.java
new file mode 100644 (file)
index 0000000..469cc01
--- /dev/null
@@ -0,0 +1,260 @@
+package com.enqualcomm.support.utils;//
+//  https://github.com/WelkinXie/AESCipher-Java
+//
+
+import android.text.TextUtils;
+import android.util.Base64;
+
+import com.ic.Base64.Base64Decoder;
+import com.ic.Base64.Base64Encoder;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import static android.util.Base64.NO_WRAP;
+
+public class AESCipher {
+
+  private final static String HEX = "0123456789ABCDEF";
+  private  static final String CBC_PKCS5_PADDING = "AES/CBC/PKCS5Padding";//AES是加密方式 CBC是工作模式 PKCS5Padding是填充模式
+  private  static final String AES = "AES";//AES 加密
+  private  static final String  SHA1PRNG="SHA1PRNG";//// SHA1PRNG 强随机种子算法, 要区别4.2以上版本的调用方法
+  /*
+   * 生成随机数,可以当做动态的密钥 加密和解密的密钥必须一致,不然将不能解密
+   */
+  public static String generateKey() {
+    try {
+      SecureRandom localSecureRandom = SecureRandom.getInstance(SHA1PRNG);
+      byte[] bytes_key = new byte[20];
+      localSecureRandom.nextBytes(bytes_key);
+      String str_key = toHex(bytes_key);
+      return str_key;
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return null;
+  }
+  // 对密钥进行处理
+  private static byte[] getRawKey(byte[] seed) throws Exception {
+    KeyGenerator kgen = KeyGenerator.getInstance(AES);
+    //for android
+    SecureRandom sr = null;
+    // 在4.2以上版本中,SecureRandom获取方式发生了改变
+    if (android.os.Build.VERSION.SDK_INT >= 17) {
+      sr = SecureRandom.getInstance(SHA1PRNG, "Crypto");
+    } else {
+      sr = SecureRandom.getInstance(SHA1PRNG);
+    }
+    // for Java
+    // secureRandom = SecureRandom.getInstance(SHA1PRNG);
+    sr.setSeed(seed);
+    kgen.init(128, sr); //256 bits or 128 bits,192bits
+    //AES中128位密钥版本有10个加密循环,192比特密钥版本有12个加密循环,256比特密钥版本则有14个加密循环。
+    SecretKey skey = kgen.generateKey();
+    byte[] raw = skey.getEncoded();
+    return raw;
+  }
+
+  /*
+   * 加密
+   */
+  public static String encrypt(String key, String cleartext) {
+    if (TextUtils.isEmpty(cleartext)) {
+      return cleartext;
+    }
+    try {
+      byte[] result = encrypt(key, cleartext.getBytes());
+      return Base64Encoder.encode(result);
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  /*
+   * 加密
+   */
+  private static byte[] encrypt(String key, byte[] clear) throws Exception {
+    byte[] raw = getRawKey(key.getBytes());
+    SecretKeySpec skeySpec = new SecretKeySpec(raw, AES);
+    Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING);
+    cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
+    byte[] encrypted = cipher.doFinal(clear);
+    return encrypted;
+  }
+
+
+  /*
+   * 解密
+   */
+  public static String decrypt(String key, String encrypted) {
+    if (TextUtils.isEmpty(encrypted)) {
+      return encrypted;
+    }
+    try {
+      byte[] enc = Base64Decoder.decodeToBytes(encrypted);
+      byte[] result = decrypt(key, enc);
+      return new String(result);
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  /*
+   * 解密
+   */
+  private static byte[] decrypt(String key, byte[] encrypted) throws Exception {
+    byte[] raw = getRawKey(key.getBytes());
+    SecretKeySpec skeySpec = new SecretKeySpec(raw, AES);
+    Cipher cipher = Cipher.getInstance(CBC_PKCS5_PADDING);
+    cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
+    byte[] decrypted = cipher.doFinal(encrypted);
+    return decrypted;
+  }
+
+  //二进制转字符
+  public static String toHex(byte[] buf) {
+    if (buf == null)
+      return "";
+    StringBuffer result = new StringBuffer(2 * buf.length);
+    for (int i = 0; i < buf.length; i++) {
+      appendHex(result, buf[i]);
+    }
+    return result.toString();
+  }
+
+  private static void appendHex(StringBuffer sb, byte b) {
+    sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f));
+  }
+
+  public void test(){
+
+    int testMaxCount = 1000;//测试的最大数据条数
+    //添加测试数据
+
+    //FastJson生成json数据
+    String jsonData = "JsonUtils.objectToJsonForFastJson(personList)";
+    ICLogger.e( "AES加密前json数据 ---->" + jsonData);
+    ICLogger.e("AES加密前json数据长度 ---->" + jsonData.length());
+
+    //生成一个动态key
+    String secretKey = generateKey();
+    ICLogger.e( "AES动态secretKey ---->" + secretKey);
+
+    //AES加密
+    long start = System.currentTimeMillis();
+    String encryStr = encrypt(secretKey, jsonData);
+    long end = System.currentTimeMillis();
+    ICLogger.e("AES加密耗时 cost time---->" + (end - start));
+    ICLogger.e( "AES加密后json数据 ---->" + encryStr);
+    ICLogger.e("AES加密后json数据长度 ---->" + encryStr.length());
+
+    //AES解密
+    start = System.currentTimeMillis();
+    String decryStr = decrypt(secretKey, encryStr);
+    end = System.currentTimeMillis();
+    ICLogger.e( "AES解密耗时 cost time---->" + (end - start));
+    ICLogger.e("AES解密后json数据 ---->" + decryStr);
+  }
+/****************************************************************/
+//KEY: "1Gator@3Xqaz@LZC"
+//IV: "1234567890123456"
+  private static final String AES_KEY = "1Gator@3Xqaz@LZC";
+  private static final String IV_STRING = "1234567890123456";
+  private static final String charset = "UTF-8";
+
+  public static String aesEncryptStringFixedKey(String content){
+
+    try{
+      return aesEncryptString(content,AES_KEY);
+    }catch(Exception e){
+        ICLogger.e(e.getMessage(),e);
+    }
+    return null;
+  }
+
+  public static String aesDecryptStringFixedKey(String content){
+
+    try{
+      return aesDecryptString(content,AES_KEY);
+    }catch(Exception e){
+      ICLogger.e(e.getMessage(),e);
+    }
+    return null;
+  }
+
+
+  public static String aesEncryptString(String content, String key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
+    byte[] contentBytes = content.getBytes(charset);
+    byte[] keyBytes = key.getBytes(charset);
+    byte[] encryptedBytes = aesEncryptBytes(contentBytes, keyBytes);
+    return Base64.encodeToString(encryptedBytes,NO_WRAP);
+  }
+
+  public static String aesDecryptString(String content, String key) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
+
+    byte[] encryptedBytes = Base64.decode(content,NO_WRAP);
+    byte[] keyBytes = key.getBytes(charset);
+    byte[] decryptedBytes = aesDecryptBytes(encryptedBytes, keyBytes);
+    return new String(decryptedBytes, charset);
+  }
+
+  public static byte[] aesEncryptBytes(byte[] contentBytes, byte[] keyBytes) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
+    return cipherOperation(contentBytes, keyBytes, Cipher.ENCRYPT_MODE);
+  }
+
+  public static byte[] aesDecryptBytes(byte[] contentBytes, byte[] keyBytes) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
+    return cipherOperation(contentBytes, keyBytes, Cipher.DECRYPT_MODE);
+  }
+
+  private static byte[] cipherOperation(byte[] contentBytes, byte[] keyBytes, int mode) throws UnsupportedEncodingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
+    SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
+
+    byte[] initParam = IV_STRING.getBytes(charset);
+    IvParameterSpec ivParameterSpec = new IvParameterSpec(initParam);
+
+    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+    cipher.init(mode, secretKey, ivParameterSpec);
+
+    return cipher.doFinal(contentBytes);
+  }
+  public static void test2(){
+
+    //FastJson生成json数据
+    String jsonData = "JsonUtils.objectToJsonForFastJson(personList)";
+    ICLogger.e( "AES加密前json数据 ---->" + jsonData);
+    ICLogger.e("AES加密前json数据长度 ---->" + jsonData.length());
+
+    //AES加密
+    long start = System.currentTimeMillis();
+    String encryStr = aesEncryptStringFixedKey(jsonData);
+    long end = System.currentTimeMillis();
+    ICLogger.e("AES加密耗时 cost time---->" + (end - start));
+    ICLogger.e( "AES加密后json数据 ---->" + encryStr);
+    ICLogger.e("AES加密后json数据长度 ---->" + encryStr.length());
+
+    //AES解密
+    start = System.currentTimeMillis();
+    String decryStr = aesDecryptStringFixedKey(encryStr);
+    end = System.currentTimeMillis();
+    ICLogger.e( "AES解密耗时 cost time---->" + (end - start));
+    ICLogger.e("AES解密后json数据 ---->" + decryStr);
+
+  }
+  /*******************************************************/
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ic/Base64/Base64Decoder.java b/app/src/main/java/com/ic/Base64/Base64Decoder.java
new file mode 100644 (file)
index 0000000..6363d71
--- /dev/null
@@ -0,0 +1,181 @@
+package com.ic.Base64;\r
+\r
+import android.text.TextUtils;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.FilterInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+\r
+public class Base64Decoder extends FilterInputStream {\r
+\r
+    private static final char[] chars = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',\r
+            'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };\r
+\r
+    // A mapping between char values and six-bit integers\r
+    private static final int[] ints = new int[128];\r
+    static {\r
+        for (int i = 0; i < 64; i++) {\r
+            ints[chars[i]] = i;\r
+        }\r
+    }\r
+\r
+    private int charCount;\r
+    private int carryOver;\r
+\r
+    /***\r
+     * Constructs a new Base64 decoder that reads input from the given\r
+     * InputStream.\r
+     *\r
+     * @param in\r
+     *            the input stream\r
+     */\r
+    private Base64Decoder(InputStream in) {\r
+        super(in);\r
+    }\r
+\r
+    /***\r
+     * Returns the next decoded character from the stream, or -1 if end of\r
+     * stream was reached.\r
+     *\r
+     * @return the decoded character, or -1 if the end of the input stream is\r
+     *         reached\r
+     * @exception IOException\r
+     *                if an I/O error occurs\r
+     */\r
+    public int read() throws IOException {\r
+        // Read the next non-whitespace character\r
+        int x;\r
+        do {\r
+            x = in.read();\r
+            if (x == -1) {\r
+                return -1;\r
+            }\r
+        } while (Character.isWhitespace((char) x));\r
+        charCount++;\r
+\r
+        // The '=' sign is just padding\r
+        if (x == '=') {\r
+            return -1; // effective end of stream\r
+        }\r
+\r
+        // Convert from raw form to 6-bit form\r
+        x = ints[x];\r
+\r
+        // Calculate which character we're decoding now\r
+        int mode = (charCount - 1) % 4;\r
+\r
+        // First char save all six bits, go for another\r
+        if (mode == 0) {\r
+            carryOver = x & 63;\r
+            return read();\r
+        }\r
+        // Second char use previous six bits and first two new bits,\r
+        // save last four bits\r
+        else if (mode == 1) {\r
+            int decoded = ((carryOver << 2) + (x >> 4)) & 255;\r
+            carryOver = x & 15;\r
+            return decoded;\r
+        }\r
+        // Third char use previous four bits and first four new bits,\r
+        // save last two bits\r
+        else if (mode == 2) {\r
+            int decoded = ((carryOver << 4) + (x >> 2)) & 255;\r
+            carryOver = x & 3;\r
+            return decoded;\r
+        }\r
+        // Fourth char use previous two bits and all six new bits\r
+        else if (mode == 3) {\r
+            int decoded = ((carryOver << 6) + x) & 255;\r
+            return decoded;\r
+        }\r
+        return -1; // can't actually reach this line\r
+    }\r
+\r
+    /***\r
+     * Reads decoded data into an array of bytes and returns the actual number\r
+     * of bytes read, or -1 if end of stream was reached.\r
+     *\r
+     * @param buf\r
+     *            the buffer into which the data is read\r
+     * @param off\r
+     *            the start offset of the data\r
+     * @param len\r
+     *            the maximum number of bytes to read\r
+     * @return the actual number of bytes read, or -1 if the end of the input\r
+     *         stream is reached\r
+     * @exception IOException\r
+     *                if an I/O error occurs\r
+     */\r
+    public int read(byte[] buf, int off, int len) throws IOException {\r
+        if (buf.length < (len + off - 1)) {\r
+            throw new IOException("The input buffer is too small: " + len + " bytes requested starting at offset " + off + " while the buffer " + " is only " + buf.length + " bytes long.");\r
+        }\r
+\r
+        // This could of course be optimized\r
+        int i;\r
+        for (i = 0; i < len; i++) {\r
+            int x = read();\r
+            if (x == -1 && i == 0) { // an immediate -1 returns -1\r
+                return -1;\r
+            } else if (x == -1) { // a later -1 returns the chars read so far\r
+                break;\r
+            }\r
+            buf[off + i] = (byte) x;\r
+        }\r
+        return i;\r
+    }\r
+\r
+    /***\r
+     * Returns the decoded form of the given encoded string, as a String. Note\r
+     * that not all binary data can be represented as a String, so this method\r
+     * should only be used for encoded String data. Use decodeToBytes()\r
+     * otherwise.\r
+     *\r
+     * @param encoded\r
+     *            the string to decode\r
+     * @return the decoded form of the encoded string\r
+     */\r
+    public static String decode(String encoded) {\r
+        if (TextUtils.isEmpty(encoded)) {\r
+            return "";\r
+        }\r
+        return new String(decodeToBytes(encoded));\r
+    }\r
+\r
+    /***\r
+     * Returns the decoded form of the given encoded string, as bytes.\r
+     *\r
+     * @param encoded\r
+     *            the string to decode\r
+     * @return the decoded form of the encoded string\r
+     */\r
+    public static byte[] decodeToBytes(String encoded) {\r
+        byte[] bytes = encoded.getBytes();\r
+        Base64Decoder in = new Base64Decoder(new ByteArrayInputStream(bytes));\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream((int) (bytes.length * 0.75));\r
+        try {\r
+            byte[] buf = new byte[4 * 1024]; // 4K buffer\r
+            int bytesRead;\r
+            while ((bytesRead = in.read(buf)) != -1) {\r
+                out.write(buf, 0, bytesRead);\r
+            }\r
+            return out.toByteArray();\r
+        } catch (IOException e) {\r
+            throw new RuntimeException(e);\r
+        } finally {\r
+            try {\r
+                in.close();\r
+            } catch (IOException e) {\r
+                throw new RuntimeException(e);\r
+            }\r
+            try {\r
+                out.close();\r
+            } catch (IOException e) {\r
+                throw new RuntimeException(e);\r
+            }\r
+        }\r
+    }\r
+}\r
+\r
diff --git a/app/src/main/java/com/ic/Base64/Base64Encoder.java b/app/src/main/java/com/ic/Base64/Base64Encoder.java
new file mode 100644 (file)
index 0000000..335b18c
--- /dev/null
@@ -0,0 +1,194 @@
+package com.ic.Base64;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.FilterOutputStream;\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+\r
+public class Base64Encoder extends FilterOutputStream {\r
+\r
+    private static final char[] chars = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',\r
+            'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };\r
+\r
+    private int charCount;\r
+    private int carryOver;\r
+    // 是否每76字节换行\r
+    private boolean isWrapBreak = true;\r
+\r
+    /***\r
+     * Constructs a new Base64 encoder that writes output to the given\r
+     * OutputStream.\r
+     *\r
+     * @param out\r
+     *            the output stream\r
+     */\r
+    private Base64Encoder(OutputStream out) {\r
+        super(out);\r
+    }\r
+\r
+    /***\r
+     * Constructs a new Base64 encoder that writes output to the given\r
+     * OutputStream.\r
+     *\r
+     * @param out\r
+     *            the output stream\r
+     */\r
+    private Base64Encoder(OutputStream out, boolean isWrapBreak) {\r
+        this(out);\r
+        this.isWrapBreak = isWrapBreak;\r
+    }\r
+\r
+    /***\r
+     * Writes the given byte to the output stream in an encoded form.\r
+     *\r
+     * @exception IOException\r
+     *                if an I/O error occurs\r
+     */\r
+    public void write(int b) throws IOException {\r
+        // Take 24-bits from three octets, translate into four encoded chars\r
+        // Break lines at 76 chars\r
+        // If necessary, pad with 0 bits on the right at the end\r
+        // Use = signs as padding at the end to ensure encodedLength % 4 == 0\r
+\r
+        // Remove the sign bit,\r
+        // thanks to Christian Schweingruber <chrigu@lorraine.ch>\r
+        if (b < 0) {\r
+            b += 256;\r
+        }\r
+\r
+        // First byte use first six bits, save last two bits\r
+        if (charCount % 3 == 0) {\r
+            int lookup = b >> 2;\r
+            carryOver = b & 3; // last two bits\r
+            out.write(chars[lookup]);\r
+        }\r
+        // Second byte use previous two bits and first four new bits,\r
+        // save last four bits\r
+        else if (charCount % 3 == 1) {\r
+            int lookup = ((carryOver << 4) + (b >> 4)) & 63;\r
+            carryOver = b & 15; // last four bits\r
+            out.write(chars[lookup]);\r
+        }\r
+        // Third byte use previous four bits and first two new bits,\r
+        // then use last six new bits\r
+        else if (charCount % 3 == 2) {\r
+            int lookup = ((carryOver << 2) + (b >> 6)) & 63;\r
+            out.write(chars[lookup]);\r
+            lookup = b & 63; // last six bits\r
+            out.write(chars[lookup]);\r
+            carryOver = 0;\r
+        }\r
+        charCount++;\r
+\r
+        // Add newline every 76 output chars (that's 57 input chars)\r
+        if (this.isWrapBreak && charCount % 57 == 0) {\r
+            out.write('\n');\r
+        }\r
+    }\r
+\r
+    /***\r
+     * Writes the given byte array to the output stream in an encoded form.\r
+     *\r
+     * @param buf\r
+     *            the data to be written\r
+     * @param off\r
+     *            the start offset of the data\r
+     * @param len\r
+     *            the length of the data\r
+     * @exception IOException\r
+     *                if an I/O error occurs\r
+     */\r
+    public void write(byte[] buf, int off, int len) throws IOException {\r
+        // This could of course be optimized\r
+        for (int i = 0; i < len; i++) {\r
+            write(buf[off + i]);\r
+        }\r
+    }\r
+\r
+    /***\r
+     * Closes the stream, this MUST be called to ensure proper padding is\r
+     * written to the end of the output stream.\r
+     *\r
+     * @exception IOException\r
+     *                if an I/O error occurs\r
+     */\r
+    public void close() throws IOException {\r
+        // Handle leftover bytes\r
+        if (charCount % 3 == 1) { // one leftover\r
+            int lookup = (carryOver << 4) & 63;\r
+            out.write(chars[lookup]);\r
+            out.write('=');\r
+            out.write('=');\r
+        } else if (charCount % 3 == 2) { // two leftovers\r
+            int lookup = (carryOver << 2) & 63;\r
+            out.write(chars[lookup]);\r
+            out.write('=');\r
+        }\r
+        super.close();\r
+    }\r
+\r
+    /***\r
+     * Returns the encoded form of the given unencoded string.<br>\r
+     * 默认是否每76字节换行\r
+     *\r
+     * @param bytes\r
+     *            the bytes to encode\r
+     * @return the encoded form of the unencoded string\r
+     * @throws IOException\r
+     */\r
+    public static String encode(byte[] bytes) {\r
+        return encode(bytes, true);\r
+    }\r
+\r
+    /***\r
+     * Returns the encoded form of the given unencoded string.\r
+     *\r
+     * @param bytes\r
+     *            the bytes to encode\r
+     * @param isWrapBreak\r
+     *            是否每76字节换行\r
+     * @return the encoded form of the unencoded string\r
+     * @throws IOException\r
+     */\r
+    public static String encode(byte[] bytes, boolean isWrapBreak) {\r
+        ByteArrayOutputStream out = new ByteArrayOutputStream((int) (bytes.length * 1.4));\r
+        Base64Encoder encodedOut = new Base64Encoder(out, isWrapBreak);\r
+        try {\r
+            encodedOut.write(bytes);\r
+        } catch (IOException e) {\r
+            throw new RuntimeException(e);\r
+        } finally {\r
+            try {\r
+                encodedOut.close();\r
+            } catch (IOException e) {\r
+                throw new RuntimeException(e);\r
+            }\r
+        }\r
+        return out.toString();\r
+    }\r
+\r
+    // public static void main(String[] args) throws Exception {\r
+    // if (args.length != 1) {\r
+    // System.err\r
+    // .println("Usage: java com.oreilly.servlet.Base64Encoder fileToEncode");\r
+    // return;\r
+    // }\r
+    // Base64Encoder encoder = null;\r
+    // BufferedInputStream in = null;\r
+    // try {\r
+    // encoder = new Base64Encoder(System.out);\r
+    // in = new BufferedInputStream(new FileInputStream(args[0]));\r
+    //\r
+    // byte[] buf = new byte[4 * 1024]; // 4K buffer\r
+    // int bytesRead;\r
+    // while ((bytesRead = in.read(buf)) != -1) {\r
+    // encoder.write(buf, 0, bytesRead);\r
+    // }\r
+    // } finally {\r
+    // if (in != null)\r
+    // in.close();\r
+    // if (encoder != null)\r
+    // encoder.close();\r
+    // }\r
+    // }\r
+}\r
+\r