diff --git a/jdk/src/share/classes/sun/security/krb5/Credentials.java b/jdk/src/share/classes/sun/security/krb5/Credentials.java index 3fde713fd3c..c003a29fa64 100644 --- a/jdk/src/share/classes/sun/security/krb5/Credentials.java +++ b/jdk/src/share/classes/sun/security/krb5/Credentials.java @@ -33,16 +33,11 @@ package sun.security.krb5; import sun.security.krb5.internal.*; import sun.security.krb5.internal.ccache.CredentialsCache; -import java.util.StringTokenizer; import sun.security.krb5.internal.ktab.*; import sun.security.krb5.internal.crypto.EType; import java.io.File; import java.io.IOException; import java.util.Date; -import java.util.Vector; -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; import java.net.InetAddress; /** @@ -378,9 +373,9 @@ public class Credentials { KRBError error = ke.getError(); // update salt in PrincipalName - byte[] newSalt = error.getSalt(); - if (newSalt != null && newSalt.length > 0) { - princ.setSalt(new String(newSalt)); + String newSalt = error.getSalt(); + if (newSalt != null && newSalt.length() > 0) { + princ.setSalt(newSalt); } // refresh keys diff --git a/jdk/src/share/classes/sun/security/krb5/KrbAsReq.java b/jdk/src/share/classes/sun/security/krb5/KrbAsReq.java index c493d405f51..fec6998ce3a 100644 --- a/jdk/src/share/classes/sun/security/krb5/KrbAsReq.java +++ b/jdk/src/share/classes/sun/security/krb5/KrbAsReq.java @@ -1,5 +1,5 @@ /* - * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * Portions Copyright 2000-2009 Sun Microsystems, Inc. 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 @@ -56,7 +56,7 @@ public class KrbAsReq extends KrbKdcReq { private boolean PA_ENC_TIMESTAMP_REQUIRED = false; private boolean pa_exists = false; private int pa_etype = 0; - private byte[] pa_salt = null; + private String pa_salt = null; private byte[] pa_s2kparams = null; // default is address-less tickets @@ -88,7 +88,7 @@ public class KrbAsReq extends KrbKdcReq { * with pre-authentication values */ KrbAsReq(PrincipalName principal, EncryptionKey[] keys, - boolean pa_exists, int etype, byte[] salt, byte[] s2kparams) + boolean pa_exists, int etype, String salt, byte[] s2kparams) throws KrbException, IOException { this(keys, // for pre-authentication pa_exists, etype, salt, s2kparams, // pre-auth values @@ -112,7 +112,7 @@ public class KrbAsReq extends KrbKdcReq { } // update with pre-auth info - public void updatePA(int etype, byte[] salt, byte[] params, PrincipalName name) { + public void updatePA(int etype, String salt, byte[] params, PrincipalName name) { // set the pre-auth values pa_exists = true; pa_etype = etype; @@ -120,9 +120,8 @@ public class KrbAsReq extends KrbKdcReq { pa_s2kparams = params; // update salt in PrincipalName - if (salt != null && salt.length > 0) { - String newSalt = new String(salt); - name.setSalt(newSalt); + if (salt != null && salt.length() > 0) { + name.setSalt(salt); if (DEBUG) { System.out.println("Updated salt from pre-auth = " + name.getSalt()); } @@ -161,7 +160,7 @@ public class KrbAsReq extends KrbKdcReq { char[] password, boolean pa_exists, int etype, - byte[] salt, + String salt, byte[] s2kparams, KDCOptions options, PrincipalName cname, @@ -246,7 +245,7 @@ public class KrbAsReq extends KrbKdcReq { EncryptionKey[] keys, boolean pa_exists, int etype, - byte[] salt, + String salt, byte[] s2kparams, KDCOptions options, PrincipalName cname, diff --git a/jdk/src/share/classes/sun/security/krb5/PrincipalName.java b/jdk/src/share/classes/sun/security/krb5/PrincipalName.java index 3761ffbb469..6705fd63e9a 100644 --- a/jdk/src/share/classes/sun/security/krb5/PrincipalName.java +++ b/jdk/src/share/classes/sun/security/krb5/PrincipalName.java @@ -38,6 +38,7 @@ import java.util.Vector; import java.io.IOException; import java.math.BigInteger; import sun.security.krb5.internal.ccache.CCacheOutputStream; +import sun.security.krb5.internal.util.KerberosString; /** @@ -246,7 +247,7 @@ public class PrincipalName DerValue subSubDer; while(subDer.getData().available() > 0) { subSubDer = subDer.getData().getDerValue(); - v.addElement(subSubDer.getGeneralString()); + v.addElement(new KerberosString(subSubDer).toString()); } if (v.size() > 0) { nameStrings = new String[v.size()]; @@ -554,7 +555,7 @@ public class PrincipalName temp = new DerOutputStream(); DerValue der[] = new DerValue[nameStrings.length]; for (int i = 0; i < nameStrings.length; i++) { - der[i] = new DerValue(DerValue.tag_GeneralString, nameStrings[i]); + der[i] = new KerberosString(nameStrings[i]).toDerValue(); } temp.putSequence(der); bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x01), temp); diff --git a/jdk/src/share/classes/sun/security/krb5/Realm.java b/jdk/src/share/classes/sun/security/krb5/Realm.java index bf57e7a54ce..7ba959bdcf4 100644 --- a/jdk/src/share/classes/sun/security/krb5/Realm.java +++ b/jdk/src/share/classes/sun/security/krb5/Realm.java @@ -31,11 +31,6 @@ package sun.security.krb5; -import sun.security.krb5.Config; -import sun.security.krb5.PrincipalName; -import sun.security.krb5.KrbException; -import sun.security.krb5.Asn1Exception; -import sun.security.krb5.RealmException; import sun.security.krb5.internal.Krb5; import sun.security.util.*; import java.io.IOException; @@ -43,6 +38,7 @@ import java.util.StringTokenizer; import java.util.Vector; import java.util.Stack; import java.util.EmptyStackException; +import sun.security.krb5.internal.util.KerberosString; /** * Implements the ASN.1 Realm type. @@ -109,7 +105,7 @@ public class Realm implements Cloneable { if (encoding == null) { throw new IllegalArgumentException("encoding can not be null"); } - realm = encoding.getGeneralString(); + realm = new KerberosString(encoding).toString(); if (realm == null || realm.length() == 0) throw new RealmException(Krb5.REALM_NULL); if (!isValidRealmString(realm)) @@ -206,7 +202,7 @@ public class Realm implements Cloneable { */ public byte[] asn1Encode() throws Asn1Exception, IOException { DerOutputStream out = new DerOutputStream(); - out.putGeneralString(this.realm); + out.putDerValue(new KerberosString(this.realm).toDerValue()); return out.toByteArray(); } diff --git a/jdk/src/share/classes/sun/security/krb5/internal/ETypeInfo.java b/jdk/src/share/classes/sun/security/krb5/internal/ETypeInfo.java index 9790bb8696f..7625a6eb5d9 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/ETypeInfo.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/ETypeInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2005-2009 Sun Microsystems, Inc. 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 @@ -28,6 +28,7 @@ package sun.security.krb5.internal; import sun.security.util.*; import sun.security.krb5.Asn1Exception; import java.io.IOException; +import sun.security.krb5.internal.util.KerberosString; /** * Implements the ASN.1 ETYPE-INFO-ENTRY type. @@ -43,7 +44,7 @@ import java.io.IOException; public class ETypeInfo { private int etype; - private byte[] salt = null; + private String salt = null; private static final byte TAG_TYPE = 0; private static final byte TAG_VALUE = 1; @@ -51,21 +52,13 @@ public class ETypeInfo { private ETypeInfo() { } - public ETypeInfo(int etype, byte[] salt) { + public ETypeInfo(int etype, String salt) { this.etype = etype; - if (salt != null) { - this.salt = salt.clone(); - } + this.salt = salt; } public Object clone() { - ETypeInfo etypeInfo = new ETypeInfo(); - etypeInfo.etype = etype; - if (salt != null) { - etypeInfo.salt = new byte[salt.length]; - System.arraycopy(salt, 0, etypeInfo.salt, 0, salt.length); - } - return etypeInfo; + return new ETypeInfo(etype, salt); } /** @@ -94,7 +87,22 @@ public class ETypeInfo { if (encoding.getData().available() > 0) { der = encoding.getData().getDerValue(); if ((der.getTag() & 0x1F) == 0x01) { - this.salt = der.getData().getOctetString(); + byte[] saltBytes = der.getData().getOctetString(); + + // Although salt is defined as an OCTET STRING, it's the + // encoding from of a string. As RFC 4120 says: + // + // "The salt, ..., is also completely unspecified with respect + // to character set and is probably locale-specific". + // + // It's known that this field is using the same encoding as + // KerberosString in most implementations. + + if (KerberosString.MSNAME) { + this.salt = new String(saltBytes, "UTF8"); + } else { + this.salt = new String(saltBytes); + } } } @@ -120,7 +128,11 @@ public class ETypeInfo { if (salt != null) { temp = new DerOutputStream(); - temp.putOctetString(salt); + if (KerberosString.MSNAME) { + temp.putOctetString(salt.getBytes("UTF8")); + } else { + temp.putOctetString(salt.getBytes()); + } bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_VALUE), temp); } @@ -135,8 +147,8 @@ public class ETypeInfo { return etype; } - public byte[] getSalt() { - return ((salt == null) ? null : salt.clone()); + public String getSalt() { + return salt; } } diff --git a/jdk/src/share/classes/sun/security/krb5/internal/ETypeInfo2.java b/jdk/src/share/classes/sun/security/krb5/internal/ETypeInfo2.java index f1b657116f6..14807745433 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/ETypeInfo2.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/ETypeInfo2.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2005-2009 Sun Microsystems, Inc. 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 @@ -28,6 +28,7 @@ package sun.security.krb5.internal; import sun.security.util.*; import sun.security.krb5.Asn1Exception; import java.io.IOException; +import sun.security.krb5.internal.util.KerberosString; /** * Implements the ASN.1 ETYPE-INFO-ENTRY type. @@ -54,11 +55,9 @@ public class ETypeInfo2 { private ETypeInfo2() { } - public ETypeInfo2(int etype, byte[] salt, byte[] s2kparams) { + public ETypeInfo2(int etype, String salt, byte[] s2kparams) { this.etype = etype; - if (salt != null) { - this.saltStr = new String(salt); - } + this.saltStr = salt; if (s2kparams != null) { this.s2kparams = s2kparams.clone(); } @@ -102,7 +101,8 @@ public class ETypeInfo2 { if (encoding.getData().available() > 0) { if ((encoding.getData().peekByte() & 0x1F) == 0x01) { der = encoding.getData().getDerValue(); - this.saltStr = der.getData().getGeneralString(); + this.saltStr = new KerberosString( + der.getData().getDerValue()).toString(); } } @@ -136,7 +136,7 @@ public class ETypeInfo2 { if (saltStr != null) { temp = new DerOutputStream(); - temp.putGeneralString(saltStr); + temp.putDerValue(new KerberosString(saltStr).toDerValue()); bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, TAG_VALUE1), temp); } @@ -157,8 +157,8 @@ public class ETypeInfo2 { return etype; } - public byte[] getSalt() { - return ((saltStr == null) ? null : saltStr.getBytes()); + public String getSalt() { + return saltStr; } public byte[] getParams() { diff --git a/jdk/src/share/classes/sun/security/krb5/internal/KRBError.java b/jdk/src/share/classes/sun/security/krb5/internal/KRBError.java index febc959cb80..e7c73181cf7 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/KRBError.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/KRBError.java @@ -1,5 +1,5 @@ /* - * Portions Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved. + * Portions Copyright 2000-2009 Sun Microsystems, Inc. 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 @@ -42,6 +42,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.math.BigInteger; import java.util.Arrays; +import sun.security.krb5.internal.util.KerberosString; /** * Implements the ASN.1 KRBError type. * @@ -97,7 +98,7 @@ public class KRBError implements java.io.Serializable { // pre-auth info private int etype = 0; - private byte[] salt = null; + private String salt = null; private byte[] s2kparams = null; private static boolean DEBUG = Krb5.DEBUG; @@ -334,8 +335,8 @@ public class KRBError implements java.io.Serializable { } // access pre-auth info - public final byte[] getSalt() { - return ((salt == null) ? null : salt.clone()); + public final String getSalt() { + return salt; } // access pre-auth info @@ -415,7 +416,8 @@ public class KRBError implements java.io.Serializable { if (der.getData().available() >0) { if ((der.getData().peekByte() & 0x1F) == 0x0B) { subDer = der.getData().getDerValue(); - eText = subDer.getData().getGeneralString(); + eText = new KerberosString(subDer.getData().getDerValue()) + .toString(); } } if (der.getData().available() >0) { @@ -515,7 +517,7 @@ public class KRBError implements java.io.Serializable { if (eText != null) { temp = new DerOutputStream(); - temp.putGeneralString(eText); + temp.putDerValue(new KerberosString(eText).toDerValue()); bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, (byte)0x0B), temp); } if (eData != null) { diff --git a/jdk/src/share/classes/sun/security/krb5/internal/crypto/Des.java b/jdk/src/share/classes/sun/security/krb5/internal/crypto/Des.java index 61a03c02a94..feaf8cf42a5 100644 --- a/jdk/src/share/classes/sun/security/krb5/internal/crypto/Des.java +++ b/jdk/src/share/classes/sun/security/krb5/internal/crypto/Des.java @@ -34,17 +34,29 @@ import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; import javax.crypto.SecretKeyFactory; import javax.crypto.SecretKey; -import java.security.Security; -import java.security.Provider; import java.security.GeneralSecurityException; import javax.crypto.spec.IvParameterSpec; import sun.security.krb5.KrbCryptoException; -import sun.security.krb5.internal.Krb5; -import java.io.UnsupportedEncodingException; import java.util.Arrays; +import sun.security.action.GetPropertyAction; public final class Des { + // RFC 3961 demands that UTF-8 encoding be used in DES's + // string-to-key function. For historical reasons, some + // implementations use a locale-specific encoding. Even + // so, when the client and server use different locales, + // they must agree on a common value, normally the one + // used when the password is set/reset. + // + // The following system property is provided to perform the + // string-to-key encoding. When set, the specified charset + // name is used. Otherwise, the system default charset. + + private final static String CHARSET = + java.security.AccessController.doPrivileged( + new GetPropertyAction("sun.security.krb5.msinterop.des.s2kcharset")); + private static final long[] bad_keys = { 0x0101010101010101L, 0xfefefefefefefefeL, 0x1f1f1f1f1f1f1f1fL, 0xe0e0e0e0e0e0e0e0L, @@ -226,7 +238,11 @@ public final class Des { // Convert password to byte array try { - cbytes = (new String(passwdChars)).getBytes(); + if (CHARSET == null) { + cbytes = (new String(passwdChars)).getBytes(); + } else { + cbytes = (new String(passwdChars)).getBytes(CHARSET); + } } catch (Exception e) { // clear-up sensitive information if (cbytes != null) { diff --git a/jdk/src/share/classes/sun/security/krb5/internal/util/KerberosString.java b/jdk/src/share/classes/sun/security/krb5/internal/util/KerberosString.java new file mode 100644 index 00000000000..2c50fe601b9 --- /dev/null +++ b/jdk/src/share/classes/sun/security/krb5/internal/util/KerberosString.java @@ -0,0 +1,82 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.security.krb5.internal.util; + +import java.io.IOException; +import java.security.AccessController; +import sun.security.action.GetBooleanAction; +import sun.security.util.DerValue; + +/** + * Implements the ASN.1 KerberosString type. + * + *
+ * KerberosString ::= GeneralString (IA5String) + *+ * + * This definition reflects the Network Working Group RFC 4120 + * specification available at + * + * http://www.ietf.org/rfc/rfc4120.txt. + */ +public final class KerberosString { + /** + * RFC 4120 defines KerberosString as GeneralString (IA5String), which + * only includes ASCII characters. However, other implementations have been + * known to use GeneralString to contain UTF-8 encoding. To interop + * with these implementations, the following system property is defined. + * When set as true, KerberosString is encoded as UTF-8. Note that this + * only affects the byte encoding, the tag of the ASN.1 type is still + * GeneralString. + */ + public static final boolean MSNAME = AccessController.doPrivileged( + new GetBooleanAction("sun.security.krb5.msinterop.kstring")); + + private final String s; + + public KerberosString(String s) { + this.s = s; + } + + public KerberosString(DerValue der) throws IOException { + if (der.tag != DerValue.tag_GeneralString) { + throw new IOException( + "KerberosString's tag is incorrect: " + der.tag); + } + s = new String(der.getDataBytes(), MSNAME?"UTF8":"ASCII"); + } + + public String toString() { + return s; + } + + public DerValue toDerValue() throws IOException { + // No need to cache the result since this method is + // only called once. + return new DerValue(DerValue.tag_GeneralString, + s.getBytes(MSNAME?"UTF8":"ASCII")); + } +} diff --git a/jdk/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java b/jdk/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java index cb4ded6e668..44dee2c86c5 100644 --- a/jdk/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java +++ b/jdk/src/windows/classes/sun/security/krb5/internal/tools/Kinit.java @@ -1,5 +1,5 @@ /* - * Portions Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * Portions Copyright 2000-2009 Sun Microsystems, Inc. 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 @@ -252,15 +252,15 @@ public class Kinit { } KRBError error = ke.getError(); int etype = error.getEType(); - byte[] salt = error.getSalt(); + String salt = error.getSalt(); byte[] s2kparams = error.getParams(); if (useKeytab) { - as_req = new KrbAsReq(skeys, true, etype, salt, s2kparams, - opt, principal, sname, + as_req = new KrbAsReq(skeys, true, etype, salt, + s2kparams, opt, principal, sname, null, null, null, null, addresses, null); } else { - as_req = new KrbAsReq(psswd, true, etype, salt, s2kparams, - opt, principal, sname, + as_req = new KrbAsReq(psswd, true, etype, salt, + s2kparams, opt, principal, sname, null, null, null, null, addresses, null); } as_rep = sendASRequest(as_req, useKeytab, realm, psswd, skeys); diff --git a/jdk/test/sun/security/krb5/RFC396xTest.java b/jdk/test/sun/security/krb5/RFC396xTest.java new file mode 100644 index 00000000000..6ab9255d4f5 --- /dev/null +++ b/jdk/test/sun/security/krb5/RFC396xTest.java @@ -0,0 +1,248 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +/* + * @test + * @bug 6862679 + * @summary ESC: AD Authentication with user with umlauts fails + */ + +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.internal.crypto.Des; +import sun.security.krb5.internal.crypto.EType; +import sun.security.krb5.internal.crypto.crc32; +import sun.security.krb5.internal.crypto.dk.AesDkCrypto; +import sun.security.krb5.internal.crypto.dk.Des3DkCrypto; +import sun.security.krb5.internal.crypto.dk.DkCrypto; +import java.nio.*; +import javax.crypto.*; +import javax.crypto.spec.*; + +public class RFC396xTest { + + static final String gclef = new String(Character.toChars(0x1d11e)); + + /** Creates a new instance of NewClass */ + public static void main(String[] args) throws Exception { + System.setProperty("sun.security.krb5.msinterop.des.s2kcharset", + "utf-8"); + test(); + } + + static void test() throws Exception { + // RFC 3961 + // A.1 + Method nfold = DkCrypto.class.getDeclaredMethod("nfold", byte[].class, Integer.TYPE); + nfold.setAccessible(true); + assertStringEquals(hex((byte[])nfold.invoke(null, "012345".getBytes("UTF-8"), 64)), "be072631276b1955"); + assertStringEquals(hex((byte[])nfold.invoke(null, "password".getBytes("UTF-8"), 56)), "78a07b6caf85fa"); + assertStringEquals(hex((byte[])nfold.invoke(null, "Rough Consensus, and Running Code".getBytes("UTF-8"), 64)), "bb6ed30870b7f0e0"); + assertStringEquals(hex((byte[])nfold.invoke(null, "password".getBytes("UTF-8"), 168)), "59e4a8ca7c0385c3c37b3f6d2000247cb6e6bd5b3e"); + assertStringEquals(hex((byte[])nfold.invoke(null, "MASSACHVSETTS INSTITVTE OF TECHNOLOGY".getBytes("UTF-8"), 192)), "db3b0d8f0b061e603282b308a50841229ad798fab9540c1b"); + assertStringEquals(hex((byte[])nfold.invoke(null, "Q".getBytes("UTF-8"), 168)), "518a54a215a8452a518a54a215a8452a518a54a215"); + assertStringEquals(hex((byte[])nfold.invoke(null, "ba".getBytes("UTF-8"), 168)), "fb25d531ae8974499f52fd92ea9857c4ba24cf297e"); + assertStringEquals(hex((byte[])nfold.invoke(null, "kerberos".getBytes("UTF-8"), 64)), "6b65726265726f73"); + assertStringEquals(hex((byte[])nfold.invoke(null, "kerberos".getBytes("UTF-8"), 128)), "6b65726265726f737b9b5b2b93132b93"); + assertStringEquals(hex((byte[])nfold.invoke(null, "kerberos".getBytes("UTF-8"), 168)), "8372c236344e5f1550cd0747e15d62ca7a5a3bcea4"); + assertStringEquals(hex((byte[])nfold.invoke(null, "kerberos".getBytes("UTF-8"), 256)), "6b65726265726f737b9b5b2b93132b935c9bdcdad95c9899c4cae4dee6d6cae4"); + + // A.2 + assertStringEquals(hex(Des.string_to_key_bytes("passwordATHENA.MIT.EDUraeburn".toCharArray())), "cbc22fae235298e3"); + assertStringEquals(hex(Des.string_to_key_bytes("potatoeWHITEHOUSE.GOVdanny".toCharArray())), "df3d32a74fd92a01"); + assertStringEquals(hex(Des.string_to_key_bytes((gclef+"EXAMPLE.COMpianist").toCharArray())), "4ffb26bab0cd9413"); + assertStringEquals(hex(Des.string_to_key_bytes("\u00dfATHENA.MIT.EDUJuri\u0161i\u0107".toCharArray())), "62c81a5232b5e69d"); + // Next 2 won't pass, since there's no real weak key here + //assertStringEquals(hex(Des.string_to_key_bytes("11119999AAAAAAAA".toCharArray())), "984054d0f1a73e31"); + //assertStringEquals(hex(Des.string_to_key_bytes("NNNN6666FFFFAAAA".toCharArray())), "c4bf6b25adf7a4f8"); + + // A.3 + Object o = Des3DkCrypto.class.getConstructor().newInstance(); + Method dr = DkCrypto.class.getDeclaredMethod("dr", byte[].class, byte[].class); + Method randomToKey = DkCrypto.class.getDeclaredMethod("randomToKey", byte[].class); + dr.setAccessible(true); + randomToKey.setAccessible(true); + assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o, + xeh("dce06b1f64c857a11c3db57c51899b2cc1791008ce973b92"), + xeh("0000000155")))), + "925179d04591a79b5d3192c4a7e9c289b049c71f6ee604cd"); + assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o, + xeh("5e13d31c70ef765746578531cb51c15bf11ca82c97cee9f2"), + xeh("00000001aa")))), + "9e58e5a146d9942a101c469845d67a20e3c4259ed913f207"); + assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o, + xeh("98e6fd8a04a4b6859b75a176540b9752bad3ecd610a252bc"), + xeh("0000000155")))), + "13fef80d763e94ec6d13fd2ca1d085070249dad39808eabf"); + assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o, + xeh("622aec25a2fe2cad7094680b7c64940280084c1a7cec92b5"), + xeh("00000001aa")))), + "f8dfbf04b097e6d9dc0702686bcb3489d91fd9a4516b703e"); + assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o, + xeh("d3f8298ccb166438dcb9b93ee5a7629286a491f838f802fb"), + xeh("6b65726265726f73")))), + "2370da575d2a3da864cebfdc5204d56df779a7df43d9da43"); + assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o, + xeh("c1081649ada74362e6a1459d01dfd30d67c2234c940704da"), + xeh("0000000155")))), + "348057ec98fdc48016161c2a4c7a943e92ae492c989175f7"); + assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o, + xeh("5d154af238f46713155719d55e2f1f790dd661f279a7917c"), + xeh("00000001aa")))), + "a8808ac267dada3dcbe9a7c84626fbc761c294b01315e5c1"); + assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o, + xeh("798562e049852f57dc8c343ba17f2ca1d97394efc8adc443"), + xeh("0000000155")))), + "c813f88a3be3b334f75425ce9175fbe3c8493b89c8703b49"); + assertStringEquals(hex((byte[])randomToKey.invoke(o, (byte[])dr.invoke(o, + xeh("26dce334b545292f2feab9a8701a89a4b99eb9942cecd016"), + xeh("00000001aa")))), + "f48ffd6e83f83e7354e694fd252cf83bfe58f7d5ba37ec5d"); + + // A.4 + assertStringEquals(hex(new Des3DkCrypto().stringToKey("passwordATHENA.MIT.EDUraeburn".toCharArray())), "850bb51358548cd05e86768c313e3bfef7511937dcf72c3e"); + assertStringEquals(hex(new Des3DkCrypto().stringToKey("potatoeWHITEHOUSE.GOVdanny".toCharArray())), "dfcd233dd0a43204ea6dc437fb15e061b02979c1f74f377a"); + assertStringEquals(hex(new Des3DkCrypto().stringToKey("pennyEXAMPLE.COMbuckaroo".toCharArray())), "6d2fcdf2d6fbbc3ddcadb5da5710a23489b0d3b69d5d9d4a"); + assertStringEquals(hex(new Des3DkCrypto().stringToKey("\u00DFATHENA.MIT.EDUJuri\u0161i\u0107".toCharArray())), "16d5a40e1ce3bacb61b9dce00470324c831973a7b952feb0"); + assertStringEquals(hex(new Des3DkCrypto().stringToKey((gclef+"EXAMPLE.COMpianist").toCharArray())), "85763726585dbc1cce6ec43e1f751f07f1c4cbb098f40b19"); + + // A.5 + assertStringEquals(hex(crc32.byte2crc32sum_bytes("foo".getBytes("UTF-8"))), "33bc3273"); + assertStringEquals(hex(crc32.byte2crc32sum_bytes("test0123456789".getBytes("UTF-8"))), "d6883eb8"); + assertStringEquals(hex(crc32.byte2crc32sum_bytes("MASSACHVSETTS INSTITVTE OF TECHNOLOGY".getBytes("UTF-8"))), "f78041e3"); + assertStringEquals(hex(crc32.byte2crc32sum_bytes(new byte[] {(byte)0x80, 0})), "4b98833b"); + assertStringEquals(hex(crc32.byte2crc32sum_bytes(new byte[] {0, 8})), "3288db0e"); + assertStringEquals(hex(crc32.byte2crc32sum_bytes(new byte[] {0, (byte)0x80})), "2083b8ed"); + assertStringEquals(hex(crc32.byte2crc32sum_bytes(new byte[] {(byte)0x80})), "2083b8ed"); + assertStringEquals(hex(crc32.byte2crc32sum_bytes(new byte[] {(byte)0x80, 0, 0, 0})), "3bb659ed"); + assertStringEquals(hex(crc32.byte2crc32sum_bytes(new byte[] {0, 0, 0, 1})), "96300777"); + + // RFC 3962 + AesDkCrypto a1 = new AesDkCrypto(128); + Method pbkdf2 = AesDkCrypto.class.getDeclaredMethod("PBKDF2", char[].class, byte[].class, Integer.TYPE, Integer.TYPE); + Method s2k = AesDkCrypto.class.getDeclaredMethod("stringToKey", char[].class, byte[].class, byte[].class); + pbkdf2.setAccessible(true); + s2k.setAccessible(true); + assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"), 1, 128)), "cd ed b5 28 1b b2 f8 01 56 5a 11 22 b2 56 35 15"); + assertStringEquals(hex(a1.stringToKey("password".toCharArray(), "ATHENA.MIT.EDUraeburn", i2b(1))), "42 26 3c 6e 89 f4 fc 28 b8 df 68 ee 09 79 9f 15"); + assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"), 1, 256)), "cd ed b5 28 1b b2 f8 01 56 5a 11 22 b2 56 35 15 0a d1 f7 a0 4b b9 f3 a3 33 ec c0 e2 e1 f7 08 37"); + assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"), 2, 128)), "01 db ee 7f 4a 9e 24 3e 98 8b 62 c7 3c da 93 5d"); + assertStringEquals(hex(a1.stringToKey("password".toCharArray(), "ATHENA.MIT.EDUraeburn", i2b(2))), "c6 51 bf 29 e2 30 0a c2 7f a4 69 d6 93 bd da 13"); + assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"), 2, 256)), "01 db ee 7f 4a 9e 24 3e 98 8b 62 c7 3c da 93 5d a0 53 78 b9 32 44 ec 8f 48 a9 9e 61 ad 79 9d 86"); + assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"), 1200, 128)), "5c 08 eb 61 fd f7 1e 4e 4e c3 cf 6b a1 f5 51 2b"); + assertStringEquals(hex(a1.stringToKey("password".toCharArray(), "ATHENA.MIT.EDUraeburn", i2b(1200))), "4c 01 cd 46 d6 32 d0 1e 6d be 23 0a 01 ed 64 2a"); + assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), "ATHENA.MIT.EDUraeburn".getBytes("UTF-8"), 1200, 256)), "5c 08 eb 61 fd f7 1e 4e 4e c3 cf 6b a1 f5 51 2b a7 e5 2d db c5 e5 14 2f 70 8a 31 e2 e6 2b 1e 13"); + assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), xeh("1234567878563412"), 5, 128)), "d1 da a7 86 15 f2 87 e6 a1 c8 b1 20 d7 06 2a 49"); + assertStringEquals(hex((byte[])s2k.invoke(a1, "password".toCharArray(), xeh("1234567878563412"), i2b(5))), "e9 b2 3d 52 27 37 47 dd 5c 35 cb 55 be 61 9d 8e"); + assertStringEquals(hex((byte[])pbkdf2.invoke(null, "password".toCharArray(), xeh("1234567878563412"), 5, 256)), "d1 da a7 86 15 f2 87 e6 a1 c8 b1 20 d7 06 2a 49 3f 98 d2 03 e6 be 49 a6 ad f4 fa 57 4b 6e 64 ee"); + assertStringEquals(hex((byte[])pbkdf2.invoke(null, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase equals block size".getBytes("UTF-8"), 1200, 128)), "13 9c 30 c0 96 6b c3 2b a5 5f db f2 12 53 0a c9"); + assertStringEquals(hex(a1.stringToKey("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase equals block size", i2b(1200))), "59 d1 bb 78 9a 82 8b 1a a5 4e f9 c2 88 3f 69 ed"); + assertStringEquals(hex((byte[])pbkdf2.invoke(null, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase equals block size".getBytes("UTF-8"), 1200, 256)), "13 9c 30 c0 96 6b c3 2b a5 5f db f2 12 53 0a c9 c5 ec 59 f1 a4 52 f5 cc 9a d9 40 fe a0 59 8e d1"); + assertStringEquals(hex((byte[])pbkdf2.invoke(null, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase exceeds block size".getBytes("UTF-8"), 1200, 128)), "9c ca d6 d4 68 77 0c d5 1b 10 e6 a6 87 21 be 61"); + assertStringEquals(hex(a1.stringToKey("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase exceeds block size", i2b(1200))), "cb 80 05 dc 5f 90 17 9a 7f 02 10 4c 00 18 75 1d"); + assertStringEquals(hex((byte[])pbkdf2.invoke(null, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase exceeds block size".getBytes("UTF-8"), 1200, 256)), "9c ca d6 d4 68 77 0c d5 1b 10 e6 a6 87 21 be 61 1a 8b 4d 28 26 01 db 3b 36 be 92 46 91 5e c8 2a"); + assertStringEquals(hex((byte[])pbkdf2.invoke(null, gclef.toCharArray(), "EXAMPLE.COMpianist".getBytes("UTF-8"), 50, 128)), "6b 9c f2 6d 45 45 5a 43 a5 b8 bb 27 6a 40 3b 39"); + assertStringEquals(hex(a1.stringToKey(gclef.toCharArray(), "EXAMPLE.COMpianist", i2b(50))), "f1 49 c1 f2 e1 54 a7 34 52 d4 3e 7f e6 2a 56 e5"); + assertStringEquals(hex((byte[])pbkdf2.invoke(null, gclef.toCharArray(), "EXAMPLE.COMpianist".getBytes("UTF-8"), 50, 256)), "6b 9c f2 6d 45 45 5a 43 a5 b8 bb 27 6a 40 3b 39 e7 fe 37 a0 c4 1e 02 c2 81 ff 30 69 e1 e9 4f 52"); + + if (EType.isSupported(EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96)) { + AesDkCrypto a2 = new AesDkCrypto(256); + assertStringEquals(hex(a2.stringToKey("password".toCharArray(), "ATHENA.MIT.EDUraeburn", i2b(1))), "fe 69 7b 52 bc 0d 3c e1 44 32 ba 03 6a 92 e6 5b bb 52 28 09 90 a2 fa 27 88 39 98 d7 2a f3 01 61"); + assertStringEquals(hex(a2.stringToKey("password".toCharArray(), "ATHENA.MIT.EDUraeburn", i2b(2))), "a2 e1 6d 16 b3 60 69 c1 35 d5 e9 d2 e2 5f 89 61 02 68 56 18 b9 59 14 b4 67 c6 76 22 22 58 24 ff"); + assertStringEquals(hex(a2.stringToKey("password".toCharArray(), "ATHENA.MIT.EDUraeburn", i2b(1200))), "55 a6 ac 74 0a d1 7b 48 46 94 10 51 e1 e8 b0 a7 54 8d 93 b0 ab 30 a8 bc 3f f1 62 80 38 2b 8c 2a"); + assertStringEquals(hex((byte[])s2k.invoke(a2, "password".toCharArray(), xeh("1234567878563412"), i2b(5))), "97 a4 e7 86 be 20 d8 1a 38 2d 5e bc 96 d5 90 9c ab cd ad c8 7c a4 8f 57 45 04 15 9f 16 c3 6e 31"); + assertStringEquals(hex(a2.stringToKey("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase equals block size", i2b(1200))), "89 ad ee 36 08 db 8b c7 1f 1b fb fe 45 94 86 b0 56 18 b7 0c ba e2 20 92 53 4e 56 c5 53 ba 4b 34"); + assertStringEquals(hex(a2.stringToKey("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX".toCharArray(), "pass phrase exceeds block size", i2b(1200))), "d7 8c 5c 9c b8 72 a8 c9 da d4 69 7f 0b b5 b2 d2 14 96 c8 2b eb 2c ae da 21 12 fc ee a0 57 40 1b"); + assertStringEquals(hex(a2.stringToKey(gclef.toCharArray(), "EXAMPLE.COMpianist", i2b(50))), "4b 6d 98 39 f8 44 06 df 1f 09 cc 16 6d b4 b8 3c 57 18 48 b7 84 a3 d6 bd c3 46 58 9a 3e 39 3f 9e"); + } + + Cipher cipher = Cipher.getInstance("AES/CTS/NoPadding"); + + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec( + xeh("63 68 69 63 6b 65 6e 20 74 65 72 69 79 61 6b 69"), "AES"), + new IvParameterSpec( + xeh("00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"), 0, 16)); + assertStringEquals(hex(cipher.doFinal( + xeh("49 20 77 6f 75 6c 64 20 6c 69 6b 65 20 74 68 65 20"))), + "c6 35 35 68 f2 bf 8c b4 d8 a5 80 36 2d a7 ff 7f 97"); + assertStringEquals(hex(cipher.doFinal( + xeh("49 20 77 6f 75 6c 64 20 6c 69 6b 65 20 74 68 65 20 47 65 6e 65 72 61 6c 20 47 61 75 27 73 20"))), + "fc 00 78 3e 0e fd b2 c1 d4 45 d4 c8 ef f7 ed 22 97 68 72 68 d6 ec cc c0 c0 7b 25 e2 5e cf e5"); + assertStringEquals(hex(cipher.doFinal( + xeh("49 20 77 6f 75 6c 64 20 6c 69 6b 65 20 74 68 65 20 47 65 6e 65 72 61 6c 20 47 61 75 27 73 20 43"))), + "39 31 25 23 a7 86 62 d5 be 7f cb cc 98 eb f5 a8 97 68 72 68 d6 ec cc c0 c0 7b 25 e2 5e cf e5 84"); + assertStringEquals(hex(cipher.doFinal( + xeh("49 20 77 6f 75 6c 64 20 6c 69 6b 65 20 74 68 65 20 47 65 6e 65 72 61 6c 20 47 61 75 27 73 20 43 68 69 63 6b 65 6e 2c 20 70 6c 65 61 73 65 2c"))), + "97 68 72 68 d6 ec cc c0 c0 7b 25 e2 5e cf e5 84 b3 ff fd 94 0c 16 a1 8c 1b 55 49 d2 f8 38 02 9e 39 31 25 23 a7 86 62 d5 be 7f cb cc 98 eb f5"); + assertStringEquals(hex(cipher.doFinal( + xeh("49 20 77 6f 75 6c 64 20 6c 69 6b 65 20 74 68 65 20 47 65 6e 65 72 61 6c 20 47 61 75 27 73 20 43 68 69 63 6b 65 6e 2c 20 70 6c 65 61 73 65 2c 20"))), + "97 68 72 68 d6 ec cc c0 c0 7b 25 e2 5e cf e5 84 9d ad 8b bb 96 c4 cd c0 3b c1 03 e1 a1 94 bb d8 39 31 25 23 a7 86 62 d5 be 7f cb cc 98 eb f5 a8"); + assertStringEquals(hex(cipher.doFinal( + xeh("49 20 77 6f 75 6c 64 20 6c 69 6b 65 20 74 68 65 20 47 65 6e 65 72 61 6c 20 47 61 75 27 73 20 43 68 69 63 6b 65 6e 2c 20 70 6c 65 61 73 65 2c 20 61 6e 64 20 77 6f 6e 74 6f 6e 20 73 6f 75 70 2e"))), + "97 68 72 68 d6 ec cc c0 c0 7b 25 e2 5e cf e5 84 39 31 25 23 a7 86 62 d5 be 7f cb cc 98 eb f5 a8 48 07 ef e8 36 ee 89 a5 26 73 0d bc 2f 7b c8 40 9d ad 8b bb 96 c4 cd c0 3b c1 03 e1 a1 94 bb d8"); + } + + static byte[] i2b(int i) { + ByteBuffer bb = ByteBuffer.allocate(4); + byte[] b = new byte[4]; + bb.putInt(i); + bb.flip(); + bb.get(b); + return b; + } + + static String hex(byte[] bs) { + StringBuffer sb = new StringBuffer(bs.length * 2); + for(byte b: bs) { + char c = (char)((b+256)%256); + if (c / 16 < 10) + sb.append((char)(c/16+'0')); + else + sb.append((char)(c/16-10+'a')); + if (c % 16 < 10) + sb.append((char)(c%16+'0')); + else + sb.append((char)(c%16-10+'a')); + } + return new String(sb); + } + + static byte[] xeh(String in) { + in = in.replaceAll(" ", ""); + int len = in.length()/2; + byte[] out = new byte[len]; + for (int i=0; i