1. 程式人生 > >RSA加密與SHA簽名用法詳解

RSA加密與SHA簽名用法詳解

基礎知識

  1. 什麼是RSA?
    答:RSA是一種非對稱加密演算法,常用來對傳輸資料進行加密, 配合上數字摘要演算法,也可以進行文字簽名。

  2. 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<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;
}

RSA加密中的Padding

  1. RSA_PKCS1_PADDING 填充模式,最常用的模式
    要求: 輸入:必須 比 RSA 鑰模長(modulus) 短至少11個位元組, 也就是 RSA_size(rsa) – 11 如果輸入的明文過長,必須切割,然後填充。
    輸出:和modulus一樣長
    根據這個要求,對於1024bit的金鑰,block length = 1024/8 – 11 = 117 位元組

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

  3. 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;
    }