package at.gv.egiz.smcc; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.KeyFactory; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.KeySpec; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.util.Arrays; import java.util.Random; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.ShortBufferException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.smartcardio.CardException; public class DNIeCryptoUtil { private final static byte[] IV = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; public static X509Certificate createCertificate(byte[] certData) throws CardException { try { InputStream inStream = new ByteArrayInputStream(certData); CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate certificate = (X509Certificate) cf .generateCertificate(inStream); inStream.close(); return certificate; } catch (Exception e) { throw new CardException("Unable to create certificate.", e); } } public static RSAPrivateKey createRSAPrivateKey(String mod, String privExponent) throws CardException { BigInteger modulus = new BigInteger(mod, 16); BigInteger privExp = new BigInteger(privExponent, 16); KeyFactory fac; RSAPrivateKey key; try { fac = KeyFactory.getInstance("RSA"); KeySpec spec = new RSAPrivateKeySpec(modulus, privExp); key = (RSAPrivateKey) fac.generatePrivate(spec); } catch (Exception e) { throw new CardException("Unable to create private key.", e); } return key; } public static RSAPublicKey createRSAPublicKey(String mod, String pubExponent) throws CardException { BigInteger modulus = new BigInteger(mod, 16); BigInteger pubExp = new BigInteger(pubExponent, 16); KeyFactory fac; RSAPublicKey key; try { fac = KeyFactory.getInstance("RSA"); KeySpec spec = new RSAPublicKeySpec(modulus, pubExp); key = (RSAPublicKey) fac.generatePublic(spec); } catch (Exception e) { throw new CardException("Unable to create public key.", e); } return key; } public static byte[] getRandomBytes(int length) { byte[] result = new byte[length]; for (int i = 0; i < length; i++) { Random rand = new Random(); byte current = (byte) rand.nextInt(255); result[i] = current; } return result; } public static byte[] computeSHA1Hash(byte[] data) throws CardException { try { MessageDigest sha = MessageDigest.getInstance("SHA"); sha.update(data); return sha.digest(); } catch (NoSuchAlgorithmException e) { throw new CardException("Error computing SHA1 hash.", e); } } public static byte[] rsaEncrypt(Key key, byte[] data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { Cipher rsa = Cipher.getInstance("RSA/ECB/NoPadding"); rsa.init(Cipher.ENCRYPT_MODE, key); byte[] encrypted = rsa.doFinal(data); return encrypted; } public static byte[] rsaDecrypt(Key key, byte[] cipher) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { Cipher rsa = Cipher.getInstance("RSA/ECB/NoPadding"); rsa.init(Cipher.DECRYPT_MODE, key); byte[] decrypted = rsa.doFinal(cipher); return decrypted; } public static byte[] applyPadding(int blockSize, byte[] data) { // add mandatory 0x80 byte[] extended = new byte[data.length + 1]; System.arraycopy(data, 0, extended, 0, data.length); extended[extended.length - 1] = (byte) 0x80; if (extended.length % blockSize == 0) { return extended; } int requiredBlocks = ((int) (extended.length / blockSize) + 1); byte[] result = new byte[requiredBlocks * blockSize]; Arrays.fill(result, (byte) 0x00); System.arraycopy(extended, 0, result, 0, extended.length); return result; } public static byte[] removePadding(byte[] paddedData) throws CardException { for (int i = paddedData.length - 1; i >= 0; i--) { byte current = paddedData[i]; if (current == (byte) 0x00) { continue; } if (current == (byte) 0x80) { // end of padding reached byte[] data = new byte[i]; System.arraycopy(paddedData, 0, data, 0, i); return data; } else { throw new CardException("Wrong padding."); } } throw new CardException( "Error removing padding from data. Unexpected data format."); } public static byte[] calculateAPDUMAC(byte[] data, byte[] key, byte[] ssc, int blockLength) throws CardException { SecretKeySpec desSingleKey = new SecretKeySpec(key, 0, blockLength, "DES"); Cipher singleDesCipher; try { singleDesCipher = Cipher.getInstance("DES/CBC/NoPadding"); } catch (Exception e) { throw new CardException("Error creating DES cipher instance.", e); } // Calculate the first n - 1 block. IvParameterSpec ivSpec; ivSpec = new IvParameterSpec(IV); int dataLen = data.length; try { singleDesCipher.init(Cipher.ENCRYPT_MODE, desSingleKey, ivSpec); } catch (Exception e) { throw new CardException("Error initializing DES cipher.", e); } byte[] result; try { result = singleDesCipher.doFinal(ssc); } catch (Exception e) { throw new CardException("Error applying DES cipher.", e); } byte[] dataBlock = new byte[blockLength]; for (int i = 0; i < dataLen - blockLength; i = i + blockLength) { System.arraycopy(data, i, dataBlock, 0, blockLength); byte[] input = xorByteArrays(result, dataBlock); try { result = singleDesCipher.doFinal(input); } catch (Exception e) { throw new CardException("Error applying DES cipher.", e); } } // calculate the last block with 3DES byte[] fullKey = new byte[24]; System.arraycopy(key, 0, fullKey, 0, 16); System.arraycopy(key, 0, fullKey, 16, 8); SecretKeySpec desKey = new SecretKeySpec(fullKey, "DESede"); Cipher cipher; try { cipher = Cipher.getInstance("DESede/CBC/NoPadding"); } catch (Exception e) { throw new CardException("Error getting 3DES cipher instance.", e); } ivSpec = new IvParameterSpec(IV); try { cipher.init(Cipher.ENCRYPT_MODE, desKey, ivSpec); } catch (Exception e) { throw new CardException("Error initializing 3DES cipher.", e); } System.arraycopy(data, data.length - blockLength, dataBlock, 0, blockLength); byte[] input = xorByteArrays(result, dataBlock); byte[] mac = new byte[4]; try { result = cipher.doFinal(input); } catch (Exception e) { throw new CardException("Error applying 3DES cipher.", e); } System.arraycopy(result, 0, mac, 0, 4); return mac; } public static byte[] xorByteArrays(byte[] array1, byte[] array2) throws CardException { if (array1 == null || array2 == null || array1.length != array2.length) { throw new CardException("Cannot xor byte arrays - invalid input."); } byte[] result = new byte[array1.length]; for (int i = 0; i < array1.length; i++) { result[i] = (byte) (array1[i] ^ array2[i]); } return result; } public static byte[] perform3DESCipherOperation(byte[] data, byte[] keyData, int mode) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, ShortBufferException, IllegalBlockSizeException, BadPaddingException { byte[] full3DESKey = new byte[24]; System.arraycopy(keyData, 0, full3DESKey, 0, 16); System.arraycopy(keyData, 0, full3DESKey, 16, 8); SecretKeySpec key = new SecretKeySpec(full3DESKey, "DESede"); IvParameterSpec ivSpec = new IvParameterSpec(IV); Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding"); cipher.init(mode, key, ivSpec); byte[] cipherText = new byte[cipher.getOutputSize(data.length)]; int ctLength = cipher.update(data, 0, data.length, cipherText, 0); ctLength += cipher.doFinal(cipherText, ctLength); return cipherText; } public static int getCutOffLength(byte[] data, int blockSize) throws CardException { int len = data.length % blockSize; // verify if (data[len - 1] == (byte) 0x01) { return len; } else { throw new CardException( "Unable to reconstruct encrypted datablock."); } } }