2017-02-16 7 views
0

Когда я пытаюсь подписать хешированное значение в моем приложении Android, которое я получаю извне, я получаю вышеупомянутое исключение.android.security.KeyStoreException: Несовместимый дайджест при подписании с RSA

Код для генерации ключей является:

public static KeyPair generateKeyPair(Context context, String username) throws Exception { 
     KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, ANDROID_KEY_STORE); 
     Calendar start = Calendar.getInstance(); 
     Calendar end = Calendar.getInstance(); 
     end.add(Calendar.YEAR, 1); 
     AlgorithmParameterSpec parameterSpec; 
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 
      parameterSpec = new KeyGenParameterSpec.Builder(MY_KEY_ALIAS, KeyProperties.PURPOSE_SIGN) 
        .setKeySize(KEY_SIZE) 
        .setCertificateSubject(usernameToSubject(username)) 
        .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA1) 
        .setCertificateNotBefore(start.getTime()) 
        .setCertificateNotAfter(end.getTime()) 
        .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) 
        .build(); 
     } else { 
      // Here I build the keys for older versions. This is not part of my problem 
     } 
     keyPairGenerator.initialize(parameterSpec); 
     return keyPairGenerator.generateKeyPair(); 
    } 

Позже я подпишу хэш я получаю из-за пределов:

public static byte[] signHash(byte[] hashToSign) { 
     try { 
      KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE); 
      keyStore.load(null); 
      KeyStore.PrivateKeyEntry keyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(MY_KEY_ALIAS, null); 
      PrivateKey privateKey = keyEntry.getPrivateKey(); 

      DigestAlgorithmIdentifierFinder hashAlgorithmFinder = new DefaultDigestAlgorithmIdentifierFinder(); 
      AlgorithmIdentifier hashingAlgorithmIdentifier = hashAlgorithmFinder.find(KeyProperties.DIGEST_SHA256); 
      DigestInfo digestInfo = new DigestInfo(hashingAlgorithmIdentifier, hashToSign); 
      byte[] hashToEncrypt = digestInfo.getEncoded(); 
      Cipher cipher = Cipher.getInstance("RSA/ECB/Pkcs1Padding"); 
      cipher.init(Cipher.ENCRYPT_MODE, privateKey); // <= the exception is thrown here 
      return cipher.doFinal(hashToEncrypt); 
     } catch (Throwable e) { 
      Log.e("KeyStoreWrapper", "Error while signing: ", e); 
     } 
     return "Could not sign the message.".getBytes(StandardCharsets.UTF_16LE); 
    } 

Я получаю ошибку является:

java.security.InvalidKeyException: Keystore operation failed 
at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1004) 
at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1024) 
at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:53) 
at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89) 
at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:263) 
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:108) 
at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:612) 
at javax.crypto.Cipher.tryCombinations(Cipher.java:532) 
at javax.crypto.Cipher.getSpi(Cipher.java:437) 
at javax.crypto.Cipher.init(Cipher.java:815) 
at javax.crypto.Cipher.init(Cipher.java:774) 
at de.new_frontiers.m2fa.security.KeyStoreWrapper.signHash(KeyStoreWrapper.java:186) 
at de.new_frontiers.m2fa.bluetooth.BluetoothHandler.handleMessage(BluetoothHandler.java:93) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:158) 
at android.app.ActivityThread.main(ActivityThread.java:7229) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120) 
Caused by: android.security.KeyStoreException: Incompatible digest 
at android.security.KeyStore.getKeyStoreException(KeyStore.java:944) 
at android.security.KeyStore.getInvalidKeyException(KeyStore.java:1024)  
at android.security.keystore.KeyStoreCryptoOperationUtils.getInvalidKeyExceptionForInit(KeyStoreCryptoOperationUtils.java:53)  
at android.security.keystore.KeyStoreCryptoOperationUtils.getExceptionForCipherInit(KeyStoreCryptoOperationUtils.java:89)  
at android.security.keystore.AndroidKeyStoreCipherSpiBase.ensureKeystoreOperationInitialized(AndroidKeyStoreCipherSpiBase.java:263)  
at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineInit(AndroidKeyStoreCipherSpiBase.java:108)  
at javax.crypto.Cipher.tryTransformWithProvider(Cipher.java:612)  
at javax.crypto.Cipher.tryCombinations(Cipher.java:532)  
at javax.crypto.Cipher.getSpi(Cipher.java:437)  
at javax.crypto.Cipher.init(Cipher.java:815)  
at javax.crypto.Cipher.init(Cipher.java:774)  
at com.mycompany.security.KeyStoreWrapper.signHash(KeyStoreWrapper.java:186)  

В android documentation я вижу:

Для операций подписи и подтверждения дайджест должен быть указан в аргументе дополнительных_параметров для начала. Если указанный дайджест не находится в дайджестах, связанных с ключом, операция должна завершиться неудачно с KM_ERROR_INCOMPATIBLE_DIGEST.

Но, как вы можете видеть, я создаю пару с ключом KeyProperties.DIGEST_SHA256 и задаю DigestInfo по тому же алгоритму. Поэтому я не понимаю, почему я получаю сообщение об ошибке «Несовместимый дайджест». Может ли кто-нибудь пролить свет на это?

О, для кого-то, кто задается вопросом, почему я не использую Signature.sign(): для этого понадобится открытый текст для создания, затем создаст хэш, DigestInfo и затем зашифрует его с помощью закрытого ключа. Я уже получаю хэш извне, и это то, что я не могу изменить.

ответ

0

pedrofbs ответ помог мне получить вещи прямо в конце концов. Я уже изменил назначение ключа на значение, упомянутое в моем комментарии к его ответу: KeyProperties.PURPOSE_SIGN|KeyProperties.PURPOSE_ENCRYPT|Key‌​Properties.PURPOSE_D‌​ECRYPT, но забыл позвонить .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1). Так что большое спасибо, педрофы, за это!

К сожалению, это все еще не сработало. Поймав с помощью разных настроек ключ, я понял, что не защитил устройство, которое я использую для тестирования с помощью пароля, булавки или чего-то еще. Поэтому добавление .setUserAuthenticationRequired(false) в KeyGenParameterSpec сделало трюк. Я знаю, что это небезопасно и нужно менять, но на данный момент я просто делаю доказательство концепции, так что все в порядке. :-)

В случае, если кто-либо еще натыкается на эту проблему: здесь весь рабочий код для генерации ключей:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 
      parameterSpec = new KeyGenParameterSpec.Builder(LOGON_KEY_ALIAS, KeyProperties.PURPOSE_SIGN|KeyProperties.PURPOSE_ENCRYPT|KeyProperties.PURPOSE_DECRYPT) 
        .setKeySize(KEY_SIZE) 
        .setUserAuthenticationRequired(false) 
        .setCertificateSubject(usernameToSubject(username)) 
        .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA1)//, KeyProperties.DIGEST_NONE, KeyProperties.DIGEST_SHA224, KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA512, KeyProperties.DIGEST_MD5) 
        .setCertificateNotBefore(start.getTime()) 
        .setCertificateNotAfter(end.getTime()) 
        .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) 
        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) 
        .build(); 
     } 
2

Шифрование с помощью закрытого ключа с использованием RSA/ECB/PKCS1Padding доступно на AndroidKeyStore с Android 18, поэтому вы должны иметь возможность выполнять действительную цифровую подпись с полученным хэшем.

Я предполагаю, что проблема заключается в установке использования ключа для подписывания, а не для шифрования (именно это вы и хотите для этого). Попробуйте это:

parameterSpec = new KeyGenParameterSpec.Builder(MY_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | | KeyProperties.PURPOSE_DECRYPT) 
       .setKeySize(KEY_SIZE) 
       .setCertificateSubject(usernameToSubject(username)) 
       .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA1) 
       .setCertificateNotBefore(start.getTime()) 
       .setCertificateNotAfter(end.getTime()) 
       .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) 
       .build(); 

Проверьте также, что hashToSign действительно SHA-256 (32 байт)

+0

Я изменил назначение KeyGenParameterSpec в 'KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT' и добавил EncrpytionPaddings, но он все еще не работает. Исключение выбрано для cipher.init(), когда шифр ничего не знает о хэше для шифрования (который составляет 32 байта). – Frank

Смежные вопросы