Path: blob/master/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java
41159 views
/*1* Copyright (c) 2000, 2019, 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 Credentials, and javax.security.auth.kerberos.KeyImpl221// Warning: called by NativeCreds.c and nativeccache.c222public 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);253254case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA256_128:255return Aes128Sha2.stringToKey(password, salt, s2kparams);256257case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA384_192:258return Aes256Sha2.stringToKey(password, salt, s2kparams);259260default:261throw new IllegalArgumentException("encryption type " +262EType.toString(keyType) + " not supported");263}264265} catch (GeneralSecurityException e) {266KrbCryptoException ke = new KrbCryptoException(e.getMessage());267ke.initCause(e);268throw ke;269} finally {270Arrays.fill(pwsalt, '0');271}272}273274// Used in javax.security.auth.kerberos.KeyImpl275public EncryptionKey(char[] password,276String salt,277String algorithm) throws KrbCryptoException {278279if (algorithm == null || algorithm.equalsIgnoreCase("DES")280|| algorithm.equalsIgnoreCase("des-cbc-md5")) {281keyType = EncryptedData.ETYPE_DES_CBC_MD5;282} else if (algorithm.equalsIgnoreCase("des-cbc-crc")) {283keyType = EncryptedData.ETYPE_DES_CBC_CRC;284} else if (algorithm.equalsIgnoreCase("DESede")285|| algorithm.equalsIgnoreCase("des3-cbc-sha1-kd")) {286keyType = EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD;287} else if (algorithm.equalsIgnoreCase("AES128")288|| algorithm.equalsIgnoreCase("aes128-cts-hmac-sha1-96")) {289keyType = EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96;290} else if (algorithm.equalsIgnoreCase("ArcFourHmac")291|| algorithm.equalsIgnoreCase("rc4-hmac")) {292keyType = EncryptedData.ETYPE_ARCFOUR_HMAC;293} else if (algorithm.equalsIgnoreCase("AES256")294|| algorithm.equalsIgnoreCase("aes256-cts-hmac-sha1-96")) {295keyType = EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96;296// validate if AES256 is enabled297if (!EType.isSupported(keyType)) {298throw new IllegalArgumentException("Algorithm " + algorithm +299" not enabled");300}301} else if (algorithm.equalsIgnoreCase("aes128-cts-hmac-sha256-128")) {302keyType = EncryptedData.ETYPE_AES128_CTS_HMAC_SHA256_128;303} else if (algorithm.equalsIgnoreCase("aes256-cts-hmac-sha384-192")) {304keyType = EncryptedData.ETYPE_AES256_CTS_HMAC_SHA384_192;305// validate if AES256 is enabled306if (!EType.isSupported(keyType)) {307throw new IllegalArgumentException("Algorithm " + algorithm +308" not enabled");309}310} else {311throw new IllegalArgumentException("Algorithm " + algorithm +312" not supported");313}314315keyValue = stringToKey(password, salt, null, keyType);316kvno = null;317}318319/**320* Generates a sub-sessionkey from a given session key.321*322* Used in AcceptSecContextToken and KrbApReq by acceptor- and initiator-323* side respectively.324*/325public EncryptionKey(EncryptionKey key) throws KrbCryptoException {326// generate random sub-session key327keyValue = Confounder.bytes(key.keyValue.length);328for (int i = 0; i < keyValue.length; i++) {329keyValue[i] ^= key.keyValue[i];330}331keyType = key.keyType;332333// check for key parity and weak keys334try {335// check for DES key336if ((keyType == EncryptedData.ETYPE_DES_CBC_MD5) ||337(keyType == EncryptedData.ETYPE_DES_CBC_CRC)) {338// fix DES key parity339if (!DESKeySpec.isParityAdjusted(keyValue, 0)) {340keyValue = Des.set_parity(keyValue);341}342// check for weak key343if (DESKeySpec.isWeak(keyValue, 0)) {344keyValue[7] = (byte)(keyValue[7] ^ 0xF0);345}346}347// check for 3DES key348if (keyType == EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {349// fix 3DES key parity350if (!DESedeKeySpec.isParityAdjusted(keyValue, 0)) {351keyValue = Des3.parityFix(keyValue);352}353// check for weak keys354byte[] oneKey = new byte[8];355for (int i=0; i<keyValue.length; i+=8) {356System.arraycopy(keyValue, i, oneKey, 0, 8);357if (DESKeySpec.isWeak(oneKey, 0)) {358keyValue[i+7] = (byte)(keyValue[i+7] ^ 0xF0);359}360}361}362} catch (GeneralSecurityException e) {363KrbCryptoException ke = new KrbCryptoException(e.getMessage());364ke.initCause(e);365throw ke;366}367}368369/**370* Constructs an instance of EncryptionKey type.371* @param encoding a single DER-encoded value.372* @exception Asn1Exception if an error occurs while decoding an ASN1373* encoded data.374* @exception IOException if an I/O error occurs while reading encoded375* data.376*377*378*/379// Used in javax.security.auth.kerberos.KeyImpl380public EncryptionKey(DerValue encoding) throws Asn1Exception, IOException {381DerValue der;382if (encoding.getTag() != DerValue.tag_Sequence) {383throw new Asn1Exception(Krb5.ASN1_BAD_ID);384}385der = encoding.getData().getDerValue();386if ((der.getTag() & (byte)0x1F) == (byte)0x00) {387keyType = der.getData().getBigInteger().intValue();388}389else390throw new Asn1Exception(Krb5.ASN1_BAD_ID);391der = encoding.getData().getDerValue();392if ((der.getTag() & (byte)0x1F) == (byte)0x01) {393keyValue = der.getData().getOctetString();394}395else396throw new Asn1Exception(Krb5.ASN1_BAD_ID);397if (der.getData().available() > 0) {398throw new Asn1Exception(Krb5.ASN1_BAD_ID);399}400}401402/**403* Returns the ASN.1 encoding of this EncryptionKey.404*405* <pre>{@code406* EncryptionKey ::= SEQUENCE {407* keytype[0] INTEGER,408* keyvalue[1] OCTET STRING }409* }</pre>410*411* <p>412* This definition reflects the Network Working Group RFC 4120413* specification available at414* <a href="http://www.ietf.org/rfc/rfc4120.txt">415* http://www.ietf.org/rfc/rfc4120.txt</a>.416*417* @return byte array of encoded EncryptionKey object.418* @exception Asn1Exception if an error occurs while decoding an ASN1419* encoded data.420* @exception IOException if an I/O error occurs while reading encoded421* data.422*423*/424public synchronized byte[] asn1Encode() throws Asn1Exception, IOException {425DerOutputStream bytes = new DerOutputStream();426DerOutputStream temp = new DerOutputStream();427temp.putInteger(keyType);428bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,429(byte)0x00), temp);430temp = new DerOutputStream();431temp.putOctetString(keyValue);432bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true,433(byte)0x01), temp);434temp = new DerOutputStream();435temp.write(DerValue.tag_Sequence, bytes);436return temp.toByteArray();437}438439public synchronized void destroy() {440if (keyValue != null)441for (int i = 0; i < keyValue.length; i++)442keyValue[i] = 0;443}444445446/**447* Parse (unmarshal) an Encryption key from a DER input stream. This form448* parsing might be used when expanding a value which is part of449* a constructed sequence and uses explicitly tagged type.450*451* @param data the Der input stream value, which contains one or more452* marshaled value.453* @param explicitTag tag number.454* @param optional indicate if this data field is optional455* @exception Asn1Exception if an error occurs while decoding an ASN1456* encoded data.457* @exception IOException if an I/O error occurs while reading encoded458* data.459* @return an instance of EncryptionKey.460*461*/462public static EncryptionKey parse(DerInputStream data, byte463explicitTag, boolean optional) throws464Asn1Exception, IOException {465if ((optional) && (((byte)data.peekByte() & (byte)0x1F) !=466explicitTag)) {467return null;468}469DerValue der = data.getDerValue();470if (explicitTag != (der.getTag() & (byte)0x1F)) {471throw new Asn1Exception(Krb5.ASN1_BAD_ID);472} else {473DerValue subDer = der.getData().getDerValue();474return new EncryptionKey(subDer);475}476}477478/**479* Writes key value in FCC format to a <code>CCacheOutputStream</code>.480*481* @param cos a <code>CCacheOutputStream</code> to be written to.482* @exception IOException if an I/O exception occurs.483* @see sun.security.krb5.internal.ccache.CCacheOutputStream484*485*/486public synchronized void writeKey(CCacheOutputStream cos)487throws IOException {488489cos.write16(keyType);490// we use KRB5_FCC_FVNO_3491cos.write16(keyType); // key type is recorded twice.492cos.write32(keyValue.length);493for (int i = 0; i < keyValue.length; i++) {494cos.write8(keyValue[i]);495}496}497498public String toString() {499return new String("EncryptionKey: keyType=" + keyType500+ " kvno=" + kvno501+ " keyValue (hex dump)="502+ (keyValue == null || keyValue.length == 0 ?503" Empty Key" : '\n'504+ Krb5.hexDumper.encodeBuffer(keyValue)505+ '\n'));506}507508/**509* Find a key with given etype510*/511public static EncryptionKey findKey(int etype, EncryptionKey[] keys)512throws KrbException {513return findKey(etype, null, keys);514}515516/**517* Determines if a kvno matches another kvno. Used in the method518* findKey(type, kvno, keys). Always returns true if either input519* is null or zero, in case any side does not have kvno info available.520*521* Note: zero is included because N/A is not a legal value for kvno522* in javax.security.auth.kerberos.KerberosKey. Therefore, the info523* that the kvno is N/A might be lost when converting between this524* class and KerberosKey.525*/526private static boolean versionMatches(Integer v1, Integer v2) {527if (v1 == null || v1 == 0 || v2 == null || v2 == 0) {528return true;529}530return v1.equals(v2);531}532533/**534* Find a key with given etype and kvno535* @param kvno if null, return any (first?) key536*/537public static EncryptionKey findKey(int etype, Integer kvno, EncryptionKey[] keys)538throws KrbException {539540// check if encryption type is supported541if (!EType.isSupported(etype)) {542throw new KrbException("Encryption type " +543EType.toString(etype) + " is not supported/enabled");544}545546int ktype;547boolean etypeFound = false;548549// When no matched kvno is found, returns tke key of the same550// etype with the highest kvno551int kvno_found = 0;552EncryptionKey key_found = null;553554for (int i = 0; i < keys.length; i++) {555ktype = keys[i].getEType();556if (EType.isSupported(ktype)) {557Integer kv = keys[i].getKeyVersionNumber();558if (etype == ktype) {559etypeFound = true;560if (versionMatches(kvno, kv)) {561return keys[i];562} else if (kv > kvno_found) {563// kv is not null564key_found = keys[i];565kvno_found = kv;566}567}568}569}570571// Key not found.572// allow DES key to be used for the DES etypes573if ((etype == EncryptedData.ETYPE_DES_CBC_CRC ||574etype == EncryptedData.ETYPE_DES_CBC_MD5)) {575for (int i = 0; i < keys.length; i++) {576ktype = keys[i].getEType();577if (ktype == EncryptedData.ETYPE_DES_CBC_CRC ||578ktype == EncryptedData.ETYPE_DES_CBC_MD5) {579Integer kv = keys[i].getKeyVersionNumber();580etypeFound = true;581if (versionMatches(kvno, kv)) {582return new EncryptionKey(etype, keys[i].getBytes());583} else if (kv > kvno_found) {584key_found = new EncryptionKey(etype, keys[i].getBytes());585kvno_found = kv;586}587}588}589}590if (etypeFound) {591return key_found;592// For compatibility, will not fail here.593//throw new KrbException(Krb5.KRB_AP_ERR_BADKEYVER);594}595return null;596}597}598599600