Android KeyStore流程

2023-05-16

文章目录

  • 一、Keystore
  • 二、Keystore架构及接口函数
    • 1. Keystore组件架构
    • 2. IKeymasterDevice.hal中的几个重要接口函数
      • 2.1 begin函数
      • 2.2 update函数
      • 2.3 finish函数
      • 2.4 abort函数
    • 3. Keymaster TA
    • 4. 对称密码函数API
  • 三、从Keystore到Keymaster的完整分析
    • 1. cts问题
    • 2. 代码流程分析
      • 2.1 模块调用关系
      • 2.2 代码分析

一、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)

为方便与大家及时交流,弄了一个微信公众号,欢迎大家留言沟通~
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android KeyStore流程 的相关文章

  • 我用 MediaRecorder 录制的文件无法播放

    我正在使用 MediaRecoder 录制声音 但录制完成后无法播放 我尝试使用Google Play Music ES Media Player 甚至将其上传到电脑并尝试使用Winamp打开它 没什么玩的了 AUDIO RECORDER
  • 如何更改我的应用程序的语言[重复]

    这个问题在这里已经有答案了 可能的重复 在 Android 中以编程方式更改语言 https stackoverflow com questions 2900023 change language programatically in an
  • 如何在 Java 中与 HttpGet 一起发送 cookie

    我试图将 cookie 与 HttpGet 请求一起发送 但每次尝试都无法成功发送 我也尝试直接修改标题 这是我的代码 DefaultHttpClient httpClient new DefaultHttpClient CookieSto
  • Volley Json 请求不起作用 - 字符串无法转换为 JsonObject / JsonArray

    我正在开发一个 Android 应用程序并从服务器获取 JsonObject JsonArray 手动将 String 转换为 Json 可以正常工作 我最近切换到 Volley 来处理服务器请求 并且想使用JsonObjectReques
  • Android 应用内购买

    我正在尝试开发一个停车应用程序 用户可以在其中为停车时间付费 我浏览了这份文件应用内产品 http developer android com google play billing billing overview html produc
  • 选项 多个活动通用的菜单项

    安卓系统设计指南 http developer android com design patterns help html表示 帮助 应始终放置为溢出菜单的最后一项 它不应该出现在 ActionBar 中 而且 它应该出现在每个活动中 以便
  • 如何在不使用adb shell的情况下获取android应用程序的pid?

    如何在不使用 adb shell 的情况下获取 Android 应用程序 pid 有没有API可以获取pid 任何帮助将不胜感激 由于每个应用程序都有自己的进程ID 因此可以通过 int pid android os Process myP
  • ScrollView 中的 ViewPager 不会垂直滚动

    我有一个layout有一个ViewPager自定义内部ScrollView并且 ViewPager 不会垂直滚动 自定义 ScrollView 用于修复使用 ScrollView 进行可怕的选项卡滑动的问题 是的 有足够的内容可以滚动 我已
  • Robolectric 3.0 不适用于 AppCompat 21+

    升级到 AppCompat 21 后 我们的许多 Robolectric 测试都失败了 Toolbar 和 AppCompatDelegate 似乎存在问题 我尝试了 support v4 appcompat 22 2 1 和 appcom
  • 在 Android 媒体播放器上播放 MediaStore 中的音频

    有没有办法通过使用 MediaPLayer 播放从 MediaStore 获得的音频 或者我走的方向完全错误 到目前为止 我已经查看了 MediaStore Audio 但没有什么能真正帮助我 我只需要知道我是否走在正确的轨道上 首先 我假
  • 方向改变后的javascript最大视口高度Android和iOS

    目标 查找设备的最大视口高度 包括设备的空间address bar这样我们就可以动态调整 min body 的大小并将内容向上推 问题 移动浏览器处理方向状态的方式不同 方向变化时更新 DOM 属性的方式也不同 使用 JavaScript
  • Android 背景 + 文本 + 按钮图标

    我想要一个图像设置为文本的背景 并在文本的左侧设置一个图标 在iPhone中非常简单 但不知道如何在Android上做到这一点 调整按钮的大小并保持图标 文本的位置和距离正确 iPhone 安卓我有这个 xml代码是
  • Android 应用程序阿拉伯语支持

    我已经按照developer android官方网站上的教程Hello L10进行操作 但没有任何关于阿拉伯语的内容 Android 应用程序要使用阿拉伯语需要执行哪些步骤 例如 用户可以使用组合框或单选按钮来选择英语或阿拉伯语 我已经这样
  • 用dagger 2查看依赖注入

    我有一个自定义视图扩展TextView 我应该在哪里调用我的组件来注入视图 component inject customTextView 因此 我发现我需要在自定义视图的构造函数中添加注入 在所有视图中 或者使一个调用另一个 Exampl
  • NestedScrollView 中带有 RecyclerView 的 ItemTouchHelper:拖动滚动不起作用

    我已经实现了 ItemTouchHelper 如本文所述 https medium com ipaulpro drag and swipe with recyclerview b9456d2b1aaf k7xm7amxi https med
  • 横向模式下视频视图不是全屏

    我正在使用用 xml 设计的视频视图 该视频在纵向模式下为全屏 但当切换到横向模式时 它会左对齐 并且宽度和高度都会换行 而不是全屏 我参考了这些 但仍然没有解决这个问题 全屏视频视图未居中 https stackoverflow com
  • 如何等待 Kotlin 协程完成

    我读过几十篇文章 但不知何故 没有一个答案似乎适用于我的情况 我想要实现的是在Fragment中等待ViewModel使用Room执行操作 Dao Query SELECT FROM my table WHERE id id suspend
  • 如何从下到上连续移动图像?

    我一直在研究这个例子http obviam net index php a very basic the game loop for android http obviam net index php a very basic the ga
  • Android Mediaplayer:下载媒体文件的 setDataSource 问题

    我有一个可以录制和播放音频文件的应用程序 一些音频文件是使用 httpclient 使用简单的标准 http 下载来下载的 很长一段时间以来 它就像一种魅力 现在我突然无法播放我下载的文件 该堆栈失败 我将文件存储在 SDCard 上 并且
  • 了解 FTS3/FTS4:什么是虚拟表并从中搜索具有可搜索的非虚拟表?

    阅读 SQLite3 的 FTS3 FTS4 文档的第一部分后 我现在感到非常困惑 我感到困惑的原因是散布在网络上的示例 我相信它没有涵盖所有可能的用例 另一个原因是我目前所处的情况 话虽如此 我有一个名为 Note 的表 其中包含两个类型

随机推荐

  • Android相机,图库获取图片

    大家的APP中经常会有从手机相机 图库获取图片 xff0c 这里封装了方法可以方便大家 xff0c 解决了Android7 0资源uri的获取方式 xff0c 兼容SDK19以上的机子 xff0c 有权限请求整合 xff0c 欢迎大家下架使
  • 《Streaming System》流式系统-序章

    本文翻译摘抄自 Streaming System xff0c 在阅读的时候进行翻译 xff0c 同时方便广大同学 xff0c 如有错误或侵权 xff0c 烦请指出 偶尔有机会搜到了这本书籍 xff0c 还看到知乎有有问小伙伴对此书佩服的五体
  • 《Streaming System》 第二章:数据处理的四要素 What Where When and How

    本文由 Streaming System 一书第二章的提炼翻译而来 xff0c 译者才疏学浅 xff0c 如有错误 xff0c 欢迎指正 转载请注明出处 xff0c 侵权必究 本章主要介绍鲁棒的处理乱序数据的核心概念 xff0c 这些概念的
  • 《Streaming System》 第三章:Watermarks

    简介 本章主要介绍鲁棒的处理乱序数据的核心概念 xff0c 这些概念的运用使流处理系统超越批处理系统的关键所在 本章我们从流计算系统的底层机制深入来探讨一下watermark 学习这些机制有助于我们更好理解和使用watermark 我们将讨
  • Centos 7 开放查看端口 防火墙关闭打开

    Centos 7 firewall 命令 xff1a 查看已经开放的端口 xff1a firewall span class hljs attribute cmd span span class hljs subst span span c
  • linux CentOS 安装rz和sz命令 lrzsz

    lrzsz在linux里可代替ftp上传和下载 lrzsz 官网入口 xff1a http freecode com projects lrzsz lrzsz是一个unix通信套件提供的X xff0c Y xff0c 和ZModem文件传输
  • (原创)Flutter开发问题:项目启动一直卡在Running Gradle task ‘assembleDebug‘

    问题描述 按照flutter官网步骤安装Flutter SDK Android studio等 xff0c 在创建第一个flutter项目后run的过程一直是Running Gradle task assembleDebug 针对这个问题
  • Redis 分页排序查询

    Redis是一个高效的内存数据库 xff0c 它支持包括String List Set SortedSet和Hash等数据类型的存储 xff0c 在Redis中通常根据数据的key查询其value值 xff0c Redis没有条件查询 xf
  • 使用Spring实现读写分离( MySQL实现主从复制)

    1 背景 我们一般应用对数据库而言都是 读多写少 xff0c 也就说对数据库读取数据的压力比较大 xff0c 有一个思路就是说采用数据库集群的方案 xff0c 其中一个是主库 xff0c 负责写入数据 xff0c 我们称之为 xff1a 写
  • 解决 Unable to load native-hadoop library for your platform

    安装hadoop启动之后总有警告 xff1a Unable to load native hadoop library for your platform using builtin java classes where applicabl
  • [音乐] 随遇而安

    黄霑真的很适合唱这种充满了江湖气息的歌 xff0c 这首歌的经典程度不亚于 沧海一声笑 有兴趣的自己搜来听听吧 人外有人山外有山 不怕拼命怕平凡 有得有失有欠有还 老天不许人太贪 挺起胸膛咬紧牙关 生死容易低头难 就算当不成英雄 也要是一条
  • Request Body数据读取

    拦截器要读取request body数据的话需要注意一个问题 xff0c 一旦拦截器把数据流从request读取出来后 xff0c 后区的接口层就拿不到数据了 xff0c 因为流是一次性的 xff0c 那么要解决这个问题 xff0c 我们就
  • scikit-learn常用的用法及问题

    对平时用机器学习算法常遇到的问题做个总结 xff5e A 交叉验证 交叉验证是为了评估当前的模型对于整个dataset的generalization error怎么样 xff08 如果太大 xff0c 表示overfit或者underfit
  • ubuntu开启SSH服务远程登录

    ssh secure shell xff0c 提供安全的远程登录 从事嵌入式开发搭建linux开发环境中 xff0c ssh的服务的安装是其中必不可少的一步 ssh方便一个开发小组中人员登录一台服务器 xff0c 从事代码的编写 编译 运行
  • ubuntu环境下增加-pie选项导致可执行程序无法通过双击启动的问题

    在ubuntu环境下 xff0c 链接可执行文件时增加 pie选项 xff0c 双击可执行程序 xff0c 无法正常启动 对于这个现象 xff0c stackoverflow有个帖子 xff0c gcc creates mime type
  • ERROR 1419 (HY000) at line 9: You do not have the SUPER privilege and binary logging is enabled (you...

    报错原因 在将函数或触发器导入MySQL数据库时 xff0c 会出现以下错误 xff1a 您没有SUPER特权 xff0c 并且启用了二进制日志记录 您 可能 想要使用不太安全的log bin trust function creators
  • Ubuntu修改时区和设置24小时时间格式

    Ubuntu 安装之后系统默认是世界标准时间 xff0c UTC时间 xff0c 东八区会晚8小时 xff0c 另外 xff0c 系统的时间是12小时 xff0c date命令查看时间 xff0c 上午显示AM xff0c 下午显示PM 修
  • (原创)Flutter基础入门:手把手教你搭建Flutter混合项目:模块代码依赖方式集成

    前言 Flutter是Google开源的构建用户界面 xff08 UI xff09 工具包 支持在不同平台构建一致的ui效果 但在实际业务中 xff0c 一般不会整个APP都用纯Flutter开发 尤其一些老的项目 xff0c 会采用接入F
  • 二、zabbix基础3-通知(告警)

    文章目录 3 通知 xff08 告警 xff09 3 1 什么是事件通知3 2 如何实现事件通知3 3 配置邮件通知方式3 3 1 定义媒介 xff08 发件人 xff09 3 3 2 配置动作 xff08 发送给谁 xff09 3 3 3
  • Android KeyStore流程

    文章目录 一 Keystore二 Keystore架构及接口函数1 Keystore组件架构2 IKeymasterDevice hal中的几个重要接口函数2 1 begin函数2 2 update函数2 3 finish函数2 4 abo