一篇搞定RSA加密與SHA簽名|與Java完全同步

看到這篇文章的同學可幸福了糕韧,當時在做RSA加密與簽名的時候網上的資料簡直不要太老酱吝,做完后實在是忍受不下去了禽车,這篇文章我會詳細講解iOS如何實現RSA加密與簽名艇搀,并且與Java完全同步臀蛛,這是我的第二篇博客亲桦,若有什么不足之處還請大家指教。

基礎知識

<p>

  1. 什么是RSA浊仆?
    答:RSA是一種非對稱加密算法客峭,常用來對傳輸數據進行加密,配合上數字摘要算法抡柿,也可以進行文字簽名舔琅。

  2. RSA加密中padding?
    答:padding即填充方式洲劣,由于RSA加密算法中要加密的明文是要比模數小的备蚓,padding就是通過一些填充方式來限制明文的長度。后面會詳細介紹padding的幾種模式以及分段加密闪檬。

  3. 加密和加簽有什么區(qū)別星著?
    答:加密:公鑰放在客戶端,并使用公鑰對數據進行加密粗悯,服務端拿到數據后用私鑰進行解密虚循;
    加簽:私鑰放在客戶端,并使用私鑰對數據進行加簽样傍,服務端拿到數據后用公鑰進行驗簽横缔。
    前者完全為了加密;后者主要是為了防惡意攻擊衫哥,防止別人模擬我們的客戶端對我們的服務器進行攻擊茎刚,導致服務器癱瘓。

基本原理<p>

RSA使用“密鑰對”對數據進行加密解密撤逢,在加密解密前需要先生存公鑰(Public Key)和私鑰(Private Key)膛锭。
公鑰(Public key): 用于加密數據. 用于公開, 一般存放在數據提供方, 例如iOS客戶端粮坞。
私鑰(Private key): 用于解密數據. 必須保密, 私鑰泄露會造成安全問題。
iOS中的Security.framework提供了對RSA算法的支持初狰,這種方式需要對密匙對進行處理, 根據public key生成證書, 通過private key生成p12格式的密匙莫杈。想想jave直接用字符串進行加密解密簡單多了。(⊙o⊙)…

實戰(zhàn)

<p>

證書生成

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<p>

//根據你的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;
    }

加密與解密 <p>

- (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<blockCount; i++) {
    unsigned long bufferSize = MIN(blockSize , [data length] - i * blockSize);
    NSData *buffer = [data subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
    OSStatus status = SecKeyEncrypt(key, kSecPaddingPKCS1, (const uint8_t *)[buffer bytes], [buffer length], cipherBuffer, &cipherBufferSize);

    if (status != noErr) {
        return nil;
    }
    
    NSData *encryptedBytes = [[NSData alloc] initWithBytes:(const void *)cipherBuffer length:cipherBufferSize];
    [encryptedData appendData:encryptedBytes];
    }

  if (cipherBuffer){
    free(cipherBuffer);
  }

  return encryptedData;
  }
    • (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 < blockCount; i++) {
    unsigned long bufferSize = MIN(blockSize , [data length] - i * blockSize);
    NSData *buffer = [data subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];

      size_t cipherLen = [buffer length];
      void *cipher = malloc(cipherLen);
      [buffer getBytes:cipher length:cipherLen];
      size_t plainLen = SecKeyGetBlockSize(key);
      void *plain = malloc(plainLen);
      
      OSStatus status = SecKeyDecrypt(key, kSecPaddingPKCS1, cipher, cipherLen, plain, &plainLen);
      
      if (status != noErr) {
          return nil;
      }
      
      NSData *decryptedBytes = [[NSData alloc] initWithBytes:(const void *)plain length:plainLen];
      [decryptedData appendData:decryptedBytes];
    

    }

    return decryptedData;
    }
    <p>

RSA加密中的Padding<p>
  • RSA_PKCS1_PADDING 填充模式,最常用的模式
    要求: 輸入:必須 比 RSA 鑰模長(modulus) 短至少11個字節(jié), 也就是 RSA_size(rsa) – 11 如果輸入的明文過長捉片,必須切割聋伦,然后填充。
    輸出:和modulus一樣長
    根據這個要求界睁,對于1024bit的密鑰觉增,block length = 1024/8 – 11 = 117 字節(jié)

  • RSA_PKCS1_OAEP_PADDING
    輸入:RSA_size(rsa) – 41
    輸出:和modulus一樣長

  • RSA_NO_PADDING  不填充
    輸入:可以和RSA鑰模長一樣長,如果輸入的明文過長翻斟,必須切割逾礁, 然后填充
    輸出:和modulus一樣長

簽名與驗證<p>

//對數據進行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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市债热,隨后出現的幾起案子砾嫉,更是在濱河造成了極大的恐慌,老刑警劉巖窒篱,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件焕刮,死亡現場離奇詭異,居然都是意外死亡墙杯,警方通過查閱死者的電腦和手機配并,發(fā)現死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來高镐,“玉大人溉旋,你說我怎么就攤上這事〖邓瑁” “怎么了观腊?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵邑闲,是天一觀的道長。 經常有香客問我梧油,道長监憎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任婶溯,我火速辦了婚禮,結果婚禮上偷霉,老公的妹妹穿的比我還像新娘迄委。我一直安慰自己,他們只是感情好类少,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布叙身。 她就那樣靜靜地躺著,像睡著了一般硫狞。 火紅的嫁衣襯著肌膚如雪信轿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天残吩,我揣著相機與錄音财忽,去河邊找鬼。 笑死泣侮,一個胖子當著我的面吹牛即彪,可吹牛的內容都是我干的。 我是一名探鬼主播活尊,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼隶校,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蛹锰?” 一聲冷哼從身側響起深胳,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎铜犬,沒想到半個月后舞终,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡癣猾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年权埠,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片煎谍。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡攘蔽,死狀恐怖,靈堂內的尸體忽然破棺而出呐粘,到底是詐尸還是另有隱情满俗,我是刑警寧澤转捕,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站唆垃,受9級特大地震影響五芝,放射性物質發(fā)生泄漏。R本人自食惡果不足惜辕万,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一枢步、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧渐尿,春花似錦醉途、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至凉夯,卻和暖如春货葬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背劲够。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工震桶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人征绎。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓尼夺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親炒瘸。 傳聞我的和親對象是個殘疾皇子淤堵,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內容

  • 看到這篇文章的同學可幸福了,當時在做RSA加密與簽名的時候網上的資料簡直不要太老顷扩,做完后實在是忍受不下去了拐邪,這篇文...
    Q6尐漒閱讀 541評論 0 0
  • 基礎知識 什么是RSA?答:RSA是一種非對稱加密算法隘截,常用來對傳輸數據進行加密扎阶,配合上數字摘要算法,也可以進行文...
    德山_閱讀 1,342評論 0 1
  • 嘟噥嘟噥:最近接到一個任務:在客戶端動態(tài)生成RSA密鑰對婶芭,然后向服務器發(fā)送這個密鑰對中的公鑰字符串东臀,由服務器進行公...
    TimmyR閱讀 8,036評論 19 21
  • 本以為事隔幾年沒了感覺 沒想到再碰觸還會疼到麻木 原來沒了骨髓 壞了神經 還依舊能感覺到痛
    你是我見過的最美風景閱讀 269評論 0 0
  • 概述 本文將對移動醫(yī)療領域的YC——Rock Health進行分析,表達個人觀點公司介紹位于舊金山的 Rock H...
    曾樑閱讀 948評論 0 1