数据加密:RSA 密钥

生成密钥对

生成密钥对的过程就是计算(e,d,n)的过程,在 iOS 中可以使用原生的方法来生成密钥对,同时也可以借助一些第三方的库比如openssl。

iOS 原生方法

- (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;
}
复制代码

密钥格式

生成的密钥对的过程是比较简单的,只要调用相应的方法就可以了。但是加密这个东西往往是需要跨平台执行的,一般都会有分发公钥这个过程,而每个平台生成的密钥格式却都是不一样的,这里就需要对密钥进行格式转换。

用 iOS 原生的方法生成的密钥为 SecKeyRef 格式的,用 openssl 生成的密钥为 RSA 格式的,用个命令上生成的密钥会是 .pem 或 .cer 格式的文件。但是不管密钥的表现形式是什么,对于公钥来说,组成就是(e,n),私钥就是(d,n)。

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查看。

2.RSA

如果使用 openssl 的话,可以将 RSA 转换成 pem 格式的字符串,同样以公钥为例:

- (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查看。

3、pem 格式

这是一个 pem 格式的公钥,以 -----BEGIN PUBLIC KEY----- 开头,以 -----END PUBLIC KEY-----结尾。这个是固定的格式,中间部分是一个 base64 格式的字符串。

-----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文件头 和 公钥信息

1、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 - 填充

2、公钥信息
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

4、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传送门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值