小程序软键盘&SM2解密方式
小程序软键盘&SM2解密方式
转载请著名出处:https://www.cnblogs.com/funnyzpc/p/17572445.html
SM2基本信息
- 私钥(primary key)
6082011f17b21dab7da93f2dc1a739b530b969171c7116bebb0535a953e20bae
- 公钥(public key)
041708d05635b28264a919b89b1370b1517e51d19851c93b49bbaa54521ca4fec0d384069374dcedd846abb55b9920cc4fdf2270b4283b30de55344a66cb3f4334
- 加密内容
hello\nyouth\n12334
- 需要的加密库(java)
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.67</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.67</version>
</dependency>
理论上版本越高越好,如果版本较低可能出现加解密问题,这点是要注意的~
- 三方实现库(gmhelper)
https://github.com/ZZMarquis/gmhelper
这里感谢大神Lijun Liao写的库,不胜感激之至~
以上内容在后面会用到,这里先声明~
首先先看看小程序官方文档
[安全键盘]文档(https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/safe-password.html)
关键内容:
'V02_' + sm2(header + timestamp + '\0' + pbkdf_hmac_hex(password, salt) + '\0' + nonce + '\0' + 随机数)
就以上,我们可以得出一些信息:
- 1.解密前必须要削掉
V02_再行解密 - 2.
sm2函数内这几项header、timestamp、\0、nonce、随机数对于后端来说并没有鸟用 - 3.
salt这个是函数pbkdf_hmac_hex的盐,是否能去掉不得而知~ - 4.函数
pbkdf_hmac_hex看似是 未知算法pbkdf+哈希算法hmac+16进制hex的综合 - 5.如果可以自定义
pbkdf_hmac_hex函数,理论上后端是可以拿到明文password(事实上不能) - 6.
Windows_SMCryptoTools/Mac_SMCryptoTools是的好东西,后面开发得用上
好了,我们先从Windows_SMCryptoTools/Mac_SMCryptoTools这个工具开始一步步打通密文
Windows_SMCryptoTools/Mac_SMCryptoTools生成密钥对(公钥&私钥)


接下来用到的公钥以及私钥以及本文在开头就已提供 => SM2基本信息
先看看 SM2 Encrypt/Decrypt


格式选择里面有四种模式,其中C1C3C2是SM2的其中一种模式 ASN1是SM2的文本编码方式,事实上这些也是微信键盘所使用的编码加密方式
公钥加密
先看到工具的明文部分是HEX(16进制)的,好的这就按照工具的逻辑将明文做16进制转换
- 代码
import org.bouncycastle.util.encoders.Hex;
// 明文需要转为16进制字符串 SRC=hello\nyouth\n12334
public byte[] buildData(){
byte[] encode = Hex.encode(SRC);
// return new String(encode,StandardCharsets.UTF_8);
return encode;
}
这是16进制编码后的字符串:68656c6c6f5c6e796f7574685c6e3132333334
然后将HEX编码字符使用加解密工具Windows_SMCryptoTools/Mac_SMCryptoTools计算到16进制密文:
307B0220440D1CF67E1E78DB263BF4AC904B7CFE1D268954961DF042E0B3C683D0E851B502201047009A29B2900047333862A2F5E933503BFDD7F4F140FE2715053FD6BE07F10420B8C18A7BD0FFEF2214D2F879324A1E399D0D4B44DE0113ED5E06A8E4A65410850413A987567A5B30A79F81F02512BE1F8F1732E2E4
记住加密后得到的一定是 16进制密文 这点很重要,在后面会用到!
私钥解密
上面我们通过工具及自定义编码方式得到了最后的密文
这个密文怎么解 需要先理一下思路:
- 1.由于明文在加密的过程中做了
HEX,所以肯定涉及到解16进制编码 - 2.由于
ASN1是一种文本编码方式,这个东东可能是是在SM2加密前也可能是加密后做的编码行为 - 3.基于[2]知道开发需要一套能实现
SM2以及ASN1编码的功能的库
首先准备一个实现库,这里我用的是gmhelper(感谢 Lijun Liao )
// 用到的三方库
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.encoders.Hex;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@Test
public void test03()throws Exception{
// 密文hex
String hex_enc = "307B0220440D1CF67E1E78DB263BF4AC904B7CFE1D268954961DF042E0B3C683D0E851B502201047009A29B2900047333862A2F5E933503BFDD7F4F140FE2715053FD6BE07F10420B8C18A7BD0FFEF2214D2F879324A1E399D0D4B44DE0113ED5E06A8E4A65410850413A987567A5B30A79F81F02512BE1F8F1732E2E4";
byte[] enc = Hex.decode(hex_enc);
// 私钥参数类
ECPrivateKeyParameters ecPrivateKeyParameters = BCECUtil.createECPrivateKeyParameters(私钥, SM2Util.DOMAIN_PARAMS);
// 这个很重要,一定要先解ASN1(ASN1就是DER编码的一种实现方式)
byte[] enc_bytes = SM2Util.decodeDERSM2Cipher(enc);
byte[] decryptData = SM2Util.decrypt( ecPrivateKeyParameters,enc_bytes );
// 上面得到的是byte数组,这里需转String
String dec_str = new String(decryptData, StandardCharsets.UTF_8);
System.out.println("SM2 decrypt result: " + dec_str);
}
最终得到我们的原始明文:
SM2 decrypt result: hello\nyouth\n12334
以上测试通过后,标志着可以进行前后端联调了~
回到题目
首先:微信安全键盘的目的是提供一种安全的密码输入环境,这个过程似乎并不希望引用所有者能得到使用者的明文密码,所以我在同事的配合测试下也大致印证了这个猜想.故:如果某些场景下确实需要得到用户输入的明文,则不推荐安全键盘~
我们通过微信官方社区以及三方库的折腾终于解出了安全键盘的密文,当然囖,这个密文是hash过的,以下是实现代码
- 代码
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.encoders.Hex;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@Test
public void test04()throws Exception{
// 私钥 7fe1e04e1cb539640282f047809c2380570be2dd72513160602c04a128071e97
// 公钥 041cb7b860ac9c353f460cac7ddca32a01cce15ffacd567db0507e32ad769fbed50f071c332e9d7003d75e227ea4489c43d286ec30bbdc4882420faabe5e24f90c
String privateKey = "7fe1e04e1cb539640282f047809c2380570be2dd72513160602c04a128071e97";
String publicKey = "041cb7b860ac9c353f460cac7ddca32a01cce15ffacd567db0507e32ad769fbed50f071c332e9d7003d75e227ea4489c43d286ec30bbdc4882420faabe5e24f90c";
// String hex_enc = "V02_3081D002200154F7A5EAFDC714962A8061201A18EAB3589B1F44A9031E3E116630E221F107022024A19B7836FDD4A419C19ACB5A2D54EC9D27E4843977C8240D5A7E2F1CC0E17C0420C9616F97AD375F54F6FDDC64AA4C3409D0AC0BDC7FD85E7D77786A3F555097B90468D249FA7CB008C2F76690BD46625980B0B4E5BA4A7095D114F9652CEE06E0AC4320E4FD5D60D246FB7185046106B3EA486C37DD34EE8A52BBC026DBCE2EB86CCE900712C2068F4C81BEA5306181C07BF543F8AD39C881A25F54F3657603F38E14AB1C6C8B6AAAFEA0";
String hex_enc = "3081D002200154F7A5EAFDC714962A8061201A18EAB3589B1F44A9031E3E116630E221F107022024A19B7836FDD4A419C19ACB5A2D54EC9D27E4843977C8240D5A7E2F1CC0E17C0420C9616F97AD375F54F6FDDC64AA4C3409D0AC0BDC7FD85E7D77786A3F555097B90468D249FA7CB008C2F76690BD46625980B0B4E5BA4A7095D114F9652CEE06E0AC4320E4FD5D60D246FB7185046106B3EA486C37DD34EE8A52BBC026DBCE2EB86CCE900712C2068F4C81BEA5306181C07BF543F8AD39C881A25F54F3657603F38E14AB1C6C8B6AAAFEA0";
byte[] enc = Hex.decode(hex_enc);
ECPrivateKeyParameters ecPrivateKeyParameters = BCECUtil.createECPrivateKeyParameters(privateKey, SM2Util.DOMAIN_PARAMS);
byte[] decryptData = SM2Util.decrypt( ecPrivateKeyParameters,SM2Util.decodeDERSM2Cipher(enc) );
String dec_str = new String(decryptData, StandardCharsets.UTF_8);
System.out.println("SM2 decrypt result: " + dec_str);
}
对于以上实现需要注意的是:
- 1.解密前必须要削掉
A02_开头字符 - 2.解密后的明文中会有
\0分割各个block,需要自行split - 3.明文在解密前以及解密后都是
HEX(16进制) - 4.在开发前建议使用工具(
Windows_SMCryptoTools/Mac_SMCryptoTools)先行测试,过不了工具这关联调肯定也是不行的!
虽然最终的代码实现几乎毫不费力,但这中间确实也付出了时间成本,主要是对加密具体算法比较陌生,以及微信官方没有sdk这就有些黑箱导致了实现的困难,再加上SM2&ASN1实现的资料太少太窄导致~
小程序软键盘&SM2解密方式的更多相关文章
- 与众不同 windows phone (24) - Input(输入)之软键盘类型, XNA 方式启动软键盘, UIElement 的 Touch 相关事件, 触摸涂鸦
原文:与众不同 windows phone (24) - Input(输入)之软键盘类型, XNA 方式启动软键盘, UIElement 的 Touch 相关事件, 触摸涂鸦 [索引页][源码下载] ...
- android控制软键盘弹出方式
android一把自带的软键盘弹出方式是会将布局顶上去,造成UI乱套的情况. 解决办法:方法一:在你的activity中的oncreate中setContentView之前写上这个代码getWindo ...
- 原创:微信小程序java实现AES解密并获取unionId
来自:微信小程序联盟 如果大家使用小程序的同时还在使用公众号的话,可能会用到unionId这种功能,由于公司业务需要,我们需要使用unionId,具体使用方法,请参考微信开放平台的说明,但是在微信小程 ...
- 微信小程序之用户数据解密(七)
[未经作者本人同意,请勿以任何形式转载] 经常看到有点的小伙伴在群里问小程序用户数据解密流程,所以打算写一篇关于小程序用户敏感数据解密教程: 加密过程微信服务器完成,解密过程在小程序和自身服务器完成, ...
- .NET 小程序 wx.getUserInfo(OBJECT) 解密 encryptedData 来获取UnionId
在小程序中通过 wx.getUserInfo 获取用户信息,而UnionId 只有关主了公众号才会返回,不关注公众号想获取UnionId则需要我们从返回的 encryptedData 中解码从而获取U ...
- thinkphp3.2.3 小程序获取手机号 php 解密
首先是把这个文件夹放到\ThinkPHP\Library\Org里面 //zll 根据加密字符串和session_key和iv获取手机号 /** * [getphone description] * ...
- 【小程序】---- 使用 Echarts 的方式
1.下载 GitHub 上的 ecomfe/echarts-for-weixin 项目,Echarts微信版. 地址:https://github.com/ecomfe/echarts-for-wei ...
- 微信小程序8种数据通信的方式
前言 数据通信在开发中是必不可少的一个环节,也是我们必须掌握的知识.知道得越多的数据通信方式,实现业务会更加得心应手. 下面我将这些通信方式归类介绍: 组件通信 全局通信 页面通信 组件通信 prop ...
- 小程序 input 键盘弹出时样式遮盖问题
设置cursor-spacing,具体可参考官方文档,代码如下: <input type='text' bindinput="bindKeyInput" placehold ...
- (二)微信小程序的三种传值方式
1.全局变量 app.js里 App({ //全局变量 globalData: { userInfo: null, host: 'http://localhost:8080/data.json' } ...
随机推荐
- python-pygal
准备写大作业的时候发现了一个绝绝子的python库. 原文:https://blog.damavis.com/en/creating-vector-graphics-with-python/ 官网:h ...
- 笔记七:进程间的通信(IPC通信之信号灯)
信号量 定义信号变量: sem_t sem1 sem2 初始化信号量 P操作 V操作 功能 信号量(POSOX) 信号量灯(IPC) 定义信号变量 sem_t sem1 ...
- vue项目webpack打包
之前一篇随笔写到vue多环境打包环境配置:https://www.cnblogs.com/shun1015/p/13411636.html 1.区分vue脚手架版本,版本不同,项目结构不同,多环境变量 ...
- Prism Sample 3 自定义Region
在例2中,我们使用了一个Region <ContentControl prism:RegionManager.RegionName="ContentRegion" /> ...
- 【开源游戏】Legends-Of-Heroes 基于ET 7.2的双端C#(.net7 + Unity3d)多人在线英雄联盟风格的球球大作战游戏。
Legends-Of-Heroes 一个LOL风格的球球大作战游戏,基于ET7.2,使用状态同步 Main 基于C#双端框架[ET7.2],同步到ET主干详情请看日志.(https://github ...
- drf重写authenticate方法实现多条件登录(源码分析)
drf重写authenticate方法实现多条件登录(源码分析) 1. 思路 JWT拓展的登录视图中, 在接受到用户名和密码时, 调用的也是Django的认证系统中提供的authenticate()来 ...
- Keepalived 高可用
Keepalived 高可用 什么是高可用 一般是指2台机器启动着完全相同的业务系统,当有一台机器down机了,另外一台服务器就能快速的接管,对于访问的用户是无感知的. 高可用通常使用的软件 keep ...
- 用go设计开发一个自己的轻量级登录库/框架吧(项目维护篇)
用go设计开发一个自己的轻量级登录库/框架吧(项目维护篇) 本篇将开始讲讲开发库/框架的最开始阶段,也就是搭建一个项目 源码:weloe/token-go: a light login library ...
- 2021-02-14:假设有排成一行的N个位置,记为1~N,N 一定大于或等于 2,开始时机器人在其中的M位置上(M 一定是 1~N 中的一个)。如果机器人来到1位置,那么下一步只能往右来到2位置;如果机器人来到N位置,那么下一步只能往左来到 N-1 位置;如果机器人来到中间位置,那么下一步可以往左走或者往右走;规定机器人必须走 K 步,最终能来到P位置(P也是1~N中的一个)的方法有多少种?
2021-02-14:假设有排成一行的N个位置,记为1~N,N 一定大于或等于 2,开始时机器人在其中的M位置上(M 一定是 1~N 中的一个).如果机器人来到1位置,那么下一步只能往右来到2位置:如 ...
- action装饰器
视图集中附加action的声明 from rest_framework.decorators import action # 追加action:返回书记的倒叙地0个书籍的信息 @action(meth ...