调用微信退款接口时出现System.Security.Cryptography.CryptographicException: 出现了内部错误 解决办法

本文介绍了一种使用X509Certificate2类加载和管理证书的方法,通过指定证书路径、密码和存储标志,实现了证书的加载、持久化及导出功能。

X509Certificate2 cer = new X509Certificate2(cerPath, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); 
req.ClientCertificates.Add(cer);

<think>我们已经在之前的回答中给出了VB.NET Web API接收encryptedData和iv的基本框架。现在,根据用户要求,我们将提供更完整的代码,包括如何解密微信小程序发送的加密数据。 ### 解密流程说明 根据微信官方文档[^1],解密用户信息的步骤如下: 1. 通过`wx.login`获取`code`,发送到开发者服务器 2. 服务器使用`code`、`appid`、`appsecret`请求微信接口获取`session_key`和`openid` 3. 小程序调用`wx.getUserInfo`获取加密数据`encryptedData`和初始向量`iv` 4. 将`encryptedData`和`iv`发送到开发者服务器 5. 服务器使用`session_key`、`iv`和`encryptedData`进行AES-CBC解密,获得用户信息 ### VB.NET Web API 完整代码实现 #### 步骤1:创建模型类 ```vbnet Public Class DecryptRequest Public Property encryptedData As String Public Property iv As String ' 可选:如果前端同发送了session_key,但通常session_key由服务器存储 ' Public Property sessionKey As String End Class Public Class DecryptResult Public Property code As Integer Public Property message As String Public Property data As Object End Class ``` #### 步骤2:解密工具类 ```vbnet Imports System Imports System.Security.Cryptography Imports System.Text Public Class WeChatDecryptor ''' <summary> ''' 解密微信小程序加密数据 ''' </summary> ''' <param name="encryptedData">加密数据</param> ''' <param name="sessionKey">会话密钥(AES密钥)</param> ''' <param name="iv">初始向量</param> ''' <returns>解密后的JSON字符串</returns> Public Shared Function Decrypt(encryptedData As String, sessionKey As String, iv As String) As String ' 使用Base64解码 Dim encryptedDataBytes As Byte() = Convert.FromBase64String(encryptedData) Dim keyBytes As Byte() = Convert.FromBase64String(sessionKey) Dim ivBytes As Byte() = Convert.FromBase64String(iv) Using aes = Aes.Create() aes.Key = keyBytes aes.IV = ivBytes aes.Mode = CipherMode.CBC aes.Padding = PaddingMode.PKCS7 Using decryptor = aes.CreateDecryptor() Dim resultBytes As Byte() = decryptor.TransformFinalBlock(encryptedDataBytes, 0, encryptedDataBytes.Length) Return Encoding.UTF8.GetString(resultBytes) End Using End Using End Function End Class ``` #### 步骤3:API控制器 ```vbnet Imports System.Web.Http Public Class WeChatController Inherits ApiController <HttpPost> <Route("api/decrypt")> Public Function PostDecryptData(<FromBody> request As DecryptRequest) As IHttpActionResult Try ' 验证参数 If String.IsNullOrEmpty(request.encryptedData) OrElse String.IsNullOrEmpty(request.iv) Then Return BadRequest("缺少必要参数: encryptedData 或 iv") End If ' 实际项目中,这里需要从服务器存储中获取当前用户的sessionKey ' 注意:sessionKey不应该由前端传递,因为不安全 Dim sessionKey As String = GetSessionKeyFromStorage() ' 请替换为实际的获取方式 If String.IsNullOrEmpty(sessionKey) Then Return BadRequest("无效的会话,请重新登录") End If ' 解密数据 Dim decryptedDataJson As String = WeChatDecryptor.Decrypt(request.encryptedData, sessionKey, request.iv) ' 解析JSON(这里假设解密后是JSON字符串) ' 实际项目中,可以反序列化为对象 ' Dim userInfo = JsonConvert.DeserializeObject(Of WeChatUserInfo)(decryptedDataJson) ' 返回解密结果 Dim result = New DecryptResult With { .code = 0, .message = "解密成功", .data = decryptedDataJson ' 或者直接返回userInfo对象 } Return Ok(result) Catch ex As CryptographicException Return BadRequest("解密失败: " & ex.Message) Catch ex As Exception Return InternalServerError(ex) End Try End Function ''' <summary> ''' 从服务器存储中获取当前用户的sessionKey(示例方法) ''' 实际项目中,需要根据用户标识(如openid)从数据库或缓存中获取 ''' </summary> Private Function GetSessionKeyFromStorage() As String ' 示例:从缓存中获取。实际需要替换为你的业务逻辑 ' 例如:Dim sessionKey = Cache.Get("session_key_" + currentUserOpenId) Return "tiihtNczf5v6AKRyjwEUhQ==" ' 示例sessionKey,实际项目中需要动态获取 End Function End Class ``` ### 前端小程序调用示例 ```javascript Page({ onLoad: function () { // 1. 获取code wx.login({ success: function (res) { if (res.code) { // 2. 将code发送到服务器,服务器用code换取session_key并存储(这里省略) // 3. 获取用户信息 wx.getUserInfo({ success: function (infoRes) { // 4. 发送加密数据到服务器 wx.request({ url: 'https://your-domain.com/api/decrypt', method: 'POST', data: { encryptedData: infoRes.encryptedData, iv: infoRes.iv }, success: function (res) { console.log('解密结果:', res.data) } }) } }) } } }) } }) ``` ### 关键问题说明 1. **`sessionKey`的存储** 在用户登录(通过`wx.login`获取的`code`),服务器应该向微信接口请求`session_key`和`openid`,并将`session_key`与用户标识(如`openid`)关联存储在服务器(数据库或缓存)。在解密,根据当前用户(通常通过token或cookie识别)获取对应的`session_key`。 2. **安全性注意事项** - 不要将`session_key`传到前端,因为`session_key`是解密的关键,泄露会导致用户数据被解密。 - 微信小程序端发送的`encryptedData`和`iv`是通过HTTPS传输,服务器也应启用HTTPS。 3. **解密后的数据结构** 解密后的数据是一个JSON字符串,包含用户信息,例如: ```json { "openId": "OPENID", "nickName": "NICKNAME", "gender": 1, "city": "CITY", "province": "PROVINCE", "country": "COUNTRY", "avatarUrl": "AVATARURL", "unionId": "UNIONID", "watermark": { "appid": "APPID", "timestamp": TIMESTAMP } } ``` 可以用Newtonsoft.Json等库反序列化。 ### 相关参考资料 [^1]: 微信小程序登录流程:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html [^2]: 微信小程序用户数据的签名验证和加解密:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html §§相关问题§§ 1. 如何根据微信小程序登录`code`获取`session_key`和`openid`? 2. VB.NET中如何将JSON字符串反序列化为对象? 3. 如何安全地存储`session_key`(比如使用分布式缓存)? 4. 微信小程序用户信息解密失败常见原因有哪些? 5. 如何验证用户数据签名以防止数据伪造? </think># VB.NET Web API 接收微信小程序加密数据完整实现 下面我将提供完整的 VB.NET Web API 解决方案,用于接收和处理微信小程序通过 `wx.request()` 发送的加密数据(encryptedData 和 iv)。 ## 完整解决方案 ### 1. 创建 Web API 控制器 ```vbnet Imports System.Web.Http Imports Newtonsoft.Json Public Class WeChatController Inherits ApiController <HttpPost> <Route("api/mini/auth")> ' 匹配小程序请求路径 Public Function ProcessWeChatData(<FromBody> data As WeChatDataRequest) As IHttpActionResult Try ' 1. 验证必需参数 If String.IsNullOrEmpty(data.encryptedData) OrElse String.IsNullOrEmpty(data.iv) Then Return BadRequest("缺少必要参数: encryptedData 或 iv") End If ' 2. 获取当前用户的会话密钥 (需要实现) Dim sessionKey As String = GetSessionKeyForCurrentUser() If String.IsNullOrEmpty(sessionKey) Then Return BadRequest("会话无效,请重新登录") End If ' 3. 解密用户数据 Dim decryptedData As String = WeChatDecryptor.DecryptData(data.encryptedData, sessionKey, data.iv) ' 4. 解析用户信息 Dim userInfo As WeChatUserInfo = JsonConvert.DeserializeObject(Of WeChatUserInfo)(decryptedData) ' 5. 验证水印信息(确保数据来自微信) If Not VerifyWatermark(userInfo.watermark) Then Return BadRequest("数据签名验证失败") End If ' 6. 处理业务逻辑(用户注册/登录等) Dim result = ProcessUserInfo(userInfo) Return Ok(New With { .code = 0, .message = "操作成功", .data = result }) Catch ex As Exception ' 记录错误日志 System.Diagnostics.Trace.TraceError($"微信数据处理失败: {ex}") Return InternalServerError(ex) End Try End Function ' 辅助方法实现... End Class ``` ### 2. 请求模型类 ```vbnet Public Class WeChatDataRequest Public Property encryptedData As String Public Property iv As String Public Property code As String ' 可选:微信登录code Public Property userId As String ' 可选:当前用户标识 End Class Public Class WeChatUserInfo Public Property openId As String Public Property nickName As String Public Property gender As Integer Public Property city As String Public Property province As String Public Property country As String Public Property avatarUrl As String Public Property unionId As String Public Property watermark As WatermarkInfo End Class Public Class WatermarkInfo Public Property appid As String Public Property timestamp As Long End Class ``` ### 3. 数据解密工具类 ```vbnet Imports System Imports System.Security.Cryptography Imports System.Text Public Class WeChatDecryptor ''' <summary> ''' 解密微信小程序加密数据 ''' </summary> ''' <param name="encryptedData">加密数据</param> ''' <param name="sessionKey">会话密钥</param> ''' <param name="iv">初始向量</param> ''' <returns>解密后的JSON字符串</returns> Public Shared Function DecryptData(encryptedData As String, sessionKey As String, iv As String) As String Dim encryptedDataBytes As Byte() = Convert.FromBase64String(encryptedData) Dim keyBytes As Byte() = Convert.FromBase64String(sessionKey) Dim ivBytes As Byte() = Convert.FromBase64String(iv) Using aes = Aes.Create() aes.Key = keyBytes aes.IV = ivBytes aes.Mode = CipherMode.CBC aes.Padding = PaddingMode.PKCS7 Using decryptor = aes.CreateDecryptor() Dim resultBytes As Byte() = decryptor.TransformFinalBlock(encryptedDataBytes, 0, encryptedDataBytes.Length) Return Encoding.UTF8.GetString(resultBytes) End Using End Using End Function End Class ``` ### 4. 会话管理类(关键部分) ```vbnet Imports System.Runtime.Caching ' 需要引用 System.Runtime.Caching Public Class SessionManager Private Shared ReadOnly Cache As MemoryCache = MemoryCache.Default ''' <summary> ''' 存储会话密钥 ''' </summary> Public Shared Sub StoreSessionKey(userId As String, sessionKey As String) Dim policy As New CacheItemPolicy With { .SlidingExpiration = TimeSpan.FromMinutes(20) ' 会话有效期20分钟 } Cache.Add(userId, sessionKey, policy) End Sub ''' <summary> ''' 获取会话密钥 ''' </summary> Public Shared Function GetSessionKey(userId As String) As String Return If(Cache(userId)?.ToString(), String.Empty) End Function End Class ``` ### 5. 微信数据验证方法 ```vbnet ''' <summary> ''' 验证微信数据签名 ''' </summary> Private Function VerifyWatermark(watermark As WatermarkInfo) As Boolean ' 1. 验证AppID是否匹配 If watermark.appid <> ConfigurationManager.AppSettings("WeChatAppId") Then Return False End If ' 2. 验证间戳是否在合理范围内(5分钟内) Dim now = DateTimeOffset.UtcNow.ToUnixTimeSeconds() If Math.Abs(now - watermark.timestamp) > 300 Then Return False End If Return True End Function ``` ## 完整工作流程 1. **小程序端**: ```javascript wx.login({ success: res => { wx.request({ url: 'https://your-domain.com/api/wechat/login', data: { code: res.code }, success: loginRes => { // 存储返回的userId const userId = loginRes.data.userId SessionManager.StoreSessionKey(userId, loginRes.data.sessionKey) } }) } }) wx.getUserInfo({ success: res => { wx.request({ url: 'https://your-domain.com/api/mini/auth', method: 'POST', data: { encryptedData: res.encryptedData, iv: res.iv, userId: getApp().globalData.userId }, success: authRes => { console.log('用户信息:', authRes.data) } }) } }) ``` 2. **服务器端处理流程**: ``` [客户端] → 发送code获取session_key (GET /api/wechat/login) → 服务器向微信接口请求session_key → 返回userId并缓存session_key [客户端] → 获取用户信息 → 发送encryptedData + iv + userId (POST /api/mini/auth) → 服务器获取缓存的session_key → 解密数据并验证 → 返回处理结果 ``` ## 安全注意事项 1. **HTTPS强制使用**: ```xml <!-- Web.config 配置 --> <system.webServer> <rewrite> <rules> <rule name="HTTP to HTTPS redirect" stopProcessing="true"> <match url="(.*)" /> <conditions> <add input="{HTTPS}" pattern="off" ignoreCase="true" /> </conditions> <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" /> </rule> </rules> </rewrite> </system.webServer> ``` 2. **敏感信息保护**: - 会话密钥(session_key)不应返回给客户端 - 存储session_key使用服务端缓存而非数据库 - 设置合理的会话过期间 3. **数据验证**: - 验证水印信息中的AppID和间戳 - 使用微信提供的签名验证方法[^1] ## 相关配置(Web.config) ```xml <appSettings> <add key="WeChatAppId" value="你的小程序AppID"/> <add key="WeChatAppSecret" value="你的小程序AppSecret"/> <add key="WeChatApiUrl" value="https://api.weixin.qq.com/sns/jscode2session"/> </appSettings> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值