CryptoTink.java

package sk.iway.iwcm.components.crypto;

import com.google.crypto.tink.*;
import com.google.crypto.tink.hybrid.HybridConfig;

import sk.iway.Password;
import sk.iway.iwcm.Constants;
import sk.iway.iwcm.Crypto;
import sk.iway.iwcm.CryptoKeys;
import sk.iway.iwcm.Logger;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.system.ConfDB;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Base64;

/**
 * CryptoTink je verzia sifrovania s vyuzitim kniznice google tink - https://github.com/google/tink
 * Pouziva sa hybridne sifrovanie pre podporu dlhych dat
 *
 *
 * Title        webjet8
 * Company      Interway a.s. (www.interway.sk)
 * Copyright    Interway a.s. (c) 2001-2019
 * @author      $Author: mhruby $
 * @version     $Revision: 1.0 $
 * created      11. 10. 2019 15:30
 * modified     11. 10. 2019 15:30
 */

public class CryptoTink implements Crypto {

    public static String ALG_KEY = "-tink-";

    private static CryptoTink INSTANCE = new CryptoTink();

    public static CryptoTink getInstance() {
        return INSTANCE;
    }

    private CryptoTink() {
        try {
            HybridConfig.register();
        } catch (Exception e) {
            sk.iway.iwcm.Logger.error(e);
        }
    }

    @Override
    public CryptoKeys generateNewPrivateAndPublicKey(String login) {
        KeysetHandle keysetHandle = this.generateNewPrivateAndPublicKey2();
        if (keysetHandle==null) return null;
        try {
            String privateKeyEncoded = this.getKeyBase64(keysetHandle);
            String publicKeyEncoded = this.getKeyBase64(keysetHandle.getPublicKeysetHandle()); // public key viem dostat z privatneho kluca
            return new CryptoKeys(null, publicKeyEncoded, privateKeyEncoded);
        } catch (GeneralSecurityException e) {
            sk.iway.iwcm.Logger.error(e);
        }
        return null;
    }

    @Override
    public String encrypt(String plainData, String publicKey) throws Exception {
        KeysetHandle publicKeyHandle = this.loadPublicKeyBase64(publicKey);
        if (publicKeyHandle == null) return "";
        HybridEncrypt hybridEncrypt = publicKeyHandle.getPrimitive(HybridEncrypt.class);
        byte[] encryptedData = hybridEncrypt.encrypt(plainData.getBytes(), this.getContextInfo().getBytes());

        return new String(Base64.getEncoder().encode(encryptedData));
    }

    @Override
    public String decrypt(String encryptedData, String privateKey) throws Exception {
        KeysetHandle privateKeyHandle = this.loadPrivateKeyBase64(privateKey);
        if (privateKeyHandle == null) return "";
        HybridDecrypt hybridDecrypt = privateKeyHandle.getPrimitive(HybridDecrypt.class);

        return new String(hybridDecrypt.decrypt(Base64.getDecoder().decode(encryptedData.getBytes()), this.getContextInfo().getBytes()));
    }

    @Override
    public String getAlgKey() {
        return ALG_KEY;
    }

    protected KeysetHandle generateNewPrivateAndPublicKey2() {
        try {
            KeysetHandle privateKey = KeysetHandle.generateNew(KeyTemplates.get("ECIES_P256_HKDF_HMAC_SHA256_AES128_CTR_HMAC_SHA256"));
            KeysetHandle publiceKey = privateKey.getPublicKeysetHandle();

            boolean showLog = Constants.getBoolean("cryptoToolsShowGeneratedKeysLog");
            if (showLog) {
                Logger.println(this.getClass(), "=== Generating keys ===");
                Logger.println(this.getClass(), "Generated Private key => " + this.getPrivateKeyBase64(privateKey));
                Logger.println(this.getClass(), "Generated Public key => " + this.getKeyBase64(publiceKey));
                Logger.println(this.getClass(), "===========================");
            }
            return privateKey;
        } catch (Exception e) {
            sk.iway.iwcm.Logger.error(e);
        }
        return null;
    }

    protected KeysetHandle loadPublicKeyBase64(String publicKey) {
        String string = new String(Base64.getDecoder().decode(publicKey));
        try {
            return loadKey(string);
        } catch (Exception e) {
            sk.iway.iwcm.Logger.error(e);
        }
        return null;
    }

    protected KeysetHandle loadPrivateKeyBase64(String privateKey) {
        String string = new String(Base64.getDecoder().decode(privateKey));
        try {
           return loadKey(string);
        } catch (Exception e) {
            sk.iway.iwcm.Logger.error(e);
        }
        return null;
    }

    private KeysetHandle loadKey(String key) throws GeneralSecurityException, IOException {
        return CleartextKeysetHandle.read(JsonKeysetReader.withString(key));
    }

    protected String getKeyBase64(KeysetHandle keysetHandle) {
        try {
            String serializedKeyset = TinkJsonProtoKeysetFormat.serializeKeyset(keysetHandle, InsecureSecretKeyAccess.get());
            return Base64.getEncoder().encodeToString(serializedKeyset.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            sk.iway.iwcm.Logger.error(e);
        }
        return null;
    }

    public String getPublicKeyFromPrivateKeyBase64(KeysetHandle keysetHandle) {
        try {
            return getKeyBase64(keysetHandle.getPublicKeysetHandle());
        } catch (Exception e) {
            sk.iway.iwcm.Logger.error(e);
        }
        return null;
    }

    protected String getPrivateKeyBase64(KeysetHandle keysetHandle) {
        return getKeyBase64(keysetHandle);
    }

    protected String getContextInfo() {
        String cryptoContextInfo = Constants.getString("cryptoContextInfo", "");
        if (Tools.isEmpty(cryptoContextInfo)) {
            cryptoContextInfo = Password.generateStringHash(16);
            Logger.println(this, "=== Generating cryptoContextInfo ===");
            ConfDB.setName("cryptoContextInfo", cryptoContextInfo);
        }
        return Constants.getString("cryptoContextInfo", "Tink");
    }
}