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

jopen 8年前發布 | 63K 次閱讀 加密解密 Java 加密/解密軟件包

本文是投稿文章,作者: Panda_iOS

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

基礎知識

什么是RSA?

答:RSA是一種非對稱加密算法,常用來對傳輸數據進行加密,配合上數字摘要算法,也可以進行文字簽名。

RSA加密中padding?

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

加密和加簽有什么區別?

答:加密:公鑰放在客戶端,并使用公鑰對數據進行加密,服務端拿到數據后用私鑰進行解密。

加簽:私鑰放在客戶端,并使用私鑰對數據進行加簽,服務端拿到數據后用公鑰進行驗簽。

前者完全為了加密;后者主要是為了防惡意攻擊,防止別人模擬我們的客戶端對我們的服務器進行攻擊,導致服務器癱瘓。

基本原理

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 fileopenssl req -new -key private_key.pem -out rsaCertReq.csr

  • 生成certification 并指定過期時間openssl x509 -req -days3650-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; }</pre>

-

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

加密與解密

- (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 nbsp="" i="" unsigned="" long="" buffersize="" nsdata="" buffer="" osstatus="" status="" if="" return="" nil="" encryptedbytes="" encrypteddata="" appenddata:encryptedbytes="" cipherbuffer="" free=""></blockcount></pre>

-

- (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; }</pre>

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; }</pre>

    -

          //這邊對簽名的數據進行驗證 驗簽成功,則返回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; }</pre>

      文章到此就結束了,希望這篇文章對大家有所幫助。想看demo的請點擊: XYRSACryptor

來自: http://www.cocoachina.com/ios/20160112/14940.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!