次の方法で共有


CryptoObject の作成

指紋認証の結果の整合性は、アプリケーションにとって重要です。これは、アプリケーションがユーザーの ID を認識する方法です。 理論的には、サードパーティのマルウェアが、指紋スキャナーによって返された結果をインターセプトして改ざんすることが可能です。 このセクションでは、指紋結果の有効性を維持するための 1 つの手法について説明します。

FingerprintManager.CryptoObject は、Java 暗号化 API のラッパーであり、認証要求の整合性を保護するために FingerprintManager によって使用されます。 通常、Javax.Crypto.Cipher オブジェクトは、指紋スキャナーの結果を暗号化するためのメカニズムです。 Cipher オブジェクト自体は、Android Keystore API を使用してアプリケーションによって作成されたキーを使用します。

これらのクラスすべてがどのように連携するかを理解するために、まず、CryptoObject を作成する方法を示す次のコードを見てから、さらに詳しく説明します。

public class CryptoObjectHelper
{
    // This can be key name you want. Should be unique for the app.
    static readonly string KEY_NAME = "com.xamarin.android.sample.fingerprint_authentication_key";

    // We always use this keystore on Android.
    static readonly string KEYSTORE_NAME = "AndroidKeyStore";

    // Should be no need to change these values.
    static readonly string KEY_ALGORITHM = KeyProperties.KeyAlgorithmAes;
    static readonly string BLOCK_MODE = KeyProperties.BlockModeCbc;
    static readonly string ENCRYPTION_PADDING = KeyProperties.EncryptionPaddingPkcs7;
    static readonly string TRANSFORMATION = KEY_ALGORITHM + "/" +
                                            BLOCK_MODE + "/" +
                                            ENCRYPTION_PADDING;
    readonly KeyStore _keystore;

    public CryptoObjectHelper()
    {
        _keystore = KeyStore.GetInstance(KEYSTORE_NAME);
        _keystore.Load(null);
    }

    public FingerprintManagerCompat.CryptoObject BuildCryptoObject()
    {
        Cipher cipher = CreateCipher();
        return new FingerprintManagerCompat.CryptoObject(cipher);
    }

    Cipher CreateCipher(bool retry = true)
    {
        IKey key = GetKey();
        Cipher cipher = Cipher.GetInstance(TRANSFORMATION);
        try
        {
            cipher.Init(CipherMode.EncryptMode, key);
        } catch(KeyPermanentlyInvalidatedException e)
        {
            _keystore.DeleteEntry(KEY_NAME);
            if(retry)
            {
                CreateCipher(false);
            } else
            {
                throw new Exception("Could not create the cipher for fingerprint authentication.", e);
            }
        }
        return cipher;
    }

    IKey GetKey()
    {
        IKey secretKey;
        if(!_keystore.IsKeyEntry(KEY_NAME))
        {
            CreateKey();
        }

        secretKey = _keystore.GetKey(KEY_NAME, null);
        return secretKey;
    }

    void CreateKey()
    {
        KeyGenerator keyGen = KeyGenerator.GetInstance(KeyProperties.KeyAlgorithmAes, KEYSTORE_NAME);
        KeyGenParameterSpec keyGenSpec =
            new KeyGenParameterSpec.Builder(KEY_NAME, KeyStorePurpose.Encrypt | KeyStorePurpose.Decrypt)
                .SetBlockModes(BLOCK_MODE)
                .SetEncryptionPaddings(ENCRYPTION_PADDING)
                .SetUserAuthenticationRequired(true)
                .Build();
        keyGen.Init(keyGenSpec);
        keyGen.GenerateKey();
    }
}

このサンプル コードでは、アプリケーションによって作成されたキーを使用して、CryptoObject ごとに新しい Cipher を作成します。 このキーは、CryptoObjectHelper クラスの先頭に設定された KEY_NAME 変数によって特定されます。 メソッド GetKey は、Android Keystore API を使用してキーの取得を試みます。 キーが存在しない場合は、メソッド CreateKey によって、アプリケーションの新しいキーが作成されます。

暗号は Cipher.GetInstance の呼び出しでインスタンス化され、"変換" (データを暗号化および暗号化解除する方法を暗号に指示する文字列値) を取得します。 Cipher.Init の呼び出しでは、アプリケーションからキーを提供することによって暗号の初期化が完了します。

次のような状況によって Android でキーが無効化される可能性があることを認識することが重要です。

  • 新しい指紋がデバイスに登録された。
  • デバイスに登録されている指紋がない。
  • ユーザーが画面のロックを無効にした。
  • ユーザーが画面のロック (screenlock の種類または使用されている PIN/パターン) を変更した。

この場合、Cipher.InitKeyPermanentlyInvalidatedException をスローします。 上記のサンプル コードでは、その例外をトラップし、キーを削除して、新しいものを作成します。

次のセクションでは、キーを作成してデバイスに格納する方法について説明します。

秘密鍵の作成

CryptoObjectHelper クラスでは、Android KeyGenerator を使用してキーを作成し、デバイスに格納します。 KeyGenerator クラスでは、キーを作成できますが、作成するキーの種類に関するメタデータが必要です。 この情報は、KeyGenParameterSpec クラスのインスタンスによって提供されます。

KeyGenerator は、GetInstance ファクトリ メソッドを使用してインスタンス化されます。 このサンプル コードでは、暗号化アルゴリズムとして Advanced Encryption Standard (AES) を使用しています。 AES では、データが固定サイズのブロックに分割され、その各ブロックが暗号化されます。

次に、KeyGenParameterSpec.Builder を使用して KeyGenParameterSpec が作成されます。 KeyGenParameterSpec.Builder によって、作成されるキーに関する次の情報がラップされます。

  • キーの名前です。
  • キーは、暗号化と暗号化解除の両方に対して有効である必要があります。
  • このサンプル コードでは、BLOCK_MODE は "暗号ブロック チェーン" (KeyProperties.BlockModeCbc) に設定されています。つまり、各ブロックは前のブロックと XOR されます (各ブロック間の依存関係が作成されます)。
  • CryptoObjectHelper"公開キー暗号化標準 #7" (PKCS7) を使用して、ブロックを埋めるバイトを生成し、それらがすべて同じサイズになるようにします。
  • SetUserAuthenticationRequired(true) は、キーを使用する前に、ユーザー認証が必要であることを意味します。

KeyGenParameterSpec が作成されると、それを使用して KeyGenerator が初期化されます。これにより、キーが生成され、デバイスに安全に格納されます。

CryptoObjectHelper の使用

サンプル コードで、CryptoWrapper を作成するためのロジックの大部分を CryptoObjectHelper クラスにカプセル化したので、このガイドの最初のコードを見直し、CryptoObjectHelper を使用して暗号を作成し、指紋スキャナーを起動します。

protected void FingerPrintAuthenticationExample()
{
    const int flags = 0; /* always zero (0) */
    
    CryptoObjectHelper cryptoHelper = new CryptoObjectHelper();
    cancellationSignal = new Android.Support.V4.OS.CancellationSignal();
    
    // Using the Support Library classes for maximum reach
    FingerprintManagerCompat fingerPrintManager = FingerprintManagerCompat.From(this);
    
    // AuthCallbacks is a C# class defined elsewhere in code.
    FingerprintManagerCompat.AuthenticationCallback authenticationCallback = new MyAuthCallbackSample(this);

    // Here is where the CryptoObjectHelper builds the CryptoObject. 
    fingerprintManager.Authenticate(cryptohelper.BuildCryptoObject(), flags, cancellationSignal, authenticationCallback, null);
}

CryptoObject を作成する方法を確認したので、次に、FingerprintManager.AuthenticationCallbacks を使用して指紋スキャナー サービスの結果を Android アプリケーションに転送する方法を確認しましょう。