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…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...