iOS下的几种加密方式

一、RSA非对称加密算法

 RSA算法是一种非对称加密算法,常被用于加密数据传输。

RSA基本原理

RSA使用"秘匙对"对数据进行加密解密.在加密解密数据前,需要先生成公钥(public key)和私钥(private key).

  • 公钥(public key): 用于加密数据. 用于公开, 一般存放在数据提供方, 例如iOS客户端.
  • 私钥(private key): 用于解密数据. 必须保密, 私钥泄露会造成安全问题.
iOS中的Security.framework提供了对RSA算法的支持.这种方式需要对密匙对进行处理, 根据public key生成证书, 通过private key生成p12格式的密匙.

使用openssl生成密匙对

Github Gist: https://gist.github.com/lvjian700/635368d6f1e421447680

#!/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."

Tips:

  • 在创建证书的时候, terminal会提示输入证书信息. 根据wizard输入对应信息就OK. 
  • 在创建p12密匙时, 会提示输入密码, 此时的密码必须记住, 之后会用到.
  • 如果上面指令有问题,请参考最新的openssl官方文档, 以官方的为准. 之前在网上搜索指令, 被坑了一圈之后, 还是会到啃官方文档上. 每条指令文档在最后都会有几个sample,参考sample即可.

iOS如何加载使用证书

将下面代码添加到项目中:

https://gist.github.com/lvjian700/204c23226fdffd6a505d

 

代码依赖Base64编码库, 如果使用cocoapods, 可以讲下面依赖添加到Podfile:

pod 'Base64nl', '~> 1.2'

加密数据

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);

__[rsa rsaEncryptString:securityText]__会返回decrypted base64编码的字符串:

console out 写道
I1Mnu33cU7QcgaC9uo2bxV0vyfJSqAwyC3DZ+p8jm0G2EmcClarrR5R2xLDdXqvtKj+UJbES7TT+AgkK1NDoQvOJ


解密数据

在iOS下解码需要先加载private key, 之后在对数据解码. 解码的时候先进行Base64 decode, 之后在用private key decrypt加密数据.

NSLog(@"decryptor using rsa");
    [rsa loadPrivateKeyFromFile:[[NSBundle mainBundle] pathForResource:@"private_key" ofType:@"p12"] password:@"123456"];
    NSString *decryptedString = [rsa rsaDecryptString:encryptedString];
    NSLog(@"decrypted data: %@", decryptedString);

之后会输出解密后的数据:

console 写道
decryptor using rsa
decrypted data: hello ~

在服务器端解码数据(Java)

 

在Java中解码需要使用下述指令生成的pkcs8 private key:

 

gen shell 写道
openssl pkcs8 -topk8 -in private_key.pem -out pkcs8_private_key.pem -nocrypt

 

具体解码步骤:

 

  1. 加载pkcs8 private key:
    1. 读取private key文件
    2. 去掉private key头尾的"-----BEGIN PRIVATE KEY-----"和"-----BEGIN PRIVATE KEY-----"
    3. 删除private key中的换行
    4. 对处理后的数据进行Base64解码
    5. 使用解码后的数据生成private key.
  2. 解密数据:
    1. 对数据进行Base64解码
    2. 使用RSA decrypt数据.

这里我将iOS中"hello ~"加密的数据在Java中进行解码:

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));
    }
}

直行成功后控制台会输出"hello ~".

 

总结

 

这种加密传输方式会被用在网银类App中.虽然网银会采用全站https方案, 但是在安全登录这块会使用另一个证书对登录信息加密, 这样可以双层确保数据安全.

基于RSA加密解密算法, 还可以将其运用在数字签名场景.以后有空在聊如何使用RSA算法实现对文件的数字签名.

二、DES和3DES对称加密算法

3DES是一种对称的加密方式,使用同样的密钥。也被称为“三重加密”!

系统提供详细的加密和解密方法。

    //3des加解密  
    + (NSString*)TripleDES:(NSString*)plainText encryptOrDecrypt:(CCOperation)encryptOrDecrypt  
    {  
          
        const void *vplainText;  
        size_t plainTextBufferSize;  
          
        if (encryptOrDecrypt == kCCDecrypt)//解密  
        {  
            NSData *EncryptData = [GTMBase64 decodeData:[plainText dataUsingEncoding:NSUTF8StringEncoding]];  
            plainTextBufferSize = [EncryptData length];  
            vplainText = [EncryptData bytes];  
        }  
        else //加密  
        {  
            NSData* data = [plainText dataUsingEncoding:NSUTF8StringEncoding];  
            plainTextBufferSize = [data length];  
            vplainText = (const void *)[data bytes];  
        }  
          
        CCCryptorStatus ccStatus;  
        uint8_t *bufferPtr = NULL;  
        size_t bufferPtrSize = 0;  
        size_t movedBytes = 0;  
          
        bufferPtrSize = (plainTextBufferSize + kCCBlockSize3DES) & ~(kCCBlockSize3DES - 1);  
        bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));  
        memset((void *)bufferPtr, 0x0, bufferPtrSize);  
        // memset((void *) iv, 0x0, (size_t) sizeof(iv));  
          
         const void *vkey = (const void *) [DESKEY UTF8String];  
       // NSString *initVec = @"init Vec";  
        //const void *vinitVec = (const void *) [initVec UTF8String];  
       //  Byte iv[] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF};  
        ccStatus = CCCrypt(encryptOrDecrypt,  
                           kCCAlgorithm3DES,  
                            kCCOptionPKCS7Padding | kCCOptionECBMode,  
                           vkey,   
                           kCCKeySize3DES,  
                           nil,   
                           vplainText,  
                           plainTextBufferSize,  
                           (void *)bufferPtr,  
                           bufferPtrSize,  
                           &movedBytes);  
        //if (ccStatus == kCCSuccess) NSLog(@"SUCCESS");  
        /*else if (ccStatus == kCC ParamError) return @"PARAM ERROR"; 
         else if (ccStatus == kCCBufferTooSmall) return @"BUFFER TOO SMALL"; 
         else if (ccStatus == kCCMemoryFailure) return @"MEMORY FAILURE"; 
         else if (ccStatus == kCCAlignmentError) return @"ALIGNMENT"; 
         else if (ccStatus == kCCDecodeError) return @"DECODE ERROR"; 
         else if (ccStatus == kCCUnimplemented) return @"UNIMPLEMENTED"; */  
          
        NSString *result;  
          
        if (encryptOrDecrypt == kCCDecrypt)  
        {  
            result = [[[NSString alloc] initWithData:[NSData dataWithBytes:(const void *)bufferPtr   
                                                                    length:(NSUInteger)movedBytes]   
                                            encoding:NSUTF8StringEncoding]   
                      autorelease];  
        }  
        else  
        {  
            NSData *myData = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];  
            result = [GTMBase64 stringByEncodingData:myData];  
        }  
          
        return result;  
但是要注意到一点:

    CCCrypt(encryptOrDecrypt,  
                           kCCAlgorithm3DES,  
                            kCCOptionPKCS7Padding | kCCOptionECBMode,  
                           vkey,   
                           kCCKeySize3DES,  
                           nil,   
                           vplainText,  
                           plainTextBufferSize,  
                           (void *)bufferPtr,  
                           bufferPtrSize,  
                           &movedBytes);  

这个里面的参数可能会根据你服务端使用的对应的3des算法构造时参数的不同而需要自己调整。

 

比如我在网上看到的这个第三个参数只是用了kCCOptionPKCS7Padding,第六个可选参数用了上面一个自定义的iv。

但是这边根据服务器,这个参数是nil才对应。

这边也附上java端的加解密代码

    public String encryptThreeDESECB(String src,String key) throws Exception  
        {  
          DESedeKeySpec dks = new DESedeKeySpec(key.getBytes("UTF-8"));  
          SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");  
          SecretKey securekey = keyFactory.generateSecret(dks);  
            
          Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");  
          cipher.init(Cipher.ENCRYPT_MODE, securekey);  
          byte[] b=cipher.doFinal(src.getBytes());  
            
          BASE64Encoder encoder = new BASE64Encoder();  
          return encoder.encode(b).replaceAll("\r", "").replaceAll("\n", "");  
            
        }  
      
        //3DESECB解密,key必须是长度大于等于 3*8 = 24 位  
        public String decryptThreeDESECB(String src,String key) throws Exception  
        {  
          //--通过base64,将字符串转成byte数组  
          BASE64Decoder decoder = new BASE64Decoder();  
          byte[] bytesrc = decoder.decodeBuffer(src);  
          //--解密的key  
          DESedeKeySpec dks = new DESedeKeySpec(key.getBytes("UTF-8"));  
          SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");  
          SecretKey securekey = keyFactory.generateSecret(dks);  
            
          //--Chipher对象解密  
          Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");    
          cipher.init(Cipher.DECRYPT_MODE, securekey);    
          byte[] retByte = cipher.doFinal(bytesrc);    
                  
          return new String(retByte);  
        }  
DES对称加密

#import "ViewController.h"
#import 
#import "GTMBase64.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSString *key = @"这是个key";
    NSString *encryptedData = [self encryptUseDES:@"this is a text" key:key];
    NSLog(@"加密后的数据是:%@", encryptedData);
    NSLog(@"解密后的数据是:%@", [self decryptUseDES:encryptedData key:key]);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*字符串加密
 *参数
 *plainText : 加密明文
 *key        : 密钥 64位
 */
- (NSString *) encryptUseDES:(NSString *)plainText key:(NSString *)key
{
    NSString *ciphertext = nil;
    const char *textBytes = [plainText UTF8String];
    NSUInteger dataLength = [plainText length];
    unsigned char buffer[1024];
    memset(buffer, 0, sizeof(char));
    Byte iv[] = {1,2,3,4,5,6,7,8};
    size_t numBytesEncrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmDES,
                                          kCCOptionPKCS7Padding,
                                          [key UTF8String], kCCKeySizeDES,
                                          iv,
                                          textBytes, dataLength,
                                          buffer, 1024,
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        NSData *data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
        
        ciphertext = [[NSString alloc] initWithData:[GTMBase64 encodeData:data] encoding:NSUTF8StringEncoding];
    }
    return ciphertext;
}

//解密
- (NSString *) decryptUseDES:(NSString*)cipherText key:(NSString*)key
{
    NSData* cipherData = [GTMBase64 decodeString:cipherText];
    unsigned char buffer[1024];
    memset(buffer, 0, sizeof(char));
    size_t numBytesDecrypted = 0;
    Byte iv[] = {1,2,3,4,5,6,7,8};
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                          kCCAlgorithmDES,
                                          kCCOptionPKCS7Padding,
                                          [key UTF8String],
                                          kCCKeySizeDES,
                                          iv,
                                          [cipherData bytes],
                                          [cipherData length],
                                          buffer,
                                          1024,
                                          &numBytesDecrypted);
    NSString* plainText = nil;
    if (cryptStatus == kCCSuccess) {
        NSData* data = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesDecrypted];
        plainText = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    }
    return plainText;
}


@end

java 代码:需要自己引入sun.misc.BASE64Decoder.jar

package com.yue;

import java.io.IOException;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

import Decoder.BASE64Decoder;
import Decoder.BASE64Encoder;
 
 
public class DesUtil {
 
    private final static String DES = "DES";
 
    public static void main(String[] args) throws Exception {
        String data = "123 456";
        String key = "abcdefgh";
        System.err.println(encrypt(data, key));
        System.err.println(decrypt(encrypt(data, key), key));
        
        System.out.println(decrypt("JF5dX/TlOg529KAhh+vywjzIp5Msktmf", key));
 
    }
     
    /**
     * Description 根据键值进行加密
     * @param data 
     * @param key  加密键byte数组
     * @return
     * @throws Exception
     */
    public static String encrypt(String data, String key) throws Exception {
        byte[] bt = encrypt(data.getBytes(), key.getBytes());
        String strs = new BASE64Encoder().encode(bt);
        return strs;
    }
 
    /**
     * Description 根据键值进行解密
     * @param data
     * @param key  加密键byte数组
     * @return
     * @throws IOException
     * @throws Exception
     */
    public static String decrypt(String data, String key) throws IOException,
            Exception {
        if (data == null)
            return null;
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] buf = decoder.decodeBuffer(data);
        byte[] bt = decrypt(buf,key.getBytes());
        return new String(bt);
    }
 
    /**
     * Description 根据键值进行加密
     * @param data
     * @param key  加密键byte数组
     * @return
     * @throws Exception
     */
    private static byte[] encrypt(byte[] data, byte[] key) throws Exception {
        // 生成一个可信任的随机数源
        SecureRandom sr = new SecureRandom();
 
        // 从原始密钥数据创建DESKeySpec对象
        DESKeySpec dks = new DESKeySpec(key);
 
        // 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
        SecretKey securekey = keyFactory.generateSecret(dks);
 
        // Cipher对象实际完成加密操作
        Cipher cipher = Cipher.getInstance(DES);
 
        // 用密钥初始化Cipher对象
        cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
 
        return cipher.doFinal(data);
    }
     
     
    /**
     * Description 根据键值进行解密
     * @param data
     * @param key  加密键byte数组
     * @return
     * @throws Exception
     */
    private static byte[] decrypt(byte[] data, byte[] key) throws Exception {
        // 生成一个可信任的随机数源
        SecureRandom sr = new SecureRandom();
 
        // 从原始密钥数据创建DESKeySpec对象
        DESKeySpec dks = new DESKeySpec(key);
 
        // 创建一个密钥工厂,然后用它把DESKeySpec转换成SecretKey对象
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES);
        SecretKey securekey = keyFactory.generateSecret(dks);
 
        // Cipher对象实际完成解密操作
        Cipher cipher = Cipher.getInstance(DES);
 
        // 用密钥初始化Cipher对象
        cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
 
        return cipher.doFinal(data);
    }
}

三、MD5


MD5算法的特点
* 同样的数据 加密结果是一样 . (32 个字符 )
* 不可逆的 .( 不能逆向解密 )
* 可用于文件校验 / 指纹识别 .

MD5算法是公开的,iOS中已经包装好了MD5算法。
可以将其写成字符串的分类:
- (NSString *)md5String
{
	const char *string = self.UTF8String;
	int length = (int)strlen(string);
	unsigned char bytes[CC_MD5_DIGEST_LENGTH];
	CC_MD5(string, length, bytes);
	return [self stringFromBytes:bytes length:CC_MD5_DIGEST_LENGTH];
}



你可能感兴趣的:(iOS)