feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

View File

@@ -0,0 +1,501 @@
/*
* Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
*/
package sun.security.krb5.internal.crypto.dk;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import java.security.spec.KeySpec;
import java.security.GeneralSecurityException;
import sun.security.krb5.KrbCryptoException;
import sun.security.krb5.Confounder;
import sun.security.krb5.internal.crypto.KeyUsage;
import java.util.Arrays;
/**
* This class provides the implementation of AES Encryption for Kerberos
* as defined RFC 3962.
* http://www.ietf.org/rfc/rfc3962.txt
*
* Algorithm profile described in [KCRYPTO]:
* +--------------------------------------------------------------------+
* | protocol key format 128- or 256-bit string |
* | |
* | string-to-key function PBKDF2+DK with variable |
* | iteration count (see |
* | above) |
* | |
* | default string-to-key parameters 00 00 10 00 |
* | |
* | key-generation seed length key size |
* | |
* | random-to-key function identity function |
* | |
* | hash function, H SHA-1 |
* | |
* | HMAC output size, h 12 octets (96 bits) |
* | |
* | message block size, m 1 octet |
* | |
* | encryption/decryption functions, AES in CBC-CTS mode |
* | E and D (cipher block size 16 |
* | octets), with next to |
* | last block as CBC-style |
* | ivec |
* +--------------------------------------------------------------------+
*
* Supports AES128 and AES256
*
* @author Seema Malkani
*/
public class AesDkCrypto extends DkCrypto {
private static final boolean debug = false;
private static final int BLOCK_SIZE = 16;
private static final int DEFAULT_ITERATION_COUNT = 4096;
private static final byte[] ZERO_IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 };
private static final int hashSize = 96/8;
private final int keyLength;
public AesDkCrypto(int length) {
keyLength = length;
}
protected int getKeySeedLength() {
return keyLength; // bits; AES key material
}
public byte[] stringToKey(char[] password, String salt, byte[] s2kparams)
throws GeneralSecurityException {
byte[] saltUtf8 = null;
try {
saltUtf8 = salt.getBytes("UTF-8");
return stringToKey(password, saltUtf8, s2kparams);
} catch (Exception e) {
return null;
} finally {
if (saltUtf8 != null) {
Arrays.fill(saltUtf8, (byte)0);
}
}
}
private byte[] stringToKey(char[] secret, byte[] salt, byte[] params)
throws GeneralSecurityException {
int iter_count = DEFAULT_ITERATION_COUNT;
if (params != null) {
if (params.length != 4) {
throw new RuntimeException("Invalid parameter to stringToKey");
}
iter_count = readBigEndian(params, 0, 4);
}
byte[] tmpKey = randomToKey(PBKDF2(secret, salt, iter_count,
getKeySeedLength()));
byte[] result = dk(tmpKey, KERBEROS_CONSTANT);
return result;
}
protected byte[] randomToKey(byte[] in) {
// simple identity operation
return in;
}
protected Cipher getCipher(byte[] key, byte[] ivec, int mode)
throws GeneralSecurityException {
// IV
if (ivec == null) {
ivec = ZERO_IV;
}
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length);
cipher.init(mode, secretKey, encIv);
return cipher;
}
// get an instance of the AES Cipher in CTS mode
public int getChecksumLength() {
return hashSize; // bytes
}
/**
* Get the truncated HMAC
*/
protected byte[] getHmac(byte[] key, byte[] msg)
throws GeneralSecurityException {
SecretKey keyKi = new SecretKeySpec(key, "HMAC");
Mac m = Mac.getInstance("HmacSHA1");
m.init(keyKi);
// generate hash
byte[] hash = m.doFinal(msg);
// truncate hash
byte[] output = new byte[hashSize];
System.arraycopy(hash, 0, output, 0, hashSize);
return output;
}
/**
* Calculate the checksum
*/
public byte[] calculateChecksum(byte[] baseKey, int usage, byte[] input,
int start, int len) throws GeneralSecurityException {
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
// Derive keys
byte[] constant = new byte[5];
constant[0] = (byte) ((usage>>24)&0xff);
constant[1] = (byte) ((usage>>16)&0xff);
constant[2] = (byte) ((usage>>8)&0xff);
constant[3] = (byte) (usage&0xff);
constant[4] = (byte) 0x99;
byte[] Kc = dk(baseKey, constant); // Checksum key
if (debug) {
System.err.println("usage: " + usage);
traceOutput("input", input, start, Math.min(len, 32));
traceOutput("constant", constant, 0, constant.length);
traceOutput("baseKey", baseKey, 0, baseKey.length);
traceOutput("Kc", Kc, 0, Kc.length);
}
try {
// Generate checksum
// H1 = HMAC(Kc, input)
byte[] hmac = getHmac(Kc, input);
if (debug) {
traceOutput("hmac", hmac, 0, hmac.length);
}
if (hmac.length == getChecksumLength()) {
return hmac;
} else if (hmac.length > getChecksumLength()) {
byte[] buf = new byte[getChecksumLength()];
System.arraycopy(hmac, 0, buf, 0, buf.length);
return buf;
} else {
throw new GeneralSecurityException("checksum size too short: " +
hmac.length + "; expecting : " + getChecksumLength());
}
} finally {
Arrays.fill(Kc, 0, Kc.length, (byte)0);
}
}
/**
* Performs encryption using derived key; adds confounder.
*/
public byte[] encrypt(byte[] baseKey, int usage,
byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len)
throws GeneralSecurityException, KrbCryptoException {
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
byte[] output = encryptCTS(baseKey, usage, ivec, new_ivec, plaintext,
start, len, true);
return output;
}
/**
* Performs encryption using derived key; does not add confounder.
*/
public byte[] encryptRaw(byte[] baseKey, int usage,
byte[] ivec, byte[] plaintext, int start, int len)
throws GeneralSecurityException, KrbCryptoException {
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
byte[] output = encryptCTS(baseKey, usage, ivec, null, plaintext,
start, len, false);
return output;
}
/**
* @param baseKey key from which keys are to be derived using usage
* @param ciphertext E(Ke, conf | plaintext | padding, ivec) | H1[1..h]
*/
public byte[] decrypt(byte[] baseKey, int usage, byte[] ivec,
byte[] ciphertext, int start, int len) throws GeneralSecurityException {
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
byte[] output = decryptCTS(baseKey, usage, ivec, ciphertext,
start, len, true);
return output;
}
/**
* Decrypts data using specified key and initial vector.
* @param baseKey encryption key to use
* @param ciphertext encrypted data to be decrypted
* @param usage ignored
*/
public byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec,
byte[] ciphertext, int start, int len)
throws GeneralSecurityException {
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
byte[] output = decryptCTS(baseKey, usage, ivec, ciphertext,
start, len, false);
return output;
}
/**
* Encrypt AES in CBC-CTS mode using derived keys.
*/
private byte[] encryptCTS(byte[] baseKey, int usage, byte[] ivec,
byte[] new_ivec, byte[] plaintext, int start, int len,
boolean confounder_exists)
throws GeneralSecurityException, KrbCryptoException {
byte[] Ke = null;
byte[] Ki = null;
if (debug) {
System.err.println("usage: " + usage);
if (ivec != null) {
traceOutput("old_state.ivec", ivec, 0, ivec.length);
}
traceOutput("plaintext", plaintext, start, Math.min(len, 32));
traceOutput("baseKey", baseKey, 0, baseKey.length);
}
try {
// derive Encryption key
byte[] constant = new byte[5];
constant[0] = (byte) ((usage>>24)&0xff);
constant[1] = (byte) ((usage>>16)&0xff);
constant[2] = (byte) ((usage>>8)&0xff);
constant[3] = (byte) (usage&0xff);
constant[4] = (byte) 0xaa;
Ke = dk(baseKey, constant); // Encryption key
byte[] toBeEncrypted = null;
if (confounder_exists) {
byte[] confounder = Confounder.bytes(BLOCK_SIZE);
toBeEncrypted = new byte[confounder.length + len];
System.arraycopy(confounder, 0, toBeEncrypted,
0, confounder.length);
System.arraycopy(plaintext, start, toBeEncrypted,
confounder.length, len);
} else {
toBeEncrypted = new byte[len];
System.arraycopy(plaintext, start, toBeEncrypted, 0, len);
}
// encryptedData + HMAC
byte[] output = new byte[toBeEncrypted.length + hashSize];
// AES in JCE
Cipher cipher = Cipher.getInstance("AES/CTS/NoPadding");
SecretKeySpec secretKey = new SecretKeySpec(Ke, "AES");
IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, encIv);
cipher.doFinal(toBeEncrypted, 0, toBeEncrypted.length, output);
// Derive integrity key
constant[4] = (byte) 0x55;
Ki = dk(baseKey, constant);
if (debug) {
traceOutput("constant", constant, 0, constant.length);
traceOutput("Ki", Ki, 0, Ke.length);
}
// Generate checksum
// H1 = HMAC(Ki, conf | plaintext | pad)
byte[] hmac = getHmac(Ki, toBeEncrypted);
// encryptedData + HMAC
System.arraycopy(hmac, 0, output, toBeEncrypted.length,
hmac.length);
return output;
} finally {
if (Ke != null) {
Arrays.fill(Ke, 0, Ke.length, (byte) 0);
}
if (Ki != null) {
Arrays.fill(Ki, 0, Ki.length, (byte) 0);
}
}
}
/**
* Decrypt AES in CBC-CTS mode using derived keys.
*/
private byte[] decryptCTS(byte[] baseKey, int usage, byte[] ivec,
byte[] ciphertext, int start, int len, boolean confounder_exists)
throws GeneralSecurityException {
byte[] Ke = null;
byte[] Ki = null;
try {
// Derive encryption key
byte[] constant = new byte[5];
constant[0] = (byte) ((usage>>24)&0xff);
constant[1] = (byte) ((usage>>16)&0xff);
constant[2] = (byte) ((usage>>8)&0xff);
constant[3] = (byte) (usage&0xff);
constant[4] = (byte) 0xaa;
Ke = dk(baseKey, constant); // Encryption key
if (debug) {
System.err.println("usage: " + usage);
if (ivec != null) {
traceOutput("old_state.ivec", ivec, 0, ivec.length);
}
traceOutput("ciphertext", ciphertext, start, Math.min(len, 32));
traceOutput("constant", constant, 0, constant.length);
traceOutput("baseKey", baseKey, 0, baseKey.length);
traceOutput("Ke", Ke, 0, Ke.length);
}
// Decrypt [confounder | plaintext ] (without checksum)
// AES in JCE
Cipher cipher = Cipher.getInstance("AES/CTS/NoPadding");
SecretKeySpec secretKey = new SecretKeySpec(Ke, "AES");
IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length);
cipher.init(Cipher.DECRYPT_MODE, secretKey, encIv);
byte[] plaintext = cipher.doFinal(ciphertext, start, len-hashSize);
if (debug) {
traceOutput("AES PlainText", plaintext, 0,
Math.min(plaintext.length, 32));
}
// Derive integrity key
constant[4] = (byte) 0x55;
Ki = dk(baseKey, constant); // Integrity key
if (debug) {
traceOutput("constant", constant, 0, constant.length);
traceOutput("Ki", Ki, 0, Ke.length);
}
// Verify checksum
// H1 = HMAC(Ki, conf | plaintext | pad)
byte[] calculatedHmac = getHmac(Ki, plaintext);
int hmacOffset = start + len - hashSize;
if (debug) {
traceOutput("calculated Hmac", calculatedHmac,
0, calculatedHmac.length);
traceOutput("message Hmac", ciphertext, hmacOffset, hashSize);
}
boolean cksumFailed = false;
if (calculatedHmac.length >= hashSize) {
for (int i = 0; i < hashSize; i++) {
if (calculatedHmac[i] != ciphertext[hmacOffset+i]) {
cksumFailed = true;
if (debug) {
System.err.println("Checksum failed !");
}
break;
}
}
}
if (cksumFailed) {
throw new GeneralSecurityException("Checksum failed");
}
if (confounder_exists) {
// Get rid of confounder
// [ confounder | plaintext ]
byte[] output = new byte[plaintext.length - BLOCK_SIZE];
System.arraycopy(plaintext, BLOCK_SIZE, output,
0, output.length);
return output;
} else {
return plaintext;
}
} finally {
if (Ke != null) {
Arrays.fill(Ke, 0, Ke.length, (byte) 0);
}
if (Ki != null) {
Arrays.fill(Ki, 0, Ki.length, (byte) 0);
}
}
}
/*
* Invoke the PKCS#5 PBKDF2 algorithm
*/
private static byte[] PBKDF2(char[] secret, byte[] salt,
int count, int keyLength) throws GeneralSecurityException {
PBEKeySpec keySpec = new PBEKeySpec(secret, salt, count, keyLength);
SecretKeyFactory skf =
SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
SecretKey key = skf.generateSecret(keySpec);
byte[] result = key.getEncoded();
return result;
}
public static final int readBigEndian(byte[] data, int pos, int size) {
int retVal = 0;
int shifter = (size-1)*8;
while (size > 0) {
retVal += (data[pos] & 0xff) << shifter;
shifter -= 8;
pos++;
size--;
}
return retVal;
}
}

View File

@@ -0,0 +1,482 @@
/*
* Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.krb5.internal.crypto.dk;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.util.*;
import sun.security.krb5.EncryptedData;
import sun.security.krb5.KrbCryptoException;
import sun.security.krb5.Confounder;
import sun.security.krb5.internal.crypto.KeyUsage;
/**
* Support for ArcFour in Kerberos
* as defined in RFC 4757.
* http://www.ietf.org/rfc/rfc4757.txt
*
* @author Seema Malkani
*/
public class ArcFourCrypto extends DkCrypto {
private static final boolean debug = false;
private static final int confounderSize = 8;
private static final byte[] ZERO_IV = new byte[] {0, 0, 0, 0, 0, 0, 0, 0};
private static final int hashSize = 16;
private final int keyLength;
public ArcFourCrypto(int length) {
keyLength = length;
}
protected int getKeySeedLength() {
return keyLength; // bits; RC4 key material
}
protected byte[] randomToKey(byte[] in) {
// simple identity operation
return in;
}
public byte[] stringToKey(char[] passwd)
throws GeneralSecurityException {
return stringToKey(passwd, null);
}
/*
* String2Key(Password)
* K = MD4(UNICODE(password))
*/
private byte[] stringToKey(char[] secret, byte[] opaque)
throws GeneralSecurityException {
if (opaque != null && opaque.length > 0) {
throw new RuntimeException("Invalid parameter to stringToKey");
}
byte[] passwd = null;
byte[] digest = null;
try {
// convert ascii to unicode
passwd = charToUtf16(secret);
// provider for MD4
MessageDigest md = sun.security.provider.MD4.getInstance();
md.update(passwd);
digest = md.digest();
} catch (Exception e) {
return null;
} finally {
if (passwd != null) {
Arrays.fill(passwd, (byte)0);
}
}
return digest;
}
protected Cipher getCipher(byte[] key, byte[] ivec, int mode)
throws GeneralSecurityException {
// IV
if (ivec == null) {
ivec = ZERO_IV;
}
SecretKeySpec secretKey = new SecretKeySpec(key, "ARCFOUR");
Cipher cipher = Cipher.getInstance("ARCFOUR");
IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length);
cipher.init(mode, secretKey, encIv);
return cipher;
}
public int getChecksumLength() {
return hashSize; // bytes
}
/**
* Get the HMAC-MD5
*/
protected byte[] getHmac(byte[] key, byte[] msg)
throws GeneralSecurityException {
SecretKey keyKi = new SecretKeySpec(key, "HmacMD5");
Mac m = Mac.getInstance("HmacMD5");
m.init(keyKi);
// generate hash
byte[] hash = m.doFinal(msg);
return hash;
}
/**
* Calculate the checksum
*/
public byte[] calculateChecksum(byte[] baseKey, int usage, byte[] input,
int start, int len) throws GeneralSecurityException {
if (debug) {
System.out.println("ARCFOUR: calculateChecksum with usage = " +
usage);
}
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
byte[] Ksign = null;
// Derive signing key from session key
try {
byte[] ss = "signaturekey".getBytes();
// need to append end-of-string 00
byte[] new_ss = new byte[ss.length+1];
System.arraycopy(ss, 0, new_ss, 0, ss.length);
Ksign = getHmac(baseKey, new_ss);
} catch (Exception e) {
GeneralSecurityException gse =
new GeneralSecurityException("Calculate Checkum Failed!");
gse.initCause(e);
throw gse;
}
// get the salt using key usage
byte[] salt = getSalt(usage);
// Generate checksum of message
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
GeneralSecurityException gse =
new GeneralSecurityException("Calculate Checkum Failed!");
gse.initCause(e);
throw gse;
}
messageDigest.update(salt);
messageDigest.update(input, start, len);
byte[] md5tmp = messageDigest.digest();
// Generate checksum
byte[] hmac = getHmac(Ksign, md5tmp);
if (debug) {
traceOutput("hmac", hmac, 0, hmac.length);
}
if (hmac.length == getChecksumLength()) {
return hmac;
} else if (hmac.length > getChecksumLength()) {
byte[] buf = new byte[getChecksumLength()];
System.arraycopy(hmac, 0, buf, 0, buf.length);
return buf;
} else {
throw new GeneralSecurityException("checksum size too short: " +
hmac.length + "; expecting : " + getChecksumLength());
}
}
/**
* Performs encryption of Sequence Number using derived key.
*/
public byte[] encryptSeq(byte[] baseKey, int usage,
byte[] checksum, byte[] plaintext, int start, int len)
throws GeneralSecurityException, KrbCryptoException {
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
// derive encryption for sequence number
byte[] salt = new byte[4];
byte[] kSeq = getHmac(baseKey, salt);
// derive new encryption key salted with sequence number
kSeq = getHmac(kSeq, checksum);
Cipher cipher = Cipher.getInstance("ARCFOUR");
SecretKeySpec secretKey = new SecretKeySpec(kSeq, "ARCFOUR");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] output = cipher.doFinal(plaintext, start, len);
return output;
}
/**
* Performs decryption of Sequence Number using derived key.
*/
public byte[] decryptSeq(byte[] baseKey, int usage,
byte[] checksum, byte[] ciphertext, int start, int len)
throws GeneralSecurityException, KrbCryptoException {
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
// derive decryption for sequence number
byte[] salt = new byte[4];
byte[] kSeq = getHmac(baseKey, salt);
// derive new encryption key salted with sequence number
kSeq = getHmac(kSeq, checksum);
Cipher cipher = Cipher.getInstance("ARCFOUR");
SecretKeySpec secretKey = new SecretKeySpec(kSeq, "ARCFOUR");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] output = cipher.doFinal(ciphertext, start, len);
return output;
}
/**
* Performs encryption using derived key; adds confounder.
*/
public byte[] encrypt(byte[] baseKey, int usage,
byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len)
throws GeneralSecurityException, KrbCryptoException {
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
if (debug) {
System.out.println("ArcFour: ENCRYPT with key usage = " + usage);
}
// get the confounder
byte[] confounder = Confounder.bytes(confounderSize);
// add confounder to the plaintext for encryption
int plainSize = roundup(confounder.length + len, 1);
byte[] toBeEncrypted = new byte[plainSize];
System.arraycopy(confounder, 0, toBeEncrypted, 0, confounder.length);
System.arraycopy(plaintext, start, toBeEncrypted,
confounder.length, len);
/* begin the encryption, compute K1 */
byte[] k1 = new byte[baseKey.length];
System.arraycopy(baseKey, 0, k1, 0, baseKey.length);
// get the salt using key usage
byte[] salt = getSalt(usage);
// compute K2 using K1
byte[] k2 = getHmac(k1, salt);
// generate checksum using K2
byte[] checksum = getHmac(k2, toBeEncrypted);
// compute K3 using K2 and checksum
byte[] k3 = getHmac(k2, checksum);
Cipher cipher = Cipher.getInstance("ARCFOUR");
SecretKeySpec secretKey = new SecretKeySpec(k3, "ARCFOUR");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] output = cipher.doFinal(toBeEncrypted, 0, toBeEncrypted.length);
// encryptedData + HMAC
byte[] result = new byte[hashSize + output.length];
System.arraycopy(checksum, 0, result, 0, hashSize);
System.arraycopy(output, 0, result, hashSize, output.length);
return result;
}
/**
* Performs encryption using derived key; does not add confounder.
*/
public byte[] encryptRaw(byte[] baseKey, int usage,
byte[] seqNum, byte[] plaintext, int start, int len)
throws GeneralSecurityException, KrbCryptoException {
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
if (debug) {
System.out.println("\nARCFOUR: encryptRaw with usage = " + usage);
}
// Derive encryption key for data
// Key derivation salt = 0
byte[] klocal = new byte[baseKey.length];
for (int i = 0; i <= 15; i++) {
klocal[i] = (byte) (baseKey[i] ^ 0xF0);
}
byte[] salt = new byte[4];
byte[] kcrypt = getHmac(klocal, salt);
// Note: When using this RC4 based encryption type, the sequence number
// is always sent in big-endian rather than little-endian order.
// new encryption key salted with sequence number
kcrypt = getHmac(kcrypt, seqNum);
Cipher cipher = Cipher.getInstance("ARCFOUR");
SecretKeySpec secretKey = new SecretKeySpec(kcrypt, "ARCFOUR");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] output = cipher.doFinal(plaintext, start, len);
return output;
}
/**
* @param baseKey key from which keys are to be derived using usage
* @param ciphertext E(Ke, conf | plaintext | padding, ivec) | H1[1..h]
*/
public byte[] decrypt(byte[] baseKey, int usage, byte[] ivec,
byte[] ciphertext, int start, int len)
throws GeneralSecurityException {
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
if (debug) {
System.out.println("\nARCFOUR: DECRYPT using key usage = " + usage);
}
// compute K1
byte[] k1 = new byte[baseKey.length];
System.arraycopy(baseKey, 0, k1, 0, baseKey.length);
// get the salt using key usage
byte[] salt = getSalt(usage);
// compute K2 using K1
byte[] k2 = getHmac(k1, salt);
// compute K3 using K2 and checksum
byte[] checksum = new byte[hashSize];
System.arraycopy(ciphertext, start, checksum, 0, hashSize);
byte[] k3 = getHmac(k2, checksum);
// Decrypt [confounder | plaintext ] (without checksum)
Cipher cipher = Cipher.getInstance("ARCFOUR");
SecretKeySpec secretKey = new SecretKeySpec(k3, "ARCFOUR");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] plaintext = cipher.doFinal(ciphertext, start+hashSize,
len-hashSize);
// Verify checksum
byte[] calculatedHmac = getHmac(k2, plaintext);
if (debug) {
traceOutput("calculated Hmac", calculatedHmac, 0,
calculatedHmac.length);
traceOutput("message Hmac", ciphertext, 0,
hashSize);
}
boolean cksumFailed = false;
if (calculatedHmac.length >= hashSize) {
for (int i = 0; i < hashSize; i++) {
if (calculatedHmac[i] != ciphertext[i]) {
cksumFailed = true;
if (debug) {
System.err.println("Checksum failed !");
}
break;
}
}
}
if (cksumFailed) {
throw new GeneralSecurityException("Checksum failed");
}
// Get rid of confounder
// [ confounder | plaintext ]
byte[] output = new byte[plaintext.length - confounderSize];
System.arraycopy(plaintext, confounderSize, output, 0, output.length);
return output;
}
/**
* Decrypts data using specified key and initial vector.
* @param baseKey encryption key to use
* @param ciphertext encrypted data to be decrypted
* @param usage ignored
*/
public byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec,
byte[] ciphertext, int start, int len, byte[] seqNum)
throws GeneralSecurityException {
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
if (debug) {
System.out.println("\nARCFOUR: decryptRaw with usage = " + usage);
}
// Derive encryption key for data
// Key derivation salt = 0
byte[] klocal = new byte[baseKey.length];
for (int i = 0; i <= 15; i++) {
klocal[i] = (byte) (baseKey[i] ^ 0xF0);
}
byte[] salt = new byte[4];
byte[] kcrypt = getHmac(klocal, salt);
// need only first 4 bytes of sequence number
byte[] sequenceNum = new byte[4];
System.arraycopy(seqNum, 0, sequenceNum, 0, sequenceNum.length);
// new encryption key salted with sequence number
kcrypt = getHmac(kcrypt, sequenceNum);
Cipher cipher = Cipher.getInstance("ARCFOUR");
SecretKeySpec secretKey = new SecretKeySpec(kcrypt, "ARCFOUR");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] output = cipher.doFinal(ciphertext, start, len);
return output;
}
// get the salt using key usage
private byte[] getSalt(int usage) {
int ms_usage = arcfour_translate_usage(usage);
byte[] salt = new byte[4];
salt[0] = (byte)(ms_usage & 0xff);
salt[1] = (byte)((ms_usage >> 8) & 0xff);
salt[2] = (byte)((ms_usage >> 16) & 0xff);
salt[3] = (byte)((ms_usage >> 24) & 0xff);
return salt;
}
// Key usage translation for MS
private int arcfour_translate_usage(int usage) {
switch (usage) {
case 3: return 8;
case 9: return 8;
case 23: return 13;
default: return usage;
}
}
}

View File

@@ -0,0 +1,223 @@
/*
* Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.security.krb5.internal.crypto.dk;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKeyFactory;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.security.spec.KeySpec;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.util.Arrays;
public class Des3DkCrypto extends DkCrypto {
private static final byte[] ZERO_IV = new byte[] {0, 0, 0, 0, 0, 0, 0, 0};
public Des3DkCrypto() {
}
protected int getKeySeedLength() {
return 168; // bits; 3DES key material has 21 bytes
}
public byte[] stringToKey(char[] salt) throws GeneralSecurityException {
byte[] saltUtf8 = null;
try {
saltUtf8 = charToUtf8(salt);
return stringToKey(saltUtf8, null);
} finally {
if (saltUtf8 != null) {
Arrays.fill(saltUtf8, (byte)0);
}
// Caller responsible for clearing its own salt
}
}
private byte[] stringToKey(byte[] secretAndSalt, byte[] opaque)
throws GeneralSecurityException {
if (opaque != null && opaque.length > 0) {
throw new RuntimeException("Invalid parameter to stringToKey");
}
byte[] tmpKey = randomToKey(nfold(secretAndSalt, getKeySeedLength()));
return dk(tmpKey, KERBEROS_CONSTANT);
}
public byte[] parityFix(byte[] value)
throws GeneralSecurityException {
// fix key parity
setParityBit(value);
return value;
}
/*
* From RFC 3961.
*
* The 168 bits of random key data are converted to a protocol key value
* as follows. First, the 168 bits are divided into three groups of 56
* bits, which are expanded individually into 64 bits as in des3Expand().
* Result is a 24 byte (192-bit) key.
*/
protected byte[] randomToKey(byte[] in) {
if (in.length != 21) {
throw new IllegalArgumentException("input must be 168 bits");
}
byte[] one = keyCorrection(des3Expand(in, 0, 7));
byte[] two = keyCorrection(des3Expand(in, 7, 14));
byte[] three = keyCorrection(des3Expand(in, 14, 21));
byte[] key = new byte[24];
System.arraycopy(one, 0, key, 0, 8);
System.arraycopy(two, 0, key, 8, 8);
System.arraycopy(three, 0, key, 16, 8);
return key;
}
private static byte[] keyCorrection(byte[] key) {
// check for weak key
try {
if (DESKeySpec.isWeak(key, 0)) {
key[7] = (byte)(key[7] ^ 0xF0);
}
} catch (InvalidKeyException ex) {
// swallow, since it should never happen
}
return key;
}
/**
* From RFC 3961.
*
* Expands a 7-byte array into an 8-byte array that contains parity bits.
* The 56 bits are expanded into 64 bits as follows:
* 1 2 3 4 5 6 7 p
* 9 10 11 12 13 14 15 p
* 17 18 19 20 21 22 23 p
* 25 26 27 28 29 30 31 p
* 33 34 35 36 37 38 39 p
* 41 42 43 44 45 46 47 p
* 49 50 51 52 53 54 55 p
* 56 48 40 32 24 16 8 p
*
* (PI,P2,...,P8) are reserved for parity bits computed on the preceding
* seven independent bits and set so that the parity of the octet is odd,
* i.e., there is an odd number of "1" bits in the octet.
*
* @param start index of starting byte (inclusive)
* @param end index of ending byte (exclusive)
*/
private static byte[] des3Expand(byte[] input, int start, int end) {
if ((end - start) != 7)
throw new IllegalArgumentException(
"Invalid length of DES Key Value:" + start + "," + end);
byte[] result = new byte[8];
byte last = 0;
System.arraycopy(input, start, result, 0, 7);
byte posn = 0;
// Fill in last row
for (int i = start; i < end; i++) {
byte bit = (byte) (input[i]&0x01);
if (debug) {
System.out.println(i + ": " + Integer.toHexString(input[i]) +
" bit= " + Integer.toHexString(bit));
}
++posn;
if (bit != 0) {
last |= (bit<<posn);
}
}
if (debug) {
System.out.println("last: " + Integer.toHexString(last));
}
result[7] = last;
setParityBit(result);
return result;
}
/**
* Sets the parity bit (0th bit) in each byte so that each byte
* contains an odd number of 1's.
*/
private static void setParityBit(byte[] key) {
for (int i = 0; i < key.length; i++) {
int b = key[i] & 0xfe;
b |= (Integer.bitCount(b) & 1) ^ 1;
key[i] = (byte) b;
}
}
protected Cipher getCipher(byte[] key, byte[] ivec, int mode)
throws GeneralSecurityException {
// NoSuchAlgorithException
SecretKeyFactory factory = SecretKeyFactory.getInstance("desede");
// InvalidKeyException
KeySpec spec = new DESedeKeySpec(key, 0);
// InvalidKeySpecException
SecretKey secretKey = factory.generateSecret(spec);
// IV
if (ivec == null) {
ivec = ZERO_IV;
}
// NoSuchAlgorithmException, NoSuchPaddingException
// NoSuchProviderException
Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding");
IvParameterSpec encIv = new IvParameterSpec(ivec, 0, ivec.length);
// InvalidKeyException, InvalidAlgorithParameterException
cipher.init(mode, secretKey, encIv);
return cipher;
}
public int getChecksumLength() {
return 20; // bytes
}
protected byte[] getHmac(byte[] key, byte[] msg)
throws GeneralSecurityException {
SecretKey keyKi = new SecretKeySpec(key, "HmacSHA1");
Mac m = Mac.getInstance("HmacSHA1");
m.init(keyKi);
return m.doFinal(msg);
}
}

View File

@@ -0,0 +1,699 @@
/*
* Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Copyright (C) 1998 by the FundsXpress, INC.
*
* All rights reserved.
*
* Export of this software from the United States of America may require
* a specific license from the United States Government. It is the
* responsibility of any person or organization contemplating export to
* obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of FundsXpress. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. FundsXpress makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
package sun.security.krb5.internal.crypto.dk;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import java.security.GeneralSecurityException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.nio.CharBuffer;
import java.nio.ByteBuffer;
import sun.misc.HexDumpEncoder;
import sun.security.krb5.Confounder;
import sun.security.krb5.internal.crypto.KeyUsage;
import sun.security.krb5.KrbCryptoException;
/**
* Implements Derive Key cryptography functionality as defined in RFC 3961.
* http://www.ietf.org/rfc/rfc3961.txt
*
* This is an abstract class. Concrete subclasses need to implement
* the abstract methods.
*/
public abstract class DkCrypto {
protected static final boolean debug = false;
// These values correspond to the ASCII encoding for the string "kerberos"
static final byte[] KERBEROS_CONSTANT =
{0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73};
protected abstract int getKeySeedLength(); // in bits
protected abstract byte[] randomToKey(byte[] in);
protected abstract Cipher getCipher(byte[] key, byte[] ivec, int mode)
throws GeneralSecurityException;
public abstract int getChecksumLength(); // in bytes
protected abstract byte[] getHmac(byte[] key, byte[] plaintext)
throws GeneralSecurityException;
/**
* From RFC 3961.
*
* encryption function conf = random string of length c
* pad = shortest string to bring confounder
* and plaintext to a length that's a
* multiple of m
* (C1, newIV) = E(Ke, conf | plaintext | pad,
* oldstate.ivec)
* H1 = HMAC(Ki, conf | plaintext | pad)
* ciphertext = C1 | H1[1..h]
* newstate.ivec = newIV
*
* @param ivec initial vector to use when initializing the cipher; if null,
* then blocksize number of zeros are used,
* @param new_ivec if non-null, it is updated upon return to be the
* new ivec to use when calling encrypt next time
*/
public byte[] encrypt(byte[] baseKey, int usage,
byte[] ivec, byte[] new_ivec, byte[] plaintext, int start, int len)
throws GeneralSecurityException, KrbCryptoException {
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
byte[] Ke = null;
byte[] Ki = null;
try {
// Derive encryption key
byte[] constant = new byte[5];
constant[0] = (byte) ((usage>>24)&0xff);
constant[1] = (byte) ((usage>>16)&0xff);
constant[2] = (byte) ((usage>>8)&0xff);
constant[3] = (byte) (usage&0xff);
constant[4] = (byte) 0xaa;
Ke = dk(baseKey, constant);
if (debug) {
System.err.println("usage: " + usage);
if (ivec != null) {
traceOutput("old_state.ivec", ivec, 0, ivec.length);
}
traceOutput("plaintext", plaintext, start, Math.min(len, 32));
traceOutput("constant", constant, 0, constant.length);
traceOutput("baseKey", baseKey, 0, baseKey.length);
traceOutput("Ke", Ke, 0, Ke.length);
}
// Encrypt
// C1 = E(Ke, conf | plaintext | pad, oldivec)
Cipher encCipher = getCipher(Ke, ivec, Cipher.ENCRYPT_MODE);
int blockSize = encCipher.getBlockSize();
byte[] confounder = Confounder.bytes(blockSize);
int plainSize = roundup(confounder.length + len, blockSize);
if (debug) {
System.err.println("confounder = " + confounder.length +
"; plaintext = " + len + "; padding = " +
(plainSize - confounder.length - len) + "; total = " +
plainSize);
traceOutput("confounder", confounder, 0, confounder.length);
}
byte[] toBeEncrypted = new byte[plainSize];
System.arraycopy(confounder, 0, toBeEncrypted,
0, confounder.length);
System.arraycopy(plaintext, start, toBeEncrypted,
confounder.length, len);
// Set padding bytes to zero
Arrays.fill(toBeEncrypted, confounder.length + len, plainSize,
(byte)0);
int cipherSize = encCipher.getOutputSize(plainSize);
int ccSize = cipherSize + getChecksumLength(); // cipher | hmac
byte[] ciphertext = new byte[ccSize];
encCipher.doFinal(toBeEncrypted, 0, plainSize, ciphertext, 0);
// Update ivec for next operation
// (last blockSize bytes of ciphertext)
// newstate.ivec = newIV
if (new_ivec != null && new_ivec.length == blockSize) {
System.arraycopy(ciphertext, cipherSize - blockSize,
new_ivec, 0, blockSize);
if (debug) {
traceOutput("new_ivec", new_ivec, 0, new_ivec.length);
}
}
// Derive integrity key
constant[4] = (byte) 0x55;
Ki = dk(baseKey, constant);
if (debug) {
traceOutput("constant", constant, 0, constant.length);
traceOutput("Ki", Ki, 0, Ke.length);
}
// Generate checksum
// H1 = HMAC(Ki, conf | plaintext | pad)
byte[] hmac = getHmac(Ki, toBeEncrypted);
if (debug) {
traceOutput("hmac", hmac, 0, hmac.length);
traceOutput("ciphertext", ciphertext, 0,
Math.min(ciphertext.length, 32));
}
// C1 | H1[1..h]
System.arraycopy(hmac, 0, ciphertext, cipherSize,
getChecksumLength());
return ciphertext;
} finally {
if (Ke != null) {
Arrays.fill(Ke, 0, Ke.length, (byte) 0);
}
if (Ki != null) {
Arrays.fill(Ki, 0, Ki.length, (byte) 0);
}
}
}
/**
* Performs encryption using given key only; does not add
* confounder, padding, or checksum. Incoming data to be encrypted
* assumed to have the correct blocksize.
* Ignore key usage.
*/
public byte[] encryptRaw(byte[] baseKey, int usage,
byte[] ivec, byte[] plaintext, int start, int len)
throws GeneralSecurityException, KrbCryptoException {
if (debug) {
System.err.println("usage: " + usage);
if (ivec != null) {
traceOutput("old_state.ivec", ivec, 0, ivec.length);
}
traceOutput("plaintext", plaintext, start, Math.min(len, 32));
traceOutput("baseKey", baseKey, 0, baseKey.length);
}
// Encrypt
Cipher encCipher = getCipher(baseKey, ivec, Cipher.ENCRYPT_MODE);
int blockSize = encCipher.getBlockSize();
if ((len % blockSize) != 0) {
throw new GeneralSecurityException(
"length of data to be encrypted (" + len +
") is not a multiple of the blocksize (" + blockSize + ")");
}
int cipherSize = encCipher.getOutputSize(len);
byte[] ciphertext = new byte[cipherSize];
encCipher.doFinal(plaintext, 0, len, ciphertext, 0);
return ciphertext;
}
/**
* Decrypts data using specified key and initial vector.
* @param baseKey encryption key to use
* @param ciphertext encrypted data to be decrypted
* @param usage ignored
*/
public byte[] decryptRaw(byte[] baseKey, int usage, byte[] ivec,
byte[] ciphertext, int start, int len)
throws GeneralSecurityException {
if (debug) {
System.err.println("usage: " + usage);
if (ivec != null) {
traceOutput("old_state.ivec", ivec, 0, ivec.length);
}
traceOutput("ciphertext", ciphertext, start, Math.min(len, 32));
traceOutput("baseKey", baseKey, 0, baseKey.length);
}
Cipher decCipher = getCipher(baseKey, ivec, Cipher.DECRYPT_MODE);
int blockSize = decCipher.getBlockSize();
if ((len % blockSize) != 0) {
throw new GeneralSecurityException(
"length of data to be decrypted (" + len +
") is not a multiple of the blocksize (" + blockSize + ")");
}
byte[] decrypted = decCipher.doFinal(ciphertext, start, len);
if (debug) {
traceOutput("decrypted", decrypted, 0,
Math.min(decrypted.length, 32));
}
return decrypted;
}
/**
* @param baseKey key from which keys are to be derived using usage
* @param ciphertext E(Ke, conf | plaintext | padding, ivec) | H1[1..h]
*/
public byte[] decrypt(byte[] baseKey, int usage, byte[] ivec,
byte[] ciphertext, int start, int len) throws GeneralSecurityException {
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
byte[] Ke = null;
byte[] Ki = null;
try {
// Derive encryption key
byte[] constant = new byte[5];
constant[0] = (byte) ((usage>>24)&0xff);
constant[1] = (byte) ((usage>>16)&0xff);
constant[2] = (byte) ((usage>>8)&0xff);
constant[3] = (byte) (usage&0xff);
constant[4] = (byte) 0xaa;
Ke = dk(baseKey, constant); // Encryption key
if (debug) {
System.err.println("usage: " + usage);
if (ivec != null) {
traceOutput("old_state.ivec", ivec, 0, ivec.length);
}
traceOutput("ciphertext", ciphertext, start, Math.min(len, 32));
traceOutput("constant", constant, 0, constant.length);
traceOutput("baseKey", baseKey, 0, baseKey.length);
traceOutput("Ke", Ke, 0, Ke.length);
}
Cipher decCipher = getCipher(Ke, ivec, Cipher.DECRYPT_MODE);
int blockSize = decCipher.getBlockSize();
// Decrypt [confounder | plaintext | padding] (without checksum)
int cksumSize = getChecksumLength();
int cipherSize = len - cksumSize;
byte[] decrypted = decCipher.doFinal(ciphertext, start, cipherSize);
if (debug) {
traceOutput("decrypted", decrypted, 0,
Math.min(decrypted.length, 32));
}
// decrypted = [confounder | plaintext | padding]
// Derive integrity key
constant[4] = (byte) 0x55;
Ki = dk(baseKey, constant); // Integrity key
if (debug) {
traceOutput("constant", constant, 0, constant.length);
traceOutput("Ki", Ki, 0, Ke.length);
}
// Verify checksum
// H1 = HMAC(Ki, conf | plaintext | pad)
byte[] calculatedHmac = getHmac(Ki, decrypted);
if (debug) {
traceOutput("calculated Hmac", calculatedHmac, 0,
calculatedHmac.length);
traceOutput("message Hmac", ciphertext, cipherSize,
cksumSize);
}
boolean cksumFailed = false;
if (calculatedHmac.length >= cksumSize) {
for (int i = 0; i < cksumSize; i++) {
if (calculatedHmac[i] != ciphertext[cipherSize+i]) {
cksumFailed = true;
break;
}
}
}
if (cksumFailed) {
throw new GeneralSecurityException("Checksum failed");
}
// Prepare decrypted msg and ivec to be returned
// Last blockSize bytes of ciphertext without checksum
if (ivec != null && ivec.length == blockSize) {
System.arraycopy(ciphertext, start + cipherSize - blockSize,
ivec, 0, blockSize);
if (debug) {
traceOutput("new_state.ivec", ivec, 0, ivec.length);
}
}
// Get rid of confounder
// [plaintext | padding]
byte[] plaintext = new byte[decrypted.length - blockSize];
System.arraycopy(decrypted, blockSize, plaintext,
0, plaintext.length);
return plaintext; // padding still there
} finally {
if (Ke != null) {
Arrays.fill(Ke, 0, Ke.length, (byte) 0);
}
if (Ki != null) {
Arrays.fill(Ki, 0, Ki.length, (byte) 0);
}
}
}
// Round up to the next blocksize
int roundup(int n, int blocksize) {
return (((n + blocksize - 1) / blocksize) * blocksize);
}
public byte[] calculateChecksum(byte[] baseKey, int usage, byte[] input,
int start, int len) throws GeneralSecurityException {
if (!KeyUsage.isValid(usage)) {
throw new GeneralSecurityException("Invalid key usage number: "
+ usage);
}
// Derive keys
byte[] constant = new byte[5];
constant[0] = (byte) ((usage>>24)&0xff);
constant[1] = (byte) ((usage>>16)&0xff);
constant[2] = (byte) ((usage>>8)&0xff);
constant[3] = (byte) (usage&0xff);
constant[4] = (byte) 0x99;
byte[] Kc = dk(baseKey, constant); // Checksum key
if (debug) {
System.err.println("usage: " + usage);
traceOutput("input", input, start, Math.min(len, 32));
traceOutput("constant", constant, 0, constant.length);
traceOutput("baseKey", baseKey, 0, baseKey.length);
traceOutput("Kc", Kc, 0, Kc.length);
}
try {
// Generate checksum
// H1 = HMAC(Kc, input)
byte[] hmac = getHmac(Kc, input);
if (debug) {
traceOutput("hmac", hmac, 0, hmac.length);
}
if (hmac.length == getChecksumLength()) {
return hmac;
} else if (hmac.length > getChecksumLength()) {
byte[] buf = new byte[getChecksumLength()];
System.arraycopy(hmac, 0, buf, 0, buf.length);
return buf;
} else {
throw new GeneralSecurityException("checksum size too short: " +
hmac.length + "; expecting : " + getChecksumLength());
}
} finally {
Arrays.fill(Kc, 0, Kc.length, (byte)0);
}
}
// DK(Key, Constant) = random-to-key(DR(Key, Constant))
byte[] dk(byte[] key, byte[] constant)
throws GeneralSecurityException {
return randomToKey(dr(key, constant));
}
/*
* From RFC 3961.
*
* DR(Key, Constant) = k-truncate(E(Key, Constant,
* initial-cipher-state))
*
* Here DR is the random-octet generation function described below, and
* DK is the key-derivation function produced from it. In this
* construction, E(Key, Plaintext, CipherState) is a cipher, Constant is
* a well-known constant determined by the specific usage of this
* function, and k-truncate truncates its argument by taking the first k
* bits. Here, k is the key generation seed length needed for the
* encryption system.
*
* The output of the DR function is a string of bits; the actual key is
* produced by applying the cryptosystem's random-to-key operation on
* this bitstring.
*
* If the Constant is smaller than the cipher block size of E, then it
* must be expanded with n-fold() so it can be encrypted. If the output
* of E is shorter than k bits it is fed back into the encryption as
* many times as necessary. The construct is as follows (where |
* indicates concatentation):
*
* K1 = E(Key, n-fold(Constant), initial-cipher-state)
* K2 = E(Key, K1, initial-cipher-state)
* K3 = E(Key, K2, initial-cipher-state)
* K4 = ...
*
* DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...)
*/
private byte[] dr(byte[] key, byte[] constant)
throws GeneralSecurityException {
Cipher encCipher = getCipher(key, null, Cipher.ENCRYPT_MODE);
int blocksize = encCipher.getBlockSize();
if (constant.length != blocksize) {
constant = nfold(constant, blocksize * 8);
}
byte[] toBeEncrypted = constant;
int keybytes = (getKeySeedLength()>>3); // from bits to bytes
byte[] rawkey = new byte[keybytes];
int posn = 0;
/* loop encrypting the blocks until enough key bytes are generated */
int n = 0, len;
while (n < keybytes) {
if (debug) {
System.err.println("Encrypting: " +
bytesToString(toBeEncrypted));
}
byte[] cipherBlock = encCipher.doFinal(toBeEncrypted);
if (debug) {
System.err.println("K: " + ++posn + " = " +
bytesToString(cipherBlock));
}
len = (keybytes - n <= cipherBlock.length ? (keybytes - n) :
cipherBlock.length);
if (debug) {
System.err.println("copying " + len + " key bytes");
}
System.arraycopy(cipherBlock, 0, rawkey, n, len);
n += len;
toBeEncrypted = cipherBlock;
}
return rawkey;
}
// ---------------------------------
// From MIT-1.3.1 distribution
/*
* n-fold(k-bits):
* l = lcm(n,k)
* r = l/k
* s = k-bits | k-bits rot 13 | k-bits rot 13*2 | ... | k-bits rot 13*(r-1)
* compute the 1's complement sum:
* n-fold = s[0..n-1]+s[n..2n-1]+s[2n..3n-1]+..+s[(k-1)*n..k*n-1]
*/
/*
* representation: msb first, assume n and k are multiples of 8, and
* that k>=16. this is the case of all the cryptosystems which are
* likely to be used. this function can be replaced if that
* assumption ever fails.
*/
/* input length is in bits */
static byte[] nfold(byte[] in, int outbits) {
int inbits = in.length;
outbits >>= 3; // count in bytes
/* first compute lcm(n,k) */
int a, b, c, lcm;
a = outbits; // n
b = inbits; // k
while (b != 0) {
c = b;
b = a % b;
a = c;
}
lcm = outbits*inbits/a;
if (debug) {
System.err.println("k: " + inbits);
System.err.println("n: " + outbits);
System.err.println("lcm: " + lcm);
}
/* now do the real work */
byte[] out = new byte[outbits];
Arrays.fill(out, (byte)0);
int thisbyte = 0;
int msbit, i, bval, oval;
// this will end up cycling through k lcm(k,n)/k times, which
// is correct
for (i = lcm-1; i >= 0; i--) {
/* compute the msbit in k which gets added into this byte */
msbit = (/* first, start with msbit in the first, unrotated byte */
((inbits<<3)-1)
/* then, for each byte, shift to right for each repetition */
+ (((inbits<<3)+13)*(i/inbits))
/* last, pick out correct byte within that shifted repetition */
+ ((inbits-(i%inbits)) << 3)) % (inbits << 3);
/* pull out the byte value itself */
// Mask off values using &0xff to get only the lower byte
// Use >>> to avoid sign extension
bval = ((((in[((inbits-1)-(msbit>>>3))%inbits]&0xff)<<8)|
(in[((inbits)-(msbit>>>3))%inbits]&0xff))
>>>((msbit&7)+1))&0xff;
/*
System.err.println("((" +
((in[((inbits-1)-(msbit>>>3))%inbits]&0xff)<<8)
+ "|" + (in[((inbits)-(msbit>>>3))%inbits]&0xff) + ")"
+ ">>>" + ((msbit&7)+1) + ")&0xff = " + bval);
*/
thisbyte += bval;
/* do the addition */
// Mask off values using &0xff to get only the lower byte
oval = (out[i%outbits]&0xff);
thisbyte += oval;
out[i%outbits] = (byte) (thisbyte&0xff);
if (debug) {
System.err.println("msbit[" + i + "] = " + msbit + "\tbval=" +
Integer.toHexString(bval) + "\toval=" +
Integer.toHexString(oval)
+ "\tsum = " + Integer.toHexString(thisbyte));
}
/* keep around the carry bit, if any */
thisbyte >>>= 8;
if (debug) {
System.err.println("carry=" + thisbyte);
}
}
/* if there's a carry bit left over, add it back in */
if (thisbyte != 0) {
for (i = outbits-1; i >= 0; i--) {
/* do the addition */
thisbyte += (out[i]&0xff);
out[i] = (byte) (thisbyte&0xff);
/* keep around the carry bit, if any */
thisbyte >>>= 8;
}
}
return out;
}
// Routines used for debugging
static String bytesToString(byte[] digest) {
// Get character representation of digest
StringBuffer digestString = new StringBuffer();
for (int i = 0; i < digest.length; i++) {
if ((digest[i] & 0x000000ff) < 0x10) {
digestString.append("0" +
Integer.toHexString(digest[i] & 0x000000ff));
} else {
digestString.append(
Integer.toHexString(digest[i] & 0x000000ff));
}
}
return digestString.toString();
}
private static byte[] binaryStringToBytes(String str) {
char[] usageStr = str.toCharArray();
byte[] usage = new byte[usageStr.length/2];
for (int i = 0; i < usage.length; i++) {
byte a = Byte.parseByte(new String(usageStr, i*2, 1), 16);
byte b = Byte.parseByte(new String(usageStr, i*2 + 1, 1), 16);
usage[i] = (byte) ((a<<4)|b);
}
return usage;
}
static void traceOutput(String traceTag, byte[] output, int offset,
int len) {
try {
ByteArrayOutputStream out = new ByteArrayOutputStream(len);
new HexDumpEncoder().encodeBuffer(
new ByteArrayInputStream(output, offset, len), out);
System.err.println(traceTag + ":" + out.toString());
} catch (Exception e) {
}
}
// String.getBytes("UTF-8");
// Do this instead of using String to avoid making password immutable
static byte[] charToUtf8(char[] chars) {
Charset utf8 = Charset.forName("UTF-8");
CharBuffer cb = CharBuffer.wrap(chars);
ByteBuffer bb = utf8.encode(cb);
int len = bb.limit();
byte[] answer = new byte[len];
bb.get(answer, 0, len);
return answer;
}
static byte[] charToUtf16(char[] chars) {
Charset utf8 = Charset.forName("UTF-16LE");
CharBuffer cb = CharBuffer.wrap(chars);
ByteBuffer bb = utf8.encode(cb);
int len = bb.limit();
byte[] answer = new byte[len];
bb.get(answer, 0, len);
return answer;
}
}