.NET RSA解密、签名、验签
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates; namespace API.Tools
{
/// <summary>
/// 类名:RSAFromPkcs8
/// 功能:RSA解密、签名、验签
/// 详细:该类对Java生成的密钥进行解密和签名以及验签专用类,不需要修改
/// 版本:2.0
/// 修改日期:2011-05-10
/// 说明:
/// 以下代码只是为了方便商户测试而提供的样例代码,商户可以根据自己网站的需要,按照技术文档编写,并非一定要使用该代码。
/// 该代码仅供学习和研究支付宝接口使用,只是提供一个参考。
/// </summary>
public sealed class Signatory
{
/// <summary>
/// 签名(C#解释证书导出的,会出现“数据不正确”的错误)
/// </summary>
/// <param name="privateKey">私钥</param>
/// <param name="content">需要签名的内容</param>
/// <param name="encode">编码格式</param>
/// <returns></returns>
public static string sign(string privateKey, string content, Encoding encode)
{
byte[] Data = encode.GetBytes(content);
RSACryptoServiceProvider rsa = DecodePemPrivateKey(privateKey);
MD5CryptoServiceProvider m5 = new MD5CryptoServiceProvider(); byte[] signData = rsa.SignData(Data, m5);
return Convert.ToBase64String(signData);
}
/// <summary>
/// 验证签名
/// </summary>
/// <param name="publicKey">公钥</param>
/// <param name="content">需要验证的内容</param>
/// <param name="signedString">签名结果</param>
/// <param name="encode">编码格式</param>
/// <returns></returns>
public static bool verify(string publicKey, string content, string signedString, Encoding encode)
{
bool result = false;
byte[] Data = encode.GetBytes(content);
byte[] data = Convert.FromBase64String(signedString);
RSAParameters paraPub = ConvertFromPublicKey(publicKey);
RSACryptoServiceProvider rsaPub = new RSACryptoServiceProvider();
rsaPub.ImportParameters(paraPub); MD5CryptoServiceProvider m5 = new MD5CryptoServiceProvider();
result = rsaPub.VerifyData(Data, m5, data);
return result;
} /// <summary>
/// 解析java生成的pem文件私钥
/// </summary>
/// <param name="pemstr"></param>
/// <returns></returns>
private static RSACryptoServiceProvider DecodePemPrivateKey(String pemstr)
{
byte[] pkcs8privatekey;
pkcs8privatekey = Convert.FromBase64String(pemstr);
if (pkcs8privatekey != null)
{ RSACryptoServiceProvider rsa = DecodePrivateKeyInfo(pkcs8privatekey);
return rsa;
}
else
return null;
} private static RSACryptoServiceProvider DecodePrivateKeyInfo(byte[] pkcs8)
{ byte[] SeqOID = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
byte[] seq = new byte[]; MemoryStream mem = new MemoryStream(pkcs8);
int lenstream = (int)mem.Length;
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = ;
ushort twobytes = ; try
{ twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null; bt = binr.ReadByte();
if (bt != 0x02)
return null; twobytes = binr.ReadUInt16(); if (twobytes != 0x0001)
return null; seq = binr.ReadBytes(); //read the Sequence OID
if (!CompareBytearrays(seq, SeqOID)) //make sure Sequence for OID is correct
return null; bt = binr.ReadByte();
if (bt != 0x04) //expect an Octet string
return null; bt = binr.ReadByte(); //read next byte, or next 2 bytes is 0x81 or 0x82; otherwise bt is the byte count
if (bt == 0x81)
binr.ReadByte();
else
if (bt == 0x82)
binr.ReadUInt16();
//------ at this stage, the remaining sequence should be the RSA private key byte[] rsaprivkey = binr.ReadBytes((int)(lenstream - mem.Position));
RSACryptoServiceProvider rsacsp = DecodeRSAPrivateKey(rsaprivkey);
return rsacsp;
} catch (Exception)
{
return null;
} finally { binr.Close(); } } private static bool CompareBytearrays(byte[] a, byte[] b)
{
if (a.Length != b.Length)
return false;
int i = ;
foreach (byte c in a)
{
if (c != b[i])
return false;
i++;
}
return true;
} private static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
{
byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; // --------- Set up stream to decode the asn.1 encoded RSA private key ------
MemoryStream mem = new MemoryStream(privkey);
BinaryReader binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading
byte bt = ;
ushort twobytes = ;
int elems = ;
try
{
twobytes = binr.ReadUInt16();
if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
binr.ReadByte(); //advance 1 byte
else if (twobytes == 0x8230)
binr.ReadInt16(); //advance 2 bytes
else
return null; twobytes = binr.ReadUInt16();
if (twobytes != 0x0102) //version number
return null;
bt = binr.ReadByte();
if (bt != 0x00)
return null; //------ all private key components are Integer sequences ----
elems = GetIntegerSize(binr);
MODULUS = binr.ReadBytes(elems); elems = GetIntegerSize(binr);
E = binr.ReadBytes(elems); elems = GetIntegerSize(binr);
D = binr.ReadBytes(elems); elems = GetIntegerSize(binr);
P = binr.ReadBytes(elems); elems = GetIntegerSize(binr);
Q = binr.ReadBytes(elems); elems = GetIntegerSize(binr);
DP = binr.ReadBytes(elems); elems = GetIntegerSize(binr);
DQ = binr.ReadBytes(elems); elems = GetIntegerSize(binr);
IQ = binr.ReadBytes(elems); // ------- create RSACryptoServiceProvider instance and initialize with public key -----
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSAParameters RSAparams = new RSAParameters();
RSAparams.Modulus = MODULUS;
RSAparams.Exponent = E;
RSAparams.D = D;
RSAparams.P = P;
RSAparams.Q = Q;
RSAparams.DP = DP;
RSAparams.DQ = DQ;
RSAparams.InverseQ = IQ;
RSA.ImportParameters(RSAparams);
return RSA;
}
catch (Exception)
{
return null;
}
finally { binr.Close(); }
} private static int GetIntegerSize(BinaryReader binr)
{
byte bt = ;
byte lowbyte = 0x00;
byte highbyte = 0x00;
int count = ;
bt = binr.ReadByte();
if (bt != 0x02) //expect integer
return ;
bt = binr.ReadByte(); if (bt == 0x81)
count = binr.ReadByte(); // data size in next byte
else
if (bt == 0x82)
{
highbyte = binr.ReadByte(); // data size in next 2 bytes
lowbyte = binr.ReadByte();
byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
count = BitConverter.ToInt32(modint, );
}
else
{
count = bt; // we already have the data size
} while (binr.ReadByte() == 0x00)
{ //remove high order zeros in data
count -= ;
}
binr.BaseStream.Seek(-, SeekOrigin.Current); //last ReadByte wasn't a removed zero, so back up a byte
return count;
} private static RSAParameters ConvertFromPublicKey(string pemFileConent)
{ byte[] keyData = Convert.FromBase64String(pemFileConent);
if (keyData.Length < )
{
throw new ArgumentException("pem file content is incorrect.");
}
byte[] pemModulus = new byte[];
byte[] pemPublicExponent = new byte[];
Array.Copy(keyData, , pemModulus, , );
Array.Copy(keyData, , pemPublicExponent, , );
RSAParameters para = new RSAParameters();
para.Modulus = pemModulus;
para.Exponent = pemPublicExponent;
return para;
}
}
}
上面的类在平常使用的时候,是没有问题的,但,我们在对接第三方支付(易联支付)时,遇到一个非常刺手的,签名不通过。
- 签名证书,是从北京数字认证拿到,里面有一个文件xxxxx-Signature.pfx 和证书密码,我们需要从该文件中,使用openssl.exe工具,导出RSA公钥和私钥
- 导出的公钥,发给合作平台配上,私钥用于数据签名
- 签名时,使用导出的私钥进行。
问题来了,调用上面程序的sign()方法,解释私钥时,总报“数据不正解”或者“无效的私钥”之类的。更奇怪的是,该私钥在java下使用没有问题。联系了对方,对方试了好多几证书,只有我们的这个有问题。好吧,算倒霉吧。
解决方法:
.net里可以直接使用证书进行RSA签名:
/// <summary>
/// 签名,直接使用证书签名
/// </summary>
/// <param name="filePath"></param>
/// <param name="password"></param>
/// <param name="content"></param>
/// <param name="encode"></param>
/// <returns></returns>
public static string sign(string filePath,string password, string content, Encoding encode)
{
byte[] Data = encode.GetBytes(content); X509Certificate2 myX509 = new X509Certificate2(filePath, password, X509KeyStorageFlags.Exportable);
var rsa = (RSACryptoServiceProvider)myX509.PrivateKey;
MD5CryptoServiceProvider m5 = new MD5CryptoServiceProvider(); byte[] signData = rsa.SignData(Data, m5);
return Convert.ToBase64String(signData);
}
问题解决!
.NET RSA解密、签名、验签的更多相关文章
- RSACryptoServiceProvider加密解密签名验签和DESCryptoServiceProvider加解密
原文:RSACryptoServiceProvider加密解密签名验签和DESCryptoServiceProvider加解密 C#在using System.Security.Cryptograph ...
- js rsa sign使用笔记(加密,解密,签名,验签)
你将会收获: js如何加密, 解密 js如何签名, 验签 js和Java交互如何相互解密, 验签(重点) 通过谷歌, 发现jsrsasign库使用者较多. 查看api发现这个库功能很健全. 本文使用方 ...
- C# RSACryptoServiceProvider加密解密签名验签和DESCryptoServic
C#在using System.Security.Cryptography下有 DESCryptoServiceProvider RSACryptoServiceProvider DESCryptoS ...
- Java RSA 加密 解密 签名 验签
原文:http://gaofulai1988.iteye.com/blog/2262802 import java.io.FileInputStream; import java.io.FileOut ...
- RSA密钥生成、加密解密、签名验签
RSA 非对称加密公钥加密,私钥解密 私钥签名,公钥验签 下面是生成随机密钥对: //随机生成密钥对 KeyPairGenerator keyPairGen = null; try { keyPair ...
- [Python3] RSA的加解密和签名/验签实现 -- 使用pycrytodome
Crypto 包介绍: pycrypto,pycrytodome 和 crypto 是一个东西,crypto 在 python 上面的名字是 pycrypto 它是一个第三方库,但是已经停止更新,所以 ...
- 利用SHA-1算法和RSA秘钥进行签名验签(带注释)
背景介绍 1.SHA 安全散列算法SHA (Secure Hash Algorithm)是美国国家标准和技术局发布的国家标准FIPS PUB 180-1,一般称为SHA-1.其对长度不超过264二进制 ...
- 支付接口中常用的加密解密以及验签rsa,md5,sha
一.常用加密类型分类 1.对称加密:采用单钥对信息进行加密和解密,即同一个秘钥既可以对信息进行加密,也可以进行解密.此类型称之为对称加密.特点速度快,常用于对大量数据信息或文件加密时使用.常用例子:D ...
- 数据安全管理:RSA加密算法,签名验签流程详解
本文源码:GitHub·点这里 || GitEE·点这里 一.RSA算法简介 1.加密解密 RSA加密是一种非对称加密,在公开密钥加密和电子商业中RSA被广泛使用.可以在不直接传递密钥的情况下,完成加 ...
- RSA签名验签
import android.util.Base64; import java.security.KeyFactory; import java.security.PrivateKey; import ...
随机推荐
- MySQL DROP 大表时的注意事项
对于表的删除,因为InnoDB引擎会在table cache层面维护一个全局独占锁一直到DROP TABLE完成为止,这样,对于表的其他操作会被HANG住.对于较大的表来说,DROP TABLE操作可 ...
- vue过滤动画
一.使用<transition name="fade"></transition>标签 name="fade", 是创建个fade的类名 ...
- kvm/qemu虚拟机桥接网络创建与配置
首先阐述一下kvm与qemu的关系,kvm是修改过的qemu,而且使用了硬件支持的仿真,仿真速度比QEMU快. 配置kvm/qemu的网络有两种方法.其一,默认方式为用户模式网络(Usermode N ...
- Spring Boot 利用插件构造QueryDSL语句时报错:You need to run build with JDK or have tools.jar on the classpath.If this occur....
You need to run build with JDK or have tools.jar on the classpath.If this occures during eclipse bui ...
- vue之计算属性和侦听器
一.计算属性 模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的.在模板中放入太多的逻辑会让模板过重且难以维护.例如: <div> {{ message.split('').rev ...
- 利用javascript判断文件是否存在
1 判断本地文件是否存在 var fso,s=filespec; // filespec="C:/path/myfile.txt" fso=new ActiveXObject(&q ...
- 安装和卸载windows服务程序
安装window服务 安装命令:InstallUtil.exe MyServiceLog.exe InstallUtil存在路径为:C:\WINDOWS\Microsoft.NET\Framework ...
- android speakerphone/
http://www.cnblogs.com/innost/archive/2011/01/22/1942149.html http://blog.sina.com.cn/s/blog_5418969 ...
- 缓存服务,还未创建完缓存时, 需要更改图层名称、服务名称、数据源位置、mxd名称等
缓存服务,还未创建完缓存时, 需要更改图层名称.服务名称.数据源位置.mxd名称等.已经创建好的缓存还可以再用吗? 测试后可以, 注意:新服务相对旧服务,符号样式没有改变,切片方案没有变化. 测试步骤 ...
- FCoin API
本文介绍FCoin API 介绍 通过了解以下信息,您可以方便的使用 FCoin 提供的 API 来接入 FCoin 交易平台. 认证 执行下面的代码进行用户验证: import fcoin api ...