0N/A/*
3321N/A * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.jgss.krb5;
0N/A
0N/Aimport javax.crypto.Cipher;
0N/Aimport javax.crypto.SecretKey;
0N/Aimport javax.crypto.spec.IvParameterSpec;
0N/Aimport javax.crypto.spec.SecretKeySpec;
0N/Aimport javax.crypto.CipherInputStream;
0N/Aimport javax.crypto.CipherOutputStream;
0N/Aimport java.io.InputStream;
0N/Aimport java.io.OutputStream;
0N/Aimport java.io.IOException;
0N/Aimport org.ietf.jgss.*;
0N/A
0N/Aimport java.security.MessageDigest;
0N/Aimport java.security.GeneralSecurityException;
0N/Aimport java.security.NoSuchAlgorithmException;
0N/Aimport sun.security.krb5.*;
0N/Aimport sun.security.krb5.internal.crypto.Des3;
0N/Aimport sun.security.krb5.internal.crypto.Aes128;
0N/Aimport sun.security.krb5.internal.crypto.Aes256;
0N/Aimport sun.security.krb5.internal.crypto.ArcFourHmac;
0N/A
0N/Aclass CipherHelper {
0N/A
0N/A // From draft-raeburn-cat-gssapi-krb5-3des-00
0N/A // Key usage values when deriving keys
0N/A private static final int KG_USAGE_SEAL = 22;
0N/A private static final int KG_USAGE_SIGN = 23;
0N/A private static final int KG_USAGE_SEQ = 24;
0N/A
0N/A private static final int DES_CHECKSUM_SIZE = 8;
0N/A private static final int DES_IV_SIZE = 8;
0N/A private static final int AES_IV_SIZE = 16;
0N/A
0N/A // ARCFOUR-HMAC
0N/A // Save first 8 octets of HMAC Sgn_Cksum
0N/A private static final int HMAC_CHECKSUM_SIZE = 8;
0N/A // key usage for MIC tokens used by MS
0N/A private static final int KG_USAGE_SIGN_MS = 15;
0N/A
0N/A // debug flag
0N/A private static final boolean DEBUG = Krb5Util.DEBUG;
0N/A
0N/A /**
0N/A * A zero initial vector to be used for checksum calculation and for
0N/A * DesCbc application data encryption/decryption.
0N/A */
0N/A private static final byte[] ZERO_IV = new byte[DES_IV_SIZE];
0N/A private static final byte[] ZERO_IV_AES = new byte[AES_IV_SIZE];
0N/A
0N/A private int etype;
0N/A private int sgnAlg, sealAlg;
0N/A private byte[] keybytes;
0N/A
0N/A // new token format from draft-ietf-krb-wg-gssapi-cfx-07
0N/A // proto is used to determine new GSS token format for "newer" etypes
0N/A private int proto = 0;
0N/A
0N/A CipherHelper(EncryptionKey key) throws GSSException {
0N/A etype = key.getEType();
0N/A keybytes = key.getBytes();
0N/A
0N/A switch (etype) {
0N/A case EncryptedData.ETYPE_DES_CBC_CRC:
0N/A case EncryptedData.ETYPE_DES_CBC_MD5:
0N/A sgnAlg = MessageToken.SGN_ALG_DES_MAC_MD5;
0N/A sealAlg = MessageToken.SEAL_ALG_DES;
0N/A break;
0N/A
0N/A case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD:
0N/A sgnAlg = MessageToken.SGN_ALG_HMAC_SHA1_DES3_KD;
0N/A sealAlg = MessageToken.SEAL_ALG_DES3_KD;
0N/A break;
0N/A
0N/A case EncryptedData.ETYPE_ARCFOUR_HMAC:
0N/A sgnAlg = MessageToken.SGN_ALG_HMAC_MD5_ARCFOUR;
0N/A sealAlg = MessageToken.SEAL_ALG_ARCFOUR_HMAC;
0N/A break;
0N/A
0N/A case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
0N/A case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
0N/A sgnAlg = -1;
0N/A sealAlg = -1;
0N/A proto = 1;
0N/A break;
0N/A
0N/A default:
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Unsupported encryption type: " + etype);
0N/A }
0N/A }
0N/A
0N/A int getSgnAlg() {
0N/A return sgnAlg;
0N/A }
0N/A
0N/A int getSealAlg() {
0N/A return sealAlg;
0N/A }
0N/A
0N/A int getProto() {
0N/A return proto;
0N/A }
0N/A
0N/A int getEType() {
0N/A return etype;
0N/A }
0N/A
0N/A boolean isArcFour() {
0N/A boolean flag = false;
0N/A if (etype == EncryptedData.ETYPE_ARCFOUR_HMAC) {
0N/A flag = true;
0N/A }
0N/A return flag;
0N/A }
0N/A
0N/A byte[] calculateChecksum(int alg, byte[] header, byte[] trailer,
0N/A byte[] data, int start, int len, int tokenId) throws GSSException {
0N/A
0N/A switch (alg) {
0N/A case MessageToken.SGN_ALG_DES_MAC_MD5:
0N/A /*
0N/A * With this sign algorithm, first an MD5 hash is computed on the
0N/A * application data. The 16 byte hash is then DesCbc encrypted.
0N/A */
0N/A try {
0N/A MessageDigest md5 = MessageDigest.getInstance("MD5");
0N/A
0N/A // debug("\t\tdata=[");
0N/A
0N/A // debug(getHexBytes(checksumDataHeader,
0N/A // checksumDataHeader.length) + " ");
0N/A md5.update(header);
0N/A
0N/A // debug(getHexBytes(data, start, len));
0N/A md5.update(data, start, len);
0N/A
0N/A if (trailer != null) {
0N/A // debug(" " +
0N/A // getHexBytes(trailer,
0N/A // optionalTrailer.length));
0N/A md5.update(trailer);
0N/A }
0N/A // debug("]\n");
0N/A
0N/A data = md5.digest();
0N/A start = 0;
0N/A len = data.length;
0N/A // System.out.println("\tMD5 Checksum is [" +
0N/A // getHexBytes(data) + "]\n");
0N/A header = null;
0N/A trailer = null;
0N/A } catch (NoSuchAlgorithmException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not get MD5 Message Digest - " + e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A // fall through to encrypt checksum
0N/A
0N/A case MessageToken.SGN_ALG_DES_MAC:
0N/A return getDesCbcChecksum(keybytes, header, data, start, len);
0N/A
0N/A case MessageToken.SGN_ALG_HMAC_SHA1_DES3_KD:
0N/A byte[] buf;
0N/A int offset, total;
0N/A if (header == null && trailer == null) {
0N/A buf = data;
0N/A total = len;
0N/A offset = start;
0N/A } else {
0N/A total = ((header != null ? header.length : 0) + len +
0N/A (trailer != null ? trailer.length : 0));
0N/A
0N/A buf = new byte[total];
0N/A int pos = 0;
0N/A if (header != null) {
0N/A System.arraycopy(header, 0, buf, 0, header.length);
0N/A pos = header.length;
0N/A }
0N/A System.arraycopy(data, start, buf, pos, len);
0N/A pos += len;
0N/A if (trailer != null) {
0N/A System.arraycopy(trailer, 0, buf, pos, trailer.length);
0N/A }
0N/A
0N/A offset = 0;
0N/A }
0N/A
0N/A try {
0N/A
0N/A /*
0N/A Krb5Token.debug("\nkeybytes: " +
0N/A Krb5Token.getHexBytes(keybytes));
0N/A Krb5Token.debug("\nheader: " + (header == null ? "NONE" :
0N/A Krb5Token.getHexBytes(header)));
0N/A Krb5Token.debug("\ntrailer: " + (trailer == null ? "NONE" :
0N/A Krb5Token.getHexBytes(trailer)));
0N/A Krb5Token.debug("\ndata: " +
0N/A Krb5Token.getHexBytes(data, start, len));
0N/A Krb5Token.debug("\nbuf: " + Krb5Token.getHexBytes(buf, offset,
0N/A total));
0N/A */
0N/A
0N/A byte[] answer = Des3.calculateChecksum(keybytes,
0N/A KG_USAGE_SIGN, buf, offset, total);
0N/A // Krb5Token.debug("\nanswer: " +
0N/A // Krb5Token.getHexBytes(answer));
0N/A return answer;
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use HMAC-SHA1-DES3-KD signing algorithm - " +
0N/A e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A case MessageToken.SGN_ALG_HMAC_MD5_ARCFOUR:
0N/A byte[] buffer;
0N/A int off, tot;
0N/A if (header == null && trailer == null) {
0N/A buffer = data;
0N/A tot = len;
0N/A off = start;
0N/A } else {
0N/A tot = ((header != null ? header.length : 0) + len +
0N/A (trailer != null ? trailer.length : 0));
0N/A
0N/A buffer = new byte[tot];
0N/A int pos = 0;
0N/A
0N/A if (header != null) {
0N/A System.arraycopy(header, 0, buffer, 0, header.length);
0N/A pos = header.length;
0N/A }
0N/A System.arraycopy(data, start, buffer, pos, len);
0N/A pos += len;
0N/A if (trailer != null) {
0N/A System.arraycopy(trailer, 0, buffer, pos, trailer.length);
0N/A }
0N/A
0N/A off = 0;
0N/A }
0N/A
0N/A try {
0N/A
0N/A /*
0N/A Krb5Token.debug("\nkeybytes: " +
0N/A Krb5Token.getHexBytes(keybytes));
0N/A Krb5Token.debug("\nheader: " + (header == null ? "NONE" :
0N/A Krb5Token.getHexBytes(header)));
0N/A Krb5Token.debug("\ntrailer: " + (trailer == null ? "NONE" :
0N/A Krb5Token.getHexBytes(trailer)));
0N/A Krb5Token.debug("\ndata: " +
0N/A Krb5Token.getHexBytes(data, start, len));
0N/A Krb5Token.debug("\nbuffer: " +
0N/A Krb5Token.getHexBytes(buffer, off, tot));
0N/A */
0N/A
0N/A // for MIC tokens, key derivation salt is 15
0N/A // NOTE: Required for interoperability. The RC4-HMAC spec
0N/A // defines key_usage of 23, however all Kerberos impl.
0N/A // MS/Solaris/MIT all use key_usage of 15 for MIC tokens
0N/A int key_usage = KG_USAGE_SIGN;
0N/A if (tokenId == Krb5Token.MIC_ID) {
0N/A key_usage = KG_USAGE_SIGN_MS;
0N/A }
0N/A byte[] answer = ArcFourHmac.calculateChecksum(keybytes,
0N/A key_usage, buffer, off, tot);
0N/A // Krb5Token.debug("\nanswer: " +
0N/A // Krb5Token.getHexBytes(answer));
0N/A
0N/A // Save first 8 octets of HMAC Sgn_Cksum
0N/A byte[] output = new byte[getChecksumLength()];
0N/A System.arraycopy(answer, 0, output, 0, output.length);
0N/A // Krb5Token.debug("\nanswer (trimmed): " +
0N/A // Krb5Token.getHexBytes(output));
0N/A return output;
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use HMAC_MD5_ARCFOUR signing algorithm - " +
0N/A e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A default:
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Unsupported signing algorithm: " + sgnAlg);
0N/A }
0N/A }
0N/A
0N/A // calculate Checksum for the new GSS tokens
0N/A byte[] calculateChecksum(byte[] header, byte[] data, int start, int len,
0N/A int key_usage) throws GSSException {
0N/A
0N/A // total length
0N/A int total = ((header != null ? header.length : 0) + len);
0N/A
0N/A // get_mic("plaintext-data" | "header")
0N/A byte[] buf = new byte[total];
0N/A
0N/A // data
0N/A System.arraycopy(data, start, buf, 0, len);
0N/A
0N/A // token header
0N/A if (header != null) {
0N/A System.arraycopy(header, 0, buf, len, header.length);
0N/A }
0N/A
0N/A // Krb5Token.debug("\nAES calculate checksum on: " +
0N/A // Krb5Token.getHexBytes(buf));
0N/A switch (etype) {
0N/A case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
0N/A try {
0N/A byte[] answer = Aes128.calculateChecksum(keybytes, key_usage,
0N/A buf, 0, total);
0N/A // Krb5Token.debug("\nAES128 checksum: " +
0N/A // Krb5Token.getHexBytes(answer));
0N/A return answer;
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use AES128 signing algorithm - " +
0N/A e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
0N/A try {
0N/A byte[] answer = Aes256.calculateChecksum(keybytes, key_usage,
0N/A buf, 0, total);
0N/A // Krb5Token.debug("\nAES256 checksum: " +
0N/A // Krb5Token.getHexBytes(answer));
0N/A return answer;
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use AES256 signing algorithm - " +
0N/A e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A default:
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Unsupported encryption type: " + etype);
0N/A }
0N/A }
0N/A
0N/A byte[] encryptSeq(byte[] ivec, byte[] plaintext, int start, int len)
0N/A throws GSSException {
0N/A
0N/A switch (sgnAlg) {
0N/A case MessageToken.SGN_ALG_DES_MAC_MD5:
0N/A case MessageToken.SGN_ALG_DES_MAC:
0N/A try {
0N/A Cipher des = getInitializedDes(true, keybytes, ivec);
0N/A return des.doFinal(plaintext, start, len);
0N/A
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not encrypt sequence number using DES - " +
0N/A e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A case MessageToken.SGN_ALG_HMAC_SHA1_DES3_KD:
0N/A byte[] iv;
0N/A if (ivec.length == DES_IV_SIZE) {
0N/A iv = ivec;
0N/A } else {
0N/A iv = new byte[DES_IV_SIZE];
0N/A System.arraycopy(ivec, 0, iv, 0, DES_IV_SIZE);
0N/A }
0N/A try {
0N/A return Des3.encryptRaw(keybytes, KG_USAGE_SEQ, iv,
0N/A plaintext, start, len);
0N/A } catch (Exception e) {
0N/A // GeneralSecurityException, KrbCryptoException
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not encrypt sequence number using DES3-KD - " +
0N/A e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A case MessageToken.SGN_ALG_HMAC_MD5_ARCFOUR:
0N/A // ivec passed is the checksum
0N/A byte[] checksum;
0N/A if (ivec.length == HMAC_CHECKSUM_SIZE) {
0N/A checksum = ivec;
0N/A } else {
0N/A checksum = new byte[HMAC_CHECKSUM_SIZE];
0N/A System.arraycopy(ivec, 0, checksum, 0, HMAC_CHECKSUM_SIZE);
0N/A }
0N/A
0N/A try {
0N/A return ArcFourHmac.encryptSeq(keybytes, KG_USAGE_SEQ, checksum,
0N/A plaintext, start, len);
0N/A } catch (Exception e) {
0N/A // GeneralSecurityException, KrbCryptoException
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not encrypt sequence number using RC4-HMAC - " +
0N/A e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A default:
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Unsupported signing algorithm: " + sgnAlg);
0N/A }
0N/A }
0N/A
0N/A byte[] decryptSeq(byte[] ivec, byte[] ciphertext, int start, int len)
0N/A throws GSSException {
0N/A
0N/A switch (sgnAlg) {
0N/A case MessageToken.SGN_ALG_DES_MAC_MD5:
0N/A case MessageToken.SGN_ALG_DES_MAC:
0N/A try {
0N/A Cipher des = getInitializedDes(false, keybytes, ivec);
0N/A return des.doFinal(ciphertext, start, len);
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not decrypt sequence number using DES - " +
0N/A e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A case MessageToken.SGN_ALG_HMAC_SHA1_DES3_KD:
0N/A byte[] iv;
0N/A if (ivec.length == DES_IV_SIZE) {
0N/A iv = ivec;
0N/A } else {
0N/A iv = new byte[8];
0N/A System.arraycopy(ivec, 0, iv, 0, DES_IV_SIZE);
0N/A }
0N/A
0N/A try {
0N/A return Des3.decryptRaw(keybytes, KG_USAGE_SEQ, iv,
0N/A ciphertext, start, len);
0N/A } catch (Exception e) {
0N/A // GeneralSecurityException, KrbCryptoException
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not decrypt sequence number using DES3-KD - " +
0N/A e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A case MessageToken.SGN_ALG_HMAC_MD5_ARCFOUR:
0N/A // ivec passed is the checksum
0N/A byte[] checksum;
0N/A if (ivec.length == HMAC_CHECKSUM_SIZE) {
0N/A checksum = ivec;
0N/A } else {
0N/A checksum = new byte[HMAC_CHECKSUM_SIZE];
0N/A System.arraycopy(ivec, 0, checksum, 0, HMAC_CHECKSUM_SIZE);
0N/A }
0N/A
0N/A try {
0N/A return ArcFourHmac.decryptSeq(keybytes, KG_USAGE_SEQ, checksum,
0N/A ciphertext, start, len);
0N/A } catch (Exception e) {
0N/A // GeneralSecurityException, KrbCryptoException
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not decrypt sequence number using RC4-HMAC - " +
0N/A e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A default:
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Unsupported signing algorithm: " + sgnAlg);
0N/A }
0N/A }
0N/A
0N/A int getChecksumLength() throws GSSException {
0N/A switch (etype) {
0N/A case EncryptedData.ETYPE_DES_CBC_CRC:
0N/A case EncryptedData.ETYPE_DES_CBC_MD5:
0N/A return DES_CHECKSUM_SIZE;
0N/A
0N/A case EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD:
0N/A return Des3.getChecksumLength();
0N/A
0N/A case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
0N/A return Aes128.getChecksumLength();
0N/A case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
0N/A return Aes256.getChecksumLength();
0N/A
0N/A case EncryptedData.ETYPE_ARCFOUR_HMAC:
0N/A // only first 8 octets of HMAC Sgn_Cksum are used
0N/A return HMAC_CHECKSUM_SIZE;
0N/A
0N/A default:
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Unsupported encryption type: " + etype);
0N/A }
0N/A }
0N/A
0N/A void decryptData(WrapToken token, byte[] ciphertext, int cStart, int cLen,
0N/A byte[] plaintext, int pStart) throws GSSException {
0N/A
0N/A /*
0N/A Krb5Token.debug("decryptData : ciphertext = " +
0N/A Krb5Token.getHexBytes(ciphertext));
0N/A */
0N/A
0N/A switch (sealAlg) {
0N/A case MessageToken.SEAL_ALG_DES:
0N/A desCbcDecrypt(token, getDesEncryptionKey(keybytes),
0N/A ciphertext, cStart, cLen, plaintext, pStart);
0N/A break;
0N/A
0N/A case MessageToken.SEAL_ALG_DES3_KD:
0N/A des3KdDecrypt(token, ciphertext, cStart, cLen, plaintext, pStart);
0N/A break;
0N/A
0N/A case MessageToken.SEAL_ALG_ARCFOUR_HMAC:
0N/A arcFourDecrypt(token, ciphertext, cStart, cLen, plaintext, pStart);
0N/A break;
0N/A
0N/A default:
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Unsupported seal algorithm: " + sealAlg);
0N/A }
0N/A }
0N/A
0N/A // decrypt data in the new GSS tokens
0N/A void decryptData(WrapToken_v2 token, byte[] ciphertext, int cStart,
0N/A int cLen, byte[] plaintext, int pStart, int key_usage)
0N/A throws GSSException {
0N/A
0N/A /*
0N/A Krb5Token.debug("decryptData : ciphertext = " +
0N/A Krb5Token.getHexBytes(ciphertext));
0N/A */
0N/A
0N/A switch (etype) {
0N/A case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
0N/A aes128Decrypt(token, ciphertext, cStart, cLen,
0N/A plaintext, pStart, key_usage);
0N/A break;
0N/A case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
0N/A aes256Decrypt(token, ciphertext, cStart, cLen,
0N/A plaintext, pStart, key_usage);
0N/A break;
0N/A default:
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Unsupported etype: " + etype);
0N/A }
0N/A }
0N/A
0N/A void decryptData(WrapToken token, InputStream cipherStream, int cLen,
0N/A byte[] plaintext, int pStart)
0N/A throws GSSException, IOException {
0N/A
0N/A switch (sealAlg) {
0N/A case MessageToken.SEAL_ALG_DES:
0N/A desCbcDecrypt(token, getDesEncryptionKey(keybytes),
0N/A cipherStream, cLen, plaintext, pStart);
0N/A break;
0N/A
0N/A case MessageToken.SEAL_ALG_DES3_KD:
0N/A
0N/A // Read encrypted data from stream
0N/A byte[] ciphertext = new byte[cLen];
0N/A try {
0N/A Krb5Token.readFully(cipherStream, ciphertext, 0, cLen);
0N/A } catch (IOException e) {
0N/A GSSException ge = new GSSException(
0N/A GSSException.DEFECTIVE_TOKEN, -1,
0N/A "Cannot read complete token");
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A des3KdDecrypt(token, ciphertext, 0, cLen, plaintext, pStart);
0N/A break;
0N/A
0N/A case MessageToken.SEAL_ALG_ARCFOUR_HMAC:
0N/A
0N/A // Read encrypted data from stream
0N/A byte[] ctext = new byte[cLen];
0N/A try {
0N/A Krb5Token.readFully(cipherStream, ctext, 0, cLen);
0N/A } catch (IOException e) {
0N/A GSSException ge = new GSSException(
0N/A GSSException.DEFECTIVE_TOKEN, -1,
0N/A "Cannot read complete token");
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A arcFourDecrypt(token, ctext, 0, cLen, plaintext, pStart);
0N/A break;
0N/A
0N/A default:
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Unsupported seal algorithm: " + sealAlg);
0N/A }
0N/A }
0N/A
0N/A void decryptData(WrapToken_v2 token, InputStream cipherStream, int cLen,
0N/A byte[] plaintext, int pStart, int key_usage)
0N/A throws GSSException, IOException {
0N/A
0N/A // Read encrypted data from stream
0N/A byte[] ciphertext = new byte[cLen];
0N/A try {
0N/A Krb5Token.readFully(cipherStream, ciphertext, 0, cLen);
0N/A } catch (IOException e) {
0N/A GSSException ge = new GSSException(
0N/A GSSException.DEFECTIVE_TOKEN, -1,
0N/A "Cannot read complete token");
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A switch (etype) {
0N/A case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
0N/A aes128Decrypt(token, ciphertext, 0, cLen,
0N/A plaintext, pStart, key_usage);
0N/A break;
0N/A case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
0N/A aes256Decrypt(token, ciphertext, 0, cLen,
0N/A plaintext, pStart, key_usage);
0N/A break;
0N/A default:
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Unsupported etype: " + etype);
0N/A }
0N/A }
0N/A
0N/A void encryptData(WrapToken token, byte[] confounder, byte[] plaintext,
0N/A int start, int len, byte[] padding, OutputStream os)
0N/A throws GSSException, IOException {
0N/A
0N/A switch (sealAlg) {
0N/A case MessageToken.SEAL_ALG_DES:
0N/A // Encrypt on the fly and write
0N/A Cipher des = getInitializedDes(true, getDesEncryptionKey(keybytes),
0N/A ZERO_IV);
0N/A CipherOutputStream cos = new CipherOutputStream(os, des);
0N/A // debug(getHexBytes(confounder, confounder.length));
0N/A cos.write(confounder);
0N/A // debug(" " + getHexBytes(plaintext, start, len));
0N/A cos.write(plaintext, start, len);
0N/A // debug(" " + getHexBytes(padding, padding.length));
0N/A cos.write(padding);
0N/A break;
0N/A
0N/A case MessageToken.SEAL_ALG_DES3_KD:
0N/A byte[] ctext = des3KdEncrypt(confounder, plaintext, start, len,
0N/A padding);
0N/A
0N/A // Write to stream
0N/A os.write(ctext);
0N/A break;
0N/A
0N/A case MessageToken.SEAL_ALG_ARCFOUR_HMAC:
0N/A byte[] ciphertext = arcFourEncrypt(token, confounder, plaintext,
0N/A start, len, padding);
0N/A
0N/A // Write to stream
0N/A os.write(ciphertext);
0N/A break;
0N/A
0N/A default:
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Unsupported seal algorithm: " + sealAlg);
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Encrypt data in the new GSS tokens
0N/A *
0N/A * Wrap Tokens (with confidentiality)
0N/A * { Encrypt(16-byte confounder | plaintext | 16-byte token_header) |
0N/A * 12-byte HMAC }
0N/A * where HMAC is on {16-byte confounder | plaintext | 16-byte token_header}
0N/A * HMAC is not encrypted; it is appended at the end.
0N/A */
3321N/A byte[] encryptData(WrapToken_v2 token, byte[] confounder, byte[] tokenHeader,
3321N/A byte[] plaintext, int start, int len, int key_usage)
3321N/A throws GSSException {
0N/A
0N/A switch (etype) {
3321N/A case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
3321N/A return aes128Encrypt(confounder, tokenHeader,
3321N/A plaintext, start, len, key_usage);
3321N/A case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
3321N/A return aes256Encrypt(confounder, tokenHeader,
3321N/A plaintext, start, len, key_usage);
3321N/A default:
3321N/A throw new GSSException(GSSException.FAILURE, -1,
3321N/A "Unsupported etype: " + etype);
0N/A }
0N/A }
0N/A
0N/A void encryptData(WrapToken token, byte[] confounder, byte[] plaintext,
0N/A int pStart, int pLen, byte[] padding, byte[] ciphertext, int cStart)
0N/A throws GSSException {
0N/A
0N/A switch (sealAlg) {
0N/A case MessageToken.SEAL_ALG_DES:
0N/A int pos = cStart;
0N/A // Encrypt and write
0N/A Cipher des = getInitializedDes(true, getDesEncryptionKey(keybytes),
0N/A ZERO_IV);
0N/A try {
0N/A // debug(getHexBytes(confounder, confounder.length));
0N/A pos += des.update(confounder, 0, confounder.length,
0N/A ciphertext, pos);
0N/A // debug(" " + getHexBytes(dataBytes, dataOffset, dataLen));
0N/A pos += des.update(plaintext, pStart, pLen,
0N/A ciphertext, pos);
0N/A // debug(" " + getHexBytes(padding, padding.length));
0N/A des.update(padding, 0, padding.length,
0N/A ciphertext, pos);
0N/A des.doFinal();
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use DES Cipher - " + e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A break;
0N/A
0N/A case MessageToken.SEAL_ALG_DES3_KD:
0N/A byte[] ctext = des3KdEncrypt(confounder, plaintext, pStart, pLen,
0N/A padding);
0N/A System.arraycopy(ctext, 0, ciphertext, cStart, ctext.length);
0N/A break;
0N/A
0N/A case MessageToken.SEAL_ALG_ARCFOUR_HMAC:
0N/A byte[] ctext2 = arcFourEncrypt(token, confounder, plaintext, pStart,
0N/A pLen, padding);
0N/A System.arraycopy(ctext2, 0, ciphertext, cStart, ctext2.length);
0N/A break;
0N/A
0N/A default:
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Unsupported seal algorithm: " + sealAlg);
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Encrypt data in the new GSS tokens
0N/A *
0N/A * Wrap Tokens (with confidentiality)
0N/A * { Encrypt(16-byte confounder | plaintext | 16-byte token_header) |
0N/A * 12-byte HMAC }
0N/A * where HMAC is on {16-byte confounder | plaintext | 16-byte token_header}
0N/A * HMAC is not encrypted; it is appended at the end.
0N/A */
0N/A int encryptData(WrapToken_v2 token, byte[] confounder, byte[] tokenHeader,
0N/A byte[] plaintext, int pStart, int pLen, byte[] ciphertext, int cStart,
0N/A int key_usage) throws GSSException {
0N/A
0N/A byte[] ctext = null;
0N/A switch (etype) {
0N/A case EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96:
0N/A ctext = aes128Encrypt(confounder, tokenHeader,
0N/A plaintext, pStart, pLen, key_usage);
0N/A break;
0N/A case EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96:
0N/A ctext = aes256Encrypt(confounder, tokenHeader,
0N/A plaintext, pStart, pLen, key_usage);
0N/A break;
0N/A default:
0N/A throw new GSSException(GSSException.FAILURE, -1,
0N/A "Unsupported etype: " + etype);
0N/A }
0N/A System.arraycopy(ctext, 0, ciphertext, cStart, ctext.length);
0N/A return ctext.length;
0N/A }
0N/A
0N/A // --------------------- DES methods
0N/A
0N/A /**
0N/A * Computes the DesCbc checksum based on the algorithm published in FIPS
0N/A * Publication 113. This involves applying padding to the data passed
0N/A * in, then performing DesCbc encryption on the data with a zero initial
0N/A * vector, and finally returning the last 8 bytes of the encryption
0N/A * result.
0N/A *
0N/A * @param key the bytes for the DES key
0N/A * @param header a header to process first before the data is.
0N/A * @param data the data to checksum
0N/A * @param offset the offset where the data begins
0N/A * @param len the length of the data
0N/A * @throws GSSException when an error occuse in the encryption
0N/A */
0N/A private byte[] getDesCbcChecksum(byte key[],
0N/A byte[] header,
0N/A byte[] data, int offset, int len)
0N/A throws GSSException {
0N/A
0N/A Cipher des = getInitializedDes(true, key, ZERO_IV);
0N/A
0N/A int blockSize = des.getBlockSize();
0N/A
0N/A /*
0N/A * Here the data need not be a multiple of the blocksize
0N/A * (8). Encrypt and throw away results for all blocks except for
0N/A * the very last block.
0N/A */
0N/A
0N/A byte[] finalBlock = new byte[blockSize];
0N/A
0N/A int numBlocks = len / blockSize;
0N/A int lastBytes = len % blockSize;
0N/A if (lastBytes == 0) {
0N/A // No need for padding. Save last block from application data
0N/A numBlocks -= 1;
0N/A System.arraycopy(data, offset + numBlocks*blockSize,
0N/A finalBlock, 0, blockSize);
0N/A } else {
0N/A System.arraycopy(data, offset + numBlocks*blockSize,
0N/A finalBlock, 0, lastBytes);
0N/A // Zero padding automatically done
0N/A }
0N/A
0N/A try {
0N/A byte[] temp = new byte[Math.max(blockSize,
0N/A (header == null? blockSize : header.length))];
0N/A
0N/A if (header != null) {
0N/A // header will be null when doing DES-MD5 Checksum
0N/A des.update(header, 0, header.length, temp, 0);
0N/A }
0N/A
0N/A // Iterate over all but the last block
0N/A for (int i = 0; i < numBlocks; i++) {
0N/A des.update(data, offset, blockSize,
0N/A temp, 0);
0N/A offset += blockSize;
0N/A }
0N/A
0N/A // Now process the final block
0N/A byte[] retVal = new byte[blockSize];
0N/A des.update(finalBlock, 0, blockSize, retVal, 0);
0N/A des.doFinal();
0N/A
0N/A return retVal;
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use DES Cipher - " + e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Obtains an initialized DES cipher.
0N/A *
0N/A * @param encryptMode true if encryption is desired, false is decryption
0N/A * is desired.
0N/A * @param key the bytes for the DES key
0N/A * @param ivBytes the initial vector bytes
0N/A */
0N/A private final Cipher getInitializedDes(boolean encryptMode, byte[] key,
0N/A byte[] ivBytes)
0N/A throws GSSException {
0N/A
0N/A
0N/A try {
0N/A IvParameterSpec iv = new IvParameterSpec(ivBytes);
0N/A SecretKey jceKey = (SecretKey) (new SecretKeySpec(key, "DES"));
0N/A
0N/A Cipher desCipher = Cipher.getInstance("DES/CBC/NoPadding");
0N/A desCipher.init(
0N/A (encryptMode ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE),
0N/A jceKey, iv);
0N/A return desCipher;
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Helper routine to decrypt fromm a byte array and write the
0N/A * application data straight to an output array with minimal
0N/A * buffer copies. The confounder and the padding are stored
0N/A * separately and not copied into this output array.
0N/A * @param key the DES key to use
0N/A * @param cipherText the encrypted data
0N/A * @param offset the offset for the encrypted data
0N/A * @param len the length of the encrypted data
0N/A * @param dataOutBuf the output buffer where the application data
0N/A * should be writte
0N/A * @param dataOffset the offser where the application data should
0N/A * be written.
0N/A * @throws GSSException is an error occurs while decrypting the
0N/A * data
0N/A */
0N/A private void desCbcDecrypt(WrapToken token, byte[] key, byte[] cipherText,
0N/A int offset, int len, byte[] dataOutBuf, int dataOffset)
0N/A throws GSSException {
0N/A
0N/A try {
0N/A
0N/A int temp = 0;
0N/A
0N/A Cipher des = getInitializedDes(false, key, ZERO_IV);
0N/A
0N/A /*
0N/A * Remove the counfounder first.
0N/A * CONFOUNDER_SIZE is one DES block ie 8 bytes.
0N/A */
0N/A temp = des.update(cipherText, offset, WrapToken.CONFOUNDER_SIZE,
0N/A token.confounder);
0N/A // temp should be CONFOUNDER_SIZE
0N/A // debug("\n\ttemp is " + temp + " and CONFOUNDER_SIZE is "
0N/A // + CONFOUNDER_SIZE);
0N/A
0N/A offset += WrapToken.CONFOUNDER_SIZE;
0N/A len -= WrapToken.CONFOUNDER_SIZE;
0N/A
0N/A /*
0N/A * len is a multiple of 8 due to padding.
0N/A * Decrypt all blocks directly into the output buffer except for
0N/A * the very last block. Remove the trailing padding bytes from the
0N/A * very last block and copy that into the output buffer.
0N/A */
0N/A
0N/A int blockSize = des.getBlockSize();
0N/A int numBlocks = len / blockSize - 1;
0N/A
0N/A // Iterate over all but the last block
0N/A for (int i = 0; i < numBlocks; i++) {
0N/A temp = des.update(cipherText, offset, blockSize,
0N/A dataOutBuf, dataOffset);
0N/A // temp should be blockSize
0N/A // debug("\n\ttemp is " + temp + " and blockSize is "
0N/A // + blockSize);
0N/A
0N/A offset += blockSize;
0N/A dataOffset += blockSize;
0N/A }
0N/A
0N/A // Now process the last block
0N/A byte[] finalBlock = new byte[blockSize];
0N/A des.update(cipherText, offset, blockSize, finalBlock);
0N/A
0N/A des.doFinal();
0N/A
0N/A /*
0N/A * There is always at least one padding byte. The padding bytes
0N/A * are all the value of the number of padding bytes.
0N/A */
0N/A
0N/A int padSize = finalBlock[blockSize - 1];
0N/A if (padSize < 1 || padSize > 8)
0N/A throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
0N/A "Invalid padding on Wrap Token");
0N/A token.padding = WrapToken.pads[padSize];
0N/A blockSize -= padSize;
0N/A
0N/A // Copy this last block into the output buffer
0N/A System.arraycopy(finalBlock, 0, dataOutBuf, dataOffset,
0N/A blockSize);
0N/A
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use DES cipher - " + e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Helper routine to decrypt from an InputStream and write the
0N/A * application data straight to an output array with minimal
0N/A * buffer copies. The confounder and the padding are stored
0N/A * separately and not copied into this output array.
0N/A * @param key the DES key to use
0N/A * @param is the InputStream from which the cipher text should be
0N/A * read
0N/A * @param len the length of the ciphertext data
0N/A * @param dataOutBuf the output buffer where the application data
0N/A * should be writte
0N/A * @param dataOffset the offser where the application data should
0N/A * be written.
0N/A * @throws GSSException is an error occurs while decrypting the
0N/A * data
0N/A */
0N/A private void desCbcDecrypt(WrapToken token, byte[] key,
0N/A InputStream is, int len, byte[] dataOutBuf, int dataOffset)
0N/A throws GSSException, IOException {
0N/A
0N/A int temp = 0;
0N/A
0N/A Cipher des = getInitializedDes(false, key, ZERO_IV);
0N/A
0N/A WrapTokenInputStream truncatedInputStream =
0N/A new WrapTokenInputStream(is, len);
0N/A CipherInputStream cis = new CipherInputStream(truncatedInputStream,
0N/A des);
0N/A /*
0N/A * Remove the counfounder first.
0N/A * CONFOUNDER_SIZE is one DES block ie 8 bytes.
0N/A */
0N/A temp = cis.read(token.confounder);
0N/A
0N/A len -= temp;
0N/A // temp should be CONFOUNDER_SIZE
0N/A // debug("Got " + temp + " bytes; CONFOUNDER_SIZE is "
0N/A // + CONFOUNDER_SIZE + "\n");
0N/A // debug("Confounder is " + getHexBytes(confounder) + "\n");
0N/A
0N/A
0N/A /*
0N/A * len is a multiple of 8 due to padding.
0N/A * Decrypt all blocks directly into the output buffer except for
0N/A * the very last block. Remove the trailing padding bytes from the
0N/A * very last block and copy that into the output buffer.
0N/A */
0N/A
0N/A int blockSize = des.getBlockSize();
0N/A int numBlocks = len / blockSize - 1;
0N/A
0N/A // Iterate over all but the last block
0N/A for (int i = 0; i < numBlocks; i++) {
0N/A // debug("dataOffset is " + dataOffset + "\n");
0N/A temp = cis.read(dataOutBuf, dataOffset, blockSize);
0N/A
0N/A // temp should be blockSize
0N/A // debug("Got " + temp + " bytes and blockSize is "
0N/A // + blockSize + "\n");
0N/A // debug("Bytes are: "
0N/A // + getHexBytes(dataOutBuf, dataOffset, temp) + "\n");
0N/A dataOffset += blockSize;
0N/A }
0N/A
0N/A // Now process the last block
0N/A byte[] finalBlock = new byte[blockSize];
0N/A // debug("Will call read on finalBlock" + "\n");
0N/A temp = cis.read(finalBlock);
0N/A // temp should be blockSize
0N/A /*
0N/A debug("Got " + temp + " bytes and blockSize is "
0N/A + blockSize + "\n");
0N/A debug("Bytes are: "
0N/A + getHexBytes(finalBlock, 0, temp) + "\n");
0N/A debug("Will call doFinal" + "\n");
0N/A */
0N/A try {
0N/A des.doFinal();
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use DES cipher - " + e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A /*
0N/A * There is always at least one padding byte. The padding bytes
0N/A * are all the value of the number of padding bytes.
0N/A */
0N/A
0N/A int padSize = finalBlock[blockSize - 1];
0N/A if (padSize < 1 || padSize > 8)
0N/A throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
0N/A "Invalid padding on Wrap Token");
0N/A token.padding = WrapToken.pads[padSize];
0N/A blockSize -= padSize;
0N/A
0N/A // Copy this last block into the output buffer
0N/A System.arraycopy(finalBlock, 0, dataOutBuf, dataOffset,
0N/A blockSize);
0N/A }
0N/A
0N/A private static byte[] getDesEncryptionKey(byte[] key)
0N/A throws GSSException {
0N/A
0N/A /*
0N/A * To meet export control requirements, double check that the
0N/A * key being used is no longer than 64 bits.
0N/A *
0N/A * Note that from a protocol point of view, an
0N/A * algorithm that is not DES will be rejected before this
0N/A * point. Also, a DES key that is not 64 bits will be
0N/A * rejected by a good JCE provider.
0N/A */
0N/A if (key.length > 8)
0N/A throw new GSSException(GSSException.FAILURE, -100,
0N/A "Invalid DES Key!");
0N/A
0N/A byte[] retVal = new byte[key.length];
0N/A for (int i = 0; i < key.length; i++)
0N/A retVal[i] = (byte)(key[i] ^ 0xf0); // RFC 1964, Section 1.2.2
0N/A return retVal;
0N/A }
0N/A
0N/A // ---- DES3-KD methods
0N/A private void des3KdDecrypt(WrapToken token, byte[] ciphertext,
0N/A int cStart, int cLen, byte[] plaintext, int pStart)
0N/A throws GSSException {
0N/A byte[] ptext;
0N/A try {
0N/A ptext = Des3.decryptRaw(keybytes, KG_USAGE_SEAL, ZERO_IV,
0N/A ciphertext, cStart, cLen);
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use DES3-KD Cipher - " + e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A /*
0N/A Krb5Token.debug("\ndes3KdDecrypt in: " +
0N/A Krb5Token.getHexBytes(ciphertext, cStart, cLen));
0N/A Krb5Token.debug("\ndes3KdDecrypt plain: " +
0N/A Krb5Token.getHexBytes(ptext));
0N/A */
0N/A
0N/A // Strip out confounder and padding
0N/A /*
0N/A * There is always at least one padding byte. The padding bytes
0N/A * are all the value of the number of padding bytes.
0N/A */
0N/A int padSize = ptext[ptext.length - 1];
0N/A if (padSize < 1 || padSize > 8)
0N/A throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
0N/A "Invalid padding on Wrap Token");
0N/A
0N/A token.padding = WrapToken.pads[padSize];
0N/A int len = ptext.length - WrapToken.CONFOUNDER_SIZE - padSize;
0N/A
0N/A System.arraycopy(ptext, WrapToken.CONFOUNDER_SIZE,
0N/A plaintext, pStart, len);
0N/A
0N/A // Needed to calculate checksum
0N/A System.arraycopy(ptext, 0, token.confounder,
0N/A 0, WrapToken.CONFOUNDER_SIZE);
0N/A }
0N/A
0N/A private byte[] des3KdEncrypt(byte[] confounder, byte[] plaintext,
0N/A int start, int len, byte[] padding) throws GSSException {
0N/A
0N/A
0N/A // [confounder | plaintext | padding]
0N/A byte[] all = new byte[confounder.length + len + padding.length];
0N/A System.arraycopy(confounder, 0, all, 0, confounder.length);
0N/A System.arraycopy(plaintext, start, all, confounder.length, len);
0N/A System.arraycopy(padding, 0, all, confounder.length + len,
0N/A padding.length);
0N/A
0N/A // Krb5Token.debug("\ndes3KdEncrypt:" + Krb5Token.getHexBytes(all));
0N/A
0N/A // Encrypt
0N/A try {
0N/A byte[] answer = Des3.encryptRaw(keybytes, KG_USAGE_SEAL, ZERO_IV,
0N/A all, 0, all.length);
0N/A // Krb5Token.debug("\ndes3KdEncrypt encrypted:" +
0N/A // Krb5Token.getHexBytes(answer));
0N/A return answer;
0N/A } catch (Exception e) {
0N/A // GeneralSecurityException, KrbCryptoException
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use DES3-KD Cipher - " + e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A }
0N/A
0N/A // ---- RC4-HMAC methods
0N/A private void arcFourDecrypt(WrapToken token, byte[] ciphertext,
0N/A int cStart, int cLen, byte[] plaintext, int pStart)
0N/A throws GSSException {
0N/A
0N/A // obtain Sequence number needed for decryption
0N/A // first decrypt the Sequence Number using checksum
0N/A byte[] seqNum = decryptSeq(token.getChecksum(),
0N/A token.getEncSeqNumber(), 0, 8);
0N/A
0N/A byte[] ptext;
0N/A try {
0N/A ptext = ArcFourHmac.decryptRaw(keybytes, KG_USAGE_SEAL, ZERO_IV,
0N/A ciphertext, cStart, cLen, seqNum);
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use ArcFour Cipher - " + e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A /*
0N/A Krb5Token.debug("\narcFourDecrypt in: " +
0N/A Krb5Token.getHexBytes(ciphertext, cStart, cLen));
0N/A Krb5Token.debug("\narcFourDecrypt plain: " +
0N/A Krb5Token.getHexBytes(ptext));
0N/A */
0N/A
0N/A // Strip out confounder and padding
0N/A /*
0N/A * There is always at least one padding byte. The padding bytes
0N/A * are all the value of the number of padding bytes.
0N/A */
0N/A int padSize = ptext[ptext.length - 1];
0N/A if (padSize < 1)
0N/A throw new GSSException(GSSException.DEFECTIVE_TOKEN, -1,
0N/A "Invalid padding on Wrap Token");
0N/A
0N/A token.padding = WrapToken.pads[padSize];
0N/A int len = ptext.length - WrapToken.CONFOUNDER_SIZE - padSize;
0N/A
0N/A System.arraycopy(ptext, WrapToken.CONFOUNDER_SIZE,
0N/A plaintext, pStart, len);
0N/A
0N/A // Krb5Token.debug("\narcFourDecrypt plaintext: " +
0N/A // Krb5Token.getHexBytes(plaintext));
0N/A
0N/A // Needed to calculate checksum
0N/A System.arraycopy(ptext, 0, token.confounder,
0N/A 0, WrapToken.CONFOUNDER_SIZE);
0N/A }
0N/A
0N/A private byte[] arcFourEncrypt(WrapToken token, byte[] confounder,
0N/A byte[] plaintext, int start, int len, byte[] padding)
0N/A throws GSSException {
0N/A
0N/A // [confounder | plaintext | padding]
0N/A byte[] all = new byte[confounder.length + len + padding.length];
0N/A System.arraycopy(confounder, 0, all, 0, confounder.length);
0N/A System.arraycopy(plaintext, start, all, confounder.length, len);
0N/A System.arraycopy(padding, 0, all, confounder.length + len,
0N/A padding.length);
0N/A
0N/A // get the token Sequence Number required for encryption
0N/A // Note: When using this RC4 based encryption type, the sequence number
0N/A // is always sent in big-endian rather than little-endian order.
0N/A byte[] seqNum = new byte[4];
0N/A token.writeBigEndian(token.getSequenceNumber(), seqNum);
0N/A
0N/A // Krb5Token.debug("\narcFourEncrypt:" + Krb5Token.getHexBytes(all));
0N/A
0N/A // Encrypt
0N/A try {
0N/A byte[] answer = ArcFourHmac.encryptRaw(keybytes, KG_USAGE_SEAL,
0N/A seqNum, all, 0, all.length);
0N/A // Krb5Token.debug("\narcFourEncrypt encrypted:" +
0N/A // Krb5Token.getHexBytes(answer));
0N/A return answer;
0N/A } catch (Exception e) {
0N/A // GeneralSecurityException, KrbCryptoException
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use ArcFour Cipher - " + e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A }
0N/A
0N/A // ---- AES methods
0N/A private byte[] aes128Encrypt(byte[] confounder, byte[] tokenHeader,
0N/A byte[] plaintext, int start, int len, int key_usage)
0N/A throws GSSException {
0N/A
0N/A // encrypt { AES-plaintext-data | filler | header }
0N/A // AES-plaintext-data { confounder | plaintext }
0N/A // WrapToken = { tokenHeader |
0N/A // Encrypt (confounder | plaintext | tokenHeader ) | HMAC }
0N/A
0N/A byte[] all = new byte[confounder.length + len + tokenHeader.length];
0N/A System.arraycopy(confounder, 0, all, 0, confounder.length);
0N/A System.arraycopy(plaintext, start, all, confounder.length, len);
0N/A System.arraycopy(tokenHeader, 0, all, confounder.length+len,
0N/A tokenHeader.length);
0N/A
0N/A // Krb5Token.debug("\naes128Encrypt:" + Krb5Token.getHexBytes(all));
0N/A try {
0N/A byte[] answer = Aes128.encryptRaw(keybytes, key_usage,
0N/A ZERO_IV_AES,
0N/A all, 0, all.length);
0N/A // Krb5Token.debug("\naes128Encrypt encrypted:" +
0N/A // Krb5Token.getHexBytes(answer));
0N/A return answer;
0N/A } catch (Exception e) {
0N/A // GeneralSecurityException, KrbCryptoException
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use AES128 Cipher - " + e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A }
0N/A
0N/A private void aes128Decrypt(WrapToken_v2 token, byte[] ciphertext,
0N/A int cStart, int cLen, byte[] plaintext, int pStart, int key_usage)
0N/A throws GSSException {
0N/A
0N/A byte[] ptext = null;
0N/A
0N/A try {
0N/A ptext = Aes128.decryptRaw(keybytes, key_usage,
0N/A ZERO_IV_AES, ciphertext, cStart, cLen);
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use AES128 Cipher - " + e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A /*
0N/A Krb5Token.debug("\naes128Decrypt in: " +
0N/A Krb5Token.getHexBytes(ciphertext, cStart, cLen));
0N/A Krb5Token.debug("\naes128Decrypt plain: " +
0N/A Krb5Token.getHexBytes(ptext));
0N/A Krb5Token.debug("\naes128Decrypt ptext: " +
0N/A Krb5Token.getHexBytes(ptext));
0N/A */
0N/A
0N/A // Strip out confounder and token header
0N/A int len = ptext.length - WrapToken_v2.CONFOUNDER_SIZE -
0N/A WrapToken_v2.TOKEN_HEADER_SIZE;
0N/A System.arraycopy(ptext, WrapToken_v2.CONFOUNDER_SIZE,
0N/A plaintext, pStart, len);
0N/A
0N/A /*
0N/A Krb5Token.debug("\naes128Decrypt plaintext: " +
0N/A Krb5Token.getHexBytes(plaintext, pStart, len));
0N/A */
0N/A }
0N/A
0N/A private byte[] aes256Encrypt(byte[] confounder, byte[] tokenHeader,
0N/A byte[] plaintext, int start, int len, int key_usage)
0N/A throws GSSException {
0N/A
0N/A // encrypt { AES-plaintext-data | filler | header }
0N/A // AES-plaintext-data { confounder | plaintext }
0N/A // WrapToken = { tokenHeader |
0N/A // Encrypt (confounder | plaintext | tokenHeader ) | HMAC }
0N/A
0N/A byte[] all = new byte[confounder.length + len + tokenHeader.length];
0N/A System.arraycopy(confounder, 0, all, 0, confounder.length);
0N/A System.arraycopy(plaintext, start, all, confounder.length, len);
0N/A System.arraycopy(tokenHeader, 0, all, confounder.length+len,
0N/A tokenHeader.length);
0N/A
0N/A // Krb5Token.debug("\naes256Encrypt:" + Krb5Token.getHexBytes(all));
0N/A
0N/A try {
0N/A byte[] answer = Aes256.encryptRaw(keybytes, key_usage,
0N/A ZERO_IV_AES, all, 0, all.length);
0N/A // Krb5Token.debug("\naes256Encrypt encrypted:" +
0N/A // Krb5Token.getHexBytes(answer));
0N/A return answer;
0N/A } catch (Exception e) {
0N/A // GeneralSecurityException, KrbCryptoException
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use AES256 Cipher - " + e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A }
0N/A
0N/A private void aes256Decrypt(WrapToken_v2 token, byte[] ciphertext,
0N/A int cStart, int cLen, byte[] plaintext, int pStart, int key_usage)
0N/A throws GSSException {
0N/A
0N/A byte[] ptext;
0N/A try {
0N/A ptext = Aes256.decryptRaw(keybytes, key_usage,
0N/A ZERO_IV_AES, ciphertext, cStart, cLen);
0N/A } catch (GeneralSecurityException e) {
0N/A GSSException ge = new GSSException(GSSException.FAILURE, -1,
0N/A "Could not use AES128 Cipher - " + e.getMessage());
0N/A ge.initCause(e);
0N/A throw ge;
0N/A }
0N/A
0N/A /*
0N/A Krb5Token.debug("\naes256Decrypt in: " +
0N/A Krb5Token.getHexBytes(ciphertext, cStart, cLen));
0N/A Krb5Token.debug("\naes256Decrypt plain: " +
0N/A Krb5Token.getHexBytes(ptext));
0N/A Krb5Token.debug("\naes256Decrypt ptext: " +
0N/A Krb5Token.getHexBytes(ptext));
0N/A */
0N/A
0N/A // Strip out confounder and token header
0N/A int len = ptext.length - WrapToken_v2.CONFOUNDER_SIZE -
0N/A WrapToken_v2.TOKEN_HEADER_SIZE;
0N/A System.arraycopy(ptext, WrapToken_v2.CONFOUNDER_SIZE,
0N/A plaintext, pStart, len);
0N/A
0N/A /*
0N/A Krb5Token.debug("\naes128Decrypt plaintext: " +
0N/A Krb5Token.getHexBytes(plaintext, pStart, len));
0N/A */
0N/A
0N/A }
0N/A
0N/A /**
0N/A * This class provides a truncated inputstream needed by WrapToken. The
0N/A * truncated inputstream is passed to CipherInputStream. It prevents
0N/A * the CipherInputStream from treating the bytes of the following token
0N/A * as part fo the ciphertext for this token.
0N/A */
0N/A class WrapTokenInputStream extends InputStream {
0N/A
0N/A private InputStream is;
0N/A private int length;
0N/A private int remaining;
0N/A
0N/A private int temp;
0N/A
0N/A public WrapTokenInputStream(InputStream is, int length) {
0N/A this.is = is;
0N/A this.length = length;
0N/A remaining = length;
0N/A }
0N/A
0N/A public final int read() throws IOException {
0N/A if (remaining == 0)
0N/A return -1;
0N/A else {
0N/A temp = is.read();
0N/A if (temp != -1)
0N/A remaining -= temp;
0N/A return temp;
0N/A }
0N/A }
0N/A
0N/A public final int read(byte[] b) throws IOException {
0N/A if (remaining == 0)
0N/A return -1;
0N/A else {
0N/A temp = Math.min(remaining, b.length);
0N/A temp = is.read(b, 0, temp);
0N/A if (temp != -1)
0N/A remaining -= temp;
0N/A return temp;
0N/A }
0N/A }
0N/A
0N/A public final int read(byte[] b,
0N/A int off,
0N/A int len) throws IOException {
0N/A if (remaining == 0)
0N/A return -1;
0N/A else {
0N/A temp = Math.min(remaining, len);
0N/A temp = is.read(b, off, temp);
0N/A if (temp != -1)
0N/A remaining -= temp;
0N/A return temp;
0N/A }
0N/A }
0N/A
0N/A public final long skip(long n) throws IOException {
0N/A if (remaining == 0)
0N/A return 0;
0N/A else {
0N/A temp = (int) Math.min(remaining, n);
0N/A temp = (int) is.skip(temp);
0N/A remaining -= temp;
0N/A return temp;
0N/A }
0N/A }
0N/A
0N/A public final int available() throws IOException {
0N/A return Math.min(remaining, is.available());
0N/A }
0N/A
0N/A public final void close() throws IOException {
0N/A remaining = 0;
0N/A }
0N/A }
0N/A}