Encryption. GOST 28147-89. bouncycastle, java

You need to implement encryption according to GOST, I found an example and based on it I built the following -

public class gost28147_89 {
        final private byte[] key;

        public gost28147_89(String keyString) {
            byte[] key = new byte[0];
            try {
                key = keyString.getBytes("UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new ThrowFabric.ForbiddenException("Кодировка UTF-8 не поддерживается");
            }
            if(key.length == 32)
                this.key = key;
            else{
                this.key = null;
            }
        }

        public String encrypt(String data) {
            if(key.length != 32) return "";

            return encrypt(data, true);
        }

        public String encrypt(String data, Boolean isEncrypt) {
            if(key.length != 32) return "";

            String result;

            byte[] dataBytes;

            try {
                dataBytes = data.getBytes("UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new ThrowFabric.ForbiddenException("Кодировка UTF-8 не поддерживается");
            }

            BufferedBlockCipher cipher = new BufferedBlockCipher(new GOST28147Engine());

            cipher.init(isEncrypt, new KeyParameter(key));
            byte[] dataoOutBytes = new byte[cipher.getOutputSize(dataBytes.length)];
            int len = cipher.processBytes(dataBytes, 0, dataBytes.length, dataoOutBytes, 0);

            try {
                cipher.doFinal(dataoOutBytes, len);
            } catch (InvalidCipherTextException e) {
                throw new ThrowFabric.ForbiddenException("Некорректный текст шифра");
            }

            result = Arrays.toString(dataoOutBytes);

            return result;
        }

        public String decrypt(String data){
            if(key.length != 32) return "";

            return encrypt(data, false);
        }
    }

doFinal crashes with data not block size aligned\norg.bouncycastle.crypto.BufferedBlockCipher.doFinal(Unknown Source) Has anyone worked with this library? I can't find any description of how to work with it...

UPDATE

I did it according to your example + adjusted the blocks to the size of multiples of 8. Encryption/Decryption worked, but the output after encryption is always one block (8 bytes) larger than the input array.

byte[] dataBytes, tmpBytes;
        tmpBytes = data.getBytes();
        if(tmpBytes.length % 8 != 0){
            dataBytes = new byte[tmpBytes.length + (8 - tmpBytes.length % 8)];
            System.arraycopy(tmpBytes, 0, dataBytes, 0, tmpBytes.length);
        } else{
            dataBytes = tmpBytes;
        }
BlockCipher engine = new GOST28147Engine();
        BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(engine));

        cipher.init(isEncrypt, new KeyParameter(key1));
        byte[] cipherText = new byte[cipher.getOutputSize(dataBytes.length)];

        int outputLen = cipher.processBytes(dataBytes, 0, dataBytes.length, cipherText, 0);

        try {
            cipher.doFinal(cipherText, outputLen);
        } catch (InvalidCipherTextException e) {
            throw new ThrowFabric.BadRequestException(e.getMessage());
        }


        result = new String(cipherText);
        return result;
Author: Anton Charov, 2019-11-11

1 answers

Any block cipher (which also includes GOST 28147-89) can encrypt only 1 block. The block size depends on the cipher, for GOST 28147-89 it is 64 bits (8 bytes).

An encryption algorithm using a block cipher usually looks like this:

  1. We divide the source text into blocks (in this case, into blocks of 8 bytes each)
  2. If the source text is not a multiple of 8 bytes, then we perform the block alignment procedure (padding) - usually PKCS#5 or PKCS#7
  3. Next, the block chaining algorithm is selected - cipher mode - usually CBC
  4. Next, the actual encryption itself, separately concatenating each block with the previous selected method of concatenation (do not forget about the initialization vectors that randomize the encryption process)

To avoid all these difficulties, a simple initialization mechanism has been devised, such as:

Cipher cipher = Cipher.getInstance("[Algorithm]/CBC/PKCS5Padding", <CryptoProvider>);

Which reads like this: algorithm [Algorithm], mod coupling CBC, the alignment algorithm PKCS5, although for this the cryptoprovider must meet certain requirements. Apparently, you have a Bouncy Castle provider, so the initialization string Cipher should be something like this:

Cipher cipher=Cipher.getInstance("GOST28147/CBC/PKCS5Padding", "BC");

Read the Bouncy Castle documentation or Google it.

Update

Try this (analogous to GOST28147/CBC/PKCS5Padding):

BlockCipher engine = new GOST28147Engine();
BufferedBlockCipher cipher = new PaddedBlockCipher(new CBCCipher(engine));

byte[] key = keyString.getBytes();
byte[] input = inputString.getBytes();

cipher.init(true, new KeyParameter(key));

byte[] cipherText = new byte[cipher.getOutputSize(input.length)];

int outputLen = cipher.processBytes(input, 0, input.length, cipherText, 0);
try
{
    cipher.doFinal(cipherText, outputLen);
}
catch (CryptoException ce)
{
    System.err.println(ce);
    System.exit(1);
}
 2
Author: Barmaley, 2019-11-11 09:30:58