精华内容
下载资源
问答
  • 如何使用Android KeyStore安全地存储任意字符串?
    2021-05-26 07:08:32

    我以可以使用AndroidKeyStore保护任意数据块为前提,并将其称为“键”。 但是,我研究得越深,就越清楚地看到KeyStore API与与安全性相关的对象(证书,密钥规格,提供程序等)深深地纠缠在一起。它不是设计来存储任意数据的,我看不到任何简单的方法 为此而弯曲的道路。

    但是,可以使用AndroidKeyStore帮助我保护敏感数据。 我可以使用它来管理加密密钥,该密钥将用于加密应用程序本地的数据。 通过结合使用AndroidKeyStore,CipherOutputStream和CipherInputStream,我们可以:

    生成,安全地存储和检索设备上的加密密钥

    加密任意数据并将其保存在设备上(在应用程序的目录中,该位置将受到文件系统权限的进一步保护)

    访问和解密数据以备后用。

    这是一些示例代码,演示了如何实现此目的。

    try {

    KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");

    keyStore.load(null);

    String alias = "key3";

    int nBefore = keyStore.size();

    // Create the keys if necessary

    if (!keyStore.containsAlias(alias)) {

    Calendar notBefore = Calendar.getInstance();

    Calendar notAfter = Calendar.getInstance();

    notAfter.add(Calendar.YEAR, 1);

    KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(this)

    .setAlias(alias)

    .setKeyType("RSA")

    .setKeySize(2048)

    .setSubject(new X500Principal("CN=test"))

    .setSerialNumber(BigInteger.ONE)

    .setStartDate(notBefore.getTime())

    .setEndDate(notAfter.getTime())

    .build();

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

    generator.initialize(spec);

    KeyPair keyPair = generator.generateKeyPair();

    }

    int nAfter = keyStore.size();

    Log.v(TAG, "Before = " + nBefore + " After = " + nAfter);

    // Retrieve the keys

    KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(alias, null);

    RSAPrivateKey privateKey = (RSAPrivateKey) privateKeyEntry.getPrivateKey();

    RSAPublicKey publicKey = (RSAPublicKey) privateKeyEntry.getCertificate().getPublicKey();

    Log.v(TAG, "private key = " + privateKey.toString());

    Log.v(TAG, "public key = " + publicKey.toString());

    // Encrypt the text

    String plainText = "This text is supposed to be a secret!";

    String dataDirectory = getApplicationInfo().dataDir;

    String filesDirectory = getFilesDir().getAbsolutePath();

    String encryptedDataFilePath = filesDirectory + File.separator + "keep_yer_secrets_here";

    Log.v(TAG, "plainText = " + plainText);

    Log.v(TAG, "dataDirectory = " + dataDirectory);

    Log.v(TAG, "filesDirectory = " + filesDirectory);

    Log.v(TAG, "encryptedDataFilePath = " + encryptedDataFilePath);

    Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");

    inCipher.init(Cipher.ENCRYPT_MODE, publicKey);

    Cipher outCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "AndroidOpenSSL");

    outCipher.init(Cipher.DECRYPT_MODE, privateKey);

    CipherOutputStream cipherOutputStream =

    new CipherOutputStream(

    new FileOutputStream(encryptedDataFilePath), inCipher);

    cipherOutputStream.write(plainText.getBytes("UTF-8"));

    cipherOutputStream.close();

    CipherInputStream cipherInputStream =

    new CipherInputStream(new FileInputStream(encryptedDataFilePath),

    outCipher);

    byte [] roundTrippedBytes = new byte[1000]; // TODO: dynamically resize as we get more data

    int index = 0;

    int nextByte;

    while ((nextByte = cipherInputStream.read()) != -1) {

    roundTrippedBytes[index] = (byte)nextByte;

    index++;

    }

    String roundTrippedString = new String(roundTrippedBytes, 0, index, "UTF-8");

    Log.v(TAG, "round tripped string = " + roundTrippedString);

    } catch (NoSuchAlgorithmException e) {

    Log.e(TAG, Log.getStackTraceString(e));

    } catch (NoSuchProviderException e) {

    Log.e(TAG, Log.getStackTraceString(e));

    } catch (InvalidAlgorithmParameterException e) {

    Log.e(TAG, Log.getStackTraceString(e));

    } catch (KeyStoreException e) {

    Log.e(TAG, Log.getStackTraceString(e));

    } catch (CertificateException e) {

    Log.e(TAG, Log.getStackTraceString(e));

    } catch (IOException e) {

    Log.e(TAG, Log.getStackTraceString(e));

    } catch (UnrecoverableEntryException e) {

    Log.e(TAG, Log.getStackTraceString(e));

    } catch (NoSuchPaddingException e) {

    Log.e(TAG, Log.getStackTraceString(e));

    } catch (InvalidKeyException e) {

    Log.e(TAG, Log.getStackTraceString(e));

    } catch (BadPaddingException e) {

    Log.e(TAG, Log.getStackTraceString(e));

    } catch (IllegalBlockSizeException e) {

    Log.e(TAG, Log.getStackTraceString(e));

    } catch (UnsupportedOperationException e) {

    Log.e(TAG, Log.getStackTraceString(e));

    }

    更多相关内容
  • keystore文件,安卓keystore加密证书100年有效,含KEY密码,正式证书,是老铁们开发是急需的正式Android签名,密码就是下载后的文件名
  • android keystore

    2019-05-28 02:43:53
    NULL 博文链接:https://mojianpo.iteye.com/blog/782037
  • Android KeyStore流程

    千次阅读 2021-12-31 11:21:02
    文章目录一、Keystore二、Keystore架构及接口函数1. Keystore组件架构2. IKeymasterDevice.hal中的几个重要接口函数2.1 begin函数2.2 update函数2.3 finish函数2.4 abort函数3. Keymaster TA4. 对称密码函数API三、...

    一、Keystore

    keystore主要是对密钥库的控制操作,包括密钥的生成导入导出、加解密、签名验签、访问控制等。
    概念的详细介绍就请看看google官网的介绍。本篇主要是想总结一下keystore的使用流程,貌似其他博客讲这个流程的不多,就整理一篇出来。

    原创不易,转载请标明出处 https://blog.csdn.net/jackone12347/article/details/122252644

    二、Keystore架构及接口函数

    1. Keystore组件架构

    keystore涉及到的模块之间的关系,一共有四个角色:

    Andriod Keystore: 提供Android framework API,封装密钥库相关的接口;
    Keystore:守护进程,通过Binder提供密钥库功能,通过AIDL与Framework keystore通讯;
    HIDL Keymaster: HIDL进程,封装调用keymaster TA的密钥函数接口;如android.hardware.keymaster@xxx-xxx
    KeyMaster TA: TEE中的TA,提供安全的密钥库操作和安全环境
    

    架构图如下:
    架构图

    2. IKeymasterDevice.hal中的几个重要接口函数

    需要看一下这几个相关的接口,HAL层的IKeymasterDevice.hal都有介绍,下面是我理解的几个,因为和接下来的章节中分析CTS问题是相关的。

    2.1 begin函数

    作用:使用指定的密钥开始加密操作。 如果一切顺利,begin() 必须返回 ErrorCode::OK 并创建一个操作句柄,该句柄必须传递给后续对 update()、finish() 或 abort() 的调用。
    函数原型:

        begin(KeyPurpose purpose, vec<uint8_t> keyBlob, vec<KeyParameter> inParams,
              HardwareAuthToken authToken)
            generates (ErrorCode error, vec<KeyParameter> outParams, OperationHandle operationHandle);
    

    2.2 update函数

    作用:向正在进行的加密操作提供数据并可能从begin()接收输出。
    函数原型:

        update(OperationHandle operationHandle, vec<KeyParameter> inParams, vec<uint8_t> input,
               HardwareAuthToken authToken, VerificationToken verificationToken)
            generates (ErrorCode error, uint32_t inputConsumed, vec<KeyParameter> outParams,
                       vec<uint8_t> output);
    

    说明:
    1、为了为缓冲区处理提供更大的灵活性,此方法的实现具有使用比提供的更少的数据的选项。
    调用者负责循环到在后续调用中提供其余数据。 必须返回consumed的输入量在 inputConsumed 参数中。
    实现必须始终至少消耗一个字节,除非operation不能再接受;

    如果提供的字节多于0且0字节是消耗,调用者必须认为这是一个错误并中止操作

    2、作为update的结果,实现还可以选择返回多少数据。 这仅与加密和解密操作相关,因为签名和验证在完成之前不会返回任何数据。 建议尽早返回数据,而不是缓冲它。

    2.3 finish函数

    作用:完成以 begin() 开始的加密操作并使 operationHandle 无效。此方法是操作中最后调用的方法,因此必须返回所有处理过的数据。
    函数原型:

        finish(OperationHandle operationHandle, vec<KeyParameter> inParams, vec<uint8_t> input,
               vec<uint8_t> signature, HardwareAuthToken authToken, VerificationToken verificationToken)
            generates (ErrorCode error, vec<KeyParameter> outParams, vec<uint8_t> output);
    

    2.4 abort函数

    中止以 begin() 开始的加密操作,释放所有内部资源并使操作句柄无效。
    函数原型:

    abort(OperationHandle operationHandle) generates (ErrorCode error);
    

    3. Keymaster TA

    keymaster_operation_t结构体,TA中会反复用到这个结构体中的数据。

    @ ta/include/operations.h
    typedef struct {
            uint8_t key_id[TAG_LENGTH];
            keymaster_key_blob_t *key;
            keymaster_blob_t nonce;
            keymaster_blob_t last_block;
            keymaster_operation_handle_t op_handle;
            keymaster_purpose_t purpose;
            keymaster_padding_t padding;
            keymaster_block_mode_t mode;
            keymaster_blob_list_item_t *sf_item;/*sign/verify data*/
            TEE_Time *last_access;
            TEE_OperationHandle *operation;
            TEE_OperationHandle *digest_op;
            size_t prev_in_size;
            uint32_t min_sec;
            uint32_t mac_length;
            uint32_t digestLength;
            uint32_t a_data_length;
            uint8_t *a_data;
            bool do_auth;
            bool got_input;
            bool buffering;
            bool padded;
            bool first;
    } keymaster_operation_t;
    

    keymaster_blob_t结构体

    typedef struct {
            uint8_t* data;
            size_t data_length;
    } keymaster_blob_t;
    
    

    4. 对称密码函数API

    Cryptographic API调用流程

    -   some_function()                             (Trusted App) -
    [1]   TEE_*()                      User space   (libutee.a)
    ------- utee_*() ----------------------------------------------
    [2]       tee_svc_*()              Kernel space
    [3]         crypto_*()                          (libtomcrypt.a and crypto.c)
    [4]           /* LibTomCrypt */                 (libtomcrypt.a)
    

    对称密码函数定义了执行对称密码操作(例如AES)的方式,涵盖分组密码和流密码。

    Cryptographic Operations API - Symmetric Cipher Functions

    void TEE_CipherInit(TEE_OperationHandle operation, const void *IV,
                        uint32_t IVLen)
    

    该函数启动对称密码操作,操作必须关联一个密钥。

    TEE_Result TEE_CipherUpdate(TEE_OperationHandle operation, const void *srcData,
                                uint32_t srcLen, void *destData, uint32_t *destLen)
    

    该函数用来加密或解密输入数据,输入数据不必是块大小的倍数,除非对此函数的一个或多个调用提供了足够的输入数据,否则不会生成任何输出。

    TEE_Result TEE_CipherDoFinal(TEE_OperationHandle operation,
                                 const void *srcData, uint32_t srcLen,
                                 void *destData, uint32_t *destLen)
    

    完成密码操作,处理以前未通过调用TEE_CipherUpdate函数处理的数据以及srcData中提供的数据。随后操作句柄可以重用或重新初始化。

    三、从Keystore到Keymaster的完整分析

    直接干巴巴地看源码,有时候也不是很直观地看出这几大模块是如何串联起来 及如何完成从上到下的功能串调。
    我们带着一个cts的问题来分析一下相关的流程吧。

    1. cts问题

    运行

    adb shell am instrument -r -e class  android.keystore.cts.AES128CBCNoPaddingCipherTest#testDoFinalResets  -w android.keystore.cts/androidx.test.runner.AndroidJUnitRunner
    

    报错:

     javax.crypto.IllegalBlockSizeException
    	at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineDoFinal(AndroidKeyStoreCipherSpiBase.java:490)
    	at javax.crypto.Cipher.doFinal(Cipher.java:2055)
    	at android.keystore.cts.BlockCipherTestBase.doFinal(BlockCipherTestBase.java:1400)
    	at android.keystore.cts.BlockCipherTestBase.assertDoFinalResetsCipher(BlockCipherTestBase.java:594)
    	at android.keystore.cts.BlockCipherTestBase.testDoFinalResets(BlockCipherTestBase.java:563)
    	at android.keystore.cts.AES128CBCNoPaddingCipherTest.testDoFinalResets(AES128CBCNoPaddingCipherTest.java:19)
    	at java.lang.reflect.Method.invoke(Native Method)
    	at junit.framework.TestCase.runTest(TestCase.java:168)
    	at junit.framework.TestCase.runBare(TestCase.java:134)
    	at junit.framework.TestResult$1.protect(TestResult.java:115)
    	at androidx.test.internal.runner.junit3.AndroidTestResult.runProtected(AndroidTestResult.java:73)
    	at junit.framework.TestResult.run(TestResult.java:118)
    	at androidx.test.internal.runner.junit3.AndroidTestResult.run(AndroidTestResult.java:51)
    	at junit.framework.TestCase.run(TestCase.java:124)
    	at androidx.test.internal.runner.junit3.NonLeakyTestSuite$NonLeakyTest.run(NonLeakyTestSuite.java:62)
    	at androidx.test.internal.runner.junit3.AndroidTestSuite$2.run(AndroidTestSuite.java:101)
    	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:462)
    	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    	at java.lang.Thread.run(Thread.java:923)
    Caused by: android.security.KeyStoreException: Keystore consumed 0 of 8 bytes provided.
    	at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.update(KeyStoreCryptoOperationChunkedStreamer.java:143)
    	at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineUpdate(AndroidKeyStoreCipherSpiBase.java:338)
    	at javax.crypto.Cipher.update(Cipher.java:1682)
    	at android.keystore.cts.BlockCipherTestBase.update(BlockCipherTestBase.java:1454)
    	at android.keystore.cts.BlockCipherTestBase.assertDoFinalResetsCipher(BlockCipherTestBase.java:593)
    

    2. 代码流程分析

    下面针对以上报错内容,进行详细的分析,包含涉及到哪些模块,以及是怎么完成这项AES测试的。

    2.1 模块调用关系

    根据前面的内容得知一共有四个角色,其实还应该包含一个角色,就是JAVA应用程序APK,即keystore调用者。
    所以五个角色为如下:
    应用程序APK android.keystore.cts
    Framework Keystore API
    Keystore守护进程
    Keymaster: HIDL进程:android.hardware.keymaster@xxx-xxx
    Keymaster TA程序

    五个角色的调用关系:

    cts apk进程通过API ==> Framework Keystore API
    Framework Keystore API 通过Binder调用 ==> Keystore进程
    Keystore进程通过HIDL ==> Keymaster HAL进程
    Keymaster HAL进程  ==> 调用TEE中的keymaster TA
    

    2.2 代码分析

    顺着cts报错,我们分析一下相关代码。

    报错内容:

    12-29 13:39:04.818  3206  3228 E TestRunner: Caused by: android.security.KeyStoreException: Keystore consumed 0 of 8 bytes provided.
    12-29 13:39:04.818  3206  3228 E TestRunner: 	at android.security.keystore.KeyStoreCryptoOperationChunkedStreamer.update(KeyStoreCryptoOperationChunkedStreamer.java:143)
    12-29 13:39:04.818  3206  3228 E TestRunner: 	at android.security.keystore.AndroidKeyStoreCipherSpiBase.engineUpdate(AndroidKeyStoreCipherSpiBase.java:338)
    12-29 13:39:04.818  3206  3228 E TestRunner: 	at javax.crypto.Cipher.update(Cipher.java:1682)
    12-29 13:39:04.818  3206  3228 E TestRunner: 	at android.keystore.cts.BlockCipherTestBase.update(BlockCipherTestBase.java:1454)
    12-29 13:39:04.818  3206  3228 E TestRunner: 	at android.keystore.cts.BlockCipherTestBase.assertDoFinalResetsCipher(BlockCipherTestBase.java:593)
    
    1. Framework KeyStore API
      BlockCipherTestBase.java的assertDoFinalResetsCipher函数如下:
        private void assertDoFinalResetsCipher(int opmode) throws Exception {
            byte[] input = getKatInput(opmode);
            byte[] expectedOutput = getKatOutput(opmode);
    
            createCipher();
            initKat(opmode);
            assertEquals(expectedOutput, doFinal(input));
    
            if ((opmode == Cipher.ENCRYPT_MODE) && (getKatIv() != null)) {
                // Assert that this cipher cannot be reused (thus making IV reuse harder)
                try {
                    doFinal(input);
                    fail();
                } catch (IllegalStateException expected) {}
                return;
            }
            // Assert that the same output is produced after the above reset
            assertEquals(expectedOutput, doFinal(input));
            assertEquals(expectedOutput, concat(
                    update(subarray(input, 0, getBlockSize() * 3 / 2)),
                    doFinal(subarray(input, getBlockSize() * 3 / 2, input.length))));
            assertEquals(expectedOutput, doFinal(input));
    
            // Assert that the IV with which the cipher was initialized is still there after the resets.
            assertEquals(getKatIv(), mCipher.getIV());
            assertAlgoritmParametersIv(getKatIv());
        }
    

    getBlockSize()返回的是16字节,刚好是AES128的128bit, concat将两个24字节update和final送入,一共48个字节。
    关于AES算法以及工作模式,请参考我写的另一博客AES及其工作模式详解

    如果送入的数据是48字节,刚好是16字节的整数倍,没有问题;
    但如果一次送入的数据是24字节,这样需要分多次送入,第一次先送入16字节,然后第二次送入8字节进行存储,然后再送入8字节,能拼成一个Blocksize 16字节进行处理。
    这项cts测试大概就是这么个目的,想看一下设备中能否处理这种不是16字节整数倍的情况。

    =》调用 mCipher.update函数

        protected byte[] update(byte[] input) {
            byte[] output = mCipher.update(input);
            assertUpdateOutputSize(
                    (input != null) ? input.length : 0, (output != null) ? output.length : 0);
            return output;
        }
    

    内部的调用流程如下:
    =》android.security.keystore.AndroidKeyStoreCipherSpiBase.engineUpdate
    =》KeyStoreCryptoOperationChunkedStreamer.update
    =》内部类MainDataStream的update实现
    =》调用KeyStore.java的update方法
    KeyStore》java的update方法,开始binder调用到守护进程Keystore

        public OperationResult update(IBinder token, KeymasterArguments arguments, byte[] input) {
            OperationPromise promise = new OperationPromise();
            try {
                mBinder.asBinder().linkToDeath(promise, 0);
                int errorCode =  mBinder.update(promise, token, arguments, input);
                if (errorCode == NO_ERROR) {
                    return interruptedPreservingGet(promise.getFuture());
                } else {
                    return new OperationResult(errorCode);
                }
            } catch (RemoteException e) {
    ,,,
        }
    

    到这里的整个过程基本上都是AOSP的流程,所以问题一般不会出现在原生的代码流程中。
    需要继续分析HAL层的调用。

    Keystore服务端返回的流程在key_store_service.cpp中,能看到AIDL相关内容了。
    调用dev->update函数和HAL keymaster进程通讯。

    @system/security/keystore/key_store_service.cpp
    Status KeyStoreService::update(const ::android::sp<IKeystoreOperationResultCallback>& cb,
                                   const ::android::sp<::android::IBinder>& token,
                                   const ::android::security::keymaster::KeymasterArguments& params,
                                   const ::std::vector<uint8_t>& input, int32_t* _aidl_return) {
        ALOGE("key_store_service update entry");
    
        if (!checkAllowedOperationParams(params.getParameters())) {
            return AIDL_RETURN(ErrorCode::INVALID_ARGUMENT);
        }
        auto dev = mKeyStore->getOperationDevice(token);
    
        dev->update(token, params.getParameters(), input, [this, cb, token](OperationResult result_) {
            if (!result_.resultCode.isOk()) {
                mKeyStore->removeOperationDevice(token);
            }
            cb->onFinished(result_);
        });
    	ALOGE("key_store_service update done");
    
        return AIDL_RETURN(ResponseCode::NO_ERROR);
    }
    
    

    因为每家的keymaster方案,基本上都属于定制,为什么呢?因为HAL封装了和keymaster TA交互的接口。所以每家遇到的CTS问题可能都不大相同,需要具体问题具体分析了。
    但各家基本上都会参考GP标准来开发

    CA调用接口
    TEEC_InvokeCommand(&sess, cmd, &op, &err_origin);

    TA调用接口
    TEE_CipherUpdate

    最后这个CTS的修改是在keymaster TA中修复,具体code就不方便贴了,遇到类似问题时请具体看各家的keymaster TA及TEE中对应的libutee中封装的函数实现。

    修复后的复测结果:

    # adb shell am instrument -r -e class  android.keystore.cts.AES128CBCNoPaddingCipherTest#testDoFinalResets  -w android.keystore.cts/androidx.test.runner.AndroidJUnitRunner
    INSTRUMENTATION_STATUS: class=android.keystore.cts.AES128CBCNoPaddingCipherTest
    INSTRUMENTATION_STATUS: current=1
    INSTRUMENTATION_STATUS: id=AndroidJUnitRunner
    INSTRUMENTATION_STATUS: numtests=1
    INSTRUMENTATION_STATUS: stream=
    android.keystore.cts.AES128CBCNoPaddingCipherTest:
    INSTRUMENTATION_STATUS: test=testDoFinalResets
    INSTRUMENTATION_STATUS_CODE: 1
    INSTRUMENTATION_STATUS: class=android.keystore.cts.AES128CBCNoPaddingCipherTest
    INSTRUMENTATION_STATUS: current=1
    INSTRUMENTATION_STATUS: id=AndroidJUnitRunner
    INSTRUMENTATION_STATUS: numtests=1
    INSTRUMENTATION_STATUS: stream=.
    INSTRUMENTATION_STATUS: test=testDoFinalResets
    INSTRUMENTATION_STATUS_CODE: 0
    INSTRUMENTATION_RESULT: stream=
    
    Time: 8.063
    
    OK (1 test)
    
    展开全文
  • Android KeyStore理解及簽名

    千次阅读 2021-05-26 07:06:20
    Android簽名概述我們已經知道的是:Android對每一個Apk文件都會進行簽名,在Apk文件安裝時,系統會對其簽名信息進行比對,判斷程序的完整性,從而決定該Apk文件是否可以安裝,在一定程度上達到安全的目的。...

    Android簽名概述

    我們已經知道的是:Android對每一個Apk文件都會進行簽名,在Apk文件安裝時,系統會對其簽名信息進行比對,判斷程序的完整性,從而決定該Apk文件是否可以安裝,在一定程度上達到安全的目的。

    給定一個Apk文件,解壓,可以看到一個META-INFO文件夾,在該文件夾下有三個文件:分別為MANIFEST.MF、CERT.SF和CERT.RSA。這三個文件分別表征以下含義:

    MANIFEST.MF:這是摘要文件。程序遍歷Apk包中的所有文件(entry),對非文件夾非簽名文件的文件,逐個用SHA1生成摘要信息,再用Base64進行編碼。如果你改變了apk包中的文件,那么在apk安裝校驗時,改變后的文件摘要信息與MANIFEST.MF的檢驗信息不同,於是程序就不能成功安裝。

    說明:如果攻擊者修改了程序的內容,有重新生成了新的摘要,那么就可以通過驗證,所以這是一個非常簡單的驗證。

    CERT.SF:這是對摘要的簽名文件。對前一步生成的MANIFEST.MF,使用SHA1-RSA算法,用開發者的私鑰進行簽名。在安裝時只能使用公鑰才能解密它。解密之后,將它與未加密的摘要信息(即,MANIFEST.MF文件)進行對比,如果相符,則表明內容沒有被異常修改。

    說明:在這一步,即使開發者修改了程序內容,並生成了新的摘要文件,但是攻擊者沒有開發者的私鑰,所以不能生成正確的簽名文件(CERT.SF)。系統在對程序進行驗證的時候,用開發者公鑰對不正確的簽名文件進行解密,得到的結果和摘要文件(MANIFEST.MF)對應不起來,所以不能通過檢驗,不能成功安裝文件。

    CERT.RSA文件中保存了公鑰、所采用的加密算法等信息。 說明:系統對簽名文件進行解密,所需要的公鑰就是從這個文件里取出來的。

    結論:從上面的總結可以看出,META-INFO里面的所有文件環環相扣,從而保證Android程序的安全性。(只是防止開發者的程序不被攻擊者修改,如果開發者的公私鑰被攻擊者得到或者開發者開發出攻擊程序,Android系統都無法檢測出來。)

    1,Android簽名機制其實是對APK包完整性和發布機構唯一性的一種校驗機制。

    2,Android簽名機制不能阻止APK包被修改,但修改后的再簽名無法與原先的簽名保持一致。(擁有私鑰的情況除外)。

    3,APK包加密的公鑰就打包在APK包內,且不同的私鑰對應不同的公鑰。換句話言之,不同的私鑰簽名的APK公鑰也必不相同。所以我們可以根據公鑰的對比,來判斷私鑰是否一致。

    MD5和SHA1

    Android的應用程序發布出來都是需要加密的,否則就會輕易被篡改或者替換,為此才有keystore。網上解釋:是Java的密鑰庫、用來進行通信加密用的、比如數字簽名。keystore就是用來保存密鑰對的,比如公鑰和私鑰。

    也就是說keystore是用來在打包apk的時候對apk程序進行加密處理用的,也就是所謂的數字證書加密。

    我們的IDE中有默認的keystore(eclipse和Android Studio),也就是數字證書,不過這個是我們run as project的時候調試用的,也就是debugkeystore,他這個是自動生成的,cmd模式下,用keytool -list -v -keystore debug.keystore可以看到其中包含的內容。所以,在正式發布apk的時候是不能用這個的,發布時候的keystore需要我們自己去生成,填寫密碼等一些信息生成。

    接下來是sha1 md5,就是把任意長度的信息,通過一些算法,生成固定長度的字符串,只要這些信息有變動,那么生成的sha1,md5就會變化,通過這樣來保護文件沒有被改動過。而且根據生成的字符串是不會推算出來原來的內容的。(sha1,md5生成的字符串長度不一樣)

    那么這樣就會根據keystore生成相應的sha1和md5,保存在manifests.mf中。

    既然apk是通過數字證書來加密的,如果程序要更新的話,怎么來保證更新的程序是對的呢,那么就是通過驗證數字證書是否是相同的,那怎么來保證數字證書是相同的呢,就是通過sha1,md5來保證的。

    查詢應用簽名

    在Eclipse中可以輕而易舉的通過menu下的Window找到MD5和SHA1的值。

    aefa76279f162f59da96db3a676c1da7.png

    而在Android Studio中在Terminal中輸入命令行(與cmd相同)

    首先我們得在系統的環境變量中配置下keytool,keytool.exe是位於我們JDK安裝的bin目錄下。

    然后通過cmd打開控制台,定位到.android文件夾下,Users/…/.android

    輸入keytool -list -v -keystore debug.keystore得到三種指紋證書,選取SHA1類型的證書,密匙口令是android,就可以獲取到MD5和SHA1(注意如果不輸入-v的話就只會出現SHA1的值)。

    還有對META-INF下的CERT.RSA解析的,大同小異,就不一一說明。

    在實際開發中為了避免注冊時弄錯簽名,建議時打包后在獲取apk的簽名。

    公鑰與私鑰

    公鑰和私鑰就是俗稱的不對稱加密方式,是從以前的對稱加密(使用用戶名與密碼)方式的提高。

    利用一對互相匹配的密鑰進行加密、解密。每個用戶自己設定一把特定的僅為本人所知的私有密鑰(私鑰),用它進行解密和簽名;同時 設定一把公共密鑰(公鑰)並由本人公開,為一組用戶所共享,用於加密和驗證簽名。當發送一份保密文件時,發送方使用接收方的公鑰對數據加密,而接收方則使 用自己的私鑰解密,這樣信息就可以安全無誤地到達目的地了。通過數字的手段保證加密過程是一個不可逆過程,即只有用私有密鑰才能解密. 在公開密鑰密碼體制中,常用的一種是RSA體制。

    用戶也可以采用自己的私鑰對信息加以處理,由於密鑰僅為本人所有,這樣就產生了別人無法生成的文件,也就形成了數字簽名。采用數字簽名,能夠確認以下兩點:

    (1)保證信息是由簽名者自己簽名發送的,簽名者不能否認或難以否認;

    (2)保證信息自簽發后到收到為止未曾作過任何修改,簽發的文件是真實文件。

    使用Android Studio簽名

    生成jks簽名文件build->Generate Signed APK...

    選擇創建一個新的簽名文件

    71526afe469c4c6785f4f04a91777678.png

    依步驟下來,可在app目錄下找到簽好名的apk

    也可在Project Stucture圖形化界面中設置每次運行都是簽名的apk。

    選中app這個module,然后切換到singning標簽欄,緊接着點擊添加,然后生成release簽名信息,緊接着點擊”OK”

    353424c0e0e269b040d4eb4e1e48815b.png

    切換到Build Types標簽,將Signing config選擇為”release”,即將剛剛生成的release簽名信息配置進去。

    8d418768f5cd208fee14cb9a035bed36.png

    點擊ok后,每次run project都是簽名的apk。

    操作完成之后,我們還可以看到app這個module的build.gradle文件多出了簽名的配置信息。

    結束語:本文僅用來學習記錄,參考查閱。

    展开全文
  • 国内使用Android Keystore加解密的应该很少吧,搜出来也基本都是Android打包时的Keystore,其实谷歌在很早之前就已经为Android提供了类似IOS的KeyChain功能,私钥存储在trustzone系统中,这个trustzone系统独立于...

    国内使用Android Keystore加解密的应该很少吧,搜出来也基本都是Android打包时的Keystore,其实谷歌在很早之前就已经为Android提供了类似IOS的KeyChain功能,私钥存储在trustzone系统中,这个trustzone系统独立于Android系统,能做到私钥安全。
    具体怎么安全,我们来了解一下加解密与签名的过程,本文不做复杂的深度解析,普通人也完全不需要了解这么透彻,想深入了解的可以google trustzon。
    keystore加解密与签名的安全性其实很好理解,因为不管是应用还是Android系统本身都无法访问私钥,只能选择创建、删除与使用,从根源角度来杜绝私钥泄露的可能。当然因为google设计问题,早期安全性还是有一些问题的,建议直接从android6.0开始使用,android4.4与5.0也能用只是需要使用早期的KeyPairGeneratorSpec来创建,此类在android6.0已经被废弃。
    说了那么多,可能有些人还是一头雾水,那么罗列一下android keystore的用处吧:
    1. 可以安全的加密本地数据,方便想安全存储但是不知道私钥怎么存放的用户(apk不管如何加固都存在被破解风险)
    2. 可以结合指纹进行指纹授权解密
    3. 在区块链行业加解密签名等尤为显著,此功能要比ios的安全区好用很多(据我所知,应用卸载,ios安全区的内容还在,这毫无安全可言)
    在使用过程中,我也遇到了几个问题,这里需要注意一下,不然一不小心就被坑了:
    1. keystore加解密是非线程安全的,所以一定要加锁(被网上的网友坑了一把,使用了别人提供的util工具,简直日了狗,毁人不倦)
    2. 不要轻易删除keystore,否则加密过的数据会面临无法解密的风险
    3. 私钥创建初始化耗时比较久,尽量一个私钥重复使用(此耗时和手机性能几乎无关,速度取决于trustzone的系统)
    好了,到了贴代码时间:
    ```
        private static final String TAG = "EncryptUtil";
        private static EncryptUtil encryptUtilInstance = new EncryptUtil();

        private KeyStore keyStore;

        private Context context;
        // 单位年
        private final int maxExpiredTime = 1000;

        private String x500PrincipalName = "CN=MyKey, O=Android Authority";

        // RSA有加密字符长度限制,所以需要分段加密
        private int rsaEncryptBlock = 244;
        private int rsaDecryptBlock = 256;

        private EncryptUtil() {
        }

        public static EncryptUtil getInstance() {
            return encryptUtilInstance;
        }

        public void init(Context context, String x500PrincipalName) {
            this.context = context;
            this.x500PrincipalName = x500PrincipalName;
        }

        public void initKeyStore(String alias) {
            synchronized (EncryptSafeUtil.class) {
                try {
                    if (null == keyStore) {
                        keyStore = KeyStore.getInstance("AndroidKeyStore");
                        keyStore.load(null);
                    }
                    createNewKeys(alias);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        private void createNewKeys(String alias) {
            if (TextUtils.isEmpty(alias)) {
                return;
            }
            try {
                if (keyStore.containsAlias(alias)) {
                    return;
                }
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                    // Create new key
                    Calendar start = Calendar.getInstance();
                    Calendar end = Calendar.getInstance();
                    end.add(Calendar.YEAR, maxExpiredTime);
                    KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context)
                            .setAlias(alias)
                            .setSubject(new X500Principal(x500PrincipalName))
                            .setSerialNumber(BigInteger.ONE)
                            .setStartDate(start.getTime())
                            .setEndDate(end.getTime())
                            .build();
                    KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
                    generator.initialize(spec);
                    generator.generateKeyPair();
                } else {
                    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
                    keyPairGenerator.initialize(
                            new KeyGenParameterSpec.Builder(
                                    alias,
                                    KeyProperties.PURPOSE_DECRYPT)
                                    .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
                                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
                                    .setUserAuthenticationRequired(false)
                                    .build());
                    keyPairGenerator.generateKeyPair();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public void clearKeystor(String alias) {
            try {
                keyStore = KeyStore.getInstance("AndroidKeyStore");
                keyStore.load(null);
                keyStore.deleteEntry(alias);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * 加密方法
         *
         * @param needEncryptWord  需要加密的字符串
         * @param alias            加密秘钥
         * @return
         */
        public String encryptString(String needEncryptWord, String alias) {
            if (TextUtils.isEmpty(needEncryptWord) || TextUtils.isEmpty(alias)) {
                return "";
            }
            String encryptStr = "";
            synchronized (EncryptSafeUtil.class) {
                initKeyStore(alias);
                try {
                    PublicKey publicKey = keyStore.getCertificate(alias).getPublicKey();
                    Cipher inCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
                    inCipher.init(Cipher.ENCRYPT_MODE, publicKey);

                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    int offSet = 0;
                    int inputLen = needEncryptWord.length();
                    byte[] inputData = needEncryptWord.getBytes();
                    for (int i = 0; inputLen - offSet > 0; offSet = i * rsaEncryptBlock) {
                        byte[] cache;
                        if (inputLen - offSet > rsaEncryptBlock) {
                            cache = inCipher.doFinal(inputData, offSet, rsaEncryptBlock);
                        } else {
                            cache = inCipher.doFinal(inputData, offSet, inputLen - offSet);
                        }
                        out.write(cache, 0, cache.length);
                        ++i;
                    }
                    byte[] encryptedData = out.toByteArray();
                    out.close();

                    encryptStr = Base64.encodeToString(encryptedData, Base64.URL_SAFE);
                } catch (Exception e) {
                    e.printStackTrace();
                    LogUtil.e(TAG, "in encryptString error:" + e.getMessage());
                }
            }
            return encryptStr;
        }


        /**
         * 解密方法
         *
         * @param needDecryptWord 需要解密的字符串
         * @param alias           key的别称
         * @return
         */
        public String decryptString(String needDecryptWord, String alias) {
            if (TextUtils.isEmpty(needDecryptWord) || TextUtils.isEmpty(alias)) {
                return "";
            }
            String decryptStr = "";
            synchronized (EncryptSafeUtil.class) {
                initKeyStore(alias);
                try {
                    PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, null);
                    Cipher outCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
                    outCipher.init(Cipher.DECRYPT_MODE, privateKey);

                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    int offSet = 0;
                    byte[] encryptedData = Base64.decode(needDecryptWord, Base64.URL_SAFE);
                    int inputLen = encryptedData.length;
                    for (int i = 0; inputLen - offSet > 0; offSet = i * rsaDecryptBlock) {
                        byte[] cache;
                        if (inputLen - offSet > rsaDecryptBlock) {
                            cache = outCipher.doFinal(encryptedData, offSet, rsaDecryptBlock);
                        } else {
                            cache = outCipher.doFinal(encryptedData, offSet, inputLen - offSet);
                        }
                        out.write(cache, 0, cache.length);
                        ++i;
                    }
                    byte[] decryptedData = out.toByteArray();
                    out.close();

                    decryptStr = new String(decryptedData, 0, decryptedData.length, "UTF-8");
                } catch (Exception e) {
                    e.printStackTrace();
                    LogUtil.e(TAG, "in decryptString error:" + e.getLocalizedMessage());
                }
            }
            return decryptStr;
        }
    ```
    创建私钥的时候判断了android系统版本,6.0之前使用KeyPairGeneratorSpec创建,6.0以及之后使用KeyGenParameterSpec创建。
    指纹授权解密下篇文章讨论。

    ### Android+GoLang+SprintBoot探讨群:186305789(疯狂的程序员),绝影大神在等你

    展开全文
  • 修改Android keystore 的四条命令,让不习惯Linux命令的小伙伴头痛不已。这份源码,有效提高效率
  • Android KeyStore总结

    千次阅读 2017-06-04 21:04:43
    一、Android KeyStore的应用 1、存储密匙:Android提供的这个KeyStore最大的作用就是不需要开发者去维护这个密匙的存储问题,相比起存储在用户的数据空间或者是外部存储器都更加安全。注意的是这个密匙随着用户清除...
  • 主要介绍了Android使用KeyStore对数据进行加密的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • Android签名文件jks和keystore相互装换的cmd命令 里面包含了文件和简单示例
  • 我正在使用KeyStore保护我的私钥,但是在以下行中:FileOutputStream fos = ctx.openFileOutput("bs.keystore", Context.MODE_PRIVATE);被执行,我有这个异常:'java.lang.NullPointerException'.我不明白问题出在...
  • Android keystore简介、生成、查看

    万次阅读 2018-08-21 14:55:29
    三部分keystore简介、生成方式,查看keystore信息 一、keystore简介 Keytool :是一个有效的安全钥匙和证书的管理工具,Java 中的 keytool.exe (位于 JDK\Bin 目录下)可以用来创建数字证书; keystore:数字...
  • Android keyStore系统存储的RSA密钥,加解密处理 Android有keysrore可以存储密钥,RSA密钥对中,公钥可以取出,私钥不能取出只能使用。本文只看23就是6.0及以上。 生产密钥对 生成密钥的参数。 spec = new ...
  • 一、eclipse 中生成android keystore建立任意一个android项目(例如:AntForAndroid)右键AntForAndroid根目录弹出菜单->Android Tools -> Export Signed Application Package... Next > 选择“Create new ...
  • 破解安卓签名文件,找回keystore密码和别名 给两个积分赞助一下吧 嘻嘻
  • 采用的是RSA加密方式进行签名和验证,同时把密钥放在AndroidKeyStore中,增加安全系数。效果如下: 生成RSA秘钥工具类:KeyStoneUtils package tsou.com.encryption.androidkeystoresign;import android.content....
  • Android Keystore System

    2018-01-04 15:26:21
    google官方文档 描述keystore相关以及指纹和keyguard授权流程
  • AndroidKeyStore的使用

    千次阅读 2016-09-22 17:57:03
    关于对称加密:Google推荐使用使用的GCM模式,但是我司代码中大量使用的CBC模式,两个模式的代码,华为代码进行...方案:直接使用androidstore生成随机数,随机数做密钥1.1 AES_CBC android 实现 函数主体:public c
  • 我想将从给定字节数组生成的密钥导入Android密钥库。但也有例外。我试过下面的代码:try {byte[] keyBytes = new byte {0x31,0x32,0x33};SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA256");...
  • Android keystore的作用以及如何生成

    万次阅读 2017-12-07 09:21:47
    Android keystore的作用以及如何生成keystore的作用 有利于程序升级:当新版程序和旧版程序的数字证书相同时,Android系统才会认为这两个程序是同一个程序的不同版本。如果新版程序和旧版程序的数字证书不相同,则...
  • flutter android keystore

    千次阅读 2019-10-02 18:13:48
    keytool-genkey-v-keystoreE:/key.jks-keyalgRSA-keysize2048-validity10000-aliaskey ...keytool-list-v-keystore E:/key.jks key.jks就是keystore文件 app签名 创建 keystore 如...
  • 引言 拥有特权的恶意软件或者可以实际访问... 通过提供对 AndroidKeystore 的介绍,你将能够理解与密钥存储库相关的常见漏洞。 本文的核心将重点介绍可用于审计应用程序的本地身份验证的开发工具。 最后将提供关...
  • 创建 Android keystore

    2018-05-25 22:23:42
    使用ADT Bundle随便创建一个项目 把空的工程发布成apk会有keystore的选项 是否创建 或使用以前的填写keystone内容这个过程中会创建一个apk和一个keystone
  • 如果你在网上搜debug.keystore的位置,大部分文章都会告诉你在 C:\Users\XXX\.android 目录下,但我电脑的该目录下确实没有这个文件,放入同事的debug.keystore也不会生效。 后来在Android SDK的子目录.android下搜...
  • 1、AndroidKeystore密钥库系统介绍 AndroidKeystore系统是一个密钥库管理系统,谷歌设计这个系统的初衷应该是为了对标苹果的钥匙串KeyChain,有意思的是谷歌在Android4.0(API14)时便引入了KeyChain,但是并未提供...
  • 我想将从给定字节数组生成的密钥导入Android密钥库。但也有例外。我试过下面的代码:try {byte[] keyBytes = new byte {0x31,0x32,0x33};SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA256");...
  • 使用Android Keystore进行加解密

    千次阅读 2019-11-12 20:41:04
    国内使用Android Keystore加解密的应该很少吧,搜出来也基本都是Android打包时的Keystore,其实谷歌在很早之前就已经为Android提供了类似IOS的KeyChain功能,私钥存储在trustzone系统中,这个trustzone系统独立于...
  • 记录使用KeyStore的公钥和私钥进行数据加密,使用SharedPreferences进行存储背景在业务中,我们可能需要面对在 Android 本地存储用户 token、用户信息等敏感数据进行加密。本文将讲述一种安全系数较高的Android本地...
  • 关于android keystore的一些问题

    千次阅读 2017-09-05 17:31:21
    关于android keystore的一些问题 1.keystore 和jks 的简单区别: 在Android Studio中通过生成的是.jks签名文件;而在eclipse时.keystore文件 关于如何生成的步骤请参考该技术文档。 2. Keystore was tampered ...
  • 一、生成keystore 安装java的jdk下载 然后找到安装目录下的keystore.exe cmd 运行到这个bin目录,执行如下命令 keytool -genkey -alias 别名.keystore -keyalg RSA -validity 36500 -keystore 生成的文件名....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 31,434
精华内容 12,573
关键字:

安卓keystore