Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/krb5/EncryptionKey.java
38830 views
/*1* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26*27* (C) Copyright IBM Corp. 1999 All Rights Reserved.28* Copyright 1997 The Open Group Research Institute. All rights reserved.29*/3031package sun.security.krb5;3233import sun.security.util.*;34import sun.security.krb5.internal.*;35import sun.security.krb5.internal.crypto.*;36import java.io.IOException;37import java.security.GeneralSecurityException;38import java.util.Arrays;39import sun.security.krb5.internal.ktab.KeyTab;40import sun.security.krb5.internal.ccache.CCacheOutputStream;41import javax.crypto.spec.DESKeySpec;42import javax.crypto.spec.DESedeKeySpec;4344/**45* This class encapsulates the concept of an EncryptionKey. An encryption46* key is defined in RFC 4120 as:47*48* EncryptionKey ::= SEQUENCE {49* keytype [0] Int32 -- actually encryption type --,50* keyvalue [1] OCTET STRING51* }52*53* keytype54* This field specifies the encryption type of the encryption key55* that follows in the keyvalue field. Although its name is56* "keytype", it actually specifies an encryption type. Previously,57* multiple cryptosystems that performed encryption differently but58* were capable of using keys with the same characteristics were59* permitted to share an assigned number to designate the type of60* key; this usage is now deprecated.61*62* keyvalue63* This field contains the key itself, encoded as an octet string.64*/6566public class EncryptionKey67implements Cloneable {6869public static final EncryptionKey NULL_KEY =70new EncryptionKey(new byte[] {}, EncryptedData.ETYPE_NULL, null);7172private int keyType;73private byte[] keyValue;74private Integer kvno; // not part of ASN1 encoding;7576private static final boolean DEBUG = Krb5.DEBUG;7778public synchronized int getEType() {79return keyType;80}8182public final Integer getKeyVersionNumber() {83return kvno;84}8586/**87* Returns the raw key bytes, not in any ASN.1 encoding.88*/89public final byte[] getBytes() {90// This method cannot be called outside sun.security, hence no91// cloning. getEncoded() calls this method.92return keyValue;93}9495public synchronized Object clone() {96return new EncryptionKey(keyValue, keyType, kvno);97}9899/**100* Obtains all versions of the secret key of the principal from a101* keytab.102*103* @param princ the principal whose secret key is desired104* @param keytab the path to the keytab file. A value of null105* will be accepted to indicate that the default path should be106* searched.107* @return an array of secret keys or null if none were found.108*/109public static EncryptionKey[] acquireSecretKeys(PrincipalName princ,110String keytab) {111112if (princ == null)113throw new IllegalArgumentException(114"Cannot have null pricipal name to look in keytab.");115116// KeyTab getInstance(keytab) will call KeyTab.getInstance()117// if keytab is null118KeyTab ktab = KeyTab.getInstance(keytab);119return ktab.readServiceKeys(princ);120}121122/**123* Obtains a key for a given etype of a principal with possible new salt124* and s2kparams125* @param cname NOT null126* @param password NOT null127* @param etype128* @param snp can be NULL129* @return never null130*/131public static EncryptionKey acquireSecretKey(PrincipalName cname,132char[] password, int etype, PAData.SaltAndParams snp)133throws KrbException {134String salt;135byte[] s2kparams;136if (snp != null) {137salt = snp.salt != null ? snp.salt : cname.getSalt();138s2kparams = snp.params;139} else {140salt = cname.getSalt();141s2kparams = null;142}143return acquireSecretKey(password, salt, etype, s2kparams);144}145146/**147* Obtains a key for a given etype with salt and optional s2kparams148* @param password NOT null149* @param salt NOT null150* @param etype151* @param s2kparams can be NULL152* @return never null153*/154public static EncryptionKey acquireSecretKey(char[] password,155String salt, int etype, byte[] s2kparams)156throws KrbException {157158return new EncryptionKey(159stringToKey(password, salt, s2kparams, etype),160etype, null);161}162163/**164* Generate a list of keys using the given principal and password.165* Construct a key for each configured etype.166* Caller is responsible for clearing password.167*/168/*169* Usually, when keyType is decoded from ASN.1 it will contain a170* value indicating what the algorithm to be used is. However, when171* converting from a password to a key for the AS-EXCHANGE, this172* keyType will not be available. Use builtin list of default etypes173* as the default in that case. If default_tkt_enctypes was set in174* the libdefaults of krb5.conf, then use that sequence.175*/176public static EncryptionKey[] acquireSecretKeys(char[] password,177String salt) throws KrbException {178179int[] etypes = EType.getDefaults("default_tkt_enctypes");180181EncryptionKey[] encKeys = new EncryptionKey[etypes.length];182for (int i = 0; i < etypes.length; i++) {183if (EType.isSupported(etypes[i])) {184encKeys[i] = new EncryptionKey(185stringToKey(password, salt, null, etypes[i]),186etypes[i], null);187} else {188if (DEBUG) {189System.out.println("Encryption Type " +190EType.toString(etypes[i]) +191" is not supported/enabled");192}193}194}195return encKeys;196}197198// Used in Krb5AcceptCredential, self199public EncryptionKey(byte[] keyValue,200int keyType,201Integer kvno) {202203if (keyValue != null) {204this.keyValue = new byte[keyValue.length];205System.arraycopy(keyValue, 0, this.keyValue, 0, keyValue.length);206} else {207throw new IllegalArgumentException("EncryptionKey: " +208"Key bytes cannot be null!");209}210this.keyType = keyType;211this.kvno = kvno;212}213214/**215* Constructs an EncryptionKey by using the specified key type and key216* value. It is used to recover the key when retrieving data from217* credential cache file.218*219*/220// Used in JSSE (KerberosWrapper), Credentials,221// javax.security.auth.kerberos.KeyImpl222public EncryptionKey(int keyType,223byte[] keyValue) {224this(keyValue, keyType, null);225}226227private static byte[] stringToKey(char[] password, String salt,228byte[] s2kparams, int keyType) throws KrbCryptoException {229230char[] slt = salt.toCharArray();231char[] pwsalt = new char[password.length + slt.length];232System.arraycopy(password, 0, pwsalt, 0, password.length);233System.arraycopy(slt, 0, pwsalt, password.length, slt.length);234Arrays.fill(slt, '0');235236try {237switch (keyType) {238case EncryptedData.ETYPE_DES_CBC_CRC:239case EncryptedData.ETYPE_DES_CBC_MD5:240return Des.string_to_key_bytes(pwsalt);241242case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD:243return Des3.stringToKey(pwsalt);244245case EncryptedData.ETYPE_ARCFOUR_HMAC:246return ArcFourHmac.stringToKey(password);247248case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:249return Aes128.stringToKey(password, salt, s2kparams);250251case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:252return Aes256.stringToKey(password, salt, s2kparams);253254default:255throw new IllegalArgumentException("encryption type " +256EType.toString(keyType) + " not supported");257}258259} catch (GeneralSecurityException e) {260KrbCryptoException ke = new KrbCryptoException(e.getMessage());261ke.initCause(e);262throw ke;263} finally {264Arrays.fill(pwsalt, '0');265}266}267268// Used in javax.security.auth.kerberos.KeyImpl269public EncryptionKey(char[] password,270String salt,271String algorithm) throws KrbCryptoException {272273if (algorithm == null || algorithm.equalsIgnoreCase("DES")) {274keyType = EncryptedData.ETYPE_DES_CBC_MD5;275} else if (algorithm.equalsIgnoreCase("DESede")) {276keyType = EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD;277} else if (algorithm.equalsIgnoreCase("AES128")) {278keyType = EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96;279} else if (algorithm.equalsIgnoreCase("ArcFourHmac")) {280keyType = EncryptedData.ETYPE_ARCFOUR_HMAC;281} else if (algorithm.equalsIgnoreCase("AES256")) {282keyType = EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96;283// validate if AES256 is enabled284if (!EType.isSupported(keyType)) {285throw new IllegalArgumentException("Algorithm " + algorithm +286" not enabled");287}288} else {289throw new IllegalArgumentException("Algorithm " + algorithm +290" not supported");291}292293keyValue = stringToKey(password, salt, null, keyType);294kvno = null;295}296297/**298* Generates a sub-sessionkey from a given session key.299*300* Used in AcceptSecContextToken and KrbApReq by acceptor- and initiator-301* side respectively.302*/303public EncryptionKey(EncryptionKey key) throws KrbCryptoException {304// generate random sub-session key305keyValue = Confounder.bytes(key.keyValue.length);306for (int i = 0; i < keyValue.length; i++) {307keyValue[i] ^= key.keyValue[i];308}309keyType = key.keyType;310311// check for key parity and weak keys312try {313// check for DES key314if ((keyType == EncryptedData.ETYPE_DES_CBC_MD5) ||315(keyType == EncryptedData.ETYPE_DES_CBC_CRC)) {316// fix DES key parity317if (!DESKeySpec.isParityAdjusted(keyValue, 0)) {318keyValue = Des.set_parity(keyValue);319}320// check for weak key321if (DESKeySpec.isWeak(keyValue, 0)) {322keyValue[7] = (byte)(keyValue[7] ^ 0xF0);323}324}325// check for 3DES key326if (keyType == EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {327// fix 3DES key parity328if (!DESedeKeySpec.isParityAdjusted(keyValue, 0)) {329keyValue = Des3.parityFix(keyValue);330}331// check for weak keys332byte[] oneKey = new byte[8];333for (int i=0; i<keyValue.length; i+=8) {334System.arraycopy(keyValue, i, oneKey, 0, 8);335if (DESKeySpec.isWeak(oneKey, 0)) {336keyValue[i+7] = (byte)(keyValue[i+7] ^ 0xF0);337}338}339}340} catch (GeneralSecurityException e) {341KrbCryptoException ke = new KrbCryptoException(e.getMessage());342ke.initCause(e);343throw ke;344}345}346347/**348* Constructs an instance of EncryptionKey type.349* @param encoding a single DER-encoded value.350* @exception Asn1Exception if an error occurs while decoding an ASN1351* encoded data.352* @exception IOException if an I/O error occurs while reading encoded353* data.354*355*356*/357// Used in javax.security.auth.kerberos.KeyImpl358public EncryptionKey(DerValue encoding) throws Asn1Exception, IOException {359DerValue der;360if (encoding.getTag() != DerValue.tag_Sequence) {361throw new Asn1Exception(Krb5.ASN1_BAD_ID);362}363der = encoding.getData().getDerValue();364if ((der.getTag() & (byte)0x1F) == (byte)0x00) {365keyType = der.getData().getBigInteger().intValue();366}367else368throw new Asn1Exception(Krb5.ASN1_BAD_ID);369der = encoding.getData().getDerValue();370if ((der.getTag() & (byte)0x1F) == (byte)0x01) {371keyValue = der.getData().getOctetString();372}373else374throw new Asn1Exception(Krb5.ASN1_BAD_ID);375if (der.getData().available() > 0) {376throw new Asn1Exception(Krb5.ASN1_BAD_ID);377}378}379380/**381* Returns the ASN.1 encoding of this EncryptionKey.382*383* <pre>{@code384* EncryptionKey ::= SEQUENCE {385* keytype[0] INTEGER,386* keyvalue[1] OCTET STRING }387* }</pre>388*389* <p>390* This definition reflects the Network Working Group RFC 4120391* specification available at392* <a href="http://www.ietf.org/rfc/rfc4120.txt">393* http://www.ietf.org/rfc/rfc4120.txt</a>.394*395* @return byte array of encoded EncryptionKey object.396* @exception Asn1Exception if an error occurs while decoding an ASN1397* encoded data.398* @exception IOException if an I/O error occurs while reading encoded399* data.400*401*/402public synchronized byte[] asn1Encode() throws Asn1Exception, IOException {403DerOutputStream bytes = new DerOutputStream();404DerOutputStream temp = new DerOutputStream();405temp.putInteger(keyType);406bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,407(byte)0x00), temp);408temp = new DerOutputStream();409temp.putOctetString(keyValue);410bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,411(byte)0x01), temp);412temp = new DerOutputStream();413temp.write(DerValue.tag_Sequence, bytes);414return temp.toByteArray();415}416417public synchronized void destroy() {418if (keyValue != null)419for (int i = 0; i < keyValue.length; i++)420keyValue[i] = 0;421}422423424/**425* Parse (unmarshal) an Encryption key from a DER input stream. This form426* parsing might be used when expanding a value which is part of427* a constructed sequence and uses explicitly tagged type.428*429* @param data the Der input stream value, which contains one or more430* marshaled value.431* @param explicitTag tag number.432* @param optional indicate if this data field is optional433* @exception Asn1Exception if an error occurs while decoding an ASN1434* encoded data.435* @exception IOException if an I/O error occurs while reading encoded436* data.437* @return an instance of EncryptionKey.438*439*/440public static EncryptionKey parse(DerInputStream data, byte441explicitTag, boolean optional) throws442Asn1Exception, IOException {443if ((optional) && (((byte)data.peekByte() & (byte)0x1F) !=444explicitTag)) {445return null;446}447DerValue der = data.getDerValue();448if (explicitTag != (der.getTag() & (byte)0x1F)) {449throw new Asn1Exception(Krb5.ASN1_BAD_ID);450} else {451DerValue subDer = der.getData().getDerValue();452return new EncryptionKey(subDer);453}454}455456/**457* Writes key value in FCC format to a <code>CCacheOutputStream</code>.458*459* @param cos a <code>CCacheOutputStream</code> to be written to.460* @exception IOException if an I/O exception occurs.461* @see sun.security.krb5.internal.ccache.CCacheOutputStream462*463*/464public synchronized void writeKey(CCacheOutputStream cos)465throws IOException {466467cos.write16(keyType);468// we use KRB5_FCC_FVNO_3469cos.write16(keyType); // key type is recorded twice.470cos.write32(keyValue.length);471for (int i = 0; i < keyValue.length; i++) {472cos.write8(keyValue[i]);473}474}475476public String toString() {477return new String("EncryptionKey: keyType=" + keyType478+ " kvno=" + kvno479+ " keyValue (hex dump)="480+ (keyValue == null || keyValue.length == 0 ?481" Empty Key" : '\n'482+ Krb5.hexDumper.encodeBuffer(keyValue)483+ '\n'));484}485486/**487* Find a key with given etype488*/489public static EncryptionKey findKey(int etype, EncryptionKey[] keys)490throws KrbException {491return findKey(etype, null, keys);492}493494/**495* Determines if a kvno matches another kvno. Used in the method496* findKey(type, kvno, keys). Always returns true if either input497* is null or zero, in case any side does not have kvno info available.498*499* Note: zero is included because N/A is not a legal value for kvno500* in javax.security.auth.kerberos.KerberosKey. Therefore, the info501* that the kvno is N/A might be lost when converting between this502* class and KerberosKey.503*/504private static boolean versionMatches(Integer v1, Integer v2) {505if (v1 == null || v1 == 0 || v2 == null || v2 == 0) {506return true;507}508return v1.equals(v2);509}510511/**512* Find a key with given etype and kvno513* @param kvno if null, return any (first?) key514*/515public static EncryptionKey findKey(int etype, Integer kvno, EncryptionKey[] keys)516throws KrbException {517518// check if encryption type is supported519if (!EType.isSupported(etype)) {520throw new KrbException("Encryption type " +521EType.toString(etype) + " is not supported/enabled");522}523524int ktype;525boolean etypeFound = false;526527// When no matched kvno is found, returns tke key of the same528// etype with the highest kvno529int kvno_found = 0;530EncryptionKey key_found = null;531532for (int i = 0; i < keys.length; i++) {533ktype = keys[i].getEType();534if (EType.isSupported(ktype)) {535Integer kv = keys[i].getKeyVersionNumber();536if (etype == ktype) {537etypeFound = true;538if (versionMatches(kvno, kv)) {539return keys[i];540} else if (kv > kvno_found) {541// kv is not null542key_found = keys[i];543kvno_found = kv;544}545}546}547}548549// Key not found.550// allow DES key to be used for the DES etypes551if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||552etype == EncryptedData.ETYPE_DES_CBC_MD5)) {553for (int i = 0; i < keys.length; i++) {554ktype = keys[i].getEType();555if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||556ktype == EncryptedData.ETYPE_DES_CBC_MD5) {557Integer kv = keys[i].getKeyVersionNumber();558etypeFound = true;559if (versionMatches(kvno, kv)) {560return new EncryptionKey(etype, keys[i].getBytes());561} else if (kv > kvno_found) {562key_found = new EncryptionKey(etype, keys[i].getBytes());563kvno_found = kv;564}565}566}567}568if (etypeFound) {569return key_found;570// For compatibility, will not fail here.571//throw new KrbException(Krb5.KRB_AP_ERR_BADKEYVER);572}573return null;574}575}576577578