From 44a0d8222f27eef7b848316ef136dc539f53c51f Mon Sep 17 00:00:00 2001 From: Daniel Fisher Date: Thu, 3 Dec 2015 16:35:27 -0500 Subject: [PATCH] Use Java 8 base64 encoder and decoder. Use StandardCharsets.UTF_8 where possible. Fixes #59 --- core/src/main/java/org/ldaptive/Credential.java | 17 +- core/src/main/java/org/ldaptive/LdapUtils.java | 17 +- .../ldaptive/ad/io/UnicodePwdValueTranscoder.java | 9 +- .../java/org/ldaptive/asn1/OctetStringType.java | 13 +- core/src/main/java/org/ldaptive/io/Base64.java | 788 --------------------- core/src/main/java/org/ldaptive/io/Hex.java | 6 +- .../test/java/org/ldaptive/LdapAttributeTest.java | 15 +- core/src/test/java/org/ldaptive/io/Base64Test.java | 38 +- core/src/test/java/org/ldaptive/io/HexTest.java | 13 +- 9 files changed, 56 insertions(+), 860 deletions(-) delete mode 100644 core/src/main/java/org/ldaptive/io/Base64.java diff --git a/core/src/main/java/org/ldaptive/Credential.java b/core/src/main/java/org/ldaptive/Credential.java index f650eb8..e07a585 100644 --- a/core/src/main/java/org/ldaptive/Credential.java +++ b/core/src/main/java/org/ldaptive/Credential.java @@ -1,7 +1,7 @@ /* See LICENSE for licensing and NOTICE for copyright. */ package org.ldaptive; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** * Provides convenience methods for converting the various types of passwords into a byte array. @@ -11,9 +11,6 @@ public class Credential { - /** UTF-8 character set. */ - private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); - /** Credential stored as a byte array. */ private final byte[] bytes; @@ -25,7 +22,7 @@ */ public Credential(final String password) { - bytes = password.getBytes(UTF8_CHARSET); + bytes = password.getBytes(StandardCharsets.UTF_8); } @@ -36,7 +33,7 @@ public Credential(final String password) */ public Credential(final char[] password) { - bytes = new String(password).getBytes(UTF8_CHARSET); + bytes = new String(password).getBytes(StandardCharsets.UTF_8); } @@ -69,7 +66,7 @@ public Credential(final byte[] password) */ public String getString() { - return new String(bytes, UTF8_CHARSET); + return new String(bytes, StandardCharsets.UTF_8); } @@ -87,6 +84,10 @@ public String getString() @Override public String toString() { - return String.format("[%s@%d::bytes=%s]", getClass().getName(), hashCode(), new String(bytes, UTF8_CHARSET)); + return String.format( + "[%s@%d::bytes=%s]", + getClass().getName(), + hashCode(), + new String(bytes, StandardCharsets.UTF_8)); } } diff --git a/core/src/main/java/org/ldaptive/LdapUtils.java b/core/src/main/java/org/ldaptive/LdapUtils.java index ac85f32..ac71934 100644 --- a/core/src/main/java/org/ldaptive/LdapUtils.java +++ b/core/src/main/java/org/ldaptive/LdapUtils.java @@ -7,13 +7,13 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Base64; import java.util.Collection; import java.util.List; import java.util.Queue; import java.util.regex.Pattern; -import org.ldaptive.io.Base64; import org.ldaptive.io.Hex; /** @@ -24,9 +24,6 @@ public final class LdapUtils { - /** UTF-8 character set. */ - private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); - /** Size of buffer in bytes to use when reading files. */ private static final int READ_BUFFER_SIZE = 128; @@ -66,7 +63,7 @@ private LdapUtils() {} */ public static String base64Encode(final byte[] value) { - return value != null ? new String(Base64.encodeToByte(value, false), UTF8_CHARSET) : null; + return value != null ? new String(Base64.getEncoder().encode(value), StandardCharsets.UTF_8) : null; } @@ -79,7 +76,7 @@ public static String base64Encode(final byte[] value) */ public static String base64Encode(final String value) { - return value != null ? base64Encode(value.getBytes(UTF8_CHARSET)) : null; + return value != null ? base64Encode(value.getBytes(StandardCharsets.UTF_8)) : null; } @@ -92,7 +89,7 @@ public static String base64Encode(final String value) */ public static String utf8Encode(final byte[] value) { - return value != null ? new String(value, UTF8_CHARSET) : null; + return value != null ? new String(value, StandardCharsets.UTF_8) : null; } @@ -105,7 +102,7 @@ public static String utf8Encode(final byte[] value) */ public static byte[] utf8Encode(final String value) { - return value != null ? value.getBytes(UTF8_CHARSET) : null; + return value != null ? value.getBytes(StandardCharsets.UTF_8) : null; } @@ -208,7 +205,7 @@ public static String percentEncode(final String value) */ public static byte[] base64Decode(final String value) { - return value != null ? Base64.decode(value.getBytes()) : null; + return value != null ? Base64.getDecoder().decode(value.getBytes()) : null; } diff --git a/core/src/main/java/org/ldaptive/ad/io/UnicodePwdValueTranscoder.java b/core/src/main/java/org/ldaptive/ad/io/UnicodePwdValueTranscoder.java index bb8bf31..56a98de 100644 --- a/core/src/main/java/org/ldaptive/ad/io/UnicodePwdValueTranscoder.java +++ b/core/src/main/java/org/ldaptive/ad/io/UnicodePwdValueTranscoder.java @@ -1,7 +1,7 @@ /* See LICENSE for licensing and NOTICE for copyright. */ package org.ldaptive.ad.io; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import org.ldaptive.io.AbstractBinaryValueTranscoder; /** @@ -12,14 +12,11 @@ public class UnicodePwdValueTranscoder extends AbstractBinaryValueTranscoder { - /** UTF-16LE character set. */ - private static final Charset UTF_16LE = Charset.forName("UTF-16LE"); - @Override public String decodeBinaryValue(final byte[] value) { - final String pwd = new String(value, UTF_16LE); + final String pwd = new String(value, StandardCharsets.UTF_16LE); if (pwd.length() < 2) { throw new IllegalArgumentException("unicodePwd must be at least 2 characters long"); } @@ -35,7 +32,7 @@ public String decodeBinaryValue(final byte[] value) } final String pwd = String.format("\"%s\"", value); - return pwd.getBytes(UTF_16LE); + return pwd.getBytes(StandardCharsets.UTF_16LE); } diff --git a/core/src/main/java/org/ldaptive/asn1/OctetStringType.java b/core/src/main/java/org/ldaptive/asn1/OctetStringType.java index 9523417..d207055 100644 --- a/core/src/main/java/org/ldaptive/asn1/OctetStringType.java +++ b/core/src/main/java/org/ldaptive/asn1/OctetStringType.java @@ -2,7 +2,7 @@ package org.ldaptive.asn1; import java.nio.ByteBuffer; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** * Converts strings to and from their DER encoded format. @@ -12,9 +12,6 @@ public class OctetStringType extends AbstractDERType implements DEREncoder { - /** Character set for this string type. */ - private static final Charset CHARSET = Charset.forName("UTF-8"); - /** String to encode. */ private final byte[] derItem; @@ -26,7 +23,7 @@ */ public OctetStringType(final String item) { - this(item.getBytes(CHARSET)); + this(item.getBytes(StandardCharsets.UTF_8)); } @@ -52,7 +49,7 @@ public OctetStringType(final byte[] item) */ public OctetStringType(final DERTag tag, final String item) { - this(tag, item.getBytes(CHARSET)); + this(tag, item.getBytes(StandardCharsets.UTF_8)); } @@ -92,7 +89,7 @@ public OctetStringType(final DERTag tag, final byte[] item) */ public static String decode(final ByteBuffer encoded) { - return new String(readBuffer(encoded), CHARSET); + return new String(readBuffer(encoded), StandardCharsets.UTF_8); } @@ -105,6 +102,6 @@ public static String decode(final ByteBuffer encoded) */ public static byte[] toBytes(final String s) { - return s.getBytes(CHARSET); + return s.getBytes(StandardCharsets.UTF_8); } } diff --git a/core/src/main/java/org/ldaptive/io/Base64.java b/core/src/main/java/org/ldaptive/io/Base64.java deleted file mode 100644 index 81d31a4..0000000 --- a/core/src/main/java/org/ldaptive/io/Base64.java +++ /dev/null @@ -1,788 +0,0 @@ -/** - * A very fast and memory efficient class to encode and decode to and from - * BASE64 in full accordance with RFC 2045. - *

- * - * On Windows XP sp1 with 1.4.2_04 and later ;), this encoder and decoder is - * about 10 times faster on small arrays (10 - 1000 bytes) and 2-3 times as fast - * on larger arrays (10000 - 1000000 bytes) compared to - * sun.misc.Encoder()/Decoder(). - *

- * - * On byte arrays the encoder is about 20% faster than Jakarta Commons Base64 - * Codec for encode and about 50% faster for decoding large arrays. This - * implementation is about twice as fast on very small arrays (< 30 bytes). If - * source/destination is a String this version is about three times - * as fast due to the fact that the Commons Codec result has to be recoded to a - * String from byte[], which is very expensive. - *

- * - * This encode/decode algorithm doesn't create any temporary arrays as many - * other codecs do, it only allocates the resulting array. This produces less - * garbage and it is possible to handle arrays twice as large as algorithms that - * create a temporary array. (E.g. Jakarta Commons Codec). It is unknown whether - * Sun's sun.misc.Encoder()/Decoder() produce temporary arrays but - * since performance is quite low it probably does. - *

- * - * The encoder produces the same output as the Sun one except that the Sun's - * encoder appends a trailing line separator if the last character isn't a pad. - * Unclear why but it only adds to the length and is probably a side effect. - * Both are in conformance with RFC 2045 though. - *
- * Commons codec seem to always add a trailing line separator.

- * - * Note! - * The encode/decode method pairs (types) come in three versions with the - * exact same algorithm and thus a lot of code redundancy. This is to not - * create any temporary arrays for transcoding to/from different format types. - * The methods not used can simply be commented out. - *

- * - * There is also a "fast" version of all decode methods that works the same way - * as the normal ones, but has a few demands on the decoded input. Normally - * though, these fast versions should be used if the source if the input is - * known and it hasn't bee tampered with. - *

- * - * If you find the code useful or you find a bug, please send me a note at - * base64 @ miginfocom . com. - * - * Licence (BSD): - * ============== - * - * Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (base64 @ miginfocom . 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: - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. 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. Neither the name of the MiG InfoCom AB nor the names - * of its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS 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 THE COPYRIGHT OWNER OR 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. - * - * @author Mikael Grev - * Date: 2004-aug-02 - * Time: 11:31:11 - */ -package org.ldaptive.io; - -import java.util.Arrays; - -/** - * Utility for base64 encoding and decoding. Adapted from the public domain - * implementation found at http://migbase64.sourceforge.net/. - * - * @author Mikael Grev - */ -// CheckStyle:MagicNumber OFF -// CheckStyle:ReturnCount OFF -public final class Base64 -{ - - /** Base64 characters. */ - private static final char[] CA = new char[] { - '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', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', - }; - - /** Decode table which stores characters base64 characters. */ - private static final int[] IA = new int[256]; - - /** Initialize the decode table. */ - static { - Arrays.fill(IA, -1); - for (int i = 0; i < CA.length; i++) { - IA[CA[i]] = i; - } - IA['='] = 0; - } - - - /** Default constructor. */ - private Base64() {} - - - /** - * Encodes a raw byte array into a BASE64 char[] representation - * in accordance with RFC 2045. - * - * @param sArr The bytes to convert. If null or length 0 an - * empty array will be returned. - * @param lineSep Optional "\r\n" after 76 characters, unless end of file. - *
- * No line separator will be in breach of RFC 2045 which specifies max 76 per - * line but will be a little faster. - * - * @return A BASE64 encoded array. Never null. - */ - public static char[] encodeToChar(final byte[] sArr, final boolean lineSep) - { - // Check special case - final int sLen = sArr != null ? sArr.length : 0; - if (sLen == 0) { - return new char[0]; - } - - // Length of even 24-bits. - final int eLen = (sLen / 3) * 3; - // Returned character count - final int cCnt = ((sLen - 1) / 3 + 1) << 2; - // Length of returned array - final int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); - final char[] dArr = new char[dLen]; - - // Encode even 24-bits - for (int s = 0, d = 0, cc = 0; s < eLen;) { - // Copy next three bytes into lower 24 bits of int, paying attention to - // sign. - final int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | - (sArr[s++] & 0xff); - - // Encode the int into four chars - dArr[d++] = CA[(i >>> 18) & 0x3f]; - dArr[d++] = CA[(i >>> 12) & 0x3f]; - dArr[d++] = CA[(i >>> 6) & 0x3f]; - dArr[d++] = CA[i & 0x3f]; - - // Add optional line separator - if (lineSep && ++cc == 19 && d < dLen - 2) { - dArr[d++] = '\r'; - dArr[d++] = '\n'; - cc = 0; - } - } - - // Pad and encode last bits if source isn't even 24 bits. - // 0 - 2. - final int left = sLen - eLen; - if (left > 0) { - // Prepare the int - final int i = ((sArr[eLen] & 0xff) << 10) | - (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); - - // Set last four chars - dArr[dLen - 4] = CA[i >> 12]; - dArr[dLen - 3] = CA[(i >>> 6) & 0x3f]; - dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '='; - dArr[dLen - 1] = '='; - } - return dArr; - } - - - /** - * Decodes a BASE64 encoded char array. All illegal characters will be ignored - * and can handle both arrays with and without line separators. - * - * @param sArr The source array. null or length 0 will return - * an empty array. - * - * @return The decoded array of bytes. May be of length 0. - * - * @throws IllegalArgumentException if the legal characters (including '=') - * isn't dividable by 4. (I.e. definitely corrupted). - */ - public static byte[] decode(final char[] sArr) - { - // Check special case - final int sLen = sArr != null ? sArr.length : 0; - if (sLen == 0) { - return new byte[0]; - } - - // Count illegal characters (including '\r', '\n') to know what size the - // returned array will be, - // so we don't have to reallocate & copy it later. - // Number of separator characters. (Actually illegal - // characters, but that's a bonus...) - int sepCnt = 0; - - // If input is "pure" (I.e. no line - // separators or illegal chars) base64 this - // loop can be commented out. - for (int i = 0; i < sLen; i++) { - if (IA[sArr[i]] < 0) { - sepCnt++; - } - } - - // Check so that legal chars (including '=') are evenly dividable by 4 as - // specified in RFC 2045. - if ((sLen - sepCnt) % 4 != 0) { - throw new IllegalArgumentException( - String.format( - "Cannot decode, '%s' not dividable by 4", String.valueOf(sArr))); - } - - int pad = 0; - for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0;) { - if (sArr[i] == '=') { - pad++; - } - } - - final int len = ((sLen - sepCnt) * 6 >> 3) - pad; - - // Preallocate byte[] of exact length - final byte[] dArr = new byte[len]; - - for (int s = 0, d = 0; d < len;) { - // Assemble three bytes into an int from four "valid" characters. - int i = 0; - // j only increased if a valid char was found - for (int j = 0; j < 4; j++) { - - final int c = IA[sArr[s++]]; - if (c >= 0) { - i |= c << (18 - j * 6); - } else { - j--; - } - } - // Add the bytes - dArr[d++] = (byte) (i >> 16); - if (d < len) { - dArr[d++] = (byte) (i >> 8); - if (d < len) { - dArr[d++] = (byte) i; - } - } - } - return dArr; - } - - - /** - * Decodes a BASE64 encoded char array that is known to be reasonably well - * formatted. The method is about twice as fast as {@link #decode(char[])}. - * The preconditions are:
- * + The array must have a line length of 76 chars OR no line separators at - * all (one line).
- * + Line separator must be "\r\n", as specified in RFC 2045 + The array must - * not contain illegal characters within the encoded string
- * + The array CAN have illegal characters at the beginning and end, those - * will be dealt with appropriately.
- * - * @param sArr The source array. Length 0 will return an empty array. - * null will throw an exception. - * - * @return The decoded array of bytes. May be of length 0. - */ - public static byte[] decodeFast(final char[] sArr) - { - // Check special case - final int sLen = sArr.length; - if (sLen == 0) { - return new byte[0]; - } - - int sIx = 0; - // Start and end index after trimming. - int eIx = sLen - 1; - - // Trim illegal chars from start - while (sIx < eIx && IA[sArr[sIx]] < 0) { - sIx++; - } - - // Trim illegal chars from end - while (eIx > 0 && IA[sArr[eIx]] < 0) { - eIx--; - } - - // get the padding count (=) (0, 1 or 2) - // Count '=' at end. - final int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; - // Content count including possible separators - final int cCnt = eIx - sIx + 1; - final int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; - - // The number of decoded bytes - final int len = ((cCnt - sepCnt) * 6 >> 3) - pad; - // Preallocate byte[] of exact length - final byte[] dArr = new byte[len]; - - // Decode all but the last 0 - 2 bytes. - int d = 0; - final int eLen = (len / 3) * 3; - for (int cc = 0; d < eLen;) { - // Assemble three bytes into an int from four "valid" characters. - final int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | - IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; - - // Add the bytes - dArr[d++] = (byte) (i >> 16); - dArr[d++] = (byte) (i >> 8); - dArr[d++] = (byte) i; - - // If line separator, jump over it. - if (sepCnt > 0 && ++cc == 19) { - sIx += 2; - cc = 0; - } - } - - if (d < len) { - // Decode last 1-3 bytes (incl '=') into 1-3 bytes - int i = 0; - for (int j = 0; sIx <= eIx - pad; j++) { - i |= IA[sArr[sIx++]] << (18 - j * 6); - } - - for (int r = 16; d < len; r -= 8) { - dArr[d++] = (byte) (i >> r); - } - } - - return dArr; - } - - - /** - * Encodes a raw byte array into a BASE64 byte[] representation - * in accordance with RFC 2045. - * - * @param sArr The bytes to convert. If null or length 0 an - * empty array will be returned. - * @param lineSep Optional "\r\n" after 76 characters, unless end of file. - *
- * No line separator will be in breach of RFC 2045 which specifies max 76 per - * line but will be a little faster. - * - * @return A BASE64 encoded array. Never null. - */ - public static byte[] encodeToByte(final byte[] sArr, final boolean lineSep) - { - // Check special case - final int sLen = sArr != null ? sArr.length : 0; - if (sLen == 0) { - return new byte[0]; - } - - // Length of even 24-bits. - final int eLen = (sLen / 3) * 3; - // Returned character count - final int cCnt = ((sLen - 1) / 3 + 1) << 2; - // Length of returned array - final int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); - final byte[] dArr = new byte[dLen]; - - // Encode even 24-bits - for (int s = 0, d = 0, cc = 0; s < eLen;) { - // Copy next three bytes into lower 24 bits of int, - // paying attention to sign. - final int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | - (sArr[s++] & 0xff); - - // Encode the int into four chars - dArr[d++] = (byte) CA[(i >>> 18) & 0x3f]; - dArr[d++] = (byte) CA[(i >>> 12) & 0x3f]; - dArr[d++] = (byte) CA[(i >>> 6) & 0x3f]; - dArr[d++] = (byte) CA[i & 0x3f]; - - // Add optional line separator - if (lineSep && ++cc == 19 && d < dLen - 2) { - dArr[d++] = '\r'; - dArr[d++] = '\n'; - cc = 0; - } - } - - // Pad and encode last bits if source isn't an even 24 bits. - // 0 - 2. - final int left = sLen - eLen; - if (left > 0) { - // Prepare the int - final int i = ((sArr[eLen] & 0xff) << 10) | - (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); - - // Set last four chars - dArr[dLen - 4] = (byte) CA[i >> 12]; - dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f]; - dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '='; - dArr[dLen - 1] = '='; - } - return dArr; - } - - - /** - * Decodes a BASE64 encoded byte array. All illegal characters will be ignored - * and can handle both arrays with and without line separators. - * - * @param sArr The source array. Length 0 will return an empty array. - * null will throw an exception. - * - * @return The decoded array of bytes. May be of length 0. - * - * @throws IllegalArgumentException if the legal characters (including '=') - * isn't dividable by 4. (I.e. definitely corrupted). - */ - public static byte[] decode(final byte[] sArr) - { - // Check special case - final int sLen = sArr.length; - - // Count illegal characters (including '\r', '\n')to know what size the - // returned array will be, so we don't have to reallocate & copy it later. - - // Number of separator characters. - // (Actually illegal characters, but that's a bonus...) - int sepCnt = 0; - // If input is "pure" (I.e. no line separators or illegal chars) base64 this - // loop can be commented out. - for (int i = 0; i < sLen; i++) { - if (IA[sArr[i] & 0xff] < 0) { - sepCnt++; - } - } - - // Check so that legal chars (including '=') are evenly dividable by 4 as - // specified in RFC 2045. - if ((sLen - sepCnt) % 4 != 0) { - throw new IllegalArgumentException( - String.format( - "Cannot decode, '%s' not dividable by 4", String.valueOf(sArr))); - } - - int pad = 0; - for (int i = sLen; i > 1 && IA[sArr[--i] & 0xff] <= 0;) { - if (sArr[i] == '=') { - pad++; - } - } - - final int len = ((sLen - sepCnt) * 6 >> 3) - pad; - - // Preallocate byte[] of exact length - final byte[] dArr = new byte[len]; - - for (int s = 0, d = 0; d < len;) { - // Assemble three bytes into an int from four "valid" characters. - int i = 0; - // j only increased if a valid char was found. - for (int j = 0; j < 4; j++) { - final int c = IA[sArr[s++] & 0xff]; - if (c >= 0) { - i |= c << (18 - j * 6); - } else { - j--; - } - } - - // Add the bytes - dArr[d++] = (byte) (i >> 16); - if (d < len) { - dArr[d++] = (byte) (i >> 8); - if (d < len) { - dArr[d++] = (byte) i; - } - } - } - - return dArr; - } - - - /** - * Decodes a BASE64 encoded byte array that is known to be reasonably well - * formatted. The method is about twice as fast as {@link #decode(byte[])}. - * The preconditions are:
- * + The array must have a line length of 76 chars OR no line separators at - * all (one line).
- * + Line separator must be "\r\n", as specified in RFC 2045 + The array must - * not contain illegal characters within the encoded string
- * + The array CAN have illegal characters at the beginning and end, those - * will be dealt with appropriately.
- * - * @param sArr The source array. Length 0 will return an empty array. - * null will throw an exception. - * - * @return The decoded array of bytes. May be of length 0. - */ - public static byte[] decodeFast(final byte[] sArr) - { - // Check special case - final int sLen = sArr.length; - if (sLen == 0) { - return new byte[0]; - } - - int sIx = 0; - // Start and end index after trimming. - int eIx = sLen - 1; - - // Trim illegal chars from start - while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0) { - sIx++; - } - - // Trim illegal chars from end - while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0) { - eIx--; - } - - // get the padding count (=) (0, 1 or 2) - // Count '=' at end. - final int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; - // Content count including possible separators - final int cCnt = eIx - sIx + 1; - final int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0; - - // The number of decoded bytes - final int len = ((cCnt - sepCnt) * 6 >> 3) - pad; - // Preallocate byte[] of exact length - final byte[] dArr = new byte[len]; - - // Decode all but the last 0 - 2 bytes. - int d = 0; - final int eLen = (len / 3) * 3; - for (int cc = 0; d < eLen;) { - // Assemble three bytes into an int from four "valid" characters. - final int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | - IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]]; - - // Add the bytes - dArr[d++] = (byte) (i >> 16); - dArr[d++] = (byte) (i >> 8); - dArr[d++] = (byte) i; - - // If line separator, jump over it. - if (sepCnt > 0 && ++cc == 19) { - sIx += 2; - cc = 0; - } - } - - if (d < len) { - // Decode last 1-3 bytes (incl '=') into 1-3 bytes - int i = 0; - for (int j = 0; sIx <= eIx - pad; j++) { - i |= IA[sArr[sIx++]] << (18 - j * 6); - } - - for (int r = 16; d < len; r -= 8) { - dArr[d++] = (byte) (i >> r); - } - } - - return dArr; - } - - - /** - * Encodes a raw byte array into a BASE64 String representation - * in accordance with RFC 2045. - * - * @param sArr The bytes to convert. If null or length 0 an - * empty array will be returned. - * @param lineSep Optional "\r\n" after 76 characters, unless end of file. - *
- * No line separator will be in breach of RFC 2045 which specifies max 76 per - * line but will be a little faster. - * - * @return A BASE64 encoded array. Never null. - */ - public static String encodeToString(final byte[] sArr, final boolean lineSep) - { - // Reuse char[] since we can't create a String incrementally anyway and - // StringBuffer/Builder would be slower. - return new String(encodeToChar(sArr, lineSep)); - } - - - /** - * Decodes a BASE64 encoded String. All illegal characters will - * be ignored and can handle both strings with and without line separators. - *
- * Note! It can be up to about 2x the speed to call - * decode(str.toCharArray()) instead. That will create a temporary - * array though. This version will use str.charAt(i) to iterate - * the string. - * - * @param str The source string. null or length 0 will return - * an empty array. - * - * @return The decoded array of bytes. May be of length 0. - * - * @throws IllegalArgumentException if the legal characters (including '=') - * isn't dividable by 4. (I.e. definitely corrupted). - */ - public static byte[] decode(final String str) - { - // Check special case - final int sLen = str != null ? str.length() : 0; - if (sLen == 0) { - return new byte[0]; - } - - // Count illegal characters (including '\r', '\n') to know what size the - // returned array will be, - // so we don't have to reallocate & copy it later. - // Number of separator characters. (Actually illegal - // characters, but that's a bonus...) - int sepCnt = 0; - - // If input is "pure" (I.e. no line - // separators or illegal chars) base64 this - // loop can be commented out. - for (int i = 0; i < sLen; i++) { - if (IA[str.charAt(i)] < 0) { - sepCnt++; - } - } - - // Check so that legal chars (including '=') are evenly dividable by 4 as - // specified in RFC 2045. - if ((sLen - sepCnt) % 4 != 0) { - throw new IllegalArgumentException( - String.format("Cannot decode, '%s' not dividable by 4", str)); - } - - // Count '=' at end - int pad = 0; - for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0;) { - if (str.charAt(i) == '=') { - pad++; - } - } - - final int len = ((sLen - sepCnt) * 6 >> 3) - pad; - - // Preallocate byte[] of exact length - final byte[] dArr = new byte[len]; - - for (int s = 0, d = 0; d < len;) { - // Assemble three bytes into an int from four "valid" characters. - int i = 0; - // j only increased if a valid char was found - for (int j = 0; j < 4; j++) { - - final int c = IA[str.charAt(s++)]; - if (c >= 0) { - i |= c << (18 - j * 6); - } else { - j--; - } - } - // Add the bytes - dArr[d++] = (byte) (i >> 16); - if (d < len) { - dArr[d++] = (byte) (i >> 8); - if (d < len) { - dArr[d++] = (byte) i; - } - } - } - return dArr; - } - - - /** - * Decodes a BASE64 encoded string that is known to be reasonably well - * formatted. The method is about twice as fast as {@link #decode(String)}. - * The preconditions are:
- * + The array must have a line length of 76 chars OR no line separators at - * all (one line).
- * + Line separator must be "\r\n", as specified in RFC 2045 + The array must - * not contain illegal characters within the encoded string
- * + The array CAN have illegal characters at the beginning and end, those - * will be dealt with appropriately.
- * - * @param s The source string. Length 0 will return an empty array. - * null will throw an exception. - * - * @return The decoded array of bytes. May be of length 0. - */ - public static byte[] decodeFast(final String s) - { - // Check special case - final int sLen = s.length(); - if (sLen == 0) { - return new byte[0]; - } - - int sIx = 0; - // Start and end index after trimming. - int eIx = sLen - 1; - - // Trim illegal chars from start - while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0) { - sIx++; - } - - // Trim illegal chars from end - while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0) { - eIx--; - } - - // get the padding count (=) (0, 1 or 2) - // Count '=' at end. - final int pad = - s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; - // Content count including possible separators - final int cCnt = eIx - sIx + 1; - final int sepCnt = - sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0; - - // The number of decoded bytes - final int len = ((cCnt - sepCnt) * 6 >> 3) - pad; - // Preallocate byte[] of exact length - final byte[] dArr = new byte[len]; - - // Decode all but the last 0 - 2 bytes. - int d = 0; - final int eLen = (len / 3) * 3; - for (int cc = 0; d < eLen;) { - // Assemble three bytes into an int from four "valid" characters. - final int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | - IA[s.charAt(sIx++)] << 6 | IA[s.charAt(sIx++)]; - - // Add the bytes - dArr[d++] = (byte) (i >> 16); - dArr[d++] = (byte) (i >> 8); - dArr[d++] = (byte) i; - - // If line separator, jump over it. - if (sepCnt > 0 && ++cc == 19) { - sIx += 2; - cc = 0; - } - } - - if (d < len) { - // Decode last 1-3 bytes (incl '=') into 1-3 bytes - int i = 0; - for (int j = 0; sIx <= eIx - pad; j++) { - i |= IA[s.charAt(sIx++)] << (18 - j * 6); - } - - for (int r = 16; d < len; r -= 8) { - dArr[d++] = (byte) (i >> r); - } - } - - return dArr; - } -} -// CheckStyle:ReturnCount ON -// CheckStyle:MagicNumber ON diff --git a/core/src/main/java/org/ldaptive/io/Hex.java b/core/src/main/java/org/ldaptive/io/Hex.java index ef2f5fe..ec0cc60 100644 --- a/core/src/main/java/org/ldaptive/io/Hex.java +++ b/core/src/main/java/org/ldaptive/io/Hex.java @@ -90,11 +90,11 @@ private Hex() {} /** - * This will convert the supplied value to a hex encoded string. Returns null if the supplied value is null. + * This will convert the supplied value from a hex encoded string. Returns null if the supplied value is null. * - * @param value to hex encode + * @param value to hex decode * - * @return hex encoded value + * @return hex decoded value * * @throws IllegalArgumentException if value is not valid hexidecimal */ diff --git a/core/src/test/java/org/ldaptive/LdapAttributeTest.java b/core/src/test/java/org/ldaptive/LdapAttributeTest.java index ec04fc3..c106c82 100644 --- a/core/src/test/java/org/ldaptive/LdapAttributeTest.java +++ b/core/src/test/java/org/ldaptive/LdapAttributeTest.java @@ -1,7 +1,7 @@ /* See LICENSE for licensing and NOTICE for copyright. */ package org.ldaptive; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -16,9 +16,6 @@ public class LdapAttributeTest { - /** UTF-8 charset. */ - private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); - /** Tests default sort behavior. */ @Test(groups = {"bean"}) @@ -118,7 +115,7 @@ public void stringValue() { final LdapAttribute la = new LdapAttribute("cn", "William Wallace"); AssertJUnit.assertEquals("William Wallace", la.getStringValue()); - AssertJUnit.assertEquals("William Wallace".getBytes(UTF8_CHARSET), la.getBinaryValue()); + AssertJUnit.assertEquals("William Wallace".getBytes(StandardCharsets.UTF_8), la.getBinaryValue()); AssertJUnit.assertEquals(1, la.getStringValues().size()); AssertJUnit.assertEquals(1, la.getBinaryValues().size()); AssertJUnit.assertEquals(la, new LdapAttribute("cn", "William Wallace")); @@ -148,8 +145,8 @@ public void stringValues() commonNames.add("William Wallace"); final List binaryCommonNames = new ArrayList<>(); - binaryCommonNames.add("Bill Wallace".getBytes(UTF8_CHARSET)); - binaryCommonNames.add("William Wallace".getBytes(UTF8_CHARSET)); + binaryCommonNames.add("Bill Wallace".getBytes(StandardCharsets.UTF_8)); + binaryCommonNames.add("William Wallace".getBytes(StandardCharsets.UTF_8)); LdapAttribute la = new LdapAttribute(SortBehavior.UNORDERED); la.setName("cn"); @@ -171,7 +168,7 @@ public void stringValues() AssertJUnit.assertEquals("Bill Wallace", la.getStringValue()); AssertJUnit.assertArrayEquals(commonNames.toArray(new String[2]), la.getStringValues().toArray(new String[2])); AssertJUnit.assertEquals(2, la.getStringValues().size()); - AssertJUnit.assertEquals("Bill Wallace".getBytes(UTF8_CHARSET), la.getBinaryValue()); + AssertJUnit.assertEquals("Bill Wallace".getBytes(StandardCharsets.UTF_8), la.getBinaryValue()); AssertJUnit.assertArrayEquals( binaryCommonNames.toArray(new byte[2][0]), la.getBinaryValues().toArray(new byte[2][0])); @@ -186,7 +183,7 @@ public void stringValues() AssertJUnit.assertEquals("Bill Wallace", la.getStringValue()); AssertJUnit.assertArrayEquals(commonNames.toArray(new String[2]), la.getStringValues().toArray(new String[2])); AssertJUnit.assertEquals(2, la.getStringValues().size()); - AssertJUnit.assertEquals("Bill Wallace".getBytes(UTF8_CHARSET), la.getBinaryValue()); + AssertJUnit.assertEquals("Bill Wallace".getBytes(StandardCharsets.UTF_8), la.getBinaryValue()); AssertJUnit.assertArrayEquals( binaryCommonNames.toArray(new byte[2][0]), la.getBinaryValues().toArray(new byte[2][0])); diff --git a/core/src/test/java/org/ldaptive/io/Base64Test.java b/core/src/test/java/org/ldaptive/io/Base64Test.java index 51f38f1..dc436ed 100644 --- a/core/src/test/java/org/ldaptive/io/Base64Test.java +++ b/core/src/test/java/org/ldaptive/io/Base64Test.java @@ -1,7 +1,8 @@ /* See LICENSE for licensing and NOTICE for copyright. */ package org.ldaptive.io; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Base64; import java.util.Scanner; import org.testng.Assert; import org.testng.annotations.DataProvider; @@ -15,9 +16,6 @@ public class Base64Test { - /** UTF-8 character set. */ - private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); - /** * Base64 test data. @@ -30,21 +28,21 @@ return new Object[][] { new Object[] { - "".getBytes(UTF8_CHARSET), + "".getBytes(StandardCharsets.UTF_8), "", }, new Object[] { - "Hello World".getBytes(UTF8_CHARSET), + "Hello World".getBytes(StandardCharsets.UTF_8), "SGVsbG8gV29ybGQ=", }, new Object[] { - "Base64 Encode".getBytes(UTF8_CHARSET), + "Base64 Encode".getBytes(StandardCharsets.UTF_8), "QmFzZTY0IEVuY29kZQ==", }, new Object[] { new Scanner( Base64Test.class.getResourceAsStream( - "/org/ldaptive/io/plaintext.txt")).useDelimiter("\\Z").next().getBytes(UTF8_CHARSET), + "/org/ldaptive/io/plaintext.txt")).useDelimiter("\\Z").next().getBytes(StandardCharsets.UTF_8), new Scanner( Base64Test.class.getResourceAsStream("/org/ldaptive/io/base64-0.txt")).useDelimiter("\\Z").next(), }, @@ -57,22 +55,22 @@ * * @return base64 test data */ - @DataProvider(name = "decode") - public Object[][] createDecodeData() + @DataProvider(name = "decode-mime") + public Object[][] createDecodeMimeData() { return new Object[][] { new Object[] { new Scanner( Base64Test.class.getResourceAsStream( - "/org/ldaptive/io/plaintext.txt")).useDelimiter("\\Z").next().getBytes(UTF8_CHARSET), + "/org/ldaptive/io/plaintext.txt")).useDelimiter("\\Z").next().getBytes(StandardCharsets.UTF_8), new Scanner( Base64Test.class.getResourceAsStream("/org/ldaptive/io/base64-76.txt")).useDelimiter("\\Z").next(), }, new Object[] { new Scanner( Base64Test.class.getResourceAsStream( - "/org/ldaptive/io/plaintext.txt")).useDelimiter("\\Z").next().getBytes(UTF8_CHARSET), + "/org/ldaptive/io/plaintext.txt")).useDelimiter("\\Z").next().getBytes(StandardCharsets.UTF_8), new Scanner( Base64Test.class.getResourceAsStream("/org/ldaptive/io/base64-64.txt")).useDelimiter("\\Z").next(), }, @@ -89,8 +87,8 @@ public Object[][] createInvalidDecode() { return new Object[][] { - new Object[] {"QmFzZTY0IEVuY29kZQ=", }, - new Object[] {"QmFzZTY0IEVuY29kZQ", }, + new Object[] {"QmFzZTY0IEVuY29kZQå", }, + new Object[] {"QmFzZTY0IEVuY29kZQç", }, }; } @@ -105,9 +103,9 @@ public void encodeAndDecode(final byte[] raw, final String encoded) throws Exception { - final String s = new String(Base64.encodeToByte(raw, false), UTF8_CHARSET); + final String s = new String(Base64.getEncoder().encode(raw), StandardCharsets.UTF_8); Assert.assertEquals(encoded, s); - Assert.assertEquals(raw, Base64.decode(s)); + Assert.assertEquals(raw, Base64.getDecoder().decode(s)); } @@ -117,11 +115,11 @@ public void encodeAndDecode(final byte[] raw, final String encoded) * * @throws Exception On test failure. */ - @Test(groups = {"io"}, dataProvider = "decode") - public void decode(final byte[] raw, final String encoded) + @Test(groups = {"io"}, dataProvider = "decode-mime") + public void decodeMime(final byte[] raw, final String encoded) throws Exception { - Assert.assertEquals(raw, Base64.decode(encoded)); + Assert.assertEquals(raw, Base64.getMimeDecoder().decode(encoded)); } @@ -135,7 +133,7 @@ public void decodeException(final String data) throws Exception { try { - Base64.decode(data); + Base64.getDecoder().decode(data); Assert.fail("Should have thrown exception"); } catch (Exception e) { Assert.assertEquals(IllegalArgumentException.class, e.getClass()); diff --git a/core/src/test/java/org/ldaptive/io/HexTest.java b/core/src/test/java/org/ldaptive/io/HexTest.java index 4ca0b41..a5c2df7 100644 --- a/core/src/test/java/org/ldaptive/io/HexTest.java +++ b/core/src/test/java/org/ldaptive/io/HexTest.java @@ -1,7 +1,7 @@ /* See LICENSE for licensing and NOTICE for copyright. */ package org.ldaptive.io; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Scanner; import org.testng.Assert; import org.testng.annotations.DataProvider; @@ -15,9 +15,6 @@ public class HexTest { - /** UTF-8 character set. */ - private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); - /** * Hex test data. @@ -30,21 +27,21 @@ return new Object[][] { new Object[] { - "".getBytes(UTF8_CHARSET), + "".getBytes(StandardCharsets.UTF_8), "", }, new Object[] { - "Hello World".getBytes(UTF8_CHARSET), + "Hello World".getBytes(StandardCharsets.UTF_8), "48656C6C6F20576F726C64", }, new Object[] { - "Hexadecimal Encode".getBytes(UTF8_CHARSET), + "Hexadecimal Encode".getBytes(StandardCharsets.UTF_8), "48657861646563696D616C20456E636F6465", }, new Object[] { new Scanner( HexTest.class.getResourceAsStream( - "/org/ldaptive/io/plaintext.txt")).useDelimiter("\\Z").next().getBytes(UTF8_CHARSET), + "/org/ldaptive/io/plaintext.txt")).useDelimiter("\\Z").next().getBytes(StandardCharsets.UTF_8), new Scanner(HexTest.class.getResourceAsStream("/org/ldaptive/io/hex.txt")).useDelimiter("\\Z").next(), }, };