【问题标题】:Unit testing encryption and decryption in JavaJava中的单元测试加密和解密
【发布时间】:2017-01-14 03:58:54
【问题描述】:

免责声明,我是一名 iOS 开发人员,一直在研究 Android 上的加密。就目前而言,我已经设法在 Android 中实现了加密,但我问自己如何对数据的加密和解密进行单元测试?

现在想到的第一个想法是这样的:

String encryptedInputData = encryptedInputData("Hello");
String decryptedData = decryptData(encryptedInputData);
Assert.assertEquals(decryptedData,"Hello");

然而,该测试存在一个缺陷...如果 encryptedInputDatadecryptData 方法确实发生了某些变化,则该测试不会说明发生了什么变化以及它现在为什么会中断。所以我想写更细粒度的测试。因此,例如给出以下代码:

Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] data = cipher.doFinal(message); 

我想确保cipher 变量在ECB 模式下使用没有填充的RSA 算法。我想测试.doFinal(message) 中的message 是否遵循特定格式等。

现在我想我可以模拟Cipher 类,这里的问题是编写的加密和解密仅用作 Util 类并且能够对此进行单元测试,我会将模拟 Cipher 传递到代码中,鉴于这是一个 Util 类,它似乎会变得混乱,即我必须创建一个 init 方法 just 用于单元测试目的或创建 setter方法只是对这个进行单元测试。这将允许我对代码进行单元测试,但是 Util 类会变得笨拙,而我实际上不需要用于生产目的的代码。

有没有什么优雅的方法可以对这样的场景进行单元测试?即encryptedInputDatadecryptData 是公共方法,但这些方法使用各种私有方法,坦率地说需要进行单元测试,那么问题是如何?

【问题讨论】:

    标签: java android unit-testing encryption


    【解决方案1】:

    真正的答案是您不必这样做。您永远不应该实现自己的加密例程。您不仅很可能会出错,而且您需要做一些极其复杂的事情来确保由于实现问题而实际上它是不可破解的(例如,如果 if 语句的一个分支比另一个分支需要更长的时间来运行,你可以算出支票的价值是多少)。您应该始终使用开源且经过良好审查的库。

    由于您不是自己实现它,因此您不需要对它进行单元测试。图书馆作家应该是。如果您愿意,可以将他们的测试套件作为您自己的一部分运行,但我认为这是浪费时间 - 他们在发布之前就已经完成了,而您实际上最多只需要运行一次。

    【讨论】:

      【解决方案2】:

      Gabe 关于在重新发明轮子时真的保守的回答是正确的;特别是当那个轮子是关于密码学/安全性/......这样的事情时。机会是:你会弄错的。黑客喜欢尝试使用自己的“安全密码”的人。

      但要回答您实际的问题:尝试使用TDD(测试驱动开发)。你看,你的问题是你创建了一些难以测试的 API。

      您是对的,您想要“端到端”之类的测试,以确保解密(加密(“某事”))带有“某事”。现在您的问题是:如果您只考虑这两种方法编写代码;那么单元测试很难。

      因此:从一开始,当您考虑“我需要哪些课程”时;以及“哪些方法进入哪个类”,您必须关注“以及如何测试它”。

      换句话说:这是 TDD 非常重要的场合之一 - 因为您真的想要设计可以测试的单元。实现这一目标的最佳实践是:首先编写测试。如果你设计你的单元以便它们可以被测试(因为你首先编写了测试);惊喜 - 他们可以被测试。事后让它们“可测试”总是很麻烦的;最常见的是:几乎不可能。

      【讨论】:

        【解决方案3】:

        据我所知,Cipherjavax.crypto class 中。所以你不需要嘲笑它。

        您可以编写单元测试来验证: 1. Key 已正确生成并符合要求。 2、CipherInputStream创建成功。 3. 将文本复制到文件并在解密后读取它确实匹配:

        @Test
        public void streamEncryptionDecryptionTest() {
        
            try {
                File file = tempFolder.newFile("test.txt");
        
                String content = "This is the text content";
                System.out.println("Original content for file = " + content);
        
        
                OutputStream fop = new FileOutputStream(file);
        
                byte[] key = EncryptionHelper.createKey();
        
                fop = EncryptionHelper.getInstance(key).getEncryptedOutputStream(fop);
        
                // if file doesn't exists, then create it
                if (!file.exists()) {
                    file.createNewFile();
                }
        
                // get the content in bytes
                byte[] contentInBytes = content.getBytes();
        
                fop.write(contentInBytes);
                fop.flush();
                fop.close();
        
                System.out.println("Encryption Done");
        
                { // Read content w/o decryption
                    InputStream fis = new FileInputStream(file);
                    System.out.println("Content w/o decryption");
        
                    int contentChars;
                    while ((contentChars = fis.read()) != -1) {
                        // convert to char and display it
                        System.out.print((char) contentChars);
                    }
        
                }
        
                { // Decrypted content
                    InputStream fis = new FileInputStream(file);
                    fis = EncryptionHelper.getInstance(key).getDecryptedInputStream(fis);
        
                    System.out.println("Total file size to read (in bytes) : "
                            + fis.available());
        
                    System.out.println("Decrypted content");
        
                    int contentChars;
                    while ((contentChars = fis.read()) != -1) {
                        // convert to char and display it
                        System.out.print((char) contentChars);
                    }
                }
        
        
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
                Assert.assertTrue("security exception", false);
            }
        }
        

        虽然该测试没有明确的断言检查,但您可以添加:

        for (int i = 0; i < buffer.length; i++) {
            byte b = buffer[i];
            System.out.print(b);
            decryptedContent[i] = b;
        }
        

        然后检查数组:

        Assert.assertArrayEquals(bytesEncoded, decryptedContent);
        

        请记住,您不应该通过将字节数组转换为字符串来比较它们,因为它会给您错误的实现。

        1. 部分缓冲区加密和解密工作正常。取决于您的需求。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-12-20
          • 2012-05-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多