/******************************************************************************
* The following is part of the KeySupport.org PIV API
*
* https://github.com/grandamp/KSJavaAPI/
*
* @author Todd E. Johnson (tejohnson@yahoo.com)
*****************************************************************************/
package org.keysupport.crypto;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.keysupport.util.DataUtil;
/**
* @author Todd E. Johnson (tejohnson@yahoo.com)
* @version $Revision: 15 $
*/
public class CipherEngine {
private final static boolean debug = false;
// 800-78 Table 6-1
public final static byte PIV_AUTH_KEY = (byte) 0x9a;
public final static byte CARD_MGMT_KEY = (byte) 0x9b;
public final static byte DIG_SIG_KEY = (byte) 0x9c;
public final static byte KEY_MGMT_KEY = (byte) 0x9d;
public final static byte CARD_AUTH_KEY = (byte) 0x9e;
// 800-78 Table 6-2
public final static byte THREE_KEY_3DES_ECB = (byte) 0x00; // Also 0x03
public final static byte TWO_KEY_3DES_ECB = (byte) 0x01;
public final static byte RSA_1024 = (byte) 0x06;
public final static byte RSA_2048 = (byte) 0x07;
public final static byte AES_128_ECB = (byte) 0x08;
public final static byte AES_192_ECB = (byte) 0x0A;
public final static byte AES_256_ECB = (byte) 0x0C;
public final static byte ECC_CURVE_P256 = (byte) 0x11;
public final static byte ECC_CURVE_P384 = (byte) 0x14;
// Algorithm OIDs from 800-78-3
// Digests
/**
* SHA-1 id-sha1 ::= {iso(1) identified-organization(3) oiw(14) secsig(3)
* algorithms(2) 26}
*/
public final static ASN1ObjectIdentifier SHA1 = new ASN1ObjectIdentifier(
"1.3.14.3.2.26");
/**
* SHA-256 id-sha256 ::= {joint-iso-itu-t(2) country(16) us(840)
* organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 1}
*/
public final static ASN1ObjectIdentifier SHA256 = new ASN1ObjectIdentifier(
"2.16.840.1.101.3.4.2.1");
/**
* SHA-384 id-sha384 ::= {joint-iso-itu-t(2) country(16) us(840)
* organization(1) gov(101) csor(3) nistalgorithm(4) hashalgs(2) 2}
*/
public final static ASN1ObjectIdentifier SHA384 = new ASN1ObjectIdentifier(
"2.16.840.1.101.3.4.2.2");
// Asymmetric Algorithms
/**
* RSA id-rsa ::= {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1)
* pkcs-1(1) 1}
*/
public final static ASN1ObjectIdentifier RSA = new ASN1ObjectIdentifier(
"1.2.840.113549.1.1.1");
/**
* ECDSA id-ecdsa::= {iso(1) member-body(2) us(840) ansi-X9-62(10045)
* id-publicKeyType(2) 1} --Technically ECDSA and ECDH Public Key
*/
public final static ASN1ObjectIdentifier ECDSA = new ASN1ObjectIdentifier(
"1.2.840.10045.2.1");
/**
* ECDH id-ecdh::= {iso(1) member-body(2) us(840) ansi-X9-62(10045)
* id-publicKeyType(2) 1} --Technically ECDSA and ECDH Public Key
*/
public final static ASN1ObjectIdentifier ECDH = new ASN1ObjectIdentifier(
"1.2.840.10045.2.1");
// Signature Algorithms
/**
* RSA with SHA-1 and PKCS #1 v1.5 padding sha1WithRSAEncryption ::= {iso(1)
* member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 5}
*/
public final static ASN1ObjectIdentifier SHA1withRSA = new ASN1ObjectIdentifier(
"1.2.840.113549.1.1.5");
/**
* RSA with SHA-256 and PKCS #1 v1.5 padding sha256WithRSAEncryption ::=
* {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 11}
*/
public final static ASN1ObjectIdentifier SHA256withRSA = new ASN1ObjectIdentifier(
"1.2.840.113549.1.1.11");
/**
* RSA with SHA-256 and PSS padding id-RSASSA-PSS ::= {iso(1) member-body(2)
* us(840) rsadsi(113549) pkcs(1) pkcs-1(1) 10}
*/
public final static ASN1ObjectIdentifier SHA256withRSAPSS = new ASN1ObjectIdentifier(
"1.2.840.113549.1.1.10");
/**
* ECDSA with SHA-256 ecdsa-with-SHA256 ::= {iso(1) member-body(2) us(840)
* ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2 (3) 2}
*/
public final static ASN1ObjectIdentifier SHA256withECDSA = new ASN1ObjectIdentifier(
"1.2.840.10045.4.3.2");
/**
* ECDSA with SHA-384 ecdsa-with-SHA384 ::= {iso(1) member-body(2) us(840)
* ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2 (3) 3}
*/
public final static ASN1ObjectIdentifier SHA384withECDSA = new ASN1ObjectIdentifier(
"1.2.840.10045.4.3.3");
// Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE is used for calling opmode
public final static int ENCRYPT_MODE = Cipher.ENCRYPT_MODE;
public final static int DECRYPT_MODE = Cipher.DECRYPT_MODE;
/**
* Method AES128CBC.
*
* @param plaintext
* byte[]
* @param key
* byte[]
* @param opmode
* int
* @return byte[]
*/
public static byte[] AES128CBC(byte[] plaintext, byte[] key, int opmode) {
byte[] ciphertext = null;
try {
IvParameterSpec iv = new IvParameterSpec(
DataUtil.stringToByteArray("00000000000000000000000000000000"));
SecretKeySpec ks = new SecretKeySpec(key, "AES");
Cipher c = Cipher.getInstance("AES/CBC/NoPadding");
c.init(opmode, ks, iv);
ciphertext = c.doFinal(plaintext);
} catch (Throwable e) {
e.printStackTrace();
}
return ciphertext;
}
/**
* Method getSigningAlgorithm.
*
* @param sigalg
* ObjectIdentifier
* @return String
* @throws NoSuchAlgorithmException
*/
public static String getSigningAlgorithm(ASN1ObjectIdentifier sigalg)
throws NoSuchAlgorithmException {
if (sigalg.equals(SHA1withRSA)) {
return "SHA1withRSA";
} else if (sigalg.equals(SHA256withRSA)) {
return "SHA256withRSA";
} else if (sigalg.equals(SHA256withECDSA)) {
return "SHA256withECDSA";
} else if (sigalg.equals(SHA384withECDSA)) {
return "SHA384withECDSA";
} else {
throw new NoSuchAlgorithmException(
"Algorithm not included in NIST 800-78");
}
}
/**
*
* @param digest
* ObjectIdentifier
* @param encryption
* ObjectIdentifier
* @return String <digest>with<encryption>
*/
public static String getSigningAlgorithm(ASN1ObjectIdentifier digest,
ASN1ObjectIdentifier encryption) {
StringBuffer sb = new StringBuffer();
if (digest.equals(SHA1)) {
sb.append("SHA1");
} else if (digest.equals(SHA256)) {
sb.append("SHA256");
} else if (digest.equals(SHA384)) {
sb.append("SHA384");
}
sb.append("with");
if (encryption.equals(RSA)
|| encryption.equals(SHA1withRSA)
|| encryption.equals(SHA256withRSA)) {
sb.append("RSA");
} else if (encryption.equals(SHA256withRSAPSS)) {
sb.append("RSAandMGF1");
} else if (encryption.equals(ECDSA)
|| encryption.equals(SHA256withECDSA)
|| encryption.equals(SHA384withECDSA)) {
sb.append("ECDSA");
}
if (debug) {
System.out.println("Constructed SigAlg Name: " + sb.toString());
}
return sb.toString();
}
/**
* Method SCP0105SSKey.
*
* @param CARD_CNG
* byte[]
* @param HOST_CNG
* byte[]
* @param key
* byte[]
* @return byte[]
*/
public static byte[] SCP0105SSKey(byte[] CARD_CNG, byte[] HOST_CNG,
byte[] key) {
byte[] HAC = { CARD_CNG[4], CARD_CNG[5], CARD_CNG[6], CARD_CNG[7],
HOST_CNG[0], HOST_CNG[1], HOST_CNG[2], HOST_CNG[3],
CARD_CNG[0], CARD_CNG[1], CARD_CNG[2], CARD_CNG[3],
HOST_CNG[4], HOST_CNG[5], HOST_CNG[6], HOST_CNG[7] };
if (debug) {
System.out.println("Sess Diver Data: "
+ DataUtil.byteArrayToString(HAC));
}
return TDES128ECB(HAC, key, ENCRYPT_MODE);
}
/**
* Method SCP03SSKey.
*
* @param CARD_CNG
* byte[]
* @param HOST_CNG
* byte[]
* @param key
* byte[]
* @param keytype
* int
* @return byte[]
*/
public static byte[] SCP03SSKey(byte[] CARD_CNG, byte[] HOST_CNG,
byte[] key, int keytype) {
int SCP03_S_ENC = 0;
int SCP03_S_MAC = 1;
byte[] S_ENC_CONSTANT = DataUtil
.stringToByteArray("01820000000000000000000000000000");
byte[] S_MAC_CONSTANT = DataUtil
.stringToByteArray("01010000000000000000000000000000");
byte[] SKEY = null;
byte[] HAC = { CARD_CNG[0], CARD_CNG[1], CARD_CNG[2], CARD_CNG[3],
CARD_CNG[4], CARD_CNG[5], CARD_CNG[6], CARD_CNG[7],
HOST_CNG[0], HOST_CNG[1], HOST_CNG[2], HOST_CNG[3],
HOST_CNG[4], HOST_CNG[5], HOST_CNG[6], HOST_CNG[7] };
SKEY = AES128CBC(HAC, key, ENCRYPT_MODE);
if (keytype == SCP03_S_ENC) {
SKEY = DataUtil.XOR(SKEY, S_ENC_CONSTANT);
} else if (keytype == SCP03_S_MAC) {
SKEY = DataUtil.XOR(SKEY, S_MAC_CONSTANT);
} else {
// bad key type, throw exception
}
SKEY = AES128CBC(SKEY, key, ENCRYPT_MODE);
return SKEY;
}
/**
* Method TDES128ECB.
*
* @param plaintext
* byte[]
* @param key
* byte[]
* @param opmode
* int
* @return byte[]
*/
public static byte[] TDES128ECB(byte[] plaintext, byte[] key, int opmode) {
byte[] ciphertext = null;
try {
// 2KTDEA key is encountered, expand to 192 bit
if (key.length == 16) {
if (debug) {
System.out.println("Expanding from 128 to 192");
}
byte[] ekey = new byte[24];
System.arraycopy(key, 0, ekey, 0, key.length);
System.arraycopy(key, 0, ekey, key.length, key.length / 2);
}
ciphertext = TDESECB(plaintext, key, opmode);
} catch (Throwable e) {
e.printStackTrace();
}
return ciphertext;
}
/**
* Method TDES198ECB.
*
* @param plaintext
* byte[]
* @param key
* byte[]
* @param opmode
* int
* @return byte[]
*/
public static byte[] TDES198ECB(byte[] plaintext, SecretKey key, int opmode) {
byte[] ciphertext = null;
try {
Cipher c = Cipher.getInstance("DESede/ECB/NoPadding");
c.init(opmode, key);
ciphertext = c.doFinal(plaintext);
} catch (Throwable e) {
e.printStackTrace();
}
return ciphertext;
}
/**
* Method TDESCBC.
*
* @param plaintext
* byte[]
* @param key
* byte[]
* @param iv
* byte[]
* @param opmode
* int
* @return byte[]
*/
public static byte[] TDESCBC(byte[] plaintext, byte[] key, byte[] iv,
int opmode) {
byte[] ciphertext = null;
try {
// 2KTDEA key is encountered, expand to 192 bit
byte[] _key;
if (key.length == 16) {
if (debug) {
System.out.println("Expanding from 128 to 192");
}
byte[] ekey = new byte[24];
System.arraycopy(key, 0, ekey, 0, key.length);
System.arraycopy(key, 0, ekey, key.length, key.length / 2);
_key = ekey;
} else {
_key = key;
}
if (debug) {
System.out.println("Key: " + DataUtil.byteArrayToString(_key));
}
IvParameterSpec ivv = new IvParameterSpec(iv);
DESedeKeySpec ks = new DESedeKeySpec(_key);
SecretKeyFactory kf = SecretKeyFactory.getInstance("DESede");
SecretKey sk = kf.generateSecret(ks);
Cipher c = Cipher.getInstance("DESede/CBC/NoPadding");
c.init(opmode, sk, ivv);
ciphertext = c.doFinal(plaintext);
} catch (Throwable e) {
e.printStackTrace();
}
return ciphertext;
}
/**
* Method TDESECB.
*
* @param plaintext
* byte[]
* @param key
* byte[]
* @param opmode
* int
* @return byte[]
*/
public static byte[] TDESECB(byte[] plaintext, byte[] key, int opmode) {
byte[] ciphertext = null;
try {
// 2KTDEA key is encountered, expand to 192 bit
byte[] _key;
if (key.length == 16) {
if (debug) {
System.out.println("Expanding from 128 to 192");
}
byte[] ekey = new byte[24];
System.arraycopy(key, 0, ekey, 0, key.length);
System.arraycopy(key, 0, ekey, key.length, key.length / 2);
_key = ekey;
} else {
_key = key;
}
if (debug) {
System.out.println("Key: " + DataUtil.byteArrayToString(_key));
}
DESedeKeySpec ks = new DESedeKeySpec(_key);
SecretKeyFactory kf = SecretKeyFactory.getInstance("DESede");
SecretKey sk = kf.generateSecret(ks);
Cipher c = Cipher.getInstance("DESede/ECB/NoPadding");
c.init(opmode, sk);
ciphertext = c.doFinal(plaintext);
} catch (Throwable e) {
e.printStackTrace();
}
return ciphertext;
}
}