1/* 2 * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26/* 27 * 28 * (C) Copyright IBM Corp. 1999 All Rights Reserved. 29 * Copyright 1997 The Open Group Research Institute. All rights reserved. 30 */ 31 32package sun.security.krb5; 33 34import sun.security.util.*; 35import sun.security.krb5.internal.crypto.*; 36import sun.security.krb5.internal.*; 37import java.io.IOException; 38import java.math.BigInteger; 39 40/** 41 * This class encapsulates Kerberos encrypted data. It allows 42 * callers access to both the ASN.1 encoded form of the EncryptedData 43 * type as well as the raw cipher text. 44 */ 45 46public class EncryptedData implements Cloneable { 47 int eType; 48 Integer kvno; // optional 49 byte[] cipher; 50 byte[] plain; // not part of ASN.1 encoding 51 52 // ----------------+-----------+----------+----------------+--------------- 53 // Encryption type |etype value|block size|minimum pad size|confounder size 54 // ----------------+-----------+----------+----------------+--------------- 55 public static final int 56 ETYPE_NULL = 0; // 1 0 0 57 public static final int 58 ETYPE_DES_CBC_CRC = 1; // 8 4 8 59 public static final int 60 ETYPE_DES_CBC_MD4 = 2; // 8 0 8 61 public static final int 62 ETYPE_DES_CBC_MD5 = 3; // 8 0 8 63 64 // draft-brezak-win2k-krb-rc4-hmac-04.txt 65 public static final int 66 ETYPE_ARCFOUR_HMAC = 23; // 1 67 // NOTE: the exportable RC4-HMAC is not supported; 68 // it is no longer a usable encryption type 69 public static final int 70 ETYPE_ARCFOUR_HMAC_EXP = 24; // 1 71 72 // draft-ietf-krb-wg-crypto-07.txt 73 public static final int 74 ETYPE_DES3_CBC_HMAC_SHA1_KD = 16; // 8 0 8 75 76 // draft-raeburn-krb-rijndael-krb-07.txt 77 public static final int 78 ETYPE_AES128_CTS_HMAC_SHA1_96 = 17; // 16 0 16 79 public static final int 80 ETYPE_AES256_CTS_HMAC_SHA1_96 = 18; // 16 0 16 81 82 /* used by self */ 83 private EncryptedData() { 84 } 85 86 public Object clone() { 87 EncryptedData new_encryptedData = new EncryptedData(); 88 new_encryptedData.eType = eType; 89 if (kvno != null) { 90 new_encryptedData.kvno = kvno.intValue(); 91 } 92 if (cipher != null) { 93 new_encryptedData.cipher = new byte[cipher.length]; 94 System.arraycopy(cipher, 0, new_encryptedData.cipher, 95 0, cipher.length); 96 } 97 return new_encryptedData; 98 } 99 100 // Used in JSSE (com.sun.net.ssl.internal.KerberosPreMasterSecret) 101 public EncryptedData( 102 int new_eType, 103 Integer new_kvno, 104 byte[] new_cipher) { 105 eType = new_eType; 106 kvno = new_kvno; 107 cipher = new_cipher; 108 } 109 110 /* 111 // Not used. 112 public EncryptedData( 113 EncryptionKey key, 114 byte[] plaintext) 115 throws KdcErrException, KrbCryptoException { 116 EType etypeEngine = EType.getInstance(key.getEType()); 117 cipher = etypeEngine.encrypt(plaintext, key.getBytes()); 118 eType = key.getEType(); 119 kvno = key.getKeyVersionNumber(); 120 } 121 */ 122 123 // used in KrbApRep, KrbApReq, KrbAsReq, KrbCred, KrbPriv 124 // Used in JSSE (com.sun.net.ssl.internal.KerberosPreMasterSecret) 125 public EncryptedData( 126 EncryptionKey key, 127 byte[] plaintext, 128 int usage) 129 throws KdcErrException, KrbCryptoException { 130 EType etypeEngine = EType.getInstance(key.getEType()); 131 cipher = etypeEngine.encrypt(plaintext, key.getBytes(), usage); 132 eType = key.getEType(); 133 kvno = key.getKeyVersionNumber(); 134 } 135 136 /* 137 // Not used. 138 public EncryptedData( 139 EncryptionKey key, 140 byte[] ivec, 141 byte[] plaintext) 142 throws KdcErrException, KrbCryptoException { 143 EType etypeEngine = EType.getInstance(key.getEType()); 144 cipher = etypeEngine.encrypt(plaintext, key.getBytes(), ivec); 145 eType = key.getEType(); 146 kvno = key.getKeyVersionNumber(); 147 } 148 */ 149 150 /* 151 // Not used. 152 EncryptedData( 153 StringBuffer password, 154 byte[] plaintext) 155 throws KdcErrException, KrbCryptoException { 156 EncryptionKey key = new EncryptionKey(password); 157 EType etypeEngine = EType.getInstance(key.getEType()); 158 cipher = etypeEngine.encrypt(plaintext, key.getBytes()); 159 eType = key.getEType(); 160 kvno = key.getKeyVersionNumber(); 161 } 162 */ 163 public byte[] decrypt( 164 EncryptionKey key, int usage) 165 throws KdcErrException, KrbApErrException, KrbCryptoException { 166 if (eType != key.getEType()) { 167 throw new KrbCryptoException( 168 "EncryptedData is encrypted using keytype " + 169 EType.toString(eType) + 170 " but decryption key is of type " + 171 EType.toString(key.getEType())); 172 } 173 174 EType etypeEngine = EType.getInstance(eType); 175 plain = etypeEngine.decrypt(cipher, key.getBytes(), usage); 176 // The service ticket will be used in S4U2proxy request. Therefore 177 // the raw ticket is still needed. 178 //cipher = null; 179 return etypeEngine.decryptedData(plain); 180 } 181 182 /* 183 // currently destructive on cipher 184 // Not used. 185 public byte[] decrypt( 186 EncryptionKey key, 187 byte[] ivec, int usage) 188 throws KdcErrException, KrbApErrException, KrbCryptoException { 189 // XXX check for matching eType and kvno here 190 EType etypeEngine = EType.getInstance(eType); 191 plain = etypeEngine.decrypt(cipher, key.getBytes(), ivec, usage); 192 cipher = null; 193 return etypeEngine.decryptedData(plain); 194 } 195 196 // currently destructive on cipher 197 // Not used. 198 byte[] decrypt(StringBuffer password) 199 throws KdcErrException, KrbApErrException, KrbCryptoException { 200 EncryptionKey key = new EncryptionKey(password); 201 // XXX check for matching eType here 202 EType etypeEngine = EType.getInstance(eType); 203 plain = etypeEngine.decrypt(cipher, key.getBytes()); 204 cipher = null; 205 return etypeEngine.decryptedData(plain); 206 } 207 */ 208 209 private byte[] decryptedData() throws KdcErrException { 210 if (plain != null) { 211 EType etypeEngine = EType.getInstance(eType); 212 return etypeEngine.decryptedData(plain); 213 } 214 return null; 215 } 216 217 /** 218 * Constructs an instance of EncryptedData type. 219 * @param encoding a single DER-encoded value. 220 * @exception Asn1Exception if an error occurs while decoding an 221 * ASN1 encoded data. 222 * @exception IOException if an I/O error occurs while reading encoded 223 * data. 224 * 225 */ 226 /* Used by self */ 227 private EncryptedData(DerValue encoding) 228 throws Asn1Exception, IOException { 229 230 DerValue der = null; 231 if (encoding.getTag() != DerValue.tag_Sequence) { 232 throw new Asn1Exception(Krb5.ASN1_BAD_ID); 233 } 234 der = encoding.getData().getDerValue(); 235 if ((der.getTag() & (byte)0x1F) == (byte)0x00) { 236 eType = (der.getData().getBigInteger()).intValue(); 237 } else { 238 throw new Asn1Exception(Krb5.ASN1_BAD_ID); 239 } 240 241 if ((encoding.getData().peekByte() & 0x1F) == 1) { 242 der = encoding.getData().getDerValue(); 243 int i = (der.getData().getBigInteger()).intValue(); 244 kvno = i; 245 } else { 246 kvno = null; 247 } 248 der = encoding.getData().getDerValue(); 249 if ((der.getTag() & (byte)0x1F) == (byte)0x02) { 250 cipher = der.getData().getOctetString(); 251 } else { 252 throw new Asn1Exception(Krb5.ASN1_BAD_ID); 253 } 254 if (encoding.getData().available() > 0) { 255 throw new Asn1Exception(Krb5.ASN1_BAD_ID); 256 } 257 } 258 259 /** 260 * Returns an ASN.1 encoded EncryptedData type. 261 * 262 * <pre>{@code 263 * EncryptedData ::= SEQUENCE { 264 * etype [0] Int32 -- EncryptionType --, 265 * kvno [1] UInt32 OPTIONAL, 266 * cipher [2] OCTET STRING -- ciphertext 267 * } 268 * }</pre> 269 * 270 * <p> 271 * This definition reflects the Network Working Group RFC 4120 272 * specification available at 273 * <a href="http://www.ietf.org/rfc/rfc4120.txt"> 274 * http://www.ietf.org/rfc/rfc4120.txt</a>. 275 * 276 * @return byte array of encoded EncryptedData object. 277 * @exception Asn1Exception if an error occurs while decoding an 278 * ASN1 encoded data. 279 * @exception IOException if an I/O error occurs while reading 280 * encoded data. 281 * 282 */ 283 public byte[] asn1Encode() throws Asn1Exception, IOException { 284 DerOutputStream bytes = new DerOutputStream(); 285 DerOutputStream temp = new DerOutputStream(); 286 temp.putInteger(BigInteger.valueOf(this.eType)); 287 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, 288 true, (byte)0x00), temp); 289 temp = new DerOutputStream(); 290 if (kvno != null) { 291 // encode as an unsigned integer (UInt32) 292 temp.putInteger(BigInteger.valueOf(this.kvno.longValue())); 293 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, 294 true, (byte)0x01), temp); 295 temp = new DerOutputStream(); 296 } 297 temp.putOctetString(this.cipher); 298 bytes.write(DerValue.createTag(DerValue.TAG_CONTEXT, true, 299 (byte)0x02), temp); 300 temp = new DerOutputStream(); 301 temp.write(DerValue.tag_Sequence, bytes); 302 return temp.toByteArray(); 303 } 304 305 306 /** 307 * Parse (unmarshal) an EncryptedData from a DER input stream. This form 308 * parsing might be used when expanding a value which is part of 309 * a constructed sequence and uses explicitly tagged type. 310 * 311 * @param data the Der input stream value, which contains one or more 312 * marshaled value. 313 * @param explicitTag tag number. 314 * @param optional indicate if this data field is optional 315 * @exception Asn1Exception if an error occurs while decoding an 316 * ASN1 encoded data. 317 * @exception IOException if an I/O error occurs while reading 318 * encoded data. 319 * @return an instance of EncryptedData. 320 * 321 */ 322 public static EncryptedData parse(DerInputStream data, 323 byte explicitTag, 324 boolean optional) 325 throws Asn1Exception, IOException { 326 if ((optional) && 327 (((byte)data.peekByte() & (byte)0x1F) != explicitTag)) 328 return null; 329 DerValue der = data.getDerValue(); 330 if (explicitTag != (der.getTag() & (byte)0x1F)) { 331 throw new Asn1Exception(Krb5.ASN1_BAD_ID); 332 } else { 333 DerValue subDer = der.getData().getDerValue(); 334 return new EncryptedData(subDer); 335 } 336 } 337 338 /** 339 * Reset asn.1 data stream after decryption, remove redundant bytes. 340 * @param data the decrypted data from decrypt(). 341 * @return the reset byte array which holds exactly one asn1 datum 342 * including its tag and length. 343 * 344 */ 345 public byte[] reset(byte[] data) { 346 byte[] bytes = null; 347 // for asn.1 encoded data, we use length field to 348 // determine the data length and remove redundant paddings. 349 if ((data[1] & 0xFF) < 128) { 350 bytes = new byte[data[1] + 2]; 351 System.arraycopy(data, 0, bytes, 0, data[1] + 2); 352 } else { 353 if ((data[1] & 0xFF) > 128) { 354 int len = data[1] & (byte)0x7F; 355 int result = 0; 356 for (int i = 0; i < len; i++) { 357 result |= (data[i + 2] & 0xFF) << (8 * (len - i - 1)); 358 } 359 bytes = new byte[result + len + 2]; 360 System.arraycopy(data, 0, bytes, 0, result + len + 2); 361 } 362 } 363 return bytes; 364 } 365 366 public int getEType() { 367 return eType; 368 } 369 370 public Integer getKeyVersionNumber() { 371 return kvno; 372 } 373 374 /** 375 * Returns the raw cipher text bytes, not in ASN.1 encoding. 376 */ 377 public byte[] getBytes() { 378 return cipher; 379 } 380} 381