一篇搞定RSA加密与SHA签名|与Java完全同步

看到这篇文章的同学可幸福了,当时在做RSA加密与签名的时候网上的资料简直不要太老,做完后实在是忍受不下去了,这篇文章我会详细讲解iOS如何实现RSA加密与签名,并且与Java完全同步,这是我的第二篇博客,若有什么不足之处还请大家指教。

基础知识

  1. 什么是RSA?

    答:RSA是一种非对称加密算法,常用来对传输数据进行加密,配合上数字摘要算法,也可以进行文字签名。

  2. RSA加密中padding?

    答:padding即填充方式,由于RSA加密算法中要加密的明文是要比模数小的,padding就是通过一些填充方式来限制明文的长度。后面会详细介绍padding的几种模式以及分段加密。

  3. 加密和加签有什么区别?

    答:加密:公钥放在客户端,并使用公钥对数据进行加密,服务端拿到数据后用私钥进行解密;

    加签:私钥放在客户端,并使用私钥对数据进行加签,服务端拿到数据后用公钥进行验签。

    前者完全为了加密;后者主要是为了防恶意攻击,防止别人模拟我们的客户端对我们的服务器进行攻击,导致服务器瘫痪。

基本原理

RSA使用“密钥对”对数据进行加密解密,在加密解密前需要先生存公钥(Public Key)和私钥(Private Key)。

公钥(Public key): 用于加密数据. 用于公开, 一般存放在数据提供方, 例如iOS客户端。

私钥(Private key): 用于解密数据. 必须保密, 私钥泄露会造成安全问题。

iOS中的Security.framework提供了对RSA算法的支持,这种方式需要对密匙对进行处理, 根据public key生成证书, 通过private key生成p12格式的密匙。想想jave直接用字符串进行加密解密简单多了。(⊙o⊙)…

实战

证书生成

RSA加密这块公钥、私钥必不可少的。Apple是不支持直接使用字符串进行加密解密的,推荐使用p12文件。这边教大家去生成在加密中使用到的所有文件,并提供给Java使用,想当年这个公钥私钥搞了半天了。 %>_

  • 生成模长为1024bit的私钥

    openssl genrsa -out private_key.pem 1024

  • 生成certification require file

    openssl req -new -key private_key.pem -out rsaCertReq.csr

  • 生成certification 并指定过期时间

    openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey private_key.pem -out rsaCert.crt

  • 生成公钥供iOS使用

    openssl x509 -outform der -in rsaCert.crt -out public_key.der

  • 生成私钥供iOS使用 这边会让你输入密码,后期用到在生成secKeyRef的时候会用到这个密码

    openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt

  • 生成pem结尾的公钥供Java使用

    openssl rsa -in private_key.pem -out rsa_public_key.pem -pubout

  • 生成pem结尾的私钥供Java使用openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt

以上所有的步骤都是在终端下完成的哦 (^__^)

生成公钥和私钥的secKeyRef

//根据你的p12文件生成私钥对应的SecKeyRef 这边返回若是nil 请检查你p12文件的生成步骤

- (SecKeyRef)getPrivateKeyRefrenceFromData:(NSData*)p12Data password:(NSString*)password {

SecKeyRef privateKeyRef = NULL;

NSMutableDictionary * options = [[NSMutableDictionary alloc] init];

[options setObject: password forKey:(__bridge id)kSecImportExportPassphrase];

CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);

OSStatus securityError = SecPKCS12Import((__bridge CFDataRef) p12Data, (__bridge CFDictionaryRef)options, &items);

if (securityError == noErr && CFArrayGetCount(items) > 0) {

CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);

SecIdentityRef identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);

securityError = SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);

if (securityError != noErr) {

privateKeyRef = NULL;

}

}

CFRelease(items);

return privateKeyRef;

}

//根据你的der文件公钥对应的SecKeyRef

- (SecKeyRef)getPublicKeyRefrenceFromeData:    (NSData*)derData {

SecCertificateRef myCertificate = SecCertificateCreateWithData(kCFAllocatorDefault, (__bridge CFDataRef)derData);

SecPolicyRef myPolicy = SecPolicyCreateBasicX509();

SecTrustRef myTrust;

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

SecTrustResultType trustResult;

if (status == noErr) {

status = SecTrustEvaluate(myTrust, &trustResult);

}

SecKeyRef securityKey = SecTrustCopyPublicKey(myTrust);

CFRelease(myCertificate);

CFRelease(myPolicy);

CFRelease(myTrust);

return securityKey;

}

加密与解密

- (NSData*)rsaEncryptData:(NSData*)data {

SecKeyRef key = [self getPublicKey];

size_t cipherBufferSize = SecKeyGetBlockSize(key);

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

size_t blockSize = cipherBufferSize - 11;

size_t blockCount = (size_t)ceil([data length] / (double)blockSize);

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

for (int i=0; i

- (NSData*)rsaDecryptData:(NSData*)data {

SecKeyRef key = [self getPrivatKey];

size_t cipherBufferSize = SecKeyGetBlockSize(key);

size_t blockSize = cipherBufferSize;

size_t blockCount = (size_t)ceil([data length] / (double)blockSize);

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

for (int i = 0; i

RSA加密中的Padding

  • RSA_PKCS1_PADDING 填充模式,最常用的模式

    要求: 输入:必须 比 RSA 钥模长(modulus) 短至少11个字节, 也就是 RSA_size(rsa) – 11 如果输入的明文过长,必须切割,然后填充。

    输出:和modulus一样长

    根据这个要求,对于1024bit的密钥,block length = 1024/8 – 11 = 117 字节

  • RSA_PKCS1_OAEP_PADDING

    输入:RSA_size(rsa) – 41

    输出:和modulus一样长

  • RSA_NO_PADDING  不填充

    输入:可以和RSA钥模长一样长,如果输入的明文过长,必须切割, 然后填充

    输出:和modulus一样长

签名与验证

//对数据进行sha256签名

- (NSData *)rsaSHA256SignData:(NSData *)plainData {

SecKeyRef key = [self getPrivatKey];

size_t signedHashBytesSize = SecKeyGetBlockSize(key);

uint8_t* signedHashBytes = malloc(signedHashBytesSize);

memset(signedHashBytes, 0x0, signedHashBytesSize);

size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;

uint8_t* hashBytes = malloc(hashBytesSize);

if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], hashBytes)) {

return nil;

}

SecKeyRawSign(key,

kSecPaddingPKCS1SHA256,

hashBytes,

hashBytesSize,

signedHashBytes,

&signedHashBytesSize);

NSData* signedHash = [NSData dataWithBytes:signedHashBytes

length:(NSUInteger)signedHashBytesSize];

if (hashBytes)

free(hashBytes);

if (signedHashBytes)

free(signedHashBytes);

return signedHash;

}

//这边对签名的数据进行验证 验签成功,则返回YES

- (BOOL)rsaSHA256VerifyData:(NSData *)plainData     withSignature:(NSData *)signature {

SecKeyRef key = [self getPublicKey];

size_t signedHashBytesSize = SecKeyGetBlockSize(key);

const void* signedHashBytes = [signature bytes];

size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;

uint8_t* hashBytes = malloc(hashBytesSize);

if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], hashBytes)) {

return NO;

}

OSStatus status = SecKeyRawVerify(key,

kSecPaddingPKCS1SHA256,

hashBytes,

hashBytesSize,

signedHashBytes,

signedHashBytesSize);

return status == errSecSuccess;

}

文章到此就结束了,希望这篇文章对大家有所帮助。想看demo的请点击:XYRSACryptor

https://github.com/panxianyue/RSACryptor.git

时间: 2024-10-16 03:38:08

一篇搞定RSA加密与SHA签名|与Java完全同步的相关文章

C#中RSA加密解密和签名与验证的实现

RSA加密算法是一种非对称加密算法.在公钥加密标准和电子商业中RSA被广泛使用.RSA是1977年由罗纳德•李维斯特(Ron Rivest).阿迪•萨莫尔(Adi Shamir)和伦纳德•阿德曼(Leonard Adleman)一起提出的.当时他们三人都在麻省理工学院工作.RSA就是他们三人姓氏开头字母拼在一起组成的..Net的推出,我们能够利用.Net Framework中的类提供的加密服务来保证数据安全.目前应用较为广泛的加密方法是使用RSA算法进行加密.在.Net Framework中与R

一篇搞定SQL语句

首先,你要知道SQL语句是常见数据库的查询语言,在关系型数据库里,表间关系有三种,通俗说就是爱情,亲情,友情,其中爱情在道德上说的是一对一,亲情就想到父母,你只有一个父亲或一个母亲,而一个当爹的就有可能有多个孩子,这就是一对多,而友情,你有多个朋友,你的某个朋友也有包括你在内的多个朋友,这就是多对多 其次,两张怎么建立上述的表间关系呢,比如一对多或多对多,有一种神奇的东西叫做外键,就一张表的列值在另外一张表的列有所对应,一对多就是一个外键,多对多就两个外键 最后,什么关系,什么操作,直接见实例就

一篇搞定SQLAlchemy--关系对象映射

要使用SQLAlchemy,必须先下载这个模块 pip3 install sqlalchemy 或 pycharm File--> Settings-->project...-->Project Interpreter-->右上+-->搜索你要安装的模块 源码安装,源码下载地址:https://www.oschina.net/news/84998/sqlalchemy-1-1-10 开始操作前,你必须清楚SQLAlchemy实现操作数据库的原理,SQLAlchemy本身是无法

一篇搞定Python正则表达式

1. 正则表达式语法 1.1 字符与字符类 1 特殊字符:\.^$?+*{}[]()| 以上特殊字符要想使用字面值,必须使用\进行转义 2 字符类    1. 包含在[]中的一个或者多个字符被称为字符类,字符类在匹配时如果没有指定量词则只会匹配其中的一个. 2. 字符类内可以指定范围,比如[a-zA-Z0-9]表示a到z,A到Z,0到9之间的任何一个字符 3. 左方括号后跟随一个^,表示否定一个字符类,比如[^0-9]表示可以匹配一个任意非数字的字符. 4. 字符类内部,除了\之外,其他特殊字符

一篇搞定spring Jpa操作数据库

开始之前你必须在项目配置好数据库,本文使用的spring boot,相比spring,spring boot省去了很多各种对以来组件复杂的配置,直接在pom配置组件,完后会自动帮我们导入组件 <!-- 导入SpringDataJPA的坐标 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa<

一篇搞定Vuex

1.简介 首先,你必须明显明白vuex到底是干啥的,主要解决开发中的哪些问题? Vuex是一个专门为Vue.js应用程序开发的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式变化 说白了,就是:提供了那么一个公共存储的地方,存放各个组件的数据,组件访问数据和操作数据访问这个地方即可 所以,Vuex主要解决是组件间数据传递问题,尤其是嵌套组件,项目一大时,父子组件间的数据传递就非常麻烦了,而Vuex则提供了一种集中管理组件数据的方案,当然小型项目就没

一篇搞定vue请求和跨域

vue本身不支持发送AJAX请求,需要使用vue-resource.axios等插件实现 axios是一个基本Promise的HTTP请求客户端,用来发送请求,也是vue2.0官方推荐的,同时不再对vue-resource进行更新和维护 axios发送AJAX请求 安装axios npm install axios -S 基本用法 axios([options]) axios.get(url[,options])  传参方式:1.通过url 传参   2.通过params选项传参 axios.p

Java并发:一篇搞定线程池

原文地址:https://www.nowcoder.com/discuss/152050?type=0&order=0&pos=6&page=0 本文是在原文的基础+理解,想要系统学习,请看原文地址. 线程池介绍 1.1 线程池的概念 线程池(thread pool): 一种线程使用模式.线程的创建销毁是十分消耗资源的(线程创建消耗内存.线程上下文切换从消耗CPU资源).使用线程池可以更加充分的协调应用CPU.内存.网络.I/O等系统资源.在程序启动首先创建线程,在程序启动后可以将

Python下RSA加密/解密, 签名/验证

原文是py2环境,而我的环境是py3,所以对原代码做了修改:decode(), encode() import rsa # 生成密钥 (pubkey, privkey) = rsa.newkeys(1024) # 保存密钥 with open('public.pem','w+') as f: f.write(pubkey.save_pkcs1().decode()) with open('private.pem','w+') as f: f.write(privkey.save_pkcs1().