当前位置: 首页 > 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…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...

python爬虫——气象数据爬取

一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用&#xff1a; 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests&#xff1a;发送 …...

海云安高敏捷信创白盒SCAP入选《中国网络安全细分领域产品名录》

近日&#xff0c;嘶吼安全产业研究院发布《中国网络安全细分领域产品名录》&#xff0c;海云安高敏捷信创白盒&#xff08;SCAP&#xff09;成功入选软件供应链安全领域产品名录。 在数字化转型加速的今天&#xff0c;网络安全已成为企业生存与发展的核心基石&#xff0c;为了解…...

FTXUI::Dom 模块

DOM 模块定义了分层的 FTXUI::Element 树&#xff0c;可用于构建复杂的终端界面&#xff0c;支持响应终端尺寸变化。 namespace ftxui {...// 定义文档 定义布局盒子 Element document vbox({// 设置文本 设置加粗 设置文本颜色text("The window") | bold | color(…...

【Redis】Redis从入门到实战:全面指南

Redis从入门到实战:全面指南 一、Redis简介 Redis(Remote Dictionary Server)是一个开源的、基于内存的键值存储系统,它可以用作数据库、缓存和消息代理。由Salvatore Sanfilippo于2009年开发,因其高性能、丰富的数据结构和广泛的语言支持而广受欢迎。 Redis核心特点:…...

基于开源AI大模型AI智能名片S2B2C商城小程序源码的中等平台型社交电商运营模式研究

摘要&#xff1a;本文聚焦中等平台型社交电商&#xff0c;探讨其与传统微商及大型社交电商平台的差异&#xff0c;尤其关注产品品类管理对代理运营的影响。通过引入开源AI大模型、AI智能名片与S2B2C商城小程序源码技术&#xff0c;构建智能化运营体系。研究结果表明&#xff0c…...