2015-11-30 43 views
0

感謝大家的意見。我不得不解釋這個問題。爲什麼使用相同的字符串,密鑰和SecureRandom加密Java密碼總是不一樣?

我知道我們不應該比較使用固定隨機發生器加密的結果,因爲它可能會降低安全性。但是,我只是想在測試中做到這一點,我會使用原始機制的隨機實際運行

的情況是,我需要通過以下步驟登錄使用帳號/密碼的服務器:

  1. 從服務器獲取信息:a.b.com/get_cipher.cgi。
  2. 接收到響應,解析它並獲取一些信息來創建密碼。
  3. 使用密碼來加密帳戶/密碼,然後撰寫以下URL ABCOM/login.cgi?加密= {encrypted_account_password}

這是一個複雜的過程,我無法請求服務器來改變protocal。我想測試整個登錄過程。因此,我試圖提供假帳戶/密碼,並檢查生成的url是否正確,而不解密結果(如果解密結果,這意味着,在這個測試案例中,我需要解密加密結果,解析url,並提取相關信息,太多與測試無關,而且,如果我在登錄過程中做了一些改動,我可能需要修改測試用例中的解密和解析過程。)

散列函數不適合我。 (原來的登錄過程不使用任何散列,因此我不想在測試用例中進行測試。而且,即使我認爲檢查散列結果是正確的,但它並不證明登錄過程是正確的。)

===原來的問題是,如下===

我有一個程序,它需要登錄。要在網絡上以純文本形式傳輸密碼,我需要對其進行加密。換句話說,登錄過程包含一個加密階段。

然後,我想爲整個登錄過程編寫一個測試用例。如果使用相同的帳戶和密碼,我認爲加密結果是相同的。

因爲它可以在加密過程中使用的SecureRandom,我寫了一個假的SecureRandom通過的Mockito如下面的代碼:

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

AA

+0

只是一對夫婦的想法...你可能會嘗試調用cipher.getIV()來查看是否有一個初始化向量不同。您可能還想通過將加密字節與常量進行比較來測試,但使用DECRYPT_MODE將從加密字節解密的值與原始值進行比較。 –

回答

0

你不應該做你想做什麼。這不是能夠比較兩個加密值以確定它們是否相同的加密點。我相信你可以使這個工作,但你會有效地禁用功能,並使你加密的一切不太安全,以使它們顯示相同。

如果您希望能夠在不解密密碼的情況下比較這兩個值,那麼您真正需要的是hash function。具體來說,請看看至少使用SHA1,還是SHA-256(更好)。

How to hash some string with sha256 in Java?

通過設計,哈希是單向的(例如,你不能沒有像一個Rainbow Table扭轉密碼)。然而,它的設計目的正是用你描述的方式,將新的價值與舊的價值進行比較。

如果您確實想要使用加密值,則應解密密碼值並將其與純文本進行比較。但是,hashing follows best practices

+0

我不想加密的結果,但我想測試它。 我更精確地描述我的情況。我需要通過以下步驟登錄服務器: 1.從服務器獲取信息:http://a.b.com/get_cipher.cgi 2.接收並解析響應並創建密碼。 3.使用密碼加密帳戶/密碼,然後撰寫以下URL http://a.b.com/login.cgi?encrypted={encrypted_account_password} 這是一個複雜的過程。我想測試它。我想用步驟1的預備響應編寫一個測試用例,並檢查步驟3的結果URL。 –

0

相當簡單回答你的解釋,只用散列函數(無需密碼):

1股的服務器和客戶端之間的祕密。這必須在之前完成。任何字符串都可以完成這項工作(您可以將其視爲密碼)。

客戶有P,服務器爲P

1之二更好的安全性:哈希P,在兩個方面:客戶端和服務器具有博士

2連接:

2A服務器創建隨機R,和發送到客戶端

2B客戶pH調至X R(在例如比特位)和散列它=>(PH×R個)H

,並將其發送

2C服務器可以做同樣的事情:(PH個R)h和進行比較

一個弱點:如果您在服務器上得到pH值,你可以欺騙客戶。爲了避免這種情況,您應該使用其他功能。

其他選項(如果您不信任服務器,則更安全):在原始代碼中使用不對稱密鑰和RSA。

0

我找到了原因。

我發現如果我在Android 4.4上運行測試用例,它不能正常工作,但它在012上工作工作 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); 
} 
相關問題