de.tsenger.animamea.crypto.KeyDerivationFunction.java Source code

Java tutorial

Introduction

Here is the source code for de.tsenger.animamea.crypto.KeyDerivationFunction.java

Source

/**
 *  Copyright 2011, Tobias Senger
 *  
 *  This file is part of animamea.
 *
 *  Animamea is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Animamea 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 for more details.
 *
 *  You should have received a copy of the GNU General Public License   
 *  along with animamea.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.tsenger.animamea.crypto;

import org.apache.log4j.Logger;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.digests.SHA256Digest;

/**
 * @author Tobias Senger (tobias@t-senger.de)
 * 
 */
public class KeyDerivationFunction {

    static Logger logger = Logger.getLogger(KeyDerivationFunction.class);

    private byte[] mergedData = null;

    /**
     * Constants that help in determining whether or not a byte array is parity
     * adjusted.
     */
    private static final byte[] PARITY = { 8, 1, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 2, 8, 0, 8, 8, 0, 8, 0, 0, 8,
            8, 0, 0, 8, 0, 8, 8, 3, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8,
            0, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0,
            0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 0,
            8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8,
            0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8,
            0, 0, 8, 8, 0, 8, 0, 0, 8, 0, 8, 8, 0, 8, 0, 0, 8, 8, 0, 0, 8, 0, 8, 8, 0, 4, 8, 8, 0, 8, 0, 0, 8, 8, 0,
            0, 8, 0, 8, 8, 0, 8, 5, 0, 8, 0, 8, 8, 0, 0, 8, 8, 0, 8, 0, 6, 8 };

    /**
     * 
     * Das MRZ-Passwort besteht aus dem SHA1-Wert der Dokumentennummer +
     * Geburtsdatum + Gltigkeitsdatum (jeweils mit Prfziffer)
     * 
     * @param documentNr
     *            Dokumentennummer plus Prfziffer
     * @param dateOfBirth
     *            Geburtsdatum aus der MRZ plus Prfziffer
     * @param dateOfExpiry
     *            Gltigkeitsdatum aus der MRZ plus Prfziffer
     * @return K = SHA-1(Serial Number||Date of Birth||Date of Expiry)
     */
    public static byte[] getMRZBytes(String documentNr, String dateOfBirth, String dateOfExpiry) {
        String mrzInfo = documentNr + dateOfBirth + dateOfExpiry;
        byte[] passwordBytes = mrzInfo.getBytes();

        byte[] K = new byte[20];

        SHA1Digest sha1 = new SHA1Digest();
        sha1.update(passwordBytes, 0, passwordBytes.length);
        sha1.doFinal(K, 0);

        return K;
    }

    /**
     * Constructor for Key Derivation Function (KDF) siehe BSI TR-03110 Kapitel
     * A.2.3
     * 
     * @param K
     *            The shared secret Value (z.B. PIN, CAN, PUK als Byte Array
     *            oder abgeleitete MRZ siehe BSI TR-03110 Tabelle A.4)
     * @param c
     *            A 32-bit, big-endian integer counter. 1 for en-/decoding, 2
     *            for MAC (checksum), 3 for deriving encryption keys from a
     *            password
     * @throws IllegalArgumentException
     *             c must be 1, 2 or 3
     */
    public KeyDerivationFunction(byte[] K, int c) {

        if (c <= 0 || c > 3)
            throw new IllegalArgumentException("c must be 1, 2 or 3!");

        byte[] cBytes = intToByteArray(c);
        //logger.info("K: " + HexString.bufferToHex(K));

        mergedData = new byte[K.length + cBytes.length];
        System.arraycopy(K, 0, mergedData, 0, K.length);
        System.arraycopy(cBytes, 0, mergedData, K.length, cBytes.length);
        //logger.info("MERGE DATA: " + HexString.bufferToHex(mergedData));
    }

    /**
     * Constructor for Key Derivation Function (KDF) siehe BSI TR-03110 Kapitel
     * A.2.3
     * 
     * @param K
     *            The shared secret Value (z.B. PIN, CAN, PUK oder abgeleitete
     *            MRZ siehe BSI TR-03110 Tabelle A.4)
     * @param r
     *            a nonce r
     * @param c
     *            A 32-bit, big-endian integer counter. 1 for en-/decoding, 2
     *            for MAC (checksum), 3 for deriving encryption keys from a
     *            password
     * @throws IllegalArgumentException
     *             c must be 1, 2 or 3
     */
    public KeyDerivationFunction(byte[] K, byte[] r, int c) throws IllegalArgumentException {

        if (c <= 0 || c > 3)
            throw new IllegalArgumentException("c must be 1, 2 or 3!");

        byte[] cBytes = intToByteArray(c);

        mergedData = new byte[K.length + +r.length + cBytes.length];
        System.arraycopy(K, 0, mergedData, 0, K.length);
        System.arraycopy(r, 0, mergedData, K.length, r.length);
        System.arraycopy(cBytes, 0, mergedData, K.length + r.length, cBytes.length);
    }

    /**
     * Erzeugt 3DES Schlssel
     * 
     * @return 112bit-3DES-Schlssel in 24 Bytes mit korrekten Parity-Bits
     */
    public byte[] getDESedeKey() {

        byte[] checksum = new byte[20];

        SHA1Digest sha1 = new SHA1Digest();
        sha1.update(mergedData, 0, mergedData.length);
        sha1.doFinal(checksum, 0);

        byte[] ka = new byte[8];
        byte[] kb = new byte[8];

        System.arraycopy(checksum, 0, ka, 0, ka.length);
        System.arraycopy(checksum, 8, kb, 0, kb.length);

        // Adjust Parity-Bits
        adjustParity(ka, 0);
        adjustParity(kb, 0);

        byte[] key = new byte[24];
        System.arraycopy(ka, 0, key, 0, 8);
        System.arraycopy(kb, 0, key, 8, 8);
        System.arraycopy(ka, 0, key, 16, 8);

        return key;

    }

    /**
     * Erzeugt AES-128 Schlssel
     * 
     * @return Schlssel als Byte-Array
     */
    public byte[] getAES128Key() {

        byte[] checksum = new byte[20];

        SHA1Digest sha1 = new SHA1Digest();
        sha1.update(mergedData, 0, mergedData.length);
        sha1.doFinal(checksum, 0);

        // keydata = H(K||r||c)
        // keydata sind die ersten 16 Byte der Hashfunktion ber "mergedData"
        byte[] keydata = new byte[16];
        System.arraycopy(checksum, 0, keydata, 0, 16);
        return keydata;
    }

    /**
     * Erzeugt AES-192 Schlssel
     * 
     * @return Schlssel als Byte-Array
     */
    public byte[] getAES192Key() {

        byte[] checksum = getAES256Key();
        byte[] keydata = new byte[24];
        System.arraycopy(checksum, 0, keydata, 0, 24);
        return keydata;
    }

    /**
     * Erzeugt AES-256 Schlssel
     * 
     * @return Schlssel als Byte-Array
     */
    public byte[] getAES256Key() {

        byte[] checksum = new byte[32];

        SHA256Digest sha256 = new SHA256Digest();
        sha256.update(mergedData, 0, mergedData.length);
        sha256.doFinal(checksum, 0);

        return checksum;
    }

    /**
     * Adjust the parity for a raw key array. This essentially means that each
     * byte in the array will have an odd number of '1' bits (the last bit in
     * each byte is unused.
     * 
     * @param kb
     *            The key array, to be parity-adjusted.
     * @param offset
     *            The starting index into the key bytes.
     */
    private void adjustParity(byte[] key, int offset) {
        for (int i = offset; i < 8; i++) {
            key[i] ^= (PARITY[key[i] & 0xff] == 8) ? 1 : 0;
        }
    }

    /**
     * @param c
     * @return
     */
    private byte[] intToByteArray(int c) {
        // int -> byte[]
        byte[] intBytes = new byte[4];
        for (int i = 0; i < 4; ++i) {
            int shift = i << 3; // i * 8
            intBytes[3 - i] = (byte) ((c & (0xff << shift)) >>> shift);
        }
        return intBytes;
    }

}