public final class

MasterKeys

extends java.lang.Object

 java.lang.Object

↳androidx.security.crypto.MasterKeys

Gradle dependencies

compile group: 'androidx.security', name: 'security-crypto', version: '1.1.0-alpha06'

  • groupId: androidx.security
  • artifactId: security-crypto
  • version: 1.1.0-alpha06

Artifact androidx.security:security-crypto:1.1.0-alpha06 it located at Google repository (https://maven.google.com/)

Overview

Convenient methods to create and obtain master keys in Android Keystore.

The master keys are used to encrypt data encryption keys for encrypting files and preferences.

Summary

Fields
public static final KeyGenParameterSpecAES256_GCM_SPEC

Methods
public static java.lang.StringgetOrCreate(KeyGenParameterSpec keyGenParameterSpec)

Creates or gets the master key provided

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public static final KeyGenParameterSpec AES256_GCM_SPEC

Methods

public static java.lang.String getOrCreate(KeyGenParameterSpec keyGenParameterSpec)

Creates or gets the master key provided

The encryption scheme is required fields to ensure that the type of encryption used is clear to developers.

Parameters:

keyGenParameterSpec: The key encryption scheme

Returns:

The key alias for the master key

Source

/*
 * Copyright 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.security.crypto;

import android.os.Build;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.ProviderException;
import java.util.Arrays;

import javax.crypto.KeyGenerator;

/**
 * Convenient methods to create and obtain master keys in Android Keystore.
 *
 * <p>The master keys are used to encrypt data encryption keys for encrypting files and preferences.
 *
 * @deprecated Use {@link MasterKey.Builder} to work with master keys.
 */
@Deprecated
@RequiresApi(Build.VERSION_CODES.M)
public final class MasterKeys {
    private MasterKeys() {
    }

    static final String MASTER_KEY_ALIAS = MasterKey.DEFAULT_MASTER_KEY_ALIAS;
    static final int KEY_SIZE = MasterKey.DEFAULT_AES_GCM_MASTER_KEY_SIZE;

    private static final String ANDROID_KEYSTORE = "AndroidKeyStore";

    @NonNull
    public static final KeyGenParameterSpec AES256_GCM_SPEC =
            createAES256GCMKeyGenParameterSpec(MASTER_KEY_ALIAS);

    private static final Object sLock = new Object();

    /**
     * Provides a safe and easy to use KenGenParameterSpec with the settings.
     * Algorithm: AES
     * Block Mode: GCM
     * Padding: No Padding
     * Key Size: 256
     *
     * @param keyAlias The alias for the master key
     * @return The spec for the master key with the specified keyAlias
     */
    @NonNull
    @SuppressWarnings("SameParameterValue")
    private static KeyGenParameterSpec createAES256GCMKeyGenParameterSpec(
            @NonNull String keyAlias) {
        KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(
                keyAlias,
                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                .setKeySize(KEY_SIZE);
        return builder.build();
    }

    /**
     * Creates or gets the master key provided
     *
     * <p>The encryption scheme is required fields to ensure that the type of
     * encryption used is clear to developers.
     *
     * @param keyGenParameterSpec The key encryption scheme
     * @return The key alias for the master key
     */
    @NonNull
    @SuppressWarnings("deprecation")
    public static String getOrCreate(
            @NonNull KeyGenParameterSpec keyGenParameterSpec)
            throws GeneralSecurityException, IOException {
        validate(keyGenParameterSpec);

        synchronized (sLock) {
            if (!MasterKeys.keyExists(keyGenParameterSpec.getKeystoreAlias())) {
                generateKey(keyGenParameterSpec);
            }
        }

        return keyGenParameterSpec.getKeystoreAlias();
    }

    @VisibleForTesting
    static void validate(KeyGenParameterSpec spec) {
        if (spec.getKeySize() != KEY_SIZE) {
            throw new IllegalArgumentException(
                    "invalid key size, want " + KEY_SIZE + " bits got " + spec.getKeySize()
                            + " bits");
        }
        if (!Arrays.equals(spec.getBlockModes(), new String[]{KeyProperties.BLOCK_MODE_GCM})) {
            throw new IllegalArgumentException(
                    "invalid block mode, want " + KeyProperties.BLOCK_MODE_GCM + " got "
                            + Arrays.toString(spec.getBlockModes()));
        }
        if (spec.getPurposes() != (KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)) {
            throw new IllegalArgumentException(
                    "invalid purposes mode, want PURPOSE_ENCRYPT | PURPOSE_DECRYPT got "
                            + spec.getPurposes());
        }
        if (!Arrays.equals(spec.getEncryptionPaddings(), new String[]
                {KeyProperties.ENCRYPTION_PADDING_NONE})) {
            throw new IllegalArgumentException(
                    "invalid padding mode, want " + KeyProperties.ENCRYPTION_PADDING_NONE + " got "
                            + Arrays.toString(spec.getEncryptionPaddings()));
        }
        if (spec.isUserAuthenticationRequired()
                && spec.getUserAuthenticationValidityDurationSeconds() < 1) {
            throw new IllegalArgumentException(
                    "per-operation authentication is not supported "
                            + "(UserAuthenticationValidityDurationSeconds must be >0)");
        }
    }

    private static void generateKey(@NonNull KeyGenParameterSpec keyGenParameterSpec)
            throws GeneralSecurityException {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(
                    KeyProperties.KEY_ALGORITHM_AES,
                    ANDROID_KEYSTORE);
            keyGenerator.init(keyGenParameterSpec);
            keyGenerator.generateKey();
        } catch (ProviderException providerException) {
            // Android 10 (API 29) throws a ProviderException under certain circumstances. Wrap
            // that as a GeneralSecurityException so it's more consistent across API levels.
            throw new GeneralSecurityException(providerException.getMessage(), providerException);
        }
    }

    private static boolean keyExists(@NonNull String keyAlias)
            throws GeneralSecurityException, IOException {
        KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
        keyStore.load(null);
        return keyStore.containsAlias(keyAlias);
    }

}