当前位置: 首页 > news >正文

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 也可以像第一行那样结合使用...

打造虚拟企业展厅,开启商务活动新时代

引言: 虚拟企业展厅是一种基于数字技术的全新商务模式&#xff0c;正在改变传统商务活动的方式&#xff0c;它比传统的企业展厅更便利&#xff0c;也更能凸显企业优势&#xff0c;展示企业风貌。 一&#xff0e;虚拟企业展厅的好处 1.打破地域限制 传统的商务活动通常需要参…...

03黑马店评-添加商户缓存和商户类型的缓存到Redis

商户查询缓存 什么是缓存 实际开发过程中数据量可以达到几千万,缓存可以作为避震器防止过高的数据访问猛冲系统,避免系统内的操作线程无法及时处理信息而瘫痪 缓存(Cache)就是数据交换的缓冲区(储存临时数据的地方),我们俗称的"缓存"实际就是缓冲区内的数据(一般从…...

LabVIEW玩转魔方

LabVIEW玩转魔方 使用LabVIEW创建一个3D魔方&#xff0c;并找出解谜题的秘密&#xff0c;给朋友留下深刻深刻的印象。游戏中内置的机制使每张脸都能独立转动&#xff0c;从而混合颜色。要解决难题&#xff0c;每个面必须是相同的纯色 魔方的奥秘在于它的简单性和不可解性。这是…...

大数据学习(1)-Hadoop

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 承认自己的无知&#xff0c;乃是开启智慧的大门 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博>主哦&#x…...

常用时序模型

常用时序模型 RNN (Recurrent Neural Network): 基本概念: RNN是一种可以处理序列数据的神经网络。它在每一时间步都接收一个新的输入,并将前一个时间步的隐藏状态作为额外的输入。问题: 它的主要问题是在处理长序列时遇到的梯度消失和梯度爆炸。这使得RNN难以捕获长期依赖关…...

阿里云/腾讯云国际站:私服服务器:什么是游戏虚拟服务器及用途讲解?

游戏虚拟服务器是一种新兴的技术&#xff0c;它可以为玩家提供更好的游戏体验。私服服务器它可以将游戏服务器的负载分散到多台服务器上&#xff0c;从而提高游戏的流畅度和稳定性。此外&#xff0c;游戏虚拟服务器还可以提供更多的游戏功能&#xff0c;比如游戏聊天室、游戏排…...

ssti 前置学习

python venv环境 可以把它想象成一个容器&#xff0c;该容器供你用来存放你的Python脚本以及安装各种Python第三方模块&#xff0c;容器里的环境和本机是完全分开的 创建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集合魔法:解锁数据去重技巧

更多资料获取 &#x1f4da; 个人网站&#xff1a;涛哥聊Python 在Python编程的魔法世界中&#xff0c;有一种数据类型几乎被忽视&#xff0c;但却拥有强大的超能力&#xff0c;那就是集合&#xff08;Set&#xff09;。 集合是一种无序、唯一的数据类型&#xff0c;它以其独…...

flutter开发实战-inappwebview实现flutter与Javascript的交互JSBridge

flutter开发实战-inappwebview实现flutter与Javascript的交互JSBridge 在使用webview中&#xff0c;需要实现flutter与Javascript交互&#xff0c;在使用webview_flutter插件的时候&#xff0c;整理了一下webview与Javascript的交互JSBridge&#xff0c;具体可以查看 https:/…...

私有云盘:lamp部署nextcloud+高可用集群

目录 一、实验准备&#xff1a; 二、配置mariadb主从复制 三台主机下载mariadb 1&#xff09;主的操作 2&#xff09;从的操作 3&#xff09;测试数据是否同步 三、配置nfs让web服务挂载 1、安装 2、配置nfs服务器 3、配置web服务的httpd 4、测试 四、web 服务器 配…...

在线制作课程表

失业在家&#xff0c;开启一天一个应用的创作节奏&#xff0c;最近学了uniapp&#xff0c;特别想做点啥&#xff0c;正好家里小孩子要打印课程表&#xff0c;而且课程表还有调课的需求&#xff0c;就寻思做一个方便大家&#xff0c;到目前位置服务完全免费的&#xff0c;新鲜上…...

聊聊分布式架构06——[NIO入门]简单的Netty NIO示例

目录 Java NIO和Netty NIO比较 Java NIO&#xff1a; Netty&#xff1a; Netty NIO中的主要模块 Transport&#xff08;传输层&#xff09; Buffer&#xff08;缓冲区&#xff09; Codec&#xff08;编解码器&#xff09; Handler&#xff08;处理器&#xff09; Even…...

H5逆向之远程RPC

引言前一讲说过H5 怎么去抓包,逆向分析。其中说到RPC。这一节详细讲一下。有一种情况,JS 比较复杂,混淆的厉害。 这个时候就用到RPC。原理就是,hook web 浏览器,直接调用js 里边的方法。 Node 服务。为什么用到Node 服务,先来看下这架构 Node 对外提供各种接口,外部可以…...

解决Ubuntu18.04安装好搜狗输入法后无法打出中文的问题

首先下载安装 搜狗拼音输入法 &#xff0c;下载选择&#xff1a; x86_64 在ubuntu中设置 fcitx 最后发现安装好了&#xff0c;图标有了 &#xff0c;但是使用时不能输入中文&#xff0c;使用下面的命令解决&#xff1a; sudo apt install libqt5qml5 libqt5quick5 libqt5qu…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

Android第十三次面试总结(四大 组件基础)

Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成&#xff0c;用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机&#xff1a; ​onCreate()​​ ​调用时机​&#xff1a;Activity 首次创建时调用。​…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

【JavaSE】多线程基础学习笔记

多线程基础 -线程相关概念 程序&#xff08;Program&#xff09; 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序&#xff0c;比如我们使用QQ&#xff0c;就启动了一个进程&#xff0c;操作系统就会为该进程分配内存…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

Vite中定义@软链接

在webpack中可以直接通过符号表示src路径&#xff0c;但是vite中默认不可以。 如何实现&#xff1a; vite中提供了resolve.alias&#xff1a;通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)

一、OpenBCI_GUI 项目概述 &#xff08;一&#xff09;项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台&#xff0c;其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言&#xff0c;首次接触 OpenBCI 设备时&#xff0c;往…...