Android KeyStore 秘钥导入
源码参考:
https://android.googlesource.com/platform/cts/+/master/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java
辅助源码参考:
https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/security/keymaster/KeymasterDefs.java
https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/main/security/keymint/aidl/aidl_api/android.hardware.security.keymint/1/android/hardware/security/keymint/
源代码测试如下:
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.security.keystore.SecureKeyImportUnavailableException;
import android.security.keystore.WrappedKeyEntry;
import android.util.Log;import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.DEREncodableVector;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERNull;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERSet;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.MGF1ParameterSpec;import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;public class TestWrapKey {private static final String ALIAS = "my key";private static final String WRAPPING_KEY_ALIAS = "my_favorite_wrapping_key";public static final String TAG = "TestWrapKey";public static final int KM_TAG_PURPOSE = 536870913;public static final int KM_TAG_ALGORITHM = 268435458;public static final int KM_TAG_KEY_SIZE = 805306371;public static final int KM_TAG_DIGEST = 536870917;public static final int KM_TAG_NO_AUTH_REQUIRED = 1879048695;public static final int KM_PURPOSE_SIGN = 2;public static final int KM_PURPOSE_VERIFY = 3;public static final int KM_DIGEST_SHA_2_224 = 3;public static final int KM_DIGEST_SHA_2_256 = 4;public static final int KM_DIGEST_SHA_2_384 = 5;public static final int KM_DIGEST_SHA_2_512 = 6;public static final int KM_KEY_FORMAT_RAW = 3;public static final int KM_PURPOSE_ENCRYPT = 0;public static final int KM_PURPOSE_DECRYPT = 1;public static final int KM_MODE_ECB = 1;public static final int KM_MODE_CBC = 2;public static final int KM_TAG_BLOCK_MODE = 536870916;public static final int KM_PAD_PKCS7 = 64;public static final int KM_PAD_NONE = 1;public static final int KM_TAG_PADDING = 536870918;public static final int KM_ALGORITHM_AES = 32;private static final int GCM_TAG_SIZE = 128;private static final int WRAPPED_FORMAT_VERSION = 0;public static final int KM_KEY_FORMAT_PKCS8 = 1;public static final int KM_ALGORITHM_RSA = 1;public static final int KM_PAD_RSA_OAEP = 2;public static final int KM_PAD_RSA_PSS = 3;public static final int KM_PAD_RSA_PKCS1_1_5_ENCRYPT = 4;public static final int KM_PAD_RSA_PKCS1_1_5_SIGN = 5;public static final int KM_DIGEST_NONE = 0;public static final int KM_DIGEST_MD5 = 1;public static final int KM_DIGEST_SHA1 = 2;SecureRandom random = new SecureRandom();private void showMessage(String message) {Log.d(TAG, message);}// 构建秘钥对private KeyPair genKeyPair(String alias, boolean isStrongBoxBacked) throws Exception {KeyPairGenerator kpg =KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");kpg.initialize(new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_WRAP_KEY).setDigests(KeyProperties.DIGEST_SHA256).setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP).setBlockModes(KeyProperties.BLOCK_MODE_ECB).setIsStrongBoxBacked(isStrongBoxBacked).build());return kpg.generateKeyPair();}private int removeTagType(int tag) {int kmTagTypeMask = 0x0FFFFFFF;return tag & kmTagTypeMask;}private DERSequence makeRsaAuthList(int size) {DEREncodableVector allPurposes = new DEREncodableVector();allPurposes.add(new DERInteger(KM_PURPOSE_ENCRYPT));allPurposes.add(new DERInteger(KM_PURPOSE_DECRYPT));allPurposes.add(new DERInteger(KM_PURPOSE_SIGN));allPurposes.add(new DERInteger(KM_PURPOSE_VERIFY));DERSet purposeSet = new DERSet(allPurposes);DERTaggedObject purpose =new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet);DERTaggedObject algorithm =new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM),new DERInteger(KM_ALGORITHM_RSA));DERTaggedObject keySize =new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new DERInteger(size));DEREncodableVector allBlockModes = new DEREncodableVector();allBlockModes.add(new DERInteger(KM_MODE_ECB));allBlockModes.add(new DERInteger(KM_MODE_CBC));DERSet blockModeSet = new DERSet(allBlockModes);DERTaggedObject blockMode =new DERTaggedObject(true, removeTagType(KM_TAG_BLOCK_MODE), blockModeSet);DEREncodableVector allDigests = new DEREncodableVector();allDigests.add(new DERInteger(KM_DIGEST_NONE));allDigests.add(new DERInteger(KM_DIGEST_MD5));allDigests.add(new DERInteger(KM_DIGEST_SHA1));allDigests.add(new DERInteger(KM_DIGEST_SHA_2_224));allDigests.add(new DERInteger(KM_DIGEST_SHA_2_256));allDigests.add(new DERInteger(KM_DIGEST_SHA_2_384));allDigests.add(new DERInteger(KM_DIGEST_SHA_2_512));DERSet digestSet = new DERSet(allDigests);DERTaggedObject digest =new DERTaggedObject(true, removeTagType(KM_TAG_DIGEST), digestSet);DEREncodableVector allPaddings = new DEREncodableVector();allPaddings.add(new DERInteger(KM_PAD_PKCS7));allPaddings.add(new DERInteger(KM_PAD_NONE));allPaddings.add(new DERInteger(KM_PAD_RSA_OAEP));allPaddings.add(new DERInteger(KM_PAD_RSA_PSS));allPaddings.add(new DERInteger(KM_PAD_RSA_PKCS1_1_5_ENCRYPT));allPaddings.add(new DERInteger(KM_PAD_RSA_PKCS1_1_5_SIGN));DERSet paddingSet = new DERSet(allPaddings);DERTaggedObject padding =new DERTaggedObject(true, removeTagType(KM_TAG_PADDING), paddingSet);DERTaggedObject noAuthRequired =new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE);// Build sequenceDEREncodableVector allItems = new DEREncodableVector();allItems.add(purpose);allItems.add(algorithm);allItems.add(keySize);allItems.add(blockMode);allItems.add(digest);allItems.add(padding);allItems.add(noAuthRequired);return new DERSequence(allItems);}private DERSequence makeAesAuthList(int size) {return makeSymKeyAuthList(size, KM_ALGORITHM_AES);}private DERSequence makeSymKeyAuthList(int size, int algo) {DEREncodableVector allPurposes = new DEREncodableVector();allPurposes.add(new DERInteger(KM_PURPOSE_ENCRYPT));allPurposes.add(new DERInteger(KM_PURPOSE_DECRYPT));DERSet purposeSet = new DERSet(allPurposes);DERTaggedObject purpose =new DERTaggedObject(true, removeTagType(KM_TAG_PURPOSE), purposeSet);DERTaggedObject algorithm =new DERTaggedObject(true, removeTagType(KM_TAG_ALGORITHM), new DERInteger(algo));DERTaggedObject keySize =new DERTaggedObject(true, removeTagType(KM_TAG_KEY_SIZE), new DERInteger(size));DEREncodableVector allBlockModes = new DEREncodableVector();allBlockModes.add(new DERInteger(KM_MODE_ECB));allBlockModes.add(new DERInteger(KM_MODE_CBC));DERSet blockModeSet = new DERSet(allBlockModes);DERTaggedObject blockMode =new DERTaggedObject(true, removeTagType(KM_TAG_BLOCK_MODE), blockModeSet);DEREncodableVector allPaddings = new DEREncodableVector();allPaddings.add(new DERInteger(KM_PAD_PKCS7));allPaddings.add(new DERInteger(KM_PAD_NONE));DERSet paddingSet = new DERSet(allPaddings);DERTaggedObject padding =new DERTaggedObject(true, removeTagType(KM_TAG_PADDING), paddingSet);DERTaggedObject noAuthRequired =new DERTaggedObject(true, removeTagType(KM_TAG_NO_AUTH_REQUIRED), DERNull.INSTANCE);// Build sequenceDEREncodableVector allItems = new DEREncodableVector();allItems.add(purpose);allItems.add(algorithm);allItems.add(keySize);allItems.add(blockMode);allItems.add(padding);allItems.add(noAuthRequired);return new DERSequence(allItems);}public byte[] wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask,int keyFormat, DERSequence authorizationList) throws Exception {return wrapKey(publicKey, keyMaterial, mask, keyFormat, authorizationList, true);}public byte[] wrapKey(PublicKey publicKey, byte[] keyMaterial, byte[] mask,int keyFormat, DERSequence authorizationList, boolean correctWrappingRequired)throws Exception {// Build descriptionDEREncodableVector descriptionItems = new DEREncodableVector();descriptionItems.add(new DERInteger(keyFormat));descriptionItems.add(authorizationList);DERSequence wrappedKeyDescription = new DERSequence(descriptionItems);// Generate 12 byte initialization vectorbyte[] iv = new byte[12];random.nextBytes(iv);// Generate 256 bit AES key. This is the ephemeral key used to encrypt the secure key.byte[] aesKeyBytes = new byte[32];random.nextBytes(aesKeyBytes);// Encrypt ephemeral keysOAEPParameterSpec spec = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);Cipher pkCipher = Cipher.getInstance("RSA/ECB/OAEPPadding");if (correctWrappingRequired) {pkCipher.init(Cipher.ENCRYPT_MODE, publicKey, spec);} else {// Use incorrect OAEPParameters while initializing cipher. By default, main digest and// MGF1 digest are SHA-1 here.pkCipher.init(Cipher.ENCRYPT_MODE, publicKey);}byte[] encryptedEphemeralKeys = pkCipher.doFinal(aesKeyBytes);// Encrypt secure keyCipher cipher = Cipher.getInstance("AES/GCM/NoPadding");SecretKeySpec secretKeySpec = new SecretKeySpec(aesKeyBytes, "AES");GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(GCM_TAG_SIZE, iv);cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, gcmParameterSpec);byte[] aad = wrappedKeyDescription.getEncoded();cipher.updateAAD(aad);byte[] encryptedSecureKey = cipher.doFinal(keyMaterial);// Get GCM tag. Java puts the tag at the end of the ciphertext data :(int len = encryptedSecureKey.length;int tagSize = (GCM_TAG_SIZE / 8);byte[] tag = Arrays.copyOfRange(encryptedSecureKey, len - tagSize, len);// Remove GCM tag from end of outputencryptedSecureKey = Arrays.copyOfRange(encryptedSecureKey, 0, len - tagSize);// Build ASN.1 DER encoded sequence WrappedKeyWrapperDEREncodableVector items = new DEREncodableVector();items.add(new DERInteger(WRAPPED_FORMAT_VERSION));items.add(new DEROctetString(encryptedEphemeralKeys));items.add(new DEROctetString(iv));items.add(wrappedKeyDescription);items.add(new DEROctetString(encryptedSecureKey));items.add(new DEROctetString(tag));return new DERSequence(items).getEncoded(ASN1Encoding.DER);}public void importWrappedKey(byte[] wrappedKey, String wrappingKeyAlias) throws Exception {KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");keyStore.load(null, null);AlgorithmParameterSpec spec = new KeyGenParameterSpec.Builder(wrappingKeyAlias,KeyProperties.PURPOSE_WRAP_KEY).setDigests(KeyProperties.DIGEST_SHA256).build();KeyStore.Entry wrappedKeyEntry = new WrappedKeyEntry(wrappedKey, wrappingKeyAlias,"RSA/ECB/OAEPPadding", spec);keyStore.setEntry(ALIAS, wrappedKeyEntry, null);}public void importWrappedKey(byte[] wrappedKey) throws Exception {importWrappedKey(wrappedKey, WRAPPING_KEY_ALIAS);}public void testRSA() throws Exception{KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");// Both TEE and Strongbox must support 2048-bit keys.int keySize = 2048;kpg.initialize(keySize);KeyPair kp = kpg.generateKeyPair();PublicKey publicKey = kp.getPublic();showMessage("publicKey:"+Hex.toHexString(publicKey.getEncoded()));PrivateKey privateKey = kp.getPrivate();showMessage("privateKey:"+Hex.toHexString(privateKey.getEncoded()));byte[] keyMaterial = privateKey.getEncoded();byte[] mask = new byte[32]; // Zero masktry {importWrappedKey(wrapKey(genKeyPair(WRAPPING_KEY_ALIAS, false).getPublic(),keyMaterial,mask,KM_KEY_FORMAT_PKCS8,makeRsaAuthList(keySize)));} catch (SecureKeyImportUnavailableException e) {e.printStackTrace();}// Use KeyKeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");keyStore.load(null, null);if(!keyStore.containsAlias(ALIAS)){showMessage("fail to import RSA key");return;}String plaintext = "hello, world";Key importedKey = keyStore.getKey(ALIAS, null);// Encrypt with KS private key, then decrypt with local public key.Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding");c.init(Cipher.ENCRYPT_MODE, importedKey);byte[] encrypted = c.doFinal(plaintext.getBytes());c.init(Cipher.DECRYPT_MODE, publicKey);showMessage("is same:"+(new String(c.doFinal(encrypted))).equals(plaintext));// Encrypt with local public key, then decrypt with KS private key.c.init(Cipher.ENCRYPT_MODE, publicKey);encrypted = c.doFinal(plaintext.getBytes());c.init(Cipher.DECRYPT_MODE, importedKey);showMessage("is same:"+(new String(c.doFinal(encrypted))).equals(plaintext));// Sign with KS private key, then verify with local public key.Signature s = Signature.getInstance("SHA256withRSA");s.initSign((PrivateKey) importedKey);s.update(plaintext.getBytes());byte[] signature = s.sign();s.initVerify(publicKey);s.update(plaintext.getBytes());showMessage("result:"+(s.verify(signature)));}public void testAES() throws Exception {// 构建原始的AES秘钥KeyGenerator kg = KeyGenerator.getInstance("AES");kg.init(256);Key swKey = kg.generateKey();byte[] keyMaterial = swKey.getEncoded();showMessage("keyMaterialHex:" + Hex.toHexString(keyMaterial));// 构建包装的RSA秘钥KeyPair keyPair = genKeyPair(WRAPPING_KEY_ALIAS, true);PublicKey aPublic = keyPair.getPublic();byte[] aPublicBytes = aPublic.getEncoded();showMessage("aPublic:" + Hex.toHexString(aPublicBytes));byte[] mask = new byte[32]; // Zero maskDERSequence asn1Encodables = makeAesAuthList(keyMaterial.length * 8);byte[] bytes = wrapKey(aPublic, keyMaterial, mask, KM_KEY_FORMAT_RAW, asn1Encodables);// 导入importWrappedKey(bytes);// Use KeyKeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");keyStore.load(null, null);if(!keyStore.containsAlias(ALIAS)){showMessage("fail to import AES key");return;}Key importedKey = keyStore.getKey(ALIAS, null);String plaintext = "hello, world";Cipher c = Cipher.getInstance("AES/ECB/PKCS7Padding");c.init(Cipher.ENCRYPT_MODE, importedKey);byte[] encrypted = c.doFinal(plaintext.getBytes());// Decrypt using key imported into keystore.c = Cipher.getInstance("AES/ECB/PKCS7Padding");c.init(Cipher.DECRYPT_MODE, importedKey);showMessage("is same:"+(new String(c.doFinal(encrypted)).equals(plaintext)));// Decrypt using local software copy of the key.c = Cipher.getInstance("AES/ECB/PKCS7Padding");c.init(Cipher.DECRYPT_MODE, swKey);showMessage("is same:"+(new String(c.doFinal(encrypted)).equals(plaintext)));}
}
需要依赖的包:
implementation files('libs/bcpkix-jdk15to18-1.64.jar')
implementation files('libs/bcprov-jdk15to18-1.64.jar')
相关文章:
Android KeyStore 秘钥导入
源码参考: https://android.googlesource.com/platform/cts//master/tests/tests/keystore/src/android/keystore/cts/ImportWrappedKeyTest.java 辅助源码参考: https://android.googlesource.com/platform/frameworks/base//master/core/java/android…...
TDengine+OpenVINO+AIxBoard,助力时序数据分类
时间序列数据分析在工业,能源,医疗,交通,金融,零售等多个领域都有广泛应用。其中时间序列数据分类是分析时序数据的常见任务之一。本文将通过一个具体的案例,介绍 Intel 团队如何使用 TDengine 作为基础软件…...
设计模式——16. 迭代器模式
1. 说明 迭代器模式(Iterator Pattern)是一种行为型设计模式,它用于提供一种访问聚合对象(如列表、数组、集合等)元素的统一接口,而不需要了解底层数据结构的具体实现。迭代器模式将遍历聚合对象的操作封装在一个独立的迭代器对象中,这样可以隔离遍历算法和数据结构,使…...
flink redis connector需要防止包冲突
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 <dependency><groupId>org.apache.bahir</groupId><artifactId...
socket can查看详细信息 命令 ip -details -statistics link show can0
ip -details -statistics link show can0 ip -details link show can0 ip -statistics link show can0 也可以像第一行那样结合使用...
打造虚拟企业展厅,开启商务活动新时代
引言: 虚拟企业展厅是一种基于数字技术的全新商务模式,正在改变传统商务活动的方式,它比传统的企业展厅更便利,也更能凸显企业优势,展示企业风貌。 一.虚拟企业展厅的好处 1.打破地域限制 传统的商务活动通常需要参…...
03黑马店评-添加商户缓存和商户类型的缓存到Redis
商户查询缓存 什么是缓存 实际开发过程中数据量可以达到几千万,缓存可以作为避震器防止过高的数据访问猛冲系统,避免系统内的操作线程无法及时处理信息而瘫痪 缓存(Cache)就是数据交换的缓冲区(储存临时数据的地方),我们俗称的"缓存"实际就是缓冲区内的数据(一般从…...
LabVIEW玩转魔方
LabVIEW玩转魔方 使用LabVIEW创建一个3D魔方,并找出解谜题的秘密,给朋友留下深刻深刻的印象。游戏中内置的机制使每张脸都能独立转动,从而混合颜色。要解决难题,每个面必须是相同的纯色 魔方的奥秘在于它的简单性和不可解性。这是…...
大数据学习(1)-Hadoop
&&大数据学习&& 🔥系列专栏: 👑哲学语录: 承认自己的无知,乃是开启智慧的大门 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一下博>主哦&#x…...
常用时序模型
常用时序模型 RNN (Recurrent Neural Network): 基本概念: RNN是一种可以处理序列数据的神经网络。它在每一时间步都接收一个新的输入,并将前一个时间步的隐藏状态作为额外的输入。问题: 它的主要问题是在处理长序列时遇到的梯度消失和梯度爆炸。这使得RNN难以捕获长期依赖关…...
阿里云/腾讯云国际站:私服服务器:什么是游戏虚拟服务器及用途讲解?
游戏虚拟服务器是一种新兴的技术,它可以为玩家提供更好的游戏体验。私服服务器它可以将游戏服务器的负载分散到多台服务器上,从而提高游戏的流畅度和稳定性。此外,游戏虚拟服务器还可以提供更多的游戏功能,比如游戏聊天室、游戏排…...
ssti 前置学习
python venv环境 可以把它想象成一个容器,该容器供你用来存放你的Python脚本以及安装各种Python第三方模块,容器里的环境和本机是完全分开的 创建venv环境安装flask #apt install python3.10-venv #cd /opt #python3 -m venv flask1 #cd /opt 选…...
uni-app:服务器端数据绘制echarts图标(renderjs解决手机端无法显示问题)
效果 代码 <template><view click"echarts.onClick" :prop"option" :change:prop"echarts.updateEcharts" id"echarts" class"echarts"></view> </template><script>export default {data()…...
Python集合魔法:解锁数据去重技巧
更多资料获取 📚 个人网站:涛哥聊Python 在Python编程的魔法世界中,有一种数据类型几乎被忽视,但却拥有强大的超能力,那就是集合(Set)。 集合是一种无序、唯一的数据类型,它以其独…...
flutter开发实战-inappwebview实现flutter与Javascript的交互JSBridge
flutter开发实战-inappwebview实现flutter与Javascript的交互JSBridge 在使用webview中,需要实现flutter与Javascript交互,在使用webview_flutter插件的时候,整理了一下webview与Javascript的交互JSBridge,具体可以查看 https:/…...
私有云盘:lamp部署nextcloud+高可用集群
目录 一、实验准备: 二、配置mariadb主从复制 三台主机下载mariadb 1)主的操作 2)从的操作 3)测试数据是否同步 三、配置nfs让web服务挂载 1、安装 2、配置nfs服务器 3、配置web服务的httpd 4、测试 四、web 服务器 配…...
在线制作课程表
失业在家,开启一天一个应用的创作节奏,最近学了uniapp,特别想做点啥,正好家里小孩子要打印课程表,而且课程表还有调课的需求,就寻思做一个方便大家,到目前位置服务完全免费的,新鲜上…...
聊聊分布式架构06——[NIO入门]简单的Netty NIO示例
目录 Java NIO和Netty NIO比较 Java NIO: Netty: Netty NIO中的主要模块 Transport(传输层) Buffer(缓冲区) Codec(编解码器) Handler(处理器) Even…...
H5逆向之远程RPC
引言前一讲说过H5 怎么去抓包,逆向分析。其中说到RPC。这一节详细讲一下。有一种情况,JS 比较复杂,混淆的厉害。 这个时候就用到RPC。原理就是,hook web 浏览器,直接调用js 里边的方法。 Node 服务。为什么用到Node 服务,先来看下这架构 Node 对外提供各种接口,外部可以…...
解决Ubuntu18.04安装好搜狗输入法后无法打出中文的问题
首先下载安装 搜狗拼音输入法 ,下载选择: x86_64 在ubuntu中设置 fcitx 最后发现安装好了,图标有了 ,但是使用时不能输入中文,使用下面的命令解决: sudo apt install libqt5qml5 libqt5quick5 libqt5qu…...
css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...
