感谢大家的评论。
但是,我必须解释这个问题。
我知道我们不应该使用修复随机生成器来比较加密结果,因为它可能会降低安全性。但是,我只想在测试中执行此操作,并且我将在实际运行中使用原始随机机制。
案例是我需要通过以下步骤使用帐户/密码登录服务器:
这是一个复杂的过程,我无法请求服务器更改协议。我想测试整个登录过程。因此,我尝试提供虚假的帐户/密码并检查生成的URL是否正确而不解密结果(如果解密结果,则意味着,在此测试用例中,我需要解密加密结果,解析网址,并提取相关信息,与测试无关。此外,如果我在登录过程中做了一些更改,我可能需要修改测试用例中的解密和解析过程。)
这意味着哈希函数对我来说不合适。 (原始登录过程不使用任何哈希,所以我不想在测试用例中测试它。而且,即使我认为检查哈希结果是正确的,它也不能证明登录过程< / strong>是正确的。)
===原始问题如下:===
我有一个需要登录的程序。为了无效在网络上以纯文本形式传输密码,我需要对其进行加密。换句话说,登录过程包含加密阶段。
然后,我想为整个登录过程编写一个测试用例。我认为如果使用相同的帐户和密码,加密结果是相同的。
由于它可能在加密过程中使用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()将使用相同的键新建一个Random,以便生成相同的结果。 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));
}
AA
答案 0 :(得分:0)
你不应该做你想做的事。这不是加密能够比较两个加密值以确定它们是否相同的点。我相信你可以让它工作,但你实际上是禁用功能,并使加密的一切都不那么安全,以使它们显示相同。
如果您希望能够在不解密密码的情况下比较这两个值,那么您真正想要的是hash function。具体来说,看一下至少使用SHA1,或SHA-256(更好)。
How to hash some string with sha256 in Java?
按照设计,散列是单向的(例如,如果没有Rainbow Table之类的东西,则无法撤消密码)。但是,它的设计完全按照您的描述使用,将新值与旧值进行比较。
如果您真的想使用加密值,则应该解密密码值并将其与纯文本进行比较。但是,hashing follows best practices。
答案 1 :(得分:0)
相当简单的解释你的解释,只有哈希函数(不需要密码):
1在服务器和客户端之间共享一个秘密。这必须在此之前完成。任何字符串都可以完成工作(您可以将其视为密码)。
客户端有P,服务器有P
1bis更好的安全性:在两端散列P:客户端和服务器都有Ph
2连接:
2a服务器创建随机R,并发送到客户端
2b客户端使Ph x R(例如位)和哈希值=&gt; (Ph x R)h
并发送
2c服务器可以做同样的事情:(Ph x R)h并比较它
一个缺点:如果你在服务器上获得Ph,你可以欺骗客户端。为避免这种情况,您应该使用其他功能。
其他选项(如果您不信任服务器则更安全):使用不对称密钥和原始代码中的RSA。
答案 2 :(得分: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()时提供提供程序。
@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);
}