feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
699
jdkSrc/jdk8/sun/security/krb5/internal/crypto/dk/DkCrypto.java
Normal file
699
jdkSrc/jdk8/sun/security/krb5/internal/crypto/dk/DkCrypto.java
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user