io.warp10.crypto.CryptoUtils.java Source code

Java tutorial

Introduction

Here is the source code for io.warp10.crypto.CryptoUtils.java

Source

//
//   Copyright 2016  Cityzen Data
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

package io.warp10.crypto;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.AESWrapEngine;
import org.bouncycastle.crypto.paddings.PKCS7Padding;
import org.bouncycastle.crypto.params.KeyParameter;

import com.google.common.primitives.Longs;

import java.util.Arrays;

public class CryptoUtils {

    public static byte[] wrap(byte[] key, byte[] data) {
        AESWrapEngine engine = new AESWrapEngine();
        KeyParameter params = new KeyParameter(key);
        engine.init(true, params);
        PKCS7Padding padding = new PKCS7Padding();
        byte[] unpadded = data;

        //
        // Add padding
        //

        byte[] padded = new byte[unpadded.length + (8 - unpadded.length % 8)];
        System.arraycopy(unpadded, 0, padded, 0, unpadded.length);
        padding.addPadding(padded, unpadded.length);

        //
        // Wrap
        //

        byte[] encrypted = engine.wrap(padded, 0, padded.length);

        return encrypted;
    }

    public static byte[] unwrap(byte[] key, byte[] data) {
        //
        // Decrypt the encrypted data
        //

        AESWrapEngine engine = new AESWrapEngine();
        CipherParameters params = new KeyParameter(key);
        engine.init(false, params);

        try {
            byte[] decrypted = engine.unwrap(data, 0, data.length);
            //
            // Unpad the decrypted data
            //

            PKCS7Padding padding = new PKCS7Padding();
            int padcount = padding.padCount(decrypted);

            //
            // Remove padding
            //

            decrypted = Arrays.copyOfRange(decrypted, 0, decrypted.length - padcount);

            return decrypted;
        } catch (InvalidCipherTextException icte) {
            return null;
        }
    }

    public static byte[] addMAC(long[] key, byte[] data) {
        long hash = SipHashInline.hash24_palindromic(key[0], key[1], data);

        byte[] authenticated = Arrays.copyOf(data, data.length + 8);
        System.arraycopy(Longs.toByteArray(hash), 0, authenticated, data.length, 8);

        return authenticated;
    }

    public static byte[] addMAC(byte[] key, byte[] data) {
        long hash = SipHashInline.hash24_palindromic(key, data);

        byte[] authenticated = Arrays.copyOf(data, data.length + 8);
        System.arraycopy(Longs.toByteArray(hash), 0, authenticated, data.length, 8);

        return authenticated;
    }

    /**
     * Check the MAC and return the verified content or null if the verification failed
     * 
     * @param key
     * @param data
     * @return
     */
    public static byte[] removeMAC(byte[] key, byte[] data) {
        long hash = SipHashInline.hash24_palindromic(key, data, 0, data.length - 8);
        long mac = Longs.fromByteArray(Arrays.copyOfRange(data, data.length - 8, data.length));

        if (mac == hash) {
            return Arrays.copyOf(data, data.length - 8);
        } else {
            return null;
        }
    }

    public static byte[] removeMAC(long[] key, byte[] data) {
        long hash = SipHashInline.hash24_palindromic(key[0], key[1], data, 0, data.length - 8);
        long mac = Longs.fromByteArray(Arrays.copyOfRange(data, data.length - 8, data.length));

        if (mac == hash) {
            return Arrays.copyOf(data, data.length - 8);
        } else {
            return null;
        }
    }

    /**
     * Compute a safe SipHash of a given input by computing the
     * forward hash, the hash of the reversed input and finally the
     * hash of the two concatenated hashes.
     * 
     * This should prevent having meaningful collisions. If two contents have colliding hashes,
     * this means that the concatenation of their forward and reverse hashes are collisions for SipHah, quite unlikely.
     * 
     * @param k0
     * @param k1
     * @param msg
     * @return
     */
    public static long safeSipHash(long k0, long k1, byte[] msg) {
        byte[] a = new byte[16];
        ByteBuffer bb = ByteBuffer.wrap(a).order(ByteOrder.BIG_ENDIAN);

        bb.putLong(SipHashInline.hash24(k0, k1, msg, 0, msg.length));
        bb.putLong(SipHashInline.hash24(k0, k1, msg, 0, msg.length, true));

        return SipHashInline.hash24(k0, k1, a, 0, a.length, false);
    }

    public static byte[] invert(byte[] key) {
        byte[] inverted = Arrays.copyOf(key, key.length);
        for (int i = 0; i < inverted.length; i++) {
            inverted[i] = (byte) (inverted[i] & 0xFF);
        }
        return inverted;
    }
}