數據加密:RSA 密鑰

生成密鑰對

生成密鑰對的過程就是計算(e,d,n)的過程,在 iOS 中可使用原生的方法來生成密鑰對,同時也能夠藉助一些第三方的庫好比openssl。git

iOS 原生方法github

- (BOOL)generateSecKeyPairWithKeySize:(NSUInteger)keySize publicKeyRef:(SecKeyRef *)publicKeyRef privateKeyRef:(SecKeyRef *)privateKeyRef{
	OSStatus sanityCheck = noErr;
	if (keySize == 512 || keySize == 1024 || keySize == 2048) {
		NSMutableDictionary * keyPairAttr = [[NSMutableDictionary alloc] init];
		[keyPairAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType];
		[keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(id)kSecAttrKeySizeInBits];
		sanityCheck = SecKeyGeneratePair((CFDictionaryRef)keyPairAttr, publicKeyRef, privateKeyRef);
		if (sanityCheck == noErr && publicKeyRef != NULL && privateKeyRef != NULL) {
			return YES;
		}
	}
	return NO;
}
複製代碼

openssl安全

+ (BOOL)generateRSAKeyPairWithKeySize:(int)keySize publicKey:(RSA **)publicKey privateKey:(RSA **)privateKey {
    if (keySize == 512 || keySize == 1024 || keySize == 2048) {
        RSA *rsa = RSA_generate_key(keySize,RSA_F4,NULL,NULL);
        if (rsa) {
            *privateKey = RSAPrivateKey_dup(rsa);
            *publicKey = RSAPublicKey_dup(rsa);
            if (publicKey && privateKey) {
                return YES;
            }
        }
    }

    return NO;
}
複製代碼

密鑰格式

生成的密鑰對的過程是比較簡單的,只要調用相應的方法就能夠了。可是加密這個東西每每是須要跨平臺執行的,通常都會有分發公鑰這個過程,而每一個平臺生成的密鑰格式卻都是不同的,這裏就須要對密鑰進行格式轉換。bash

用 iOS 原生的方法生成的密鑰爲 SecKeyRef 格式的,用 openssl 生成的密鑰爲 RSA 格式的,用個命令上生成的密鑰會是 .pem 或 .cer 格式的文件。可是無論密鑰的表現形式是什麼,對於公鑰來講,組成就是(e,n),私鑰就是(d,n)。ui

1.SecKeyRef

對於 SecKeyRef 這個格式並無提供一些能夠直接操做的API,可是 SecKeyRef 能夠轉換爲 NSData 格式,這個 data 的格式其實就是 cer 格式。以公鑰爲例:加密

static NSString * const kTransfromIdenIdentifierPublic = @"kTransfromIdenIdentifierPublic";

- (NSData *)publicKeyBitsFromSecKey:(SecKeyRef)givenKey {
  NSData *peerTag = [kTransfromIdenIdentifierPublic dataUsingEncoding:NSUTF8StringEncoding];

  OSStatus sanityCheck = noErr;
  NSData * keyBits = nil;

  NSMutableDictionary * queryKey = [[NSMutableDictionary alloc] init];
  [queryKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
  [queryKey setObject:(id)kSecAttrKeyClassPublic forKey:(id)kSecAttrKeyClass];
  [queryKey setObject:peerTag forKey:(__bridge id)kSecAttrApplicationTag];

  [queryKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];

  [queryKey setObject:(__bridge id)givenKey forKey:(__bridge id)kSecValueRef];
  [queryKey setObject:@YES forKey:(__bridge id)kSecReturnData];

  CFTypeRef result;
  sanityCheck = SecItemAdd((__bridge CFDictionaryRef) queryKey, &result);
  if (sanityCheck == errSecSuccess) {
  	keyBits = CFBridgingRelease(result);

  	(void)SecItemDelete((__bridge CFDictionaryRef) queryKey);
  }

  return keyBits;
}
複製代碼

固然這個過程也是可逆的,具體的方法能夠下載Demo查看。spa

2.RSA

若是使用 openssl 的話,能夠將 RSA 轉換成 pem 格式的字符串,一樣以公鑰爲例:3d

- (NSString *)PEMFormatPublicKey:(RSA *)rsaPublic
{
    BIO *bio = BIO_new(BIO_s_mem());
    PEM_write_bio_RSA_PUBKEY(bio, rsaPublic);

    BUF_MEM *bptr;
    BIO_get_mem_ptr(bio, &bptr);
    BIO_set_close(bio, BIO_NOCLOSE); /* So BIO_free() leaves BUF_MEM alone */
    BIO_free(bio);

    return [NSString stringWithUTF8String:bptr->data];
}
複製代碼

一樣這個過程也是可逆的,能夠將 pem 格式的字符串轉換成 RSA 格式的密鑰。具體的方法能夠下載Demo查看。code

三、pem 格式

這是一個 pem 格式的公鑰,以 -----BEGIN PUBLIC KEY----- 開頭,以 -----END PUBLIC KEY-----結尾。這個是固定的格式,中間部分是一個 base64 格式的字符串。orm

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+paaDqx9RoXuI5MIUcf
/nXGYBNAd9GcwqvBxZYrsssnXQ9L9VF7omh8x8JqEZizTbgXHFCnIYqRGYwZQR33
AH0cVwR/oajX9oQIHJPaR8PbXBs51xdNXILd5UFqShI8IeUT7jRex5huzMvpHOvy
vsAzsbHPf6anvtF4L6glnpEF1fCC0aNFCdDPXooZgLyQHm8qZs8SuXMjWtcV5B76
zrD39uNvb0YLRkhI8ixVMaF97nq690LnqSfsvmhFnQ1fXQE0KqalliXTu/K5RFHi
mOWEQwLS1D4AXUyq2vZI3BzCqU4jJvPQPN51A3KI00uh43xAPvbOsmVYcFBILwjv
GwIDAQAB
-----END PUBLIC KEY-----
複製代碼

若是把上面pem格式中 base64 格式的字符串轉換成 data 的話是這樣的

30820122 300d0609 2a864886 f70d0101 01050003 82010f00 3082010a 02820101
00cfea5a 683ab1f5 1a17b88e 4c21471f fe75c660 134077d1 9cc2abc1 c5962bb2
cb275d0f 4bf5517b a2687cc7 c26a1198 b34db817 1c50a721 8a91198c 19411df7
007d1c57 047fa1a8 d7f68408 1c93da47 c3db5c1b 39d7174d 5c82dde5 416a4a12
3c21e513 ee345ec7 986ecccb e91cebf2 bec033b1 b1cf7fa6 a7bed178 2fa8259e
9105d5f0 82d1a345 09d0cf5e 8a1980bc 901e6f2a 66cf12b9 73235ad7 15e41efa
ceb0f7f6 e36f6f46 0b464848 f22c5531 a17dee7a baf742e7 a927ecbe 68459d0d
5f5d0134 2aa6a596 25d3bbf2 b94451e2 98e58443 02d2d43e 005d4caa daf648dc
1cc2a94e 2326f3d0 3cde7503 7288d34b a1e37c40 3ef6ceb2 65587050 482f08ef
1b020301 0001
複製代碼

整個數據段能夠先分爲兩部分 :PEM文件頭 和 公鑰信息

一、PEM文件頭

30820122 300d0609 2a864886 f70d0101 01050003 82010f00
複製代碼

30820122
30 - tag
82 - 表示後面 2bytes 爲包長 0122 - 包長length

300d0609 2a864886 f70d0101 010500
30 - tag
0d - 包長length
0609 2a864886 f70d0101 010500 - value

0382010f
30 - tag
82 - 表示後面 2bytes 爲包長
010f - 包長length

00 - 填充

二、公鑰信息

3082010a 02820101 00cfea5a 683ab1f5 1a17b88e 4c21471f fe75c660 134077d1
9cc2abc1 c5962bb2 cb275d0f 4bf5517b a2687cc7 c26a1198 b34db817 1c50a721
8a91198c 19411df7 007d1c57 047fa1a8 d7f68408 1c93da47 c3db5c1b 39d7174d
5c82dde5 416a4a12 3c21e513 ee345ec7 986ecccb e91cebf2 bec033b1 b1cf7fa6
a7bed178 2fa8259e 9105d5f0 82d1a345 09d0cf5e 8a1980bc 901e6f2a 66cf12b9
73235ad7 15e41efa ceb0f7f6 e36f6f46 0b464848 f22c5531 a17dee7a baf742e7
a927ecbe 68459d0d 5f5d0134 2aa6a596 25d3bbf2 b94451e2 98e58443 02d2d43e
005d4caa daf648dc 1cc2a94e 2326f3d0 3cde7503 7288d34b a1e37c40 3ef6ceb2
65587050 482f08ef 1b020301 0001
複製代碼

3082010a
30 - tag
82 - 表示後面 2bytes 爲包長
010a - 包長length

02820101
30 - tag
82 - 表示後面 2bytes 爲包長
0101 - 包長length

00 - 填充

cf...1b - modulus

0203
02 - tag
03 - 包長length

010001 - publicExponent

四、cer 格式

// 512
30480241 0085e253 307970b8 4ae3aa44 8ee01cd1 efbed508 7123a219 d2eb0168
56a139f0 da62d142 bbf25bc9 79bc2543 1378a3c5 b34deb3f cc1c0bcf 580f9b7a
432fc7cd 3f020301 0001

// 1024
30818902 818100ce e47bf40a 6c8f314a 931152bc bc25b2d8 99088303 3d8a71e2
09aedc83 f5b179e8 088ba3b8 8beb9be9 8cb46c8d 1dae5328 48d2a1a1 ea07532c
ec6f4eae e514014a ba2df7a3 46e49d4a 76adb8fc 365a63b4 a76078f1 3310e3fc
89432679 47b5d07a f7c563f9 d3c64536 2a8e461b 2967aabc 0fffaf20 c84deefe
d2c23ce6 7c8eb102 03010001

// 2048
3082010a 02820101 00c35f4a 6c9c8784 bda331f1 4b33abda 7e1ee442 697439bc
041b8bc4 e15076ed 5b268e7e 5e633dc3 b7a9f4b8 2217812a 6822e6cf f594b769
8241faa6 54a729d7 ee480767 e6edb868 5d471d55 02e0075d 8c95aaec 28a451cf
8519ddc4 66309a5e 0c542142 a6de8466 6544c1be a5791cb4 ddf3b86c 6993a618
bf2c3ee5 22012e09 245820c1 939057e8 1d637b40 4c2eccb1 1f49b40b 267368de
61a63905 ae092cda 0e266594 19aa1f97 c007b4e9 c8a55456 56319117 51811874
0f19fc1e 6fb1d4a8 f0f1def2 69c2b871 6645d9f6 26fce75b 2ce9551a c5447e33
2def7f12 3f2b4553 d1d23f54 d7d4b7d8 788225bf 84ffa9eb daa034c7 547e98a6
9f911b73 9a88434f 1b020301 0001
複製代碼

cer 格式相對於 pem 格式少了一些文件頭信息,只包含了公鑰信息。

這裏說一下tag(0x30)後面跟着的包長數據。81 表示後面的 1 bytes 爲包長,82 表示後面 2bytes 爲包長;若是沒有則當前這個位置的值就是包長。
好比在 512 長度的公鑰中,48 就表示包長; 1024 長度的公鑰中,89 就表示包長 ; 2048 長度的公鑰中,010a 就表示包長

指數和模數

對於公鑰來講,主要就是指數和模數,只要能獲得指數和模數就能夠按照指定的方式組裝成對應的公鑰格式。關於指數和模數的獲取和組裝,Demo中已經給出了方法,這裏就不在貼代碼了。

用指數和模數生成 SecKeyRef 的時候要注意一下:在 iOS9 以上的系統 模數前面要加 00,否則會轉換失敗

因爲通常往外分發的都是公鑰,私鑰是保密的因此這裏就沒有探討私鑰的相關東西。而且若是私鑰外泄的話就至關於公鑰也外泄,那這個密鑰對就沒有什麼安全行了。


Demo傳送門

相關文章
相關標籤/搜索