RSA加密在ECB模式下的应用

3

我在我的代码中遇到了问题。

我试图使用ECB模式加密文件(即对文本块进行加密,而不是链接到下一个块)。

它有时可以完美地工作,但有时候就不行了。问题出现在它加密128字节数据并写入129字节时。它会在第一次这样做时完美地工作,但然后解密会少一个字节,会破坏一切。我之所以知道这个原因是因为当它出错时,可以看到密码(在线88的字节数组)的长度为129而不是128,然后将其写入文件。

以下是我所说的例子: 加密: testtesttesttesttesttesttest testtesttesttesttesttesttest testtesttesttesttesttesttest testtesttesttesttesttesttest testtesttesttesttesttesttest

输出: testtesttesttesttesttesttest testtesttesttesttesttesttest testtesttesttesttesttesttest testtesttesttesttesttesttest testtest(500个杂乱无序的数据)

我附上了整个代码;为了快速运行,请打开解密以在加密后直接输出到stdout。

所以只需要运行: -k key (将生成key.public和key.private) -e key.public -i input -o output (将加密一些文件input并将其存储在output中,读取该文件,解密它,发送到stdout)。

任何帮助都将不胜感激!

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Scanner;
import java.io.*;
import java.util.List;
import java.util.Random;

public class RSA {


    public static void main(String[] args) throws UnsupportedEncodingException {
        List<String> list = Arrays.asList(args);
        if(list.contains("-h")) {
            System.out.println("Usage:");
            System.out.println("RSA -h - View command-line arguments.");
            System.out.println("RSA -k <key file> -b <bit size> - Generate public/private keyfiles of size <bit size>.");
            System.out.println("RSA -e <key file>.public -i <input file> -o <output file> - Encrypt <input file> with key <key file>, store in <output file>.");
            System.out.println("RSA -d <key file>.private -i <input file> -o <output file> - Decrypt <input file> with key <key file>, store in <output file>.");
        } else if (list.contains("-k")) {
            String key_file = "";
            try {
                key_file = list.get(list.indexOf("-k") + 1);
                if(key_file.equals("-b")) {
                    System.out.println("Usage:");
                    System.out.println("RSA -k <key file> -b <bit size> - Generate public/private keyfiles of size <bit size>.");
                    System.exit(1);
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                System.out.println("Usage:");
                System.out.println("RSA -k <key file> -b <bit size> - Generate public/private keyfiles of size <bit size>.");
                System.exit(1);
            }
            int bit_size = 0;
            if(!list.contains("-b")) {
                bit_size = 1024;
            } else {
                try {
                    bit_size = Integer.parseInt(list.get(list.indexOf("-b") + 1));
                } catch(ArrayIndexOutOfBoundsException e) {
                    System.out.println("Usage:");
                    System.out.println("RSA -k <key file> -b <bit size> - Generate public/private keyfiles of size <bit size>.");
                    System.exit(1);
                }
            }

            generate_key(bit_size, key_file);

        } else if (list.contains("-e")) {
            //get input file and output file
            String input_file = "";
            String key_file = "";
            String output_file = "";
            try {
                input_file = list.get(list.indexOf("-i") + 1);
                output_file = list.get(list.indexOf("-o") + 1);
                key_file = list.get(list.indexOf("-e") + 1);
            } catch (ArrayIndexOutOfBoundsException e) {
                System.out.println("Usage:");
                System.out.println("RSA -e <key file>.public -i <input file> -o <output file> - Encrypt <input file> with key <key file>, store in <output file>.");
                System.exit(1);
            }

            String public_key = read_file(key_file);
            String private_key = read_file("key.private");

            BigInteger public_modulus = new BigInteger(public_key.substring(1, public_key.indexOf(',')));
            BigInteger public_exponent = new BigInteger(public_key.substring(public_key.indexOf(',') + 1, public_key.length() - 1));

            byte[] file = read_bytes(input_file);
            byte[] cipher = new byte[128];
            byte[] decrypted = new byte[128];

            BigInteger d = new BigInteger(private_key.substring(1, private_key.indexOf(',')));
            BigInteger modulus = new BigInteger(private_key.substring(private_key.indexOf(',') + 1, private_key.length() - 1));

            write_file(output_file, "", false);
            int index = 0;
            while (index<file.length) {
                byte[] block = Arrays.copyOfRange(file, index, index+128);
                cipher = new BigInteger(block).modPow(public_exponent, public_modulus).toByteArray();
                append_bytes(output_file, cipher);
                index+=128;
            }

            byte[] encrypted = read_bytes(output_file);


            index = 0;
            while(index < encrypted.length) {
                byte[] block = Arrays.copyOfRange(encrypted, index, index+256);
                decrypted =  new BigInteger(block).modPow(d, modulus).toByteArray();
                System.out.println(new String(decrypted));
                index+= 256;
            }


        } else if (list.contains("-d")) {
            /*String input_file, output_file, key_file;
            input_file = output_file = key_file = "";

            try {
                input_file = list.get(list.indexOf("-i") + 1);
                output_file = list.get(list.indexOf("-o") + 1);
                key_file = list.get(list.indexOf("-d") + 1);
            } catch (ArrayIndexOutOfBoundsException e) {
                System.out.println("Usage:");
                System.out.println("RSA -d <key file>.private -i <input file> -o <output file> - Decrypt <input file> with key <key file>, store in <output file>.");
                System.exit(1);
            }

            String private_key = read_file(key_file).toString();
            BigInteger d = new BigInteger(private_key.substring(1, private_key.indexOf(',')));
            BigInteger modulus = new BigInteger(private_key.substring(private_key.indexOf(',') + 1, private_key.length() - 1));

            byte[] encrypted = null;
            /* todo */

        } else {
            System.out.println("Usage:");
            System.out.println("RSA -h - View command-line arguments.");
            System.out.println("RSA -k <key file> -b <bit size> - Generate public/private keyfiles of size <bit size>.");
            System.out.println("RSA -e <key file>.public -i <input file> -o <output file> - Encrypt <input file> with key <key file>, store in <output file>.");
            System.out.println("RSA -d <key file>.private -i <input file> -o <output file> - Decrypt <input file> with key <key file>, store in <output file>.");
        }
    }

    private static void generate_key(int bit_size, String key_file) {
        BigInteger p = BigInteger.probablePrime(bit_size, new Random());
        BigInteger q = BigInteger.probablePrime(bit_size, new Random());

        BigInteger one = new BigInteger("1");
        BigInteger phi = new BigInteger("1");
        BigInteger e = new BigInteger("65537");

        boolean done = false;

        while(!done) {
            BigInteger temp = p.subtract(one);
            BigInteger temp2 = q.subtract(one);
            phi = (temp.multiply(temp2));
            if(phi.gcd(e).equals(one)) {
                done = true;
            } else {
                e = BigInteger.probablePrime(bit_size, new Random());
            }
        }

        BigInteger public_modulus = p.multiply(q);
        BigInteger public_exponent = e;
        BigInteger private_key = public_exponent.modInverse(phi); //d

        try {
            write_file(key_file + ".public", "(" + public_modulus + "," + e + ")", false);
            write_file(key_file + ".private", "(" + private_key.toString() + "," + public_modulus + ")", false);
        } catch (Exception ex) {
            System.out.println("Error creating key files.");
            System.exit(1);
        }

    }


    public static void write_bytes(String file_name, byte[] bytes) {
        try {
            FileOutputStream fos = new FileOutputStream(new File(file_name));
            fos.write(bytes);
            fos.close();
        } catch (IOException e) {
            System.out.println("Error writing bytes to file.");
            System.exit(1);
        }
    }

    public static String read_file(String file_name) {
        boolean is_key = false;
        if(file_name.contains(".public") || file_name.contains(".private"))
            is_key = true;

        Scanner sc = null;
        try {
            sc = new Scanner (new File(file_name));
        } catch (FileNotFoundException e) {
            System.out.println("Input file does not exist.");
            System.exit(1);
        }

        StringBuilder buf = new StringBuilder("");
        while (sc.hasNext ()) {
           buf.append (sc.nextLine());
           if(!is_key)
               buf.append("\n");
        }
        sc.close();
        return buf.toString();
    }

    public static byte[] read_bytes(String file_name) {
        Path path = Paths.get(file_name);
        byte[] encrypted = null;
        try {
            encrypted = Files.readAllBytes(path);
        } catch (IOException e) {
            System.out.println("Error reading bytes from " + file_name);
            System.exit(1);
        }
        return encrypted;
    }
    public static void append_bytes(String file_name, byte[] bytes) {
        try {
            OutputStream fos = new FileOutputStream(file_name, true);
            fos.write(bytes);
            fos.flush();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Error appending bytes to file.");
            System.exit(1);
        }
    }

    public static void write_file(String file_name, String message, boolean append) {
        try {
            FileWriter fstream = new FileWriter(file_name, append);
            BufferedWriter out = new BufferedWriter(fstream);
            out.write(message);
            out.close();
        } catch (Exception e){
            System.out.println("Error writing to file.");
            System.exit(1);
        }
    }
}
2个回答

5

ECB是分组密码运算模式RSA是一种公钥加密方案,而不是分组密码

通常,使用RSA直接加密长消息是没有意义的(无法处理)。相反,您可以使用混合加密:为对称密码(如AES)选择一个随机密钥,使用对称密码加密消息,然后使用RSA加密对称密钥。

此外,在使用RSA加密密钥时,请记得使用适当的填充方案,例如OAEP;不要使用未填充的“教科书式RSA”,这是不安全的。(或者使用像RSA-KEM这样不需要填充的方案。)同样,不要使用ECB模式进行AES加密;应该使用语义上安全的模式,如CBC或CTR,或者更好的是authenticated encryption模式。

这是为了一个作业而做的,不是我计划使用的东西。谢谢! - Bobby Brown
@Bobby:在这种情况下,我会说这要么是一个愚蠢的任务,要么是你误解了它所要求的内容。虽然从技术上讲,你所要求的是可能的,但这是一件非常愚蠢的事情。 - Ilmari Karonen
有关KEM的有趣链接,但我认为将实现留给任何人有点棘手。特别是生成0到模数N之间的随机M这一行似乎很棘手,而且我不确定创建一个比一个位小的随机数是否会引入不安全因素(维基页面没有说明)。 - Maarten Bodewes
1
@Bobby:为了允许任何n位块作为输入,你的模数必须至少为2^n。但由于它不能完全等于2^n,因此某些输出将至少为n+1位长。你需要将所有输出填充到至少与模数的位数相同,或者以某种方式指示每个输出块的结束和下一个块的开始(例如通过在每个块前面添加一个单字节来给出其长度)。 - Ilmari Karonen
生成一个在0和N-1之间的均匀随机数并不像正确实现OAEP这样难。总之,我看不出将范围缩小几位能给攻击者带来超过几位优势;毕竟,即使是正确选择的均匀随机数也有相对较高的可能性很小。(而且,由于这是一个公钥方案,如果知道加密了几个这样的小数字会提供任何额外的优势,攻击者可以自己生成一些。) - Ilmari Karonen
显示剩余2条评论

0
问题在于"BigInteger.toByteArray()"。这个字节数组需要进行后处理。 数组的第一个元素携带符号位,其他字节被视为无符号。如果正数的最高有效位在位7,则该方法添加一个前导0表示正号。如果您删除此0字节,则输出具有恒定的块大小。反之,在从数组构造正BigInteger时,它需要一个前导零位7。
关于密钥生成:q、p的位数通常是一半,"n = p*q"见Wikipedia RSA Key Generation 关于安全性:如上所述,“教科书RSA”对于流式传输不安全。最小要求的修改是在每个块中添加一些随机字节“盐”。常见的RSA实现为此保留约25%的块大小。另外更好的方法是切换到操作模式CBC
关于接受:几乎没有可供所有Java运行时提供程序和版本使用的便携式加密算法,每个变种都在持续开发并具有有限的生命周期。此外,实现被隐藏并可能存在后门。自己编写是可移植的,可追踪的,并且因为分解问题而安全,攻击者不知道个别修改。最后,性能的缺失是一个安全因素,例如当您引入密码相关哈希时,暴力密码搜索将不会在时间内成功。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接