I've ported my previous patch to fix bug 6415637 to the current jdk8-tl
forrest.
There are two related changes (quoting from the initial submission):
1. The password and salt expansion resulted in a division by zero for
empty password strings.
2. Practically speaking, there are two different ways of deriving keys
from an empty passphrase: the terminating NUL character is required
by the specification, but is left out by some implementations
(including OpenJDK if the first bug is fixed). OpenSSL tries to
decrypt with both encodings, and the patch implements that as well.
It is difficult to properly implement the retry behavior without
changing any interfaces, so this patch uses "\0" for the password
*without* a NUL terminator. This is a bit confusing, but it ensures
that passing an empty string as the password creates a PKCS#12 store
which is compliant with the specification.
Because of the division of zero issue, the second change does not
actually modify visible behavior.
To my knowledge, there is now an OCA which covers this change.
--
Florian Weimer <[email protected]>
BFK edv-consulting GmbH http://www.bfk.de/
Kriegsstraße 100 tel: +49-721-96201-1
D-76133 Karlsruhe fax: +49-721-96201-99
diff -r 13978750cb87 src/share/classes/com/sun/crypto/provider/PBEKey.java
--- a/src/share/classes/com/sun/crypto/provider/PBEKey.java Tue Jan 31 10:31:19 2012 +0000
+++ b/src/share/classes/com/sun/crypto/provider/PBEKey.java Tue Jan 31 14:04:55 2012 +0100
@@ -55,9 +55,12 @@
// Should allow an empty password.
passwd = new char[0];
}
- for (int i=0; i<passwd.length; i++) {
- if ((passwd[i] < '\u0020') || (passwd[i] > '\u007E')) {
- throw new InvalidKeySpecException("Password is not ASCII");
+ // Accept "\0" to signify "zero-length password with no terminator".
+ if (!(passwd.length == 1 && passwd[0] == 0)) {
+ for (int i=0; i<passwd.length; i++) {
+ if ((passwd[i] < '\u0020') || (passwd[i] > '\u007E')) {
+ throw new InvalidKeySpecException("Password is not ASCII");
+ }
}
}
this.key = new byte[passwd.length];
diff -r 13978750cb87 src/share/classes/com/sun/crypto/provider/PKCS12PBECipherCore.java
--- a/src/share/classes/com/sun/crypto/provider/PKCS12PBECipherCore.java Tue Jan 31 10:31:19 2012 +0000
+++ b/src/share/classes/com/sun/crypto/provider/PKCS12PBECipherCore.java Tue Jan 31 14:04:55 2012 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -60,11 +60,15 @@
static byte[] derive(char[] chars, byte[] salt,
int ic, int n, int type) {
- // Add in trailing NULL terminator.
- int length = chars.length*2;
- if (length != 0) {
- length += 2;
- }
+ // Add in trailing NULL terminator. Special case:
+ // no terminator if password is "\0".
+ int length = chars.length*2;
+ if (length == 2 && chars[0] == 0) {
+ chars = new char[0];
+ length = 0;
+ } else
+ length += 2;
+
byte[] passwd = new byte[length];
for (int i = 0, j = 0; i < chars.length; i++, j+=2) {
passwd[j] = (byte) ((chars[i] >>> 8) & 0xFF);
@@ -133,6 +137,8 @@
}
private static void concat(byte[] src, byte[] dst, int start, int len) {
+ if (src.length == 0)
+ return;
int loop = len / src.length;
int off, i;
for (i = 0, off = 0; i < loop; i++, off += src.length)
diff -r 13978750cb87 src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java
--- a/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java Tue Jan 31 10:31:19 2012 +0000
+++ b/src/share/classes/sun/security/pkcs12/PKCS12KeyStore.java Tue Jan 31 14:04:55 2012 +0100
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -253,11 +253,25 @@
}
try {
- // Use JCE
- SecretKey skey = getPBEKey(password);
- Cipher cipher = Cipher.getInstance(algOid.toString());
- cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
- byte[] privateKeyInfo = cipher.doFinal(encryptedKey);
+ byte[] privateKeyInfo;
+ while (true) {
+ try {
+ // Use JCE
+ SecretKey skey = getPBEKey(password);
+ Cipher cipher = Cipher.getInstance(algOid.toString());
+ cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
+ privateKeyInfo = cipher.doFinal(encryptedKey);
+ break;
+ } catch (Exception e) {
+ if (password.length == 0) {
+ // Retry using an empty password
+ // without a NULL terminator.
+ password = new char[1];
+ continue;
+ }
+ throw e;
+ }
+ }
PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(privateKeyInfo);
@@ -1251,17 +1265,25 @@
ObjectIdentifier algOid = in.getOID();
AlgorithmParameters algParams = parseAlgParameters(in);
- try {
- // Use JCE
- SecretKey skey = getPBEKey(password);
- Cipher cipher = Cipher.getInstance(algOid.toString());
- cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
- safeContentsData = cipher.doFinal(safeContentsData);
-
- } catch (Exception e) {
- throw new IOException("failed to decrypt safe"
- + " contents entry: " + e, e);
- }
+ while (true) {
+ try {
+ // Use JCE
+ SecretKey skey = getPBEKey(password);
+ Cipher cipher = Cipher.getInstance(algOid.toString());
+ cipher.init(Cipher.DECRYPT_MODE, skey, algParams);
+ safeContentsData = cipher.doFinal(safeContentsData);
+ break;
+ } catch (Exception e) {
+ if (password.length == 0) {
+ // Retry using an empty password
+ // without a NULL terminator.
+ password = new char[1];
+ continue;
+ }
+ throw new IOException(
+ "failed to decrypt safe contents entry: " + e, e);
+ }
+ }
} else {
throw new IOException("public key protected PKCS12" +
" not supported");
diff -r 13978750cb87 test/sun/security/pkcs12/Bug6415637.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/pkcs12/Bug6415637.java Tue Jan 31 14:04:55 2012 +0100
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6415637
+ * @summary Support PKCS#12 key stores protected with an empty password
+ */
+
+import java.io.ByteArrayInputStream;
+import java.math.BigInteger;
+import java.security.KeyStore;
+import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPrivateCrtKey;
+
+public class Bug6415637 {
+
+ public static void main(String[] args) throws Exception {
+ check(WITH_NULL);
+ check(WITHOUT_NULL);
+ }
+
+ private static void check(String encodedBlob) throws Exception {
+ byte[] blob = new byte[encodedBlob.length() * 2];
+ for (int i = 0; i < blob.length; ) {
+ final char ch = encodedBlob.charAt(i / 2);
+ blob[i++] = (byte) (ch >> 8);
+ blob[i++] = (byte) ch;
+ }
+ KeyStore store = KeyStore.getInstance("PKCS12");
+ store.load(new ByteArrayInputStream(blob), new char[0]);
+ if (!store.aliases().nextElement().equals("test"))
+ throw new Exception("test alias not found");
+ KeyStore.PrivateKeyEntry e =
+ (KeyStore.PrivateKeyEntry) store.getEntry("test",
+ new KeyStore.PasswordProtection(new char[0]));
+ X509Certificate cert = (X509Certificate) e.getCertificateChain()[0];
+ if (!cert.getSubjectDN().toString().equals("CN=Test Key"))
+ throw new Exception("invalid certificate subject DN");
+ RSAPrivateCrtKey key = (RSAPrivateCrtKey) e.getPrivateKey();
+ if (!key.getPublicExponent().equals(BigInteger.valueOf(65537)))
+ throw new Exception("invalid public exponent");
+ }
+
+ private static final String WITH_NULL =
+ "\u3082\u097c\u0201\u0330\u8209\u3606\u092a\u8648\u86f7\u0d01" +
+ "\u0701\ua082\u0927\u0482\u0923\u3082\u091f\u3082\u0564\u0609" +
+ "\u2a86\u4886\uf70d\u0107\u01a0\u8205\u5504\u8205\u5130\u8205" +
+ "\u4d30\u8205\u4906\u0b2a\u8648\u86f7\u0d01\u0c0a\u0102\ua082" +
+ "\u04fa\u3082\u04f6\u3028\u060a\u2a86\u4886\uf70d\u010c\u0103" +
+ "\u301a\u0414\u8317\ucfd6\u89ab\uc03b\u79d6\u4c45\u1b10\uc3bd" +
+ "\u3923\ub806\u0202\u0400\u0482\u04c8\ud51d\uf953\ud79e\u92c6" +
+ "\u83c0\u92dc\ucd05\u69ab\ucc1b\ud538\u2ed9\u7796\ua426\uc14e" +
+ "\udcbf\ue541\u40be\ud264\u3b5b\uf51f\u8e1a\u892f\u2813\ucdd6" +
+ "\uf72e\uef55\u35ef\u4620\ude18\ued5e\ufae0\ubed4\uf84e\u276b" +
+ "\u3596\uc33f\ub251\ue617\u6e00\ua80f\u6c82\u4acd\u7303\u26be" +
+ "\uffb5\u1e49\u5fb1\uf87c\ua873\ue60a\u7415\u655c\u39f1\ucf16" +
+ "\u8f5c\u85c6\u4100\u4130\u565b\u649a\u60d6\u6054\u868d\u7267" +
+ "\u97a8\u8492\uc5a0\udb5e\u2880\udf55\ub0ee\ua641\u8224\u76d2" +
+ "\u9b1e\u2a67\u1e32\ue1fc\u0a77\u435b\u669f\ued00\u6c30\u963f" +
+ "\u7ee3\uc5c8\u198f\u8ede\u30e1\u015d\u1195\uc850\u3371\ub9e5" +
+ "\u6968\u84c3\ub0e4\u22b7\u2a08\u4a9d\u9166\ua9ba\ud945\u0529" +
+ "\ue1e7\u8aba\ub4ef\u7445\udc9a\ucf73\ud77b\ufafe\ue1df\u3180" +
+ "\u9585\ued73\uca40\u06b0\ufdee\u95ba\u1aa3\ubd67\ua5c1\u84b4" +
+ "\u4b50\uc1e1\u4547\ud837\u21bc\uac0d\u0a65\uebb5\u7281\ud9bc" +
+ "\u2e2c\ua9bc\u7714\u0fc0\uab41\uce09\ud5e8\u5f8c\uc35d\uba6e" +
+ "\u98a2\u95c3\u87ff\uba8c\u056a\udc9f\uf254\u3d38\uf40a\u77dd" +
+ "\u4e30\u01de\ubef7\ud288\ue59c\ua143\ub30b\ud0ae\u63b9\u138b" +
+ "\uc793\u3474\u18ca\udeed\u78d9\u2ae8\u63cc\ua5d1\u6779\u0229" +
+ "\u7b72\ucfd4\ueecb\ue167\u08c0\u7556\u181d\u8d62\uc401\ub092" +
+ "\u8cf5\ued71\uf29f\u843e\u13e1\u7e7b\uf589\u0329\u92bd\ud0e3" +
+ "\u8dcc\u7541\uf195\ueef2\u3f3a\ueb01\uf5b0\u1869\u2216\uf351" +
+ "\u488d\udffb\u6243\u1121\u9447\u8a3a\u006f\u008c\ue2b3\ued31" +
+ "\u7f57\u6492\ue02f\u6f68\u387d\u58c0\uaadf\u2ee3\uf304\u3de9" +
+ "\u9741\u47fa\udde8\ufe8a\u679a\u597d\u8c7c\u9c71\u570e\u1dbd" +
+ "\ud555\ue853\uff63\u0fcb\u4b28\u3691\u33c8\uc31f\uc510\u6cba" +
+ "\ud92c\u6462\u733a\u739e\uc792\ud861\u743e\u3bd3\u006b\u2276" +
+ "\u9fb3\u0a31\u1eb3\ub97e\u4a80\uc076\uaabc\u35a0\u678d\u17c3" +
+ "\ua225\ua77c\u7d9c\uef2d\u83e2\u6996\uba70\uf6f8\u79a1\u9399" +
+ "\uc86b\u1cc5\ub2a5\u02c1\ud676\ua274\u4933\u6c60\u6832\ub0be" +
+ "\u5354\u5af9\uae23\u0963\u722d\u9ad2\u4461\ub768\u2068\u0ccb" +
+ "\u94fd\u88ac\u0f58\u3bc0\u212d\u30c8\u8860\uf7c9\u1dde\ub6b4" +
+ "\u3549\u5bcd\ucf83\u9420\u3a40\u16ad\uc4d7\ucd87\ue73a\ue1c7" +
+ "\u21df\u7f4f\u8659\u9f79\u5b36\uf206\uac66\uc9f3\u6336\u164d" +
+ "\u9046\uf4d5\u285d\ufcd8\ubd55\u1fb9\ua533\u9101\u1e87\uc7b0" +
+ "\u64e9\u3817\u216c\u8d41\uba51\u743a\uc74e\ue4ab\u2820\u972f" +
+ "\ue191\u85b4\u0ea7\ud896\u23cf\u7df5\u1653\ua9f3\ub724\ucbc9" +
+ "\u9738\ud2f8\u464e\ucf12\u99b8\u64e0\uf03b\u8d02\u85a8\uab52" +
+ "\u8da3\uea34\ube99\ue5f8\u2b38\ub082\u399d\ue61e\u64a1\u7f90" +
+ "\u26e9\ueb74\u6107\ufe2f\u82ca\u87a5\u3028\u8e1f\ue859\u61d4" +
+ "\ud26d\u23a9\uaadc\u02a7\u8ab2\u43d4\uf6b9\udf7a\u8935\u45a4" +
+ "\ufad6\ue7e4\u92b7\u35d7\u1044\u8ed3\u74ef\uaaa9\u713f\u6ebc" +
+ "\u1158\u5e5c\u7522\ufe17\ua515\u59a1\u75dd\ue7ac\uafd9\u16a9" +
+ "\u190e\u18fc\uc041\ufc9e\u3e16\u60c4\ufe51\u6d53\ufa52\u4c08" +
+ "\uce2a\ue546\u017b\ud96b\ube18\u8cb9\udd50\ued40\u14b0\u7da1" +
+ "\u2f2c\ubf9d\uc7c7\u1b73\ua155\ucaf8\ue54d\uebb0\u160a\ubd64" +
+ "\u5ef7\ue1cf\u4633\u86c1\ubc91\u839d\ub148\u9f31\uf2b1\ud133" +
+ "\u168f\u9374\u4667\u6aa9\u0482\ua2a6\ub5c0\ud9b7\ua070\uf6bd" +
+ "\u16fe\u0f41\u986b\u3d33\u7cb9\u291d\u24f0\u704a\uc946\u10a2" +
+ "\udbcf\u6c5f\u5a83\u5507\u036e\ube9f\uf60a\u9da8\u72dd\u23c9" +
+ "\u8878\udd67\uf486\u1384\u751f\u4694\uee3c\udc2e\ud5d7\ud99a" +
+ "\u5ee2\u5455\ub82d\u1837\u336d\u5724\u635b\ubd0b\u2e7c\u92be" +
+ "\u2110\u9c0e\u1662\u43f6\u62ae\u32e3\uaea4\u1cc5\uadc0\u7511" +
+ "\u6ad4\u0228\ue399\u5741\u2050\ue31a\u7dc8\uf6db\u67bb\u994a" +
+ "\u5b5a\uaac6\u2210\u95b0\u462e\u0684\u335e\uac36\u7ab9\uab1e" +
+ "\u0b75\u0f05\u74c5\ufcb3\ua0a5\ube7e\u45f8\u92d5\u3399\u7dd6" +
+ "\uf96e\u7e01\u7823\u6690\u231c\u4c47\u2d10\u7e7f\u5eb8\u70dd" +
+ "\u98d2\u6204\u3a92\u3990\u502b\u7cdb\u952a\ufa97\uea3b\ud990" +
+ "\u436f\uf33a\u070d\u2aff\u7497\u2591\u37e4\ua590\ue7ba\u2c1e" +
+ "\u53d9\u73fa\udc53\u944f\ua3a5\u5093\u33a4\uf080\u1193\u37f2" +
+ "\u7642\ub033\u7f90\u9b44\uff89\ue6ef\u81be\u9e6e\u68a4\u5a00" +
+ "\u9232\u4372\u40aa\u2748\u229d\u534d\u316b\u6e89\ufcb7\uff2e" +
+ "\ub654\u1649\ucb13\u3c28\u4940\u43aa\uc07d\u247c\u313c\u3017" +
+ "\u0609\u2a86\u4886\uf70d\u0109\u1431\u0a1e\u0800\u7400\u6500" +
+ "\u7300\u7430\u2106\u092a\u8648\u86f7\u0d01\u0915\u3114\u0412" +
+ "\u5469\u6d65\u2031\u3330\u3833\u3132\u3234\u3236\u3437\u3082" +
+ "\u03b3\u0609\u2a86\u4886\uf70d\u0107\u06a0\u8203\ua430\u8203" +
+ "\ua002\u0100\u3082\u0399\u0609\u2a86\u4886\uf70d\u0107\u0130" +
+ "\u2806\u0a2a\u8648\u86f7\u0d01\u0c01\u0630\u1a04\u14de\ud8d8" +
+ "\ua792\uf9d9\u6875\ua51d\u98ec\udf03\uc2b6\u5100\u8a02\u0204" +
+ "\u0080\u8203\u6074\ub909\u3c60\ua522\ue4ac\u0f60\u2396\u7baa" +
+ "\ud208\ub76c\u89a5\ue4ef\u205d\u2062\u4a5b\ua684\uceae\u01b9" +
+ "\u1e7a\u6e03\ud996\u555a\u615b\uba70\u406f\u80a9\u901e\ua947" +
+ "\u5b8f\u73f3\udea3\ud8b1\u9782\uac87\u231a\udcd2\u3ef0\u3a17" +
+ "\u4092\u509f\u0e79\u4cd7\u8516\u5111\uebe1\u86e0\uc548\u5ffc" +
+ "\u9a99\u11ed\uef13\u17af\u2707\u8984\u8770\u7064\u1943\u1dd3" +
+ "\u45cf\u9f80\u65f8\u9b3e\u1f70\u6bd0\uc726\u5506\ufb20\u6bdc" +
+ "\uba8c\u0b19\ucd01\ud0f0\u7040\udf63\u48a5\udf5f\u6559\u1b33" +
+ "\ubdae\u8183\uc13f\ued10\ud6dc\ud0f0\u6a7f\ubc36\uc7ca\u320f" +
+ "\u50b8\ud422\ufd99\u8843\u65e8\ue201\u843b\u64ee\ub891\u3ba2" +
+ "\uecae\ufda0\u72d6\u8394\u2551\ufc44\u3778\u27c3\u061a\u6d3b" +
+ "\ubd80\ue010\u06df\u39e7\u3d6a\u5ae2\u93fa\u4de4\u938f\u6f27" +
+ "\ufd39\u4380\u60da\uf215\u79d4\uf6f1\ua02f\u959a\ua0ea\u1c38" +
+ "\u80e3\u2744\u7506\u54b3\u77ad\u18ce\ucfec\u555e\u7bbe\u2e2f" +
+ "\u9900\ub2ef\ua5b9\ubdf0\u5e15\ua681\u92c7\u4f86\u2e1a\ub893" +
+ "\u01fc\u01d2\ub674\uff19\u04c3\ua1a0\u2cea\u72e0\ua8f1\u1358" +
+ "\ube79\u7caa\u269d\u728a\ue435\u37bd\u6495\uc106\u8830\u9b17" +
+ "\ue16d\uef78\uae2b\u5313\u1c96\uc0ee\u3098\ud743\ucd1c\u7407" +
+ "\uf4f9\uee72\ub95e\u31e7\u6435\u0173\u0336\u93c5\u8a1b\u05b4" +
+ "\u4359\uc4be\ud92b\u8d21\u83a9\u32b7\u6433\ua9bc\u27c2\ud842" +
+ "\ua4f2\u81c5\ua86c\u2fd2\uba30\u53bd\uc277\u659f\u203b\u60e5" +
+ "\u37f7\u0984\u31c2\u838a\u2107\u5840\u6411\u1b8d\u044e\ub0b6" +
+ "\uf558\ue6d3\u62bb\u5464\uf83a\u4d5b\uf153\u9e18\ua353\ubd05" +
+ "\uf204\ud543\u037d\ue5aa\u473a\ueb13\uac19\u0494\ua08e\u76c3" +
+ "\ufbd7\u9f1c\u8ca9\u57ad\ud218\uc018\u67ac\u0ae9\ub559\ufe38" +
+ "\u5641\uec0c\ue0ee\u606c\u1989\ue5a2\uff09\u8c61\u1386\ueb51" +
+ "\u7cbd\u95cd\u80c5\u3532\u8605\u596d\u4cfd\u7797\u1e82\ud2fe" +
+ "\uad6b\ua16e\ub6cf\u8fce\ud5a9\u207f\u1d0a\udabe\uc3a6\u5633" +
+ "\u2023\u925f\u809f\uee7c\u5362\u5fd9\u8dfc\u6b5f\uc95b\u0ae9" +
+ "\u7b26\u9e5b\u97e8\u9d6e\uaf91\u6d1a\u1d19\ufc27\u0815\uccbc" +
+ "\u83d4\u2ce2\ue06e\u21a1\u88da\u09af\u9671\uc510\uac23\u398d" +
+ "\ubea2\ua9a1\uf0d3\u490d\ub94b\u7ff7\u6636\ub1fa\u9b10\u1be3" +
+ "\u179b\u6a8a\u4a6c\ude1f\u5da7\u7c02\u96ec\u70ac\u5045\udd2c" +
+ "\u9f6d\uc37d\u5ba6\u4895\ue142\u0db9\uf2dc\uba2e\ud054\ud33e" +
+ "\u1ed9\u144b\u5d85\u9156\u3a90\ue8cd\u0a01\u67f5\ua81b\u4f56" +
+ "\u99dd\u4950\ua551\uacdb\udf31\u1f05\u7169\u3231\u0071\u80ec" +
+ "\ua4e9\ud74e\u62cf\u8931\u11f1\uc925\u0319\uabd4\ufb86\u73c2" +
+ "\u1479\u005b\uf05d\u4f8d\u44e4\u942b\ud338\ud05d\u2b3b\uf6f5" +
+ "\udc0d\uf741\u798b\ud8e9\u36a5\u577b\u8a95\ud773\uffcb\u17b3" +
+ "\u7174\u9616\u9b5e\ua577\u983c\u6e7a\u6cc8\u4a04\u042b\u503e" +
+ "\ud744\ub65e\ue5de\ufa24\u8c71\u1127\ud47f\ud290\ufd4c\u5cbb" +
+ "\u0e21\u77fd\u6553\ub82b\ucb49\u41e7\u8e3d\u4539\u925d\u6ba9" +
+ "\uae47\u391c\ua79e\ub6e2\u7142\u7cb3\u02f5\u6495\u7a85\u2dea" +
+ "\u787b\u22b7\u6ec2\uea8d\uf930\u3d30\u2130\u0906\u052b\u0e03" +
+ "\u021a\u0500\u0414\ubfef\u99f5\u0bb0\uc9b3\uf96a\ue267\u6bc0" +
+ "\u0202\u6d78\ub923\u0414\u5500\u095a\u2a04\u2d7e\u708d\u9779" +
+ "\u9bdb\u2c4f\u82f2\uf89f\u0202\u0400";
+
+ private static final String WITHOUT_NULL =
+ "\u3082\u097c\u0201\u0330\u8209\u3606\u092a\u8648\u86f7\u0d01" +
+ "\u0701\ua082\u0927\u0482\u0923\u3082\u091f\u3082\u0564\u0609" +
+ "\u2a86\u4886\uf70d\u0107\u01a0\u8205\u5504\u8205\u5130\u8205" +
+ "\u4d30\u8205\u4906\u0b2a\u8648\u86f7\u0d01\u0c0a\u0102\ua082" +
+ "\u04fa\u3082\u04f6\u3028\u060a\u2a86\u4886\uf70d\u010c\u0103" +
+ "\u301a\u0414\ud258\ubbe7\ub641\ud196\u4969\u3c88\u70f1\u8c97" +
+ "\u95b1\u8bf3\u0202\u0400\u0482\u04c8\u096a\u4686\uf519\u61da" +
+ "\u1b3b\uebfd\u89b1\u044b\u3bd8\u79a7\ud022\ud880\ud173\ucde1" +
+ "\ud2c1\u2c5d\u8ebb\u6bd4\u46db\ub90b\u04b9\ub091\ud1f3\ud468" +
+ "\u3e93\u2c88\uca5a\u1c54\u5342\u1eca\u8565\ubbbd\ua022\u1ead" +
+ "\ud0bb\u1a8c\u69cf\uf0f4\ucbfb\u488a\ube99\uf190\ue01c\ud87d" +
+ "\u78ca\u9e5c\u82f9\u76ad\u811f\u37d0\u272b\u0481\u500c\u0a27" +
+ "\u08d3\ub637\u3e39\u6db1\ubcba\ue354\u6924\ua9d5\u3555\u20d6" +
+ "\u4c6b\u3189\u5f91\u382c\uf351\u4de2\ubade\u2a14\uea84\u16b6" +
+ "\uf7f7\u36de\ubba6\ue952\u5f5d\u8243\u2318\ucf3d\u8ac8\u33d3" +
+ "\u706c\ue3db\u6619\u7935\u7300\u89b3\u0bcd\uca9f\u0333\ua450" +
+ "\u1be1\u3e42\ub465\uced5\ub055\u5843\uf40f\ua0f2\u6fea\u94fa" +
+ "\ua51e\u4b5d\u93c9\ucb2e\u977e\uafd9\u2a2f\u784b\u0320\u5550" +
+ "\u273f\u469f\uc42b\u2ce7\uedea\u4e0d\u54a5\u1a25\u4fac\ue346" +
+ "\u2102\u7ab6\uea86\u554f\u7706\u8a80\uf6dd\u04f8\u3b37\u005a" +
+ "\u4562\u2ef8\u59f9\u32b7\u31c0\ue7dc\ucbde\ue0e1\u2fd9\u0960" +
+ "\u3e7a\ub4e5\u2a58\u1e2b\uef14\u9a44\u5444\u806d\uc475\u12ab" +
+ "\ucc3d\ua03b\ubd52\ubf1c\ua9a6\u58aa\uee8b\u96c2\ud0c9\ua029" +
+ "\u1db4\ub118\u4807\uecaa\ue182\uabb7\ud9ed\u66c5\u2c80\uc6a6" +
+ "\u3f54\ubc73\u2632\ue1b0\u0d74\u001c\u5740\uc74b\ufadb\u25b4" +
+ "\ua10e\u3191\u69e6\u0861\u452b\u955c\uac56\ud3c4\u86b7\u45f8" +
+ "\u777a\uc336\u8cc7\ud471\u76b6\u11d8\ueb84\u14e4\uf44f\uc9ff" +
+ "\u8929\u0d84\ubcfe\u8cc2\u9d07\u94e6\u1cf9\u19b5\u773a\u012d" +
+ "\u0453\u4ff3\u40f2\ub144\ufc80\u571c\u0e13\uf890\u9fed\u2045" +
+ "\u7baf\ufd88\u4920\u2b86\u491d\uecf8\ua5d9\u1e12\u48c7\u2c84" +
+ "\u3fbe\u4df5\u11ce\u7b81\u83fc\u3efa\u697d\u1f3d\u8d81\u01b0" +
+ "\u0bf1\u9012\u697f\u3b25\u3574\u5286\udded\u5be0\u7e92\u0a02" +
+ "\ua486\ud19b\ue0b5\ua05c\u5ac2\u0ad5\u0d04\ua763\ub5c8\uf7e6" +
+ "\u6e77\u2df3\ub9e7\uda30\ufccb\u7642\u5dc1\udf1f\uc922\uff69" +
+ "\u4471\u4749\u937a\ud77d\u7c0d\u917c\uf2ef\u122c\u13b2\u8943" +
+ "\u33aa\uad59\u86e8\u21c4\ueaa0\uf200\ue5f3\u6da0\ue8ef\uce7e" +
+ "\u37b2\u3ddf\u0480\u08fc\uf89a\ud927\u3f5b\u75d3\ubdfe\u6ebd" +
+ "\ufab1\u9f54\u1c20\u625b\u1391\u2af0\u43ba\u4395\udf22\u299e" +
+ "\uf3bf\u7750\u5f68\u0120\u0ee0\u6960\ud939\u621f\uf845\u0025" +
+ "\ue33c\u7ed9\ueadf\u0005\u6306\u7274\u5e67\ucf7a\uf3c6\u7371" +
+ "\u487b\u79d7\u2142\ubc1a\ubfe4\u3536\u15db\ufe23\u4352\u6321" +
+ "\u329d\uc251\u84c8\ufc0e\uc0ca\u5be6\uf530\u0177\ud9cb\ud132" +
+ "\uf752\u3f26\uda90\ud9cf\u2e46\u3e09\u5d9a\u6902\udb3e\ub06c" +
+ "\u722d\uf498\u3e93\u6cae\u43b5\u535a\u1cd1\uf0b2\u8d80\u9e53" +
+ "\ue02e\uf782\u01ce\u5063\u73d1\u5571\uf0e7\ufa22\u7e48\u0c31" +
+ "\u4642\u29fd\udcab\ue8d4\u7a77\u0880\u4855\u88c7\u7aa5\u0d9c" +
+ "\uf8b7\uc91c\u127d\u2dd7\ude53\u9d3f\u132b\u965c\ubc80\udd97" +
+ "\u87bf\ua0e8\ub2a2\u4e1f\u98fd\u72f3\u16ea\uc415\u5be3\ue8df" +
+ "\u5681\u1f11\u4e3a\uac5d\u1684\u6602\ueb14\u0a96\ufcef\uaebf" +
+ "\u1f2e\ud1a9\u435c\uf4e5\ub6b4\uaae2\u8244\u96a4\u0d3a\u752f" +
+ "\uce21\u1bc9\u219e\uf17b\ud95e\ucd12\u1b0a\ucb85\ub0cd\u4ecb" +
+ "\u6bb4\u5f7c\u2a93\ubb24\u9d7c\u6822\u80cd\u3f54\u78ad\u4fde" +
+ "\ud57f\uec1d\ub54c\u0d78\u5946\u84c1\ua9ad\u0dea\u0292\ub279" +
+ "\u1c76\u817e\ub910\ub1fa\ub1c0\u839d\u9eca\u6f83\u8211\u4112" +
+ "\u440c\u4fbd\u6ef2\u897d\udfa9\ude9e\u1aef\u0f21\u26fb\uaca4" +
+ "\u637e\ub072\u264f\ud24d\u9357\uc801\u0b84\u2d34\ueddf\u6063" +
+ "\udc5d\u90dd\u5c62\ufb48\u8c5e\u7c4e\u3bdb\ub590\u7a75\udbd1" +
+ "\udd78\uc8be\u5915\u7c8b\u8874\u578d\u3116\ub65a\uab8e\ud2ef" +
+ "\u5d35\ubf8b\u2828\u8983\ua790\uedcf\u9698\ue023\u5786\u627b" +
+ "\u9037\u1db7\u900e\u1f45\u0001\u7cf8\u14fd\ue437\u0dfd\ucacc" +
+ "\u5edf\u1742\u7f6e\u612a\ud57d\udca0\u73a4\ud601\uc7f0\uca0e" +
+ "\u5a44\u00b4\u233a\u84f2\u95b5\u5f16\uc291\u04fb\u369d\u6b99" +
+ "\ue127\u493f\u66be\u86e9\u9672\u2849\u64a7\u851b\ue420\u8491" +
+ "\ueb07\u6563\uc753\uc28e\ucad5\uec05\u6920\u8955\u5605\u25f6" +
+ "\u6193\ubee5\u7a1a\ub73d\ucc27\uc8ce\u7179\u57c1\u7a2a\u37c1" +
+ "\ua6c7\u2d8a\u4025\uc97d\u8c9d\u7b4b\u1ad5\uc6d7\u50fc\u246d" +
+ "\u91a9\ua55d\u677a\udc83\u04c7\u3e14\u9950\u420c\udf02\u749b" +
+ "\ude88\u5459\u2074\ua4ae\ud12d\uaf60\uba98\u630d\u313c\u3017" +
+ "\u0609\u2a86\u4886\uf70d\u0109\u1431\u0a1e\u0800\u7400\u6500" +
+ "\u7300\u7430\u2106\u092a\u8648\u86f7\u0d01\u0915\u3114\u0412" +
+ "\u5469\u6d65\u2031\u3330\u3833\u3132\u3431\u3138\u3238\u3082" +
+ "\u03b3\u0609\u2a86\u4886\uf70d\u0107\u06a0\u8203\ua430\u8203" +
+ "\ua002\u0100\u3082\u0399\u0609\u2a86\u4886\uf70d\u0107\u0130" +
+ "\u2806\u0a2a\u8648\u86f7\u0d01\u0c01\u0630\u1a04\u14af\ud4f5" +
+ "\u0ff4\u0ede\u0da0\u6cc5\ufd9d\u3502\uae5e\u4cef\u3102\u0204" +
+ "\u0080\u8203\u6028\ua7e6\u088b\u56b6\uf453\u9747\u68ec\uc064" +
+ "\u2254\u693f\u25c5\uaa39\u3d87\uc97c\uc558\u5194\u7553\ude3a" +
+ "\u4575\u9d85\ud843\u2bd0\ua2e8\u244f\u8593\uac84\u54b4\ubdc6" +
+ "\ucea6\uba1a\ud3da\ua510\uee9d\uaf31\ub5c2\u3329\u0fed\u0e08" +
+ "\u426b\u46fe\udcc5\u0979\ua9ed\u3123\u9a50\ud222\u3fc0\u771a" +
+ "\u6f55\u9664\ud56f\u6b03\u6020\u78a4\u63b2\ue35e\u0816\u43a7" +
+ "\u1909\u52e1\u8183\u1b8d\u9f5b\u19e4\uad73\u8461\ucc86\u3b49" +
+ "\u322e\ue9d9\u3c66\uea22\u091e\u6621\ua8bf\u0169\u72d0\u535e" +
+ "\u77dc\u1002\ubded\u7a91\u6cee\u58fa\uc295\uae8e\ue009\uabe9" +
+ "\u6638\ucaea\u8bbf\uca27\udef5\u2881\u72ec\u8aa5\u582b\u9d6e" +
+ "\u26bb\u3c70\u8bd6\uf5ec\u34ae\ua967\u5bb1\u22cb\u4b74\u0e50" +
+ "\u5062\uc6f7\u7cb4\u58a3\uf43d\u57c0\u9654\u2f9c\u9308\u4546" +
+ "\u6f4a\u37fe\u8d5d\u1465\u8621\u4cd8\u68d6\u0456\u96a4\ud3e2" +
+ "\u76d1\u2675\u7654\u7649\u10e9\u9d0e\u8b04\uffb6\u020a\u2eb4" +
+ "\uf24f\u150e\u7f0d\uf41b\u2c76\u538f\uc2df\u79dc\u0472\u1119" +
+ "\uc148\ue2e8\u1820\ucd45\u08a7\u6bcd\u6eb0\ubd0a\ufff4\uec28" +
+ "\u819b\u2adb\uefc8\ue8f7\ue233\u6535\uc938\u9771\u3681\u87cf" +
+ "\u3a24\u4c71\ue1df\u3e19\u259c\uae5b\u27ed\u8a67\uf3e6\u7af0" +
+ "\u48e1\uc542\uc471\ud8f4\ue317\u46e9\u0b4f\uec45\ua1d3\u2b88" +
+ "\u8a22\udda1\u7c1a\u273c\ua0f7\u8bac\u3771\u28d2\u6ef8\u28d2" +
+ "\ud83c\u196f\ue3fd\u9c79\u4305\u01b8\u3490\u0a91\ue4f3\uebc6" +
+ "\u25a2\u7dd2\u72db\u7531\ucfca\u432f\u2beb\uc649\uf9c5\uc533" +
+ "\u9f3a\ua611\u935c\ubca6\ud293\u54d6\u0dd1\u0aff\u82fb\u2d69" +
+ "\u3da0\u3b33\u0986\u45b3\u3353\ub968\u7348\u454f\u9117\ub3dc" +
+ "\ud7af\u06ca\ua34a\u9357\ue22f\uad3d\u4c76\ub386\ua8d7\u2a90" +
+ "\u6d17\u9321\u7b00\u21e4\u1994\u9d18\u6439\u04c8\u8282\ub269" +
+ "\uf786\u75c6\ua505\u983a\ua075\uffa0\ud662\u6ae5\ub126\u96d1" +
+ "\u9e5e\u346b\ub7ee\ub0a3\u4ee8\ud204\u77ec\u2325\u5da8\ua326" +
+ "\ua018\u0fd8\ue50e\u93cc\ucc40\u2d89\u2ffb\u54e0\u091a\u19fd" +
+ "\u45d7\uc0ab\u77a2\u66ae\u794b\u6644\u21c3\ud782\u1e9e\u53e5" +
+ "\u782e\u55e8\ud44e\u93e8\u379e\u5aa8\u353b\u95de\u7bc1\ucaf3" +
+ "\u5223\ub5e9\uacbb\ub86b\u6014\u0626\ue7ad\ufd93\ue43a\ud864" +
+ "\u1e6d\u14b2\ua12a\u94c5\u2ed9\ua7f7\u14f4\u0cbd\uca3b\u7c21" +
+ "\ua85a\uf834\u6c99\ue1aa\u3832\u2515\u8170\u3c93\u7def\u94fe" +
+ "\u9c3d\u4ab0\u73ed\u6c72\u8b94\ua407\uc719\uad1e\u6306\u4167" +
+ "\u921e\uae53\u3fd4\uf569\u6f0b\u82b0\u0ca6\ud61f\ud526\u23c9" +
+ "\u168d\u4baf\ucc4f\ud8a2\uc64a\ud649\u55e3\u7019\u8f20\u680c" +
+ "\u5581\u2cb1\ub3a4\u3e37\u5fd3\ua3ca\uc115\u979c\uf910\u3797" +
+ "\u05cb\u51d6\u74a4\uc5c0\u597b\uf27f\ud5e2\ue8ac\u4f3d\uc0c3" +
+ "\u9594\u7799\u6876\ub1a3\u059a\uff03\uc2ee\uc8c2\uf224\u3720" +
+ "\u9177\uabdb\u9202\u18d8\uffbe\u0516\u2a76\uedb5\ufe9e\u6d65" +
+ "\u4c35\ue4cb\u75aa\u02be\ud24c\ua482\ufc67\ue4f9\u70c7\u3567" +
+ "\ufc3f\uaa89\ue80a\u6507\u0a65\u4e18\uf919\u071d\u423c\u1756" +
+ "\u30e5\u37f3\u19b3\u10fb\u6c30\u3d30\u2130\u0906\u052b\u0e03" +
+ "\u021a\u0500\u0414\ufd05\u4444\ud347\u673c\u6da4\udb7c\u0733" +
+ "\ud7bf\ud263\uc6b2\u0414\udd17\u155e\u2d4c\u25cb\ua028\u1a23" +
+ "\ub8b0\uf6be\u925f\ude3a\u0202\u0400";
+
+}