基于RSA算法的ios客户端加密和C#服务端解密的解决方案

By | 05月09日
Advertisement

来源:http://theosoft.net/

RSA是一种比较常用的非对称加密算法,其原理是基于大整数因数分解的计算安全,这里不做介绍。非对称加密的好处在于其密码分为公钥和私钥两部分,你可以随意分发你的公钥,让用户用来加密数据;等上传到服务器端后再用私钥就可以解密里面的数据。所以,这样的体系特别适合用于客户端–尤其使手机客户端的数据加密,而不用担心你的程序被反编译、破解后泄露了你的密码。

正是因为有着这么好的安全特性,早在年初,我还在使用windows mobile手机的时候,就把RSA算法写入了我的那个利用飞信发短信的客户端里。每当要发送短信时,客户端负责使用公钥将短信内容加密,并连同收件人一起传输到接收数据的服务器上。服务器收到信息后直接存储到数据库里。然后由另一台专门负责短信发送的服务器,每隔1分钟查询一次数据库,发现有需要发送的短信后,用私钥将其解密,并通过飞信发送出去。无论是否发送成功,最终都会再发送一条短信,向我报告本次发送的结果。

基于RSA算法的ios客户端加密和C#服务端解密的解决方案
基于RSA算法的ios客户端加密和C#服务端解密的解决方案

自从 10月份换了iphone 4后,第一周我就写了一个 ios上不加密的同样功能的程序,但是RSA的加密算法却让我纠结了很长时间。因为原来使用windows mobile的时候,RSA的公钥是以原始数据的形式保存在文件里的。但是到了ios上,苹果似乎并不允许直接使用原始的密钥。google了很久,虽然找了一个可能可行的办法,但是因为非常复杂,需要先将密钥导入手机内的密钥库,然后再取出来,试了很多次一直都没成功。后来在网上某位大牛的指点下发现,虽然ios无法直接使用原始的密钥加密数据,但是对证书的支持确非常好,于是花了2天的时间研究了使用证书加密数据的方法,效果非常好,呵呵。下面简单说一下使用证书加密数据的方法。

首先当然是要生成一张证书。微软的.Net framework SDK为我们提供了一个生成X.509数字证书的命令行工具Makecert.exe。

基于RSA算法的ios客户端加密和C#服务端解密的解决方案

打开.Net的控制台,使用如下命令生成证书:

makecert -sr LocalMachine -ss My -n CN=Theoservice -sky exchange -pe

Makecert命令的详细说明请参看微软Makecert.exe工具的文档:http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/cptools/html/cpgrfcertificatecreationtoolmakecertexe.asp

这样就生成了一张名为Theoservice的证书到你的电脑里。然后,开始->运行->MMC,打开MMC控制台。文件->添加/删除管理单元->添加按钮->选”证书”->添加->选”计算机账户”->关闭->确定,然后你就可以在 “个人->证书” 里看到刚才生成的证书了。证书采用1024位密钥加密。现在,你需要做得就是导出这张证书。如果你的服务器并不是本机,你首先需要导出一个带私钥的pfx格式的证书。导出时需要你填写密码来保护这张证书,然后将其导入到服务器上就好了。此外,你还需要导出一份不带私钥的cer格式的证书。这张证书只含有公钥,是用来和客户端一起发布出去用来加密数数据的。

基于RSA算法的ios客户端加密和C#服务端解密的解决方案
基于RSA算法的ios客户端加密和C#服务端解密的解决方案

证书已经有了,接下来就要写算法来加密和解秘数据了。先来看看C#服务器端的解密过程:

public class RSAHandler
{
const string CERT = “Theoservice”;
const int KEYLENGTH = 128;
const int BLOCKSIZE = KEYLENGTH - 11;

private static X509Certificate2 GetRSACertificate()
{
X509Certificate2 clientCert = null;
if (clientCert == null)
{
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
foreach (var certificate in store.Certificates)
{
if (certificate.GetNameInfo(X509NameType.SimpleName, false) == CERT)
{
clientCert = certificate;
break;
}
}
}

return clientCert;
}

public static RSACryptoServiceProvider GetPrivateKey()
{
var clientCert = GetRSACertificate();
var publicKey = (RSACryptoServiceProvider)clientCert.PrivateKey;

return publicKey;
}

public static string RSADecrypt(string rawText, RSACryptoServiceProvider rsa)
{
try
{
var encryptedBytes = Convert.FromBase64String(rawText);
int numBlock = encryptedBytes.Length / KEYLENGTH;
byte[] rawResult = new byte[0];
var buffer = new byte[KEYLENGTH];
for (var i = 0; i < numBlock; i++)
{
Array.Copy(encryptedBytes, i * KEYLENGTH, buffer, 0, buffer.Length);
var decryptedBytes = rsa.Decrypt(buffer, false);
var resultBuffer = new byte[rawResult.Length + decryptedBytes.Length];
Array.Copy(rawResult, resultBuffer, rawResult.Length);
Array.Copy(decryptedBytes, 0, resultBuffer, rawResult.Length, decryptedBytes.Length);
rawResult = resultBuffer;
}

var plaintext = Encoding.UTF8.GetString(rawResult);
return plaintext;
}
catch (CryptographicException e)
{
return e.Message;
}
}
}

这个类里面有两个public的函数:GetPrivateKey()和RSADecrypt()。前者用来从电脑的密钥库中找到指定的证书,并取出该证书的私钥;后者则使用密钥将数据解密。为了方便在存储和传输,所有加密后的数据都采用base64编码,所以RSADecrypt在解密数据之前首先做base64解码,然后计算密文的长度,分组解密。这里需要普及一下RSA分组加密的知识:因为采用了1024位的密钥,所以密钥长度为1024/8=128个byte。而C#默认采用#PKSC1的padding模式,每次最多可以加密128-11=117个byte。也就是说,RSA分组加密算法每次从明文里取<=117个byte,然后加密成128个byte的密文;解密的时候,每次就取128个byte的密文,将其解密成<=117个byte的明文。因为#PKSC1的padding模式每次padding的内容是随机的,所以即使是加密同一段明文,每次的结果也不一样,这大大的增加了数据安全性。最后把所有解密的结果连起来,识别成utf-8格式的字符串,就是我们之前加密的明文了。

再来看看手机客户端的加密算法:

- (SecKeyRef)getPublicKey{

NSString *certPath = [[NSBundle mainBundle] pathForResource:@”Theoservice” ofType:@”cer”];

SecCertificateRef myCertificate = nil;

NSData *certificateData = [[NSData alloc] initWithContentsOfFile:certPath];

myCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)certificateData);

SecPolicyRef myPolicy = SecPolicyCreateBasicX509();

SecTrustRef myTrust;

OSStatus status = SecTrustCreateWithCertificates(myCertificate,myPolicy,&myTrust);

SecTrustResultType trustResult;

if (status == noErr) {

status = SecTrustEvaluate(myTrust, &trustResult);

}

return SecTrustCopyPublicKey(myTrust);

}

- (NSData *)encrypt:(NSString *)plainText usingKey:(SecKeyRef)key error:(NSError **)err

{

size_t cipherBufferSize = SecKeyGetBlockSize(key);

uint8_t *cipherBuffer = NULL;

cipherBuffer = malloc(cipherBufferSize * sizeof(uint8_t));

memset((void *)cipherBuffer, 0×0, cipherBufferSize);

NSData *plainTextBytes = [plainText dataUsingEncoding:NSUTF8StringEncoding];

int blockSize = cipherBufferSize – 11;

int numBlock = (int)ceil([plainTextBytes length] / (double)blockSize);

NSMutableData *encryptedData = [[NSMutableData alloc] init];

for (int i=0; i<numBlock; i++) {

int bufferSize = MIN(blockSize,[plainTextBytes length] – i * blockSize);

NSData *buffer = [plainTextBytes subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];

OSStatus status = SecKeyEncrypt(key, kSecPaddingPKCS1,

(const uint8_t *)[buffer bytes],

[buffer length], cipherBuffer,

&cipherBufferSize);

if (status == noErr)

{

NSData *encryptedBytes = [[[NSData alloc]

initWithBytes:(const void *)cipherBuffer

length:cipherBufferSize] autorelease];

[encryptedData appendData:encryptedBytes];

}

else

{

*err = [NSError errorWithDomain:@"errorDomain" code:status userInfo:nil];

NSLog(@”encrypt:usingKey: Error: %d”, status);

return nil;

}

}

if (cipherBuffer)

{

free(cipherBuffer);

}

NSLog(@”Encrypted text (%d bytes): %@”,

[encryptedData length], [encryptedData description]);

return encryptedData;

}

加密算法和解密差不多,就是每次取117个byte的明文并加密成128个byte的密文,最后连起来做base64编码。需要注意的是公钥的获取方法getPublicKey。前面不是导出了一张只含有公钥的cer格式的证书吗?现在把它加到程序的Resources里,这里取名Theoservice.cer。然后通过[[NSBundle mainBundle] pathForResource:@”Theoservice” ofType:@”cer”]就可以把里面的内容读出来,生成SecCertificateRef实体。然后用分别创建一个SecPolicyRef和SecTrustRef,就可以获取到这个公钥的SecKeyRef了,其实还是蛮简单的,不是吗?!

最后,来show一下最新的iphone版短信发送程序,哈哈!

基于RSA算法的ios客户端加密和C#服务端解密的解决方案
基于RSA算法的ios客户端加密和C#服务端解密的解决方案
基于RSA算法的ios客户端加密和C#服务端解密的解决方案
基于RSA算法的ios客户端加密和C#服务端解密的解决方案
基于RSA算法的ios客户端加密和C#服务端解密的解决方案

Similar Posts:

  • 基于RSA算法在asp中加密与解密对应的函数

    在ASP中加密方法有对应的解密方法好象不多,现在根据前辈资料整理出在asp中加密与解密函数,根据RSA 算法实现的. 什么是RSA? RSA算法是第一个能同时用于加密和数字签名的算法,也易于理解和操作. RSA是被研究得最广泛的公钥算法,从提出到现在已近二十年,经历了各种攻击的考验,逐渐为人们接受,普遍认为是目前最优秀的公钥方案之一.RSA的安全性依赖于大数的因子分解,但并没有从理论上证明破译RSA的难度与大数分解难度等价.即RSA的重大缺陷是无法从理论上把握它的保密性能如何,而且密码学界多数人

  • Javascript端加密java服务端解密

    Javascript端加密java服务端解密 通常我们会通过htts来保证传输安全,但如果我们不用https,如何通过javascript来保证浏览器端发送的参数进行加密,并且通过RSA算法来处理. 这里我们可以利用jquery的一个加密插件jcryption来处理,可以参考 http://jcryption.org/#examples 现在版本是3.0 但是没有java端的实现,下次有时间再研究.现在这个用的是1.1的版本 这个可以在 http://linkwithweb.googlecode

  • 基于 IOCP 的通用异步 Windows Socket TCP 高性能服务端组件的设计与实现

    基于 IOCP 的通用异步 Windows Socket TCP 高性能服务端组件的设计与实现 设计概述 服务端通信组件的设计是一项非常严谨的工作,其中性能.伸缩性和稳定性是必须考虑的硬性质量指标,若要把组件设计为通用组件提供给多种已知或未知的上层应用使用,则设计的难度更会大大增加,通用性.可用性和灵活性必须考虑在内. 现以一个基于 IOCP 的通用异步 Windows Socket TCP 服务端组件为例子,讲述其设计与实现相关的问题,希望能引发大家的思考,对大家日后开展相关类似工作时有所帮助

  • JavaWeb-1-IOS或Android客户端上传图片到Java服务端存到数据库,再从数据库取出下载下来的过程

    前言:一直想写段代码实现IOS或Android客户端上传图片到Java服务端存到数据库,再从数据库取出下载下来的过程,今天终于忍不住将代码吐了出来,这里仅提供了Javaweb端servlet的响应代码.我觉得客户端用base64转换图片比较简单就在此没有提供,如果需要,留言即可,天天在线. 推荐李刚疯狂系列丛书:http://download.csdn.net/detail/iot_li/9188759 李刚疯狂swift源代码: http://download.csdn.net/detail/

  • Asp.Net 客户端JS如何访问服务端控件的值?

    Asp.Net 客户端JS如何访问服务端控件的值? 这是asp.net 初学者经常提出的问题. Asp.Net 中有三种不同形式的控件: html控件.html服务端控件.服务端控件. 服务端控件:服务端控件的提出大大方便了程序员的编程工作,程序员可以在后台对这些控件进行操作,或都将数据查询结果输出到客户端.典形的服务端控件有: TextBox控件.DropDownList控件.Button控件.DataGridView控件.DataGridView是一个数据输出控件,具有十分强大的功能,有了这

  • JST+JSON+AJAX——使用客户端js模版代替服务端数据绑定

    本文给出一个通过组合使用JST,JSON和AJAX技术,使用客户端js模版代替服务端数据绑定的范例.很显然的,使用客户端数据绑定代替服务端数据绑定能够大大减少服务端的内存和CPU消耗,在硬件不变的情况下,大大提升服务器负载能力. 名词解释 首先,先简单介绍一下JST和JSON.都直接引用官方介绍了. JST (JavaScript Templates) For web application developers, the JavaScript Templates engine from Tri

  • JAVASCRIPT 客户端加密 PHP服务端解密

    hi 大家好. 我用 crypto-js 在客户端加密: function encrypt(str) { var key = $.cookie('key'); var encrypted = CryptoJS.TripleDES.encrypt(str, key, {mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.ZeroPadding}); return encrypted; } 服务端: $decrypt = mcrypt_decrypt (MC

  • 基于c/s的通讯工具类似qq,服务端和客户端应该怎样分工?

    如果要实现添加好友删除好友这样的操作,在客户端的界面上进行操作,但是真正数据库里的东西确实在服务端上进行添加或者删除的. 那么当鼠标事件触发的时候,是否应在事件监听里面写上发送给服务器的操作? 但是应该怎样把操作的指令发给服务器呢?不是很懂 还有服务器要怎样实现处理两个以上客户端发来的指令呢?求详解,最好能有代码示例 --cut-- dongshimou在2015-12-24 04:17:54回答到: 不是很明白你的意思,你发送聊天消息给服务器,跟你发送删好友这类命令不是一个道理么? 我刚好在写

  • android开发步步为营之4:客户端通过http和服务端进行交互

    可以说目前市场上的android应用和游戏,客户端和服务端交互的主要方式是通过http请求实现的,因为方便,执行速度也快.本文主要介绍通过org.apache.http.client.HttpClient和java.net.HttpURLConnection实现http请求.包括Get和Post两种方式的调用方式.HttpClient需要和HttpGet.HttpPost.HttpEntity这几个类相互配合实现和服务端交互.HttpURLConnection则主要和InputStreamRea

  • 【android,7】7.android在web下的应用-将客户端信息提交到服务端

    将客户端提交到服务端: 一.用户登录将用户名和密码传递个服务器端:get方式提交 1.设置布局:layout/main,xml <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orienta

Tags: