2016-04-22 47 views
4

下面的方法已過時KeyPairGeneratorSpec置換KeyGenParameterSpec.Builder等價物 - 密鑰存儲操作失敗

KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore"); 

KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(this) 
          .setAlias(alias) 
          .setSubject(new X500Principal("CN=Sample Name, O=Android Authority")) 
          .setSerialNumber(BigInteger.ONE) 
          .setStartDate(start.getTime()) 
          .setEndDate(end.getTime()) 
          .build(); 

generator.initialize(spec); 

我來到看起來像更換此

KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore"); 

generator.initialize(new KeyGenParameterSpec.Builder 
          (alias, KeyProperties.PURPOSE_SIGN) 
          .setDigests(KeyProperties.DIGEST_SHA256) 
          .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) 
          .build()); 

雖然我可以用這個來產生密鑰對和加密值,我無法解密它

public void encryptString(String alias) { 
     try { 
      KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null); 
      RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey(); 

      String initialText = startText.getText().toString(); 
      if(initialText.isEmpty()) { 
       Toast.makeText(this, "Enter text in the 'Initial Text' widget", Toast.LENGTH_LONG).show(); 
       return; 
      } 

      //Security.getProviders(); 

      Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidKeyStoreBCWorkaround"); 
      inCipher.init(Cipher.ENCRYPT_MODE, publicKey); 

      ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 
      CipherOutputStream cipherOutputStream = new CipherOutputStream(
        outputStream, inCipher); 
      cipherOutputStream.write(initialText.getBytes("UTF-8")); 
      cipherOutputStream.close(); 

      byte [] vals = outputStream.toByteArray(); 
      encryptedText.setText(Base64.encodeToString(vals, Base64.DEFAULT)); 
     } catch (Exception e) { 
      Toast.makeText(this, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show(); 
      Log.e(TAG, Log.getStackTraceString(e)); 
     } 
    } 

public void decryptString(String alias) { 
     try { 
      KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null); 

      Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidKeyStoreBCWorkaround"); 
      output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey()); 

      String cipherText = encryptedText.getText().toString(); 
      CipherInputStream cipherInputStream = new CipherInputStream(
        new ByteArrayInputStream(Base64.decode(cipherText, Base64.DEFAULT)), output); 
      ArrayList<Byte> values = new ArrayList<>(); 
      int nextByte; 
      while ((nextByte = cipherInputStream.read()) != -1) { 
       values.add((byte)nextByte); 
      } 

      byte[] bytes = new byte[values.size()]; 
      for(int i = 0; i < bytes.length; i++) { 
       bytes[i] = values.get(i).byteValue(); 
      } 

      String finalText = new String(bytes, 0, bytes.length, "UTF-8"); 
      decryptedText.setText(finalText); 

     } catch (Exception e) { 
      Toast.makeText(this, "Exception " + e.getMessage() + " occured", Toast.LENGTH_LONG).show(); 
      Log.e(TAG, Log.getStackTraceString(e)); 
     } 

decrypt方法,以下命令將失敗:

Cipher output = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidKeyStoreBCWorkaround"); 
       output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey()); 

java.security.InvalidKeyException: Keystore operation failed 

我認爲它做的KeyGenParamaterSpec.Builder有不正確的情況下,同樣的加密密碼類型是不正確的字符串,解密函數中也是這樣。

但是,這可以追溯到新的KeygenParameterSpec.Builder的使用,因爲使用舊的棄用方法允許我加密和解密。

如何解決?

+0

試圖通過調用keypairgeneratorspec.builder()和iam獲取通過它單獨的不推薦使用的代碼的目標Android 18 -23?我有一個if語句針對18-23?如果我嘗試使用keygenparameterspec.builder()我得到一個編譯時錯誤紅線?看起來像是沒有選擇,但使用不推薦的調用?任何建議任何人? –

回答

1

鑑於您沒有提供異常的完整堆棧跟蹤,因此很難100%確定。

您的代碼會生成私鑰,使其僅被授權用於簽名而不是解密。加密工作正常,因爲它不使用私鑰 - 它使用公鑰和Android Keystore公鑰可以不受任何限制地使用。解密失敗,因爲它需要使用私鑰,但您的代碼沒有授權使用私鑰解密。

它看起來像立即修復是授權私鑰用於解密。 Thia是通過在調用KeyGenParameterSpec.Builder構造函數時列出KeyProperties.PURPOSE_DECRYPT來實現的。如果不應該使用密鑰進行簽名,請從中刪除KeyProperties.PURPOSE_SIGN以及刪除setSignaturePaddings。

您還需要授權與PKCS1Padding私鑰使用:調用setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)

+0

處理異常,沒有生成堆棧跟蹤的崩潰,但我可以開始手動記錄它 – CQM

+0

您可以使用encryptPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)保存我。我還設置了NONE-PADDING,但它在Android 6上崩潰。您的解決方案非常完美。非常感謝。 –

1

亞歷克斯提到一個遺漏的部分是其他KeyProperties.PURPOSE_DECRYPT一個是setSignaturePaddings,而不是爲你必須使用setEncryptionPaddings方法。這是示例代碼片段。

new KeyGenParameterSpec.Builder(ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) 
      .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) 
      // other options 
      .build() 

請參閱documentation瞭解更多信息。

+0

謝謝,救我一天。 – Devon