0

谢谢大家的意见。

但是,我必须解释这个问题。

我知道我们不应该使用修复随机生成器来比较加密的结果,因为它可能会降低安全性。但是,我只是想在测试中进行,我将在实际运行中使用随机的原始机制

情况是我需要通过以下步骤使用帐户/密码登录服务器:

  1. 从服务器获取信息:abcom/get_cipher.cgi。
  2. 接收响应,解析它并获取一些信息来创建密码。
  3. 使用密码加密帐户/密码,然后编写以下 URL abcom/login.cgi?encrypted={encrypted_account_password}

这是一个复杂的过程,我无法请求服务器更改协议。我想测试整个登录过程。因此,我尝试提供假帐户/密码,并在不解密结果的情况下检查生成的 url 是否正确(如果解密结果,则意味着在这个测试用例中,我需要解密加密的结果,解析 url,并提取相关信息,与测试无关的信息太多了。另外,如果我在登录过程中做一些改变,我可能需要修改测试用例中的解密和解析过程。)

这意味着哈希函数不适合我。(原来的登录过程没有使用任何哈希,所以我不想在测试用例中对其进行测试。而且,即使我检查了哈希结果是正确的,也不能证明登录过程是正确的。)

===原来的问题如下===

我有一个需要登录的程序。为了避免在网络上以纯文本形式传输密码,我需要对其进行加密。换句话说,登录过程包含一个加密阶段。

然后,我想为整个登录过程编写一个测试用例。如果使用相同的帐户和密码,我认为加密结果是相同的。

由于它可能在加密过程中使用 SecureRandom,我用 Mockito 编写了一个假的 SecureRandom,代码如下:

private static final long RANDOM_SEED = 3670875202692512518L;
private Random generateRandomWithFixSeed() {
    Random random = new Random(RANDOM_SEED);
    return random;
}

private SecureRandom generateSecureRandomWithFixSeed() {
    final Random random = generateRandomWithFixSeed();
    final SecureRandom secureRandom = new SecureRandom();
    final SecureRandom spySecureRandom = Mockito.spy(secureRandom);

    Mockito.doAnswer(new Answer<Object>() {
        @Override
        public Object answer(InvocationOnMock invocation) throws Throwable {
            Object[] args = invocation.getArguments();
            byte[] bytes = (byte[]) args[0];
            random.nextBytes(bytes);
            return bytes;
        }
    })
            .when(spySecureRandom)
            .nextBytes(Matchers.<byte[]>anyObject());

    return spySecureRandom;
}

@Test
public void test_SecureRandom_WithFixSeed() {
    final SecureRandom secureRandom1 = generateSecureRandomWithFixSeed();
    final SecureRandom secureRandom2 = generateSecureRandomWithFixSeed();

    final byte[] bytes1 = new byte[20];
    final byte[] bytes2 = new byte[20];

    secureRandom1.nextBytes(bytes1);
    secureRandom2.nextBytes(bytes2);

    boolean isTheSameSeries = true;
    for(int i = 0; i < 20; i++) {
        isTheSameSeries &= (bytes1[i]==bytes2[i]);
    }

    assertThat(isTheSameSeries, is(true));
}

generateRandomWithFixSeed()将使用相同的密钥新建一个随机数,以便生成相同的结果。generateSecureRandomWithFixSeed()使用Makito 检测函数调用 nextBytes() 并始终回答随机结果。测试test_SecureRandom_WithFixSeed()还显示两个不同的 SecureRandom 实例生成相同的结果。

但是,如果我在密码中使用 generateSecureRandomWithFixSeed() 如下,它不能总是返回相同的结果。

    @Test
public void test_cipher() {
    final SecureRandom secureRandomWithFixSeed = generateSecureRandomWithFixSeed();

    final String pkcs = "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv7n+/uWHHVC7229QLEObeH0vUcOagavDukf/gkveqgZsszzGkZQaXfsrjdPiCnvjozCy1tbnLu5EInDy4w8B+a9gtK8KqsvlsfuaT9kRSMUS8CfgpWj8JcJwijmeZhjR52k0UBpWLfn3JmRYW8xjZW6dlOSnS0yqwREPU7myyqUzhk1vyyUG7wLpk7uK9Bxmup0tnbnD4MeqDboAXlQYnIFVV+CXywlAQfHHCfQRsGhsEtu4excZVw7FD1rjnro9bcWFY9cm/KdDBxZCYQoT/UW0OBbipoINycrmfMKt1r4mGE9/MdVoIEMBc54aI6sb2g5J2GtNCYfEu+1/gA99xY0+5B3ydH74cbqfHYOZIvu11Q7GnpZ6l8zTLlMuF/pvlSily76I45H0YZ3HcdQnf/GoKC942P6fNsynHEX01zASYM8dzyMxHQpNEx7fcXGi+uiBUD/Xdm2jwzr9ZEP5eEVlrpcAvr8c9S5ylE50lwR+Mp3vaZxPoLdSGZrfyXy4v97UZSnYARQBacmn6KgsIHIOKhYOxNgUG0jwCO/zrPvlbjiYTHQYLOCcldTULvXOdn51enJFGVjscGoZfRj6vZgyHVCUW4iip4iSbSKPcPbf0GMZuniS9qJ3Wybve0/xpppdOv1c40ez0NKQyQkEZRb+V0qfesatJKZd/hUGr+MCAwEAAQ==";
    final byte bytePKCS[] = Base64.base64ToByteArray(pkcs);
    final X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(bytePKCS);

    PublicKey pubKey = null;
    try {
        pubKey = KeyFactory.getInstance("RSA").generatePublic(pubKeySpec);
    } catch (InvalidKeySpecException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }

    final String targetResultText = "NZqTzuNli92vuXEQNchGeF6faN/NBHykhfqBFcWzBHZhbgljZaWAcAzasFSm/odZZ6vBD2jK7J4oi5BmDjxNdEjkXyv3OZ2sOTLCfudgPwXcXmmhOwWHDLY02OX0X3RwBHzqWczqAd4dwslo59Gp5CT59GWXenJPL8wvG90WH2XAKOmHg5uEZj55ZvErRQ6StPVzLkiNCMPOhga7FZWK/rSEpT6BHDy3CibDZ0PNRtAW4wiYAr0Cw6brqiqkN301Bz6DzrV5380KDHkw26GjM8URSTFekwvZ7FISQ72UaNHhjnh1WgMIPf/QDbrEh5b+rmdZjzc5bdjyONrQuoj0rzrWLN4z8lsrBnKFVo+zVyUeqr0IxqD2aHDLyz5OE4fb5IZJHEMfYr/R79Zfe8IuQ2tusA02ZlFzGRGBhAkb0VygXxJxPXkjbkPaLbZQZOsKLUoIDkcbNoUTxeS9+4LWVg1j5q3HR9OSvmsF5I/SszvVrnXdNaz1IKCfVYkwpIBQ+Y+xI/K360dWIHR/vn7TU4UsGmWtwVciq0jWLuBN/qRE6MV47TDRQu63GzeV00yAM/xUM33qWNXCV1tbGXNZw8jHpakgflTY0hcjOFHPpq2UfJCyxiSBtZ0b7hw9Rvhi8VwYc243jXO9CvGq+J6JYvchvKHjq2+YKn1UB2+gs20=";
    final String plainText = "a";
    String resultText = "";
    try {
        Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey, secureRandomWithFixSeed);
        final byte[] result = cipher.doFinal(plainText.getBytes());
        resultText = Base64.byteArrayToBase64(result);
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (NoSuchPaddingException e) {
        e.printStackTrace();
    } catch (IllegalBlockSizeException e) {
        e.printStackTrace();
    } catch (BadPaddingException e) {
        e.printStackTrace();
    } catch (InvalidKeyException e) {
        e.printStackTrace();
    }
    assertThat(resultText, is(targetResultText));
}

4

3 回答 3

0

对您的解释的回答相当简单,只有散列函数(不需要密码):

1 在服务器和客户端之间共享一个秘密。这必须在之前完成。任何字符串都可以完成这项工作(您可以将其视为密码)。

客户端有P,服务器有P

1bis更好的安全性:散列P,在双方:客户端和服务器有Ph

2连接:

2a服务器创建随机R,并发送给客户端

2b 客户端生成 Ph x R(例如,逐位)并对其进行哈希处理 => (Ph x R)h

并发送

2c 服务器可以做同样的事情:(Ph x R)h 并比较它

一个弱点:如果你在服务器上获得 Ph,你可以欺骗客户端。为避免这种情况,您应该使用其他功能。

其他选项(如果您不信任服务器,则更安全):在原始代码中使用非对称密钥和 RSA。

于 2015-12-01T08:29:56.057 回答
0

你不应该做你想做的事。这不是能够比较两个加密值以确定它们是否相同的加密点。我相信你可以让它工作,但你会有效地禁用功能并降低你加密的所有内容的安全性,以使它们显示相同。

如果您希望能够在不解密密码的情况下比较两个值,那么您真正想要的是哈希函数。具体来说,至少看看使用 SHA1 或 SHA-256(更好)。

如何在 Java 中使用 sha256 散列一些字符串?

按照设计,哈希是单向的(例如,如果没有Rainbow Table 之类的东西,您将无法反转密码)。但是,它旨在完全按照您的描述使用,将新值与旧值进行比较。

如果您真的想使用加密值,您应该解密密码值并将其与纯文本进行比较。但是,散列遵循最佳实践

于 2015-11-30T12:42:32.433 回答
0

我找到了原因。

我发现如果我在 Android 4.4 上运行测试用例,它不能工作,但它可以在 4.1.2 上工作。

通过更多调查,Android 似乎在较新版本(AndroidOpenSSL)中使用 OpenSSL 进行加密/描述。

在文件中,external/conscrypt/src/main/java/org/conscrypt/OpenSSLCipherRSA.java

@Override
protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
    engineInitInternal(opmode, key);
}

它表明它使用本机 OpenSSL 库来加密并且不使用给定的 SecureRandom。

解决方案是在 Cipher.getInstance() 时提供Provider 。

@Test
public void test_cipher() {
    private static final String PROVIDER_NAME = "BC";
    final Provider provider = Security.getProvider(PROVIDER_NAME);

    Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", provider);
}
于 2015-12-28T05:59:18.073 回答