变更记录
方案名称 数据加密 - RSA 加密(包含JAVA服务器端解密)
关键字 数据加密 \ 加密 \ 解密 \ RSA \ 非对称加密算法
需求场景
移动端与服务器敏感数据通讯加密需求
参考链接
ITEYE - iOS中使用RSA对数据进行加密解密
GitHub - gen_rsakey.sh
GitHub - RSAEncryptor
详细内容 #####1. 使用openssl生成密匙对
gen_rsakey.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # !/usr/bin/env bash echo "Generating RSA key pair ..." echo "1024 RSA key: private_key.pem" openssl genrsa -out private_key.pem 1024 echo "create certification require file: rsaCertReq.csr" openssl req -new -key private_key.pem -out rsaCertReq.csr echo "create certification using x509: rsaCert.crt" openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey private_key.pem -out rsaCert.crt echo "create public_key.der For IOS" openssl x509 -outform der -in rsaCert.crt -out public_key.der echo "create private_key.p12 For IOS. Please remember your password. The password will be used in iOS." openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt echo "create rsa_public_key.pem For Java" openssl rsa -in private_key.pem -out rsa_public_key.pem -pubout echo "create pkcs8_private_key.pem For Java" openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt echo "finished."
#####2. 导入工具类 RSAEncryptor
RSAEncryptor.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 // // RSAEncryptor.h // RSATestProjcet // // Created by Alfred Jiang on 3/12/15. // Copyright (c) 2015 Alfred Jiang. All rights reserved. // #import <Foundation/Foundation.h> @interface RSAEncryptor : NSObject #pragma mark - Instance Methods -(void) loadPublicKeyFromFile: (NSString*) derFilePath; -(void) loadPublicKeyFromData: (NSData*) derData; -(void) loadPrivateKeyFromFile: (NSString*) p12FilePath password:(NSString*)p12Password; -(void) loadPrivateKeyFromData: (NSData*) p12Data password:(NSString*)p12Password; -(NSString*) rsaEncryptString:(NSString*)string; -(NSData*) rsaEncryptData:(NSData*)data ; -(NSString*) rsaDecryptString:(NSString*)string; -(NSData*) rsaDecryptData:(NSData*)data; -(BOOL) rsaSHA1VerifyData:(NSData *) plainData withSignature:(NSData *) signature; #pragma mark - Class Methods +(void) setSharedInstance: (RSAEncryptor*)instance; +(RSAEncryptor*) sharedInstance; @end
RSAEncryptor.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 // // RSAEncryptor.m // RSATestProjcet // // Created by Alfred Jiang on 3/12/15. // Copyright (c) 2015 Alfred Jiang. All rights reserved. // #import <Security/Security.h> #import "RSAEncryptor.h" #import "GTMBase64.h" #import <CommonCrypto/CommonCrypto.h> @implementation RSAEncryptor { SecKeyRef publicKey; SecKeyRef privateKey; } -(void)dealloc { if (nil != publicKey) { CFRelease(publicKey); } if (nil != privateKey) { CFRelease(privateKey); } } -(SecKeyRef) getPublicKey { return publicKey; } -(SecKeyRef) getPrivateKey { return privateKey; } -(void) loadPublicKeyFromFile: (NSString*) derFilePath { NSData *derData = [[NSData alloc] initWithContentsOfFile:derFilePath]; [self loadPublicKeyFromData: derData]; } -(void) loadPublicKeyFromData: (NSData*) derData { publicKey = [self getPublicKeyRefrenceFromeData: derData]; } -(void) loadPrivateKeyFromFile: (NSString*) p12FilePath password:(NSString*)p12Password { NSData *p12Data = [NSData dataWithContentsOfFile:p12FilePath]; [self loadPrivateKeyFromData: p12Data password:p12Password]; } -(void) loadPrivateKeyFromData: (NSData*) p12Data password:(NSString*)p12Password { privateKey = [self getPrivateKeyRefrenceFromData: p12Data password: p12Password]; } #pragma mark - Private Methods -(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; } -(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; } #pragma mark - Encrypt -(NSString*) rsaEncryptString:(NSString*)string { NSData* data = [string dataUsingEncoding:NSUTF8StringEncoding]; NSData* encryptedData = [self rsaEncryptData: data]; NSString* base64EncryptedString = [GTMBase64 stringByEncodingData:encryptedData]; // [encryptedData base64EncodedString]; return base64EncryptedString; } // 加密的大小受限于SecKeyEncrypt函数,SecKeyEncrypt要求明文和密钥的长度一致,如果要加密更长的内容,需要把内容按密钥长度分成多份,然后多次调用SecKeyEncrypt来实现 -(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++) { int 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){ NSData *encryptedBytes = [[NSData alloc] initWithBytes:(const void *)cipherBuffer length:cipherBufferSize]; [encryptedData appendData:encryptedBytes]; }else{ if (cipherBuffer) { free(cipherBuffer); } return nil; } } if (cipherBuffer){ free(cipherBuffer); } return encryptedData; } #pragma mark - Decrypt -(NSString*) rsaDecryptString:(NSString*)string { NSData* data = [[NSData alloc] initWithBase64EncodedString:string options:NSDataBase64DecodingIgnoreUnknownCharacters]; NSData* decryptData = [self rsaDecryptData: data]; NSString* result = [[NSString alloc] initWithData: decryptData encoding:NSUTF8StringEncoding]; return result; } -(NSData*) rsaDecryptData:(NSData*)data { SecKeyRef key = [self getPrivateKey]; size_t cipherLen = [data length]; void *cipher = malloc(cipherLen); [data getBytes:cipher length:cipherLen]; size_t plainLen = SecKeyGetBlockSize(key) - 12; void *plain = malloc(plainLen); OSStatus status = SecKeyDecrypt(key, kSecPaddingPKCS1, cipher, cipherLen, plain, &plainLen); if (status != noErr) { return nil; } NSData *decryptedData = [[NSData alloc] initWithBytes:(const void *)plain length:plainLen]; return decryptedData; } #pragma marke - verify file SHA1 -(BOOL) rsaSHA1VerifyData:(NSData *) plainData withSignature:(NSData *) signature { size_t signedHashBytesSize = SecKeyGetBlockSize([self getPublicKey]); const void* signedHashBytes = [signature bytes]; size_t hashBytesSize = CC_SHA1_DIGEST_LENGTH; uint8_t* hashBytes = malloc(hashBytesSize); if (!CC_SHA1([plainData bytes], (CC_LONG)[plainData length], hashBytes)) { return NO; } OSStatus status = SecKeyRawVerify(publicKey, kSecPaddingPKCS1SHA1, hashBytes, hashBytesSize, signedHashBytes, signedHashBytesSize); return status == errSecSuccess; } #pragma mark - Class Methods static RSAEncryptor* sharedInstance = nil; +(void) setSharedInstance: (RSAEncryptor*)instance { sharedInstance = instance; } +(RSAEncryptor*) sharedInstance { return sharedInstance; } @end
#####3. 使用 RSAEncryptor 进行RSA加密解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 RSAEncryptor *rsa = [[RSAEncryptor alloc] init]; NSLog(@"encryptor using rsa"); NSString *publicKeyPath = [[NSBundle mainBundle] pathForResource:@"public_key" ofType:@"der"]; NSLog(@"public key: %@", publicKeyPath); [rsa loadPublicKeyFromFile:publicKeyPath]; NSString *securityText = @"hello ~"; NSString *encryptedString = [rsa rsaEncryptString:securityText]; NSLog(@"encrypted data: %@", encryptedString); NSLog(@"decryptor using rsa"); [rsa loadPrivateKeyFromFile:[[NSBundle mainBundle] pathForResource:@"private_key" ofType:@"p12"] password:@"1234"]; NSString *decryptedString = [rsa rsaDecryptString:encryptedString]; NSLog(@"decrypted data: %@", decryptedString);
#####4. 在服务器端解码数据(Java)
生成的pkcs8 private key
1 openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt
具体解码步骤
1 2 3 4 5 6 7 8 9 10 加载pkcs8 private key: 读取private key文件 去掉private key头尾的"-----BEGIN PRIVATE KEY-----"和"-----BEGIN PRIVATE KEY-----" 删除private key中的换行 对处理后的数据进行Base64解码 使用解码后的数据生成private key. 解密数据: 对数据进行Base64解码 使用RSA decrypt数据.
实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Base64; import static java.lang.String.format; public class Encryptor { public static void main(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { PrivateKey privateKey = readPrivateKey(); String message = "AFppaFPTbmboMZD55cjCfrVaWUW7+hZkaq16Od+6fP0lwz/yC+Rshb/8cf5BpBlUao2EunchnzeKxzpiPqtCcCITKvk6HcFKZS0sN9wOhlQFYT+I4f/CZITwBVAJaldZ7mkyOiuvM+raXMwrS+7MLKgYXkd5cFPxEsTxpMSa5Nk="; System.out.println(format("- decrypt rsa encrypted base64 message: %s", message)); // hello ~, encrypted and encoded with Base64: byte[] data = encryptedData(message); String text = decrypt(privateKey, data); System.out.println(text); } private static String decrypt(PrivateKey privateKey, byte[] data) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decryptedData = cipher.doFinal(data); return new String(decryptedData); } private static byte[] encryptedData(String base64Text) { return Base64.getDecoder().decode(base64Text.getBytes(Charset.forName("UTF-8"))); } private static PrivateKey readPrivateKey() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { byte[] privateKeyData = Files.readAllBytes( Paths.get("/Users/twer/macspace/ios_workshop/Security/SecurityLogin/tools/pkcs8_private_key.pem")); byte[] decodedKeyData = Base64.getDecoder() .decode(new String(privateKeyData) .replaceAll("-----\\w+ PRIVATE KEY-----", "") .replace("\n", "") .getBytes()); return KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decodedKeyData)); } }
效果图 (无)
备注 RSA使用”秘匙对”对数据进行加密解密.在加密解密数据前,需要先生成公钥(public key)和私钥(private key).
公钥(public key): 用于加密数据. 用于公开, 一般存放在数据提供方, 例如iOS客户端.
私钥(private key): 用于解密数据. 必须保密, 私钥泄露会造成安全问题.