0

Thanks the comments from everyone.

However, I have to explain this question.

I know that we shouldn't compare the result of encryption by using a fix random generator due to it may reduce the secure. However, I just want to do it in testing and I'll use a original mechanism of random in real running.

The case is that I need to login a server with account/password by the following steps:

  1. Get info from server: a.b.com/get_cipher.cgi.
  2. receive the response, parse it and get some info to create a cipher.
  3. use the cipher to encrypt account/password and then compose the following URL a.b.com/login.cgi?encrypted={encrypted_account_password}

It's a complex process and I can't request the server to change the protocal. I want to test the whole login process. Thus, I tried to provide fake account/password and check if the generated url is correct without deciphering the result (if deciphering the result, it means that, in this test case, I need to decipher the the encrypted result, parse the url, and extract the related info, there are too much not related to the testing. Moreover, if I do some change in the login process, I may need to modify the deciphering and parsing process in the test cases.)

That means a hash function is not proper for me. (The original login process don't use any hash so that I don't want to test it in a test cases. Moreover, even thought I check the hash result is correct, it doesn't prove the login process is correct.)

=== The original question is as the following ===

I have a program which needs login. To void transfer the password in plain text on the network, I need to encipher it. In other word, the login process contains a ciphering phase.

Then, I want to write a test case for the whole login process. I think the encryption result of be the same if it use the same account and password.

Since it may use SecureRandom in the encryption process, I write a fake SecureRandom by Mockito as the following code:

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));
}

The generateRandomWithFixSeed() will new a Random with the same key, so that it will generate the same result. The generateSecureRandomWithFixSeed() uses Makito to detect the function call nextBytes() and always answer the result of the random. the test test_SecureRandom_WithFixSeed() also show two different SecureRandom instances generate the same results.

However, if I use generateSecureRandomWithFixSeed() in the cipher as the following, it can't always return the same result.

    @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));
}

aa

Xanadu Wang
  • 161
  • 1
  • 7
  • Just a couple ideas...You might try calling cipher.getIV() to see if there's an initialization vector which varies. You might also want to test not by comparing the encrypted bytes to a constant but by using DECRYPT_MODE to compare the value decrypted from the encrypted bytes to the original. – aro_tech Nov 30 '15 at 11:31

3 Answers3

0

You shouldn't do what you're trying to do. That is not the point of encryption to be able to compare two encrypted values to determine if they're the same. I'm sure you CAN get this to work, but you'd effectively be disabling features and making everything you encrypt less secure in order to make them show up the same.

If you want to be able to compare the two values without decrypting the password, what you really want is a hash function. Specifically, take a look at using SHA1 at least, or SHA-256 (Better).

How to hash some string with sha256 in Java?

By design, a hash is one-way (e.g. You can't reverse the password without something like a Rainbow Table). However, its designed to be used exactly how you're describing, comparing the new value with the old.

If you really want to work with encrypted values, you should be decrypting the password value and comparing it against the plain-text. However, hashing follows best practices.

Community
  • 1
  • 1
Doug
  • 6,446
  • 9
  • 74
  • 107
  • I don't want to work the encrypted result but I want to test it. I describe my case more precisely. I need to login a server with account/password by the following steps: 1. Get info from server: http://a.b.com/get_cipher.cgi 2. receive and parse the response and create a cipher. 3. use the cipher to encrypt account/password and then compose the following URL http://a.b.com/login.cgi?encrypted={encrypted_account_password} It's a complex process. I want to test it. I want to write a test case with a prepared response of step 1 and check the result url of step 3. – Xanadu Wang Nov 30 '15 at 13:45
0

rather simple answer to your explanations, with only hash functions (no cipher needed):

1 share a secret between server and client. this must be done before. Any string does the job (you can see it as a password).

Client has P, Server has P

1bis better security: hash the P, at both sides: Client and Server have Ph

2 connection:

2a server create random R, and send to client

2b client make Ph x R (bit at bit for example) and hash it => (Ph x R)h

and sends it

2c server can do the same thing: (Ph x R)h and compares it

One weakness : if you get Ph at the server, you can spoof the client. To avoid that, you should use other functions.

Other option (more secure if you dont trust the server): use an asymetric key and RSA as in your original code.

0

I've found the reason.

I found that if I run the test case on Android 4.4, it can't work but it works on 4.1.2.

By more survey, it seems that Android use OpenSSL for encryption/description in newer version (AndroidOpenSSL).

In the file, external/conscrypt/src/main/java/org/conscrypt/OpenSSLCipherRSA.java

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

It shows that it use native OpenSSL library to encipher and don't use the given SecureRandom.

The solution is to provider the Provider when Cipher.getInstance().

@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);
}
Xanadu Wang
  • 161
  • 1
  • 7