❶ 四、公钥和私钥,加密和数字签名
本文涉及到支付宝SDK的内容,均摘自支付宝开放平台。
因为支付宝SDK使用RSA来加密和生成数字签名,所以本文中涉及到的概念也都是针对于RSA的。
一对儿密钥生成后,会有公钥和私钥之分,我们需要把私钥保存下来,而把公钥发布出去。一对儿公钥和私钥,不能由其中一个导出另一个。
比如使用支付宝SDK的时候,我们商户端会生成一对儿密钥A和B,A是私钥,B是公钥,支付宝也会生成一对儿密钥C和D,C是私钥,D是公钥。我们商户端需要把商户端私钥A保存下来,而把商户端公钥B发布出去给支付宝,支付宝需要把支付宝私钥C保存下来,而把支付宝公钥D发布出去给我们商户端。
加密是指我们使用一对儿密钥中的一个来对数据加密,而使用另一个来对数据解密的技术,需要注意的是公钥和私钥都可以用来加密,也都可以用来解密 ,并不是规定死了只能用公钥加密私钥解密,但是加解密必须是一对儿密钥之间的互相加解密,否则不能成功。
加密的目的是为了保证数据的不可读性,防止数据在传输过程中被截获。
知道了加密这个概念,我们先看一下支付宝的加密过程,再引出数字签名这个概念。接着第1小节的例子,当我们商户端和支付宝互相发布了公钥之后,我们商户端手里就有 商户端私钥 和 支付宝公钥 两个密钥,支付宝手里也有 商户端公钥 和 支付宝私钥 两个密钥。现在假设我们商户端要给支付宝传输订单信息,那么为了保证传输订单信息时数据的安全性,结合我们商户端手里所拥有的密钥,可以有两套加密方案
貌似这两套加密方案都能达到对订单信息加密的效果,而且如果采用方案二,我们商户端甚至只需要存储支付宝公钥这一个密钥,都不用去申请一对儿商户端的公私钥来维护,支付宝也不用保存我们一堆商户那么多的商户端公钥了,这不是更简单吗,那为什么支付宝开放平台让我们采用的是方案一而不是方案二呢?下面来回答一下。
支付宝开放平台说明:当我们采用RSA(1024位密钥)来加密的时候,支付宝分配给所有商户的支付宝公钥都是一样的,即支付宝针对那么多的商户只负责维护一对儿支付宝公私钥,这就意味着支付宝公钥随便什么人拿到后都是一样的;而当我们采用RSA2(2048位密钥)来加密的时候,支付宝会分配给每个商户单独的一个支付宝公钥,即支付宝为每一个的商户单独的维护一对独立的支付宝公私钥,当然一个商户下的多个App的支付宝公钥是一样的。RSA是早就支持的,RSA2是最近才支持的。
知道了上面这段话,现在假设我们采用的是方案二,并且采用RSA加密(很多老业务并没有使用RSA2加密),业务逻辑将会是下面这样。
这就出问题了, RSA加密下,支付宝公钥是公开发布的,而且所有的商户用的都是同一个支付宝公钥(上面声明了RSA2加密下,支付宝才针对每个商户维护了一对儿公私钥),攻击者很容易就能获取到,而 notify_url 也很容易被截获,那攻击者拿到这两个东西就可以做和商户一样的操作来发起支付请求,这样就会一直给小明充钱了。
所以 支付宝就需要确认支付请求确实是商户发给他们的,而不是攻击者发给他们的。 这就用到了 数字签名 ,我们会通过方案一的实现流程来引出数字签名的具体概念。如果我们采用的是方案一,我们商户端保存的就是商户端私钥和支付宝公钥,而支付宝保存的就是需要存着商户端公钥和支付宝私钥的,业务逻辑将会是下面这样。
这样就可以保证交易的安全性了,我们也可以看出使用支付宝SDK保证交易的安全性注重的其实不是订单信息是否加密,而是如何确保商户端和支付宝能够互相确认身份,订单信息是明文的,但是后面拼接了数字签名。
数字签名其实就是明文数据加密之后得到的一个密文,只不过它是用私钥加密生成的而已,我们一般会把数字签名拼接在明文数据后面一起传递给接收方,接收方收到后用公钥解密数字签名,从而验证发送方的身份、以及明文数据是否被篡改。数字签名的生成过程其实就是一个加密过程,数字签名的验签过程就是一个解密过程。
数字签名的目的有两个:一、发送方和接收方互相验证身份;二、验证数据是否被篡改。
从上面第一部分我们知道为了确保商户和支付宝交易的安全性,约定采用的是给订单信息加数字签名传输的方式。支付宝也为我们提供了 一键生成RSA密钥的工具 ,可以帮助我们很快的生成一对商户端公私钥。以下会对支付宝SDK的支付流程做个大概的解释,并点出实际开发中我们使用支付宝SDK时应该注意的地方。
由我们商户端自己生成的RSA私钥(必须与商户端公钥是一对),生成后要保存在服务端,绝对不能保存在客户端,也绝对不能从服务端传输给客户端。
用来对订单信息加签,加签过程一定要在服务端完成,绝对不能在客户端做加,客户端只负责用加签后的订单信息调起支付宝来支付。
由我们商户端自己生成的RSA公钥(必须与商户端私钥是一对),生成后需要填写在支付宝开放平台。
用来给支付宝服务端验签经过我们加签后的订单信息,以确保订单信息确实是我们商户端发给支付宝的,并且确保订单信息在传输过程中未被篡改。
这个和我们就没关系了,支付宝私钥是他们自己生成的,也是他们自己保存的。
用来对支付结果进行加签。
支付宝公钥和支付宝私钥是一对,也是支付宝生成的,当我们把商户端公钥填写在支付宝开放平台后,平台就会给我们生成一个支付宝公钥,我们可以复制下来保存在服务端,同样不要保存在客户端,并且不要传输给客户端。
用来让服务端对支付宝服务端返给我们的同步或异步支付结果进行验签,以确保支付结果确实是由支付宝服务端返给我们服务端的,而且没有被篡改,对支付结果的验签工作也一定要在服务端完成。
上面已经说过了: 订单信息的加签和支付结果的验签是一定要在服务端做的,绝对不能在客户端做。
下面是在客户端对订单信息加签的过程,仅仅是为了模拟服务端来表明订单信息是如何通过加签最终转变为orderString的, 千万不要觉得订单信息的加签过程也可以放在客户端完成 。
假设我们服务端收到了来自支付宝服务端的支付结果,即: 支付结果+数字签名 。
那么我们服务端就会对支付结果进行验签,怎么个验法呢?
❷ 求RSA算法的源代码(c语言)
这个是我帮个朋友写的,写的时候发现其实这个没那么复杂,不过,时间复杂度要高于那些成型了的,为人所熟知的rsa算法的其他语言实现.
#include
int
candp(int
a,int
b,int
c)
{
int
r=1;
b=b+1;
while(b!=1)
{
r=r*a;
r=r%c;
b--;
}
printf("%d",r);
return
r;
}
void
main()
{
int
p,q,e,d,m,n,t,c,r;
char
s;
{printf("input
the
p:\n");
scanf("%d\n",&p);
printf("input
the
q:\n");
scanf("%d%d\n",&p);
n=p*q;
printf("so,the
n
is
%3d\n",n);
t=(p-1)*(q-1);
printf("so,the
t
is
%3d\n",t);
printf("please
intput
the
e:\n");
scanf("%d",&e);
if(e<1||e>t)
{printf("e
is
error,please
input
again;");
scanf("%d",&e);}
d=1;
while
(((e*d)%t)!=1)
d++;
printf("then
caculate
out
that
the
d
is
%5d",d);
printf("if
you
want
to
konw
the
cipher
please
input
1;\n
if
you
want
to
konw
the
plain
please
input
2;\n");
scanf("%d",&r);
if(r==1)
{
printf("input
the
m
:"
);/*输入要加密的明文数字*/
scanf("%d\n",&m);
c=candp(m,e,n);
printf("so
,the
cipher
is
%4d",c);}
if(r==2)
{
printf("input
the
c
:"
);/*输入要解密的密文数字*/
scanf("%d\n",&c);
m=candp(c,d,n);
printf("so
,the
cipher
is
%4d\n",m);
printf("do
you
want
to
use
this
programe:yes
or
no");
scanf("%s",&s);
}while(s=='y');
}
}
❸ 如何使用RSA签名给给信息加密和解密
java">{
publicstaticfinalStringKEY_ALGORITHM="RSA";
_ALGORITHM="MD5withRSA";
_KEY="RSAPublicKey";
_KEY="RSAPrivateKey";
/**
*用私钥对信息生成数字签名
*
*@paramdata
*加密数据
*@paramprivateKey
*私钥
*
*@return
*@throwsException
*/
publicstaticStringsign(byte[]data,StringprivateKey)throwsException{
//解密由base64编码的私钥
byte[]keyBytes=decryptBASE64(privateKey);
//构造PKCS8EncodedKeySpec对象
=newPKCS8EncodedKeySpec(keyBytes);
//KEY_ALGORITHM指定的加密算法
KeyFactorykeyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//取私钥匙对象
PrivateKeypriKey=keyFactory.generatePrivate(pkcs8KeySpec);
//用私钥对信息生成数字签名
Signaturesignature=Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(priKey);
signature.update(data);
returnencryptBASE64(signature.sign());
}
/**
*校验数字签名
*
*@paramdata
*加密数据
*@parampublicKey
*公钥
*@paramsign
*数字签名
*
*@return校验成功返回true失败返回false
*@throwsException
*
*/
publicstaticbooleanverify(byte[]data,StringpublicKey,Stringsign)
throwsException{
//解密由base64编码的公钥
byte[]keyBytes=decryptBASE64(publicKey);
//构造X509EncodedKeySpec对象
X509EncodedKeySpeckeySpec=newX509EncodedKeySpec(keyBytes);
//KEY_ALGORITHM指定的加密算法
KeyFactorykeyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
//取公钥匙对象
PublicKeypubKey=keyFactory.generatePublic(keySpec);
Signaturesignature=Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initVerify(pubKey);
signature.update(data);
//验证签名是否正常
returnsignature.verify(decryptBASE64(sign));
}
/**
*解密<br>
*用私钥解密
*
*@paramdata
*@paramkey
*@return
*@throwsException
*/
publicstaticbyte[]decryptByPrivateKey(byte[]data,Stringkey)
throwsException{
//对密钥解密
byte[]keyBytes=decryptBASE64(key);
//取得私钥
=newPKCS8EncodedKeySpec(keyBytes);
KeyFactorykeyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
KeyprivateKey=keyFactory.generatePrivate(pkcs8KeySpec);
//对数据解密
Ciphercipher=Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE,privateKey);
returncipher.doFinal(data);
}
/**
*解密<br>
*用私钥解密
*
*@paramdata
*@paramkey
*@return
*@throwsException
*/
publicstaticbyte[]decryptByPublicKey(byte[]data,Stringkey)
throwsException{
//对密钥解密
byte[]keyBytes=decryptBASE64(key);
//取得公钥
X509EncodedKeySpecx509KeySpec=newX509EncodedKeySpec(keyBytes);
KeyFactorykeyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
KeypublicKey=keyFactory.generatePublic(x509KeySpec);
//对数据解密
Ciphercipher=Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE,publicKey);
returncipher.doFinal(data);
}
/**
*加密<br>
*用公钥加密
*
*@paramdata
*@paramkey
*@return
*@throwsException
*/
publicstaticbyte[]encryptByPublicKey(byte[]data,Stringkey)
throwsException{
//对公钥解密
byte[]keyBytes=decryptBASE64(key);
//取得公钥
X509EncodedKeySpecx509KeySpec=newX509EncodedKeySpec(keyBytes);
KeyFactorykeyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
KeypublicKey=keyFactory.generatePublic(x509KeySpec);
//对数据加密
Ciphercipher=Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE,publicKey);
returncipher.doFinal(data);
}
/**
*加密<br>
*用私钥加密
*
*@paramdata
*@paramkey
*@return
*@throwsException
*/
publicstaticbyte[]encryptByPrivateKey(byte[]data,Stringkey)
throwsException{
//对密钥解密
byte[]keyBytes=decryptBASE64(key);
//取得私钥
=newPKCS8EncodedKeySpec(keyBytes);
KeyFactorykeyFactory=KeyFactory.getInstance(KEY_ALGORITHM);
KeyprivateKey=keyFactory.generatePrivate(pkcs8KeySpec);
//对数据加密
Ciphercipher=Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE,privateKey);
returncipher.doFinal(data);
}
/**
*取得私钥
*
*@paramkeyMap
*@return
*@throwsException
*/
(Map<String,Object>keyMap)
throwsException{
Keykey=(Key)keyMap.get(PRIVATE_KEY);
returnencryptBASE64(key.getEncoded());
}
/**
*取得公钥
*
*@paramkeyMap
*@return
*@throwsException
*/
(Map<String,Object>keyMap)
throwsException{
Keykey=(Key)keyMap.get(PUBLIC_KEY);
returnencryptBASE64(key.getEncoded());
}
/**
*初始化密钥
*
*@return
*@throwsException
*/
publicstaticMap<String,Object>initKey()throwsException{
KeyPairGeneratorkeyPairGen=KeyPairGenerator
.getInstance(KEY_ALGORITHM);
keyPairGen.initialize(1024);
KeyPairkeyPair=keyPairGen.generateKeyPair();
//公钥
RSAPublicKeypublicKey=(RSAPublicKey)keyPair.getPublic();
//私钥
RSAPrivateKeyprivateKey=(RSAPrivateKey)keyPair.getPrivate();
Map<String,Object>keyMap=newHashMap<String,Object>(2);
keyMap.put(PUBLIC_KEY,publicKey);
keyMap.put(PRIVATE_KEY,privateKey);
returnkeyMap;
}
}
❹ java RSA算法实现256位密钥怎么做
【下载实例】本文介绍RSA2加密与解密,RSA2是RSA的加强版本,在密钥长度上采用2048, RSA2比RSA更安全,更可靠, 本人的另一篇文章RSA已经发表,有想了解的可以点开下面的RSA文章
❺ java字符串转换为一个RSA公钥问题,怎么解决
//将byte数组变成RSAPublicKey
publicRSAPublicKeybytes2PK(byte[]buf){
buf=Base64.decode(buf);
bytesize=buf[0];
bytesize2=buf[1];
byte[]b1=newbyte[size];
System.array(buf,2,b1,0,b1.length);
byte[]b2=newbyte[size2];
System.array(buf,b1.length+2,b2,0,b2.length);
BigIntegerB1=newBigInteger(b1);
BigIntegerB2=newBigInteger(b2);
RSAPublicKeySpecspec=newRSAPublicKeySpec(B1,B2);//存储的就是这两个大整形数
KeyFactorykeyFactory;
PublicKeypk=null;
try{
keyFactory=KeyFactory.getInstance("RSA");
pk=keyFactory.generatePublic(spec);
}catch(Exceptione){
e.printStackTrace();
}
return(RSAPublicKey)pk;
}
❻ Java RSA 加密解密中 密钥保存并读取,数据加密解密并保存读取 问题
帮你完善了下代码。
importjava.io.File;
importjava.io.FileOutputStream;
importjava.io.FileReader;
importjava.io.OutputStream;
importjava.io.PrintWriter;
importjava.io.Reader;
importjava.util.Map;
publicclassTest{
staticStringpublicKey;
staticStringprivateKey;
publicTest()throwsException{
//TODOAuto-generatedconstructorstub
Map<String,Object>keyMap=RSAUtils.genKeyPair();
publicKey=RSAUtils.getPublicKey(keyMap);
privateKey=RSAUtils.getPrivateKey(keyMap);
//保存密钥,名字分别为publicKey。txt和privateKey。txt;
PrintWriterpw1=newPrintWriter(newFileOutputStream(
"D:/publicKey.txt"));
PrintWriterpw2=newPrintWriter(newFileOutputStream(
"D:/privateKey.txt"));
pw1.print(publicKey);
pw2.print(privateKey);
pw1.close();
pw2.close();
//从保存的目录读取刚才的保存的公钥,
Stringpubkey=readFile("D:/publicKey.txt");//读取的公钥内容;
Stringdata=readFile("D:/1.txt");//需要公钥加密的文件的内容(如D:/1.txt)
byte[]encByPubKeyData=RSAUtils.encryptByPublicKey(data.getBytes(),
pubkey);
//将加密数据base64后写入文件
writeFile("D:/Encfile.txt",Base64Utils.encode(encByPubKeyData).getBytes("UTF-8"));
//加密后的文件保存在
Stringprikey=readFile("D:/privateKey.txt");//从保存的目录读取刚才的保存的私钥,
StringEncdata=readFile("D:/Encfile.txt");//刚才加密的文件的内容;
byte[]encData=Base64Utils.decode(Encdata);
byte[]decByPriKeyData=RSAUtils.decryptByPrivateKey(encData,prikey);
//解密后后的文件保存在D:/Decfile.txt
writeFile("D:/Decfile.txt",decByPriKeyData);
}
privatestaticStringreadFile(StringfilePath)throwsException{
FileinFile=newFile(filePath);
longfileLen=inFile.length();
Readerreader=newFileReader(inFile);
char[]content=newchar[(int)fileLen];
reader.read(content);
System.out.println("读取到的内容为:"+newString(content));
returnnewString(content);
}
privatestaticvoidwriteFile(StringfilePath,byte[]content)
throwsException{
System.out.println("待写入文件的内容为:"+newString(content));
FileoutFile=newFile(filePath);
OutputStreamout=newFileOutputStream(outFile);
out.write(content);
if(out!=null)out.close();
}
publicstaticvoidmain(String[]args)throwsException{
//TODOAuto-generatedmethodstub
newTest();
}
}
测试结果:
读取到的内容为:++lXfZxzNpeA+rHaxmeQ2qI+5ES9AF7G6KIwjzakKsA08Ly+1y3dp0BnoyHF7/Pj3AS28fDmE5piea7w36vp4E3Ts+F9vwIDAQAB
读取到的内容为:锘县ahaha
❼ 怎么用 python 模拟 js 里 JSEncrypt 模块的加密方式
PC登录新浪微博时,在客户端用js预先对用户名、密码都进行了加密,而且在POST之前会GET一组参数,这也将作为POST_DATA的一部分。这样,就不能用通常的那种简单方法来模拟POST登录(比如人人网)。
通过爬虫获取新浪微博数据,模拟登录是必不可少的。
1、在提交POST请求之前,需要GET获取四个参数(servertime,nonce,pubkey和rsakv),不是之前提到的只是获取简单的servertime,nonce,这里主要是由于js对用户名、密码加密方式改变了。
1.1 由于加密方式的改变,我们这里将使用到RSA模块,有关RSA公钥加密算法的介绍可以参考网络中的有关内容。下载并安装rsa模块:
下载:https//pypi.python.org/pypi/rsa/3.1.1
rsa模块文档地址:http//stuvel.eu/files/python-rsa-doc/index.html
根据自己的Python版本选择适合自己的rsa安装包(.egg),在win下安装需要通过命令行使用easy_install.exe(win上安装setuptool从这里下载:setuptools-0.6c11.win32-py2.6.exe 安装文件 )进行安装,例如:easy_install rsa-3.1.1-py2.6.egg,最终命令行下测试import rsa,未报错则安装成功。
1.2 获得以及查看新浪微博登录js文件
查看新浪通行证url (http//login.sina.com.cn/signup/signin.php)的源代码,其中可以找到该js的地址 http//login.sina.com.cn/js/sso/ssologin.js,不过打开后里面的内容是加密过的,可以在网上找个在线解密站点解密,查看最终用户名和密码的加密方式。
1.3 登录
登录第一步,添加自己的用户名(username),请求prelogin_url链接地址:
prelogin_url = 'http//login.sina.com.cn/sso/prelogin.php?entry=sso&callback=sinaSSOController.preloginCallBack&su=%s&rsakt=mod&client=ssologin.js(v1.4.4)' % username
使用get方法得到以下类似内容:
sinaSSOController.preloginCallBack({"retcode":0,"servertime":1362041092,"pcid":"gz-","nonce":"IRYP4N","pubkey":"","rsakv":"1330428213","exectime":1})
进而从中提取到我们想要的servertime,nonce,pubkey和rsakv。当然,pubkey和rsakv的值我们可以写死在代码中,它们是固定值。
2、之前username 经过BASE64计算:
复制代码 代码如下:
username_ = urllib.quote(username)
username = base64.encodestring(username)[:-1]
password经过三次SHA1加密,且其中加入了 servertime 和 nonce 的值来干扰。即:两次SHA1加密后,结果加上servertime和nonce的值,再SHA1算一次。
在最新的rsa加密方法中,username还是以前一样的处理;
password加密方式和原来有所不同:
2.1 先创建一个rsa公钥,公钥的两个参数新浪微博都给了固定值,不过给的都是16进制的字符串,第一个是登录第一步中的pubkey,第二个是js加密文件中的‘10001'。
这两个值需要先从16进制转换成10进制,不过也可以写死在代码里。这里就把10001直接写死为65537。代码如下:
复制代码 代码如下:
rsaPublickey = int(pubkey, 16)
key = rsa.PublicKey(rsaPublickey, 65537) #创建公钥
message = str(servertime) + '\t' + str(nonce) + '\n' + str(password) #拼接明文js加密文件中得到
passwd = rsa.encrypt(message, key) #加密
passwd = binascii.b2a_hex(passwd) #将加密信息转换为16进制。
2.2 请求通行证url:login_url =‘http//login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.4)'
需要发送的报头信息
复制代码 代码如下:
postPara = {
'entry': 'weibo',
'gateway': '1',
'from': '',
'savestate': '7',
'userticket': '1',
'ssosimplelogin': '1',
'vsnf': '1',
'vsnval': '',
'su': encodedUserName,
'service': 'miniblog',
'servertime': serverTime,
'nonce': nonce,
'pwencode': 'rsa2',
'sp': encodedPassWord,
'encoding': 'UTF-8',
'prelt': '115',
'rsakv' : rsakv,
'url': 'http//weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack',
'returntype': 'META'
}
请求的内容中添加了rsakv,将pwencode的值修改为rsa2,其他跟以前一致。
将参数组织好,POST请求。检验是否登录成功,可以参考POST后得到的内容中的一句 location.replace("http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack&retcode=101&reason=%B5%C7%C2%BC%C3%FB%BB%F2%C3%DC%C2%EB%B4%ED%CE%F3");
如果retcode=101则表示登录失败。登录成功后结果与之类似,不过retcode的值是0。
3、登录成功后,在body中的replace信息中的url就是我们下一步要使用的url。然后对上面的url使用GET方法来向服务器发请求,保存这次请求的Cookie信息,就是我们需要的登录Cookie了。