jcifs.pac.PacMac Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcifs-ng Show documentation
Show all versions of jcifs-ng Show documentation
A pure-java CIFS/SMB client library
/*
* © 2017 AgNO3 Gmbh & Co. KG
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package jcifs.pac;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Map;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.kerberos.KerberosKey;
@SuppressWarnings ( "javadoc" )
public class PacMac {
/**
*
*/
private static final String HMAC_KEY = "HMAC";
private static final byte[] MD5_CONSTANT = "signaturekey\0".getBytes(StandardCharsets.US_ASCII);
private static final byte[] ZERO_IV = new byte[] {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
public static byte[] calculateMacArcfourHMACMD5 ( int keyusage, Key key, byte[] data ) throws GeneralSecurityException {
int ms_usage = mapArcfourMD5KeyUsage(keyusage);
Mac mac = Mac.getInstance("HmacMD5");
MessageDigest md = MessageDigest.getInstance("MD5");
mac.init(key);
byte[] dk = mac.doFinal(MD5_CONSTANT);
try {
// little endian
md.update((byte) ( ms_usage & 0xFF ));
md.update((byte) ( ( ms_usage >> 8 ) & 0xFF ));
md.update((byte) ( ( ms_usage >> 16 ) & 0xFF ));
md.update((byte) ( ( ms_usage >> 24 ) & 0xFF ));
byte[] dgst = md.digest(data);
mac.reset();
mac.init(new SecretKeySpec(dk, HMAC_KEY));
return mac.doFinal(dgst);
}
finally {
Arrays.fill(dk, 0, dk.length, (byte) 0);
}
}
private static int mapArcfourMD5KeyUsage ( int keyusage ) {
int ms_usage = keyusage;
switch ( ms_usage ) {
case 3:
ms_usage = 8;
case 9:
ms_usage = 8;
case 23:
ms_usage = 13;
}
return ms_usage;
}
public static byte[] calculateMacHMACAES ( int usage, KerberosKey baseKey, byte[] input ) throws GeneralSecurityException {
byte[] cst = new byte[] {
(byte) ( ( usage >> 24 ) & 0xFF ), (byte) ( ( usage >> 16 ) & 0xFF ), (byte) ( ( usage >> 8 ) & 0xFF ), (byte) ( usage & 0xFF ),
(byte) 0x99
};
byte[] output = new byte[12];
byte[] dk = deriveKeyAES(baseKey, cst); // Checksum key
try {
Mac m = Mac.getInstance("HmacSHA1");
m.init(new SecretKeySpec(dk, HMAC_KEY));
System.arraycopy(m.doFinal(input), 0, output, 0, 12);
return output;
}
finally {
Arrays.fill(dk, 0, dk.length, (byte) 0);
}
}
public static byte[] deriveKeyAES ( KerberosKey key, byte[] constant ) throws GeneralSecurityException {
byte[] keybytes = key.getEncoded();
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keybytes, "AES"), new IvParameterSpec(ZERO_IV, 0, ZERO_IV.length));
if ( constant.length != cipher.getBlockSize() ) {
constant = expandNFold(constant, cipher.getBlockSize());
}
byte[] enc = constant;
int klen = keybytes.length;
byte[] dk = new byte[klen];
for ( int n = 0; n < klen; ) {
byte[] block = cipher.doFinal(enc);
int len = Math.min(klen - n, block.length);
System.arraycopy(block, 0, dk, n, len);
n += len;
enc = block;
}
return dk;
}
public static byte[] expandNFold ( byte[] data, int outlen ) {
int lcm = lcm(outlen, data.length);
byte[] buf = new byte[outlen];
int carry = 0;
for ( int i = lcm - 1; i >= 0; i-- ) {
carry = carry_add(data, buf, carry, i);
}
if ( carry != 0 ) {
for ( int i = outlen - 1; i >= 0; i-- ) {
carry += ( buf[ i ] & 0xff );
buf[ i ] = (byte) ( carry & 0xff );
carry >>>= 8;
}
}
return buf;
}
private static int carry_add ( byte[] data, byte[] out, int c, int i ) {
int ilen = data.length, olen = out.length;
int msbit = ( ( ( ilen << 3 ) - 1 ) + ( ( ( ilen << 3 ) + 13 ) * ( i / ilen ) ) + ( ( ilen - ( i % ilen ) ) << 3 ) ) % ( ilen << 3 );
int mshigh = msbit >>> 3, mslow = msbit & 7;
int b = c + ( out[ i % olen ] & 0xff ) + ( ( ( ( ( data[ ( ( ilen - 1 ) - mshigh ) % ilen ] & 0xff ) << 8 )
| ( data[ ( ilen - mshigh ) % ilen ] & 0xff ) ) >>> ( mslow + 1 ) ) & 0xff );
out[ i % olen ] = (byte) ( b & 0xff );
return b >>>= 8;
}
private static int lcm ( int u, int v ) {
int a = u, b = v, c;
while ( b != 0 ) {
c = b;
b = a % b;
a = c;
}
return u * v / a;
}
/**
* @param type
* @param keys
* @return the calculated mac
* @throws PACDecodingException
*/
public static byte[] calculateMac ( int type, Map keys, byte[] data ) throws PACDecodingException {
try {
int usage = 17;
if ( type == PacSignature.KERB_CHECKSUM_HMAC_MD5 ) {
KerberosKey key = keys.get(PacSignature.ETYPE_ARCFOUR_HMAC);
if ( key == null ) {
throw new PACDecodingException("Missing key");
}
return calculateMacArcfourHMACMD5(usage, key, data);
}
else if ( type == PacSignature.HMAC_SHA1_96_AES128 || type == PacSignature.HMAC_SHA1_96_AES256 ) {
KerberosKey key = type == PacSignature.HMAC_SHA1_96_AES128 ? keys.get(PacSignature.ETYPE_AES128_CTS_HMAC_SHA1_96)
: keys.get(PacSignature.ETYPE_AES256_CTS_HMAC_SHA1_96);
if ( key == null ) {
throw new PACDecodingException("Missing key");
}
return calculateMacHMACAES(usage, key, data);
}
else {
throw new PACDecodingException("Invalid MAC algorithm");
}
}
catch ( GeneralSecurityException e ) {
throw new PACDecodingException("Failed to calculate MAC", e);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy