Kiedy próbuję podpisać wartość znieczulenia w mojej aplikacji na Androida, którą dostaję z zewnątrz, otrzymuję wyżej wymienione wyjątek.

Kodeks generowania klapy jest:

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

Później podpisuję hash, który dostałem z zewnątrz:

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

Pojawia się błąd:

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) 

W Dokumentacja Android Widzę:

W przypadku operacji podpisywania i weryfikacji Digest musi być określony w argumencie Addent_Params na początku. Jeśli podanym strawieniem nie jest w trawie związane z kluczem, operacja musi zawieść z KM_ERROR_INCOMPATIBLE_DIGEST.

Ale jak widać, tworzę klawiaturę z KeyProperties.DIGEST_SHA256 i ustawić Digestinfo do tego samego algorytmu. Więc nie rozumiem, dlaczego dostaję błąd "Niekompatybilne trawienie". Czy ktoś może na to rzucić trochę światła?

Och, dla każdego, kto zastanawia się, dlaczego nie używam Signature.sign(): Potrzebowałaby to zwykłego tekstu podpisać, a następnie stwarza Hash, Digestinfo, a następnie szyfruje go kluczem prywatnym. Mam już skrótów z zewnątrz i to jest coś, czego nie mogę się zmienić.

1
Frank 16 luty 2017, 13:50

2 odpowiedzi

Najlepsza odpowiedź

Pedrofbs Odpowiedź pomogła mi uzyskać rzeczy w końcu. Zmieniłem już cel na klucz do wartości wymienionych w moim komentarzu do jego odpowiedzi: KeyProperties.PURPOSE_SIGN|KeyProperties.PURPOSE_ENCRYPT|Key‌​Properties.PURPOSE_D‌​ECRYPT, ale zapomniałem zadzwonić .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1). Więc wielkie dziękuję, pedrofs, za spotkać to!

Niestety nadal nie działa. Bawiąc się z różnymi ustawieniami na kluczu, zdałem sobie sprawę, że nie chroniłem urządzenia, którego używam do testowania hasłem, pinem lub czymś innym. Tak dodanie .setUserAuthenticationRequired(false) do keygenparameterspec zrobił sztuczkę. Wiem, że nie jest to bezpieczne i musi być zmienione, ale w tej chwili tylko robię dowód koncepcji, więc jest w porządku. :-)

W przypadku, gdy ktoś inny potyka się na tym problemie: Oto cały kod pracy dla generacji klucza:

 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
Frank 16 luty 2017, 14:43

Szyfrowanie z kluczem prywatnym przy użyciu {x0}} jest dostępny na AndroidKeyStore z Android 18, więc powinieneś być w stanie wykonać prawidłowy podpis cyfrowy z otrzymanym hashem.

Myślę, że problem ustawia kluczowe wykorzystanie do podpisania, a nie do szyfrowania (to jest naprawdę tym, dla którego chcesz). Spróbuj tego:

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

Sprawdź również, że hashToSign jest naprawdę SHA-256 (32 bajty)

4
pedrofb 16 luty 2017, 12:24