All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.fluxcapacitor.common.encryption.ChaCha20Poly1305Encryption Maven / Gradle / Ivy

/*
 * Copyright (c) Flux Capacitor IP B.V. or its affiliates. All Rights Reserved.
 *
 * 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.fluxcapacitor.common.encryption;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * Adopted from 
 *     https://github.com/java-crypto/cross_platform_crypto
 */
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class ChaCha20Poly1305Encryption implements Encryption {

    public static final String ALGORITHM = "ChaCha20";
    private static final SecureRandom secureRandom = new SecureRandom();
    private static final String TRANSFORMATION = "ChaCha20-Poly1305/None/NoPadding";
    private static final int NONCE_LENGTH = 12;
    private static final int KEY_SIZE = 256;
    private static final int TAG_LENGTH = 16;

    private final SecretKey encryptionKey;

    public ChaCha20Poly1305Encryption() {
        this(generateEncryptionKey());
    }

    public ChaCha20Poly1305Encryption(String encryptionKey) {
        this(new SecretKeySpec(decode(encryptionKey), ALGORITHM));
    }

    @Override
    @SneakyThrows
    public String encrypt(String value) {
        IvParameterSpec ivParameterSpec = new IvParameterSpec(nextNonce());
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, ivParameterSpec);
        byte[] ciphertextWithTag = cipher.doFinal(value.getBytes(StandardCharsets.UTF_8));
        byte[] ciphertext = new byte[(ciphertextWithTag.length - TAG_LENGTH)];
        byte[] tag = new byte[TAG_LENGTH];
        System.arraycopy(ciphertextWithTag, 0, ciphertext, 0, (ciphertextWithTag.length - TAG_LENGTH));
        System.arraycopy(ciphertextWithTag, (ciphertextWithTag.length - TAG_LENGTH), tag, 0, TAG_LENGTH);
        return encode(ivParameterSpec.getIV()) + ":" + encode(ciphertext) + ":" + encode(tag);
    }

    @Override
    @SneakyThrows
    public String decrypt(String value) {
        String[] parts = value.split(":", 0);
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new IvParameterSpec(decode(parts[0])));
        return new String(cipher.doFinal(concatenate(decode(parts[1]), decode(parts[2]))));
    }

    @Override
    public String getAlgorithm() {
        return ALGORITHM;
    }

    @Override
    public String getEncryptionKey() {
        return encode(encryptionKey.getEncoded());
    }

    @Override
    public boolean isEncrypted(String value) {
        throw new UnsupportedOperationException();
    }

    @SneakyThrows
    private static SecretKey generateEncryptionKey() {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
        keyGenerator.init(KEY_SIZE);
        return keyGenerator.generateKey();
    }

    private static byte[] nextNonce() {
        byte[] newNonce = new byte[NONCE_LENGTH];
        secureRandom.nextBytes(newNonce);
        return newNonce;
    }

    private static String encode(byte[] input) {
        return Base64.getEncoder().encodeToString(input);
    }

    private static byte[] decode(String input) {
        return Base64.getDecoder().decode(input);
    }

    private static byte[] concatenate(byte[] a, byte[] b) {
        return ByteBuffer.allocate(a.length + b.length).put(a).put(b).array();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy