RSA加密(非对称加密)和对称加密(AES/DES)在公司项目的使用

在看公司的项目代码的时候,注意到公司在请求数据,获取数据的时候,客户端都做了加密和解密,因为之前并没有在实际开发中使用过,因此也认真去查看了一下RSA算法。
首推 阮一峰RSA算法原理(一) - 阮一峰的网络日志RSA算法原理(二) - 阮一峰的网络日志

算法的部分可以查看上面的文章,我想说的在实际开发中RSA算法的应用,我们都知道RSA算法是目前使用最多的比较安全的非对称加密算法, 阮一峰的文章中

提到了公钥(n,e) 只能加密小于n的整数m,那么如果要加密大于n的整数,该怎么办?有两种解决方法:一种是把长信息分割成若干段短消息,每段分别加密;另一种是先选择一种"对称性加密算法"(比如DES / AES),用这种算法的密钥加密信息,再用RSA公钥加密DES/AES密钥。

本文只写了AES,DES也是一样的,修改一些配置参数罢了。

对称加密

引入<CommonCrypto/CommonCryptor.h>,还好苹果自身带有加密的api,开发者只需要调用api即可,核心框架是CommonCrypto,先贴下来代码
先来看一下CommonCrypto库的头文件以及核心操作函数,一些关键的地方写了注释方便理解

/*!
    @header     CommonCryptor.h
    @abstract   Generic interface for symmetric encryption. 

    @discussion This interface provides access to a number of symmetric 
                encryption algorithms. Symmetric encryption algorithms come 
                in two "flavors" -  block ciphers, and stream ciphers. Block
                ciphers process data (while both encrypting and decrypting) 
                in discrete chunks of  data called blocks; stream ciphers 
                operate on arbitrary sized data. 

                The object declared in this interface, CCCryptor, provides
                access to both block ciphers and stream ciphers with the same
                API; however some options are available for block ciphers that
                do not apply to stream ciphers. 

                The general operation of a CCCryptor is: initialize it
                with raw key data and other optional fields with
                CCCryptorCreate(); process input data via one or more calls to
                CCCryptorUpdate(), each of which may result in output data
                being written to caller-supplied memory; and obtain possible
                remaining output data with CCCryptorFinal(). The CCCryptor is
                disposed of via CCCryptorRelease(), or it can be reused (with
                the same key data as provided to CCCryptorCreate()) by calling
                CCCryptorReset(). 

                CCCryptors can be dynamically allocated by this module, or 
                their memory can be allocated by the caller. See discussion for
                CCCryptorCreate() and CCCryptorCreateFromData() for information 
                on CCCryptor allocation.
                
                One option for block ciphers is padding, as defined in PKCS7;
                when padding is enabled, the total amount of data encrypted
                does not have to be an even multiple of the block size, and 
                the actual length of plaintext is calculated during decryption. 
                //CBC模式需要起始向量(如果要选择CBC模式 应该将CCOptions 设置成0x0000即可) 文档中没有这个CCOptions,下面有注释说到
                Another option for block ciphers is Cipher Block Chaining, known
                as CBC mode. When using CBC mode, an Initialization Vector (IV)
                is provided along with the key when starting an encrypt
                or decrypt operation. If CBC mode is selected and no IV is 
                provided, an IV of all zeroes will be used. 

                CCCryptor also implements block bufferring, so that individual
                calls to CCCryptorUpdate() do not have to provide data whose
                length is aligned to the block size. (If padding is disabled,
                encrypting with block ciphers does require that the *total*
                length of data input to CCCryptorUpdate() call(s) be aligned
                to the block size.)

                A given CCCryptor can only be used by one thread at a time;
                multiple threads can use safely different CCCryptors at the
                same time.              
*/

/*!
    @enum       CCAlgorithm
    @abstract   Encryption algorithms implemented by this module.

    @constant   kCCAlgorithmAES128  Advanced Encryption Standard, 128-bit block
                                    This is kept for historical reasons.  It's
                                    preferred now to use kCCAlgorithmAES since
                                    128-bit blocks are part of the standard.
    @constant   kCCAlgorithmAES     Advanced Encryption Standard, 128-bit block
    //AES128和AES 一样的,只是历史遗留问题
    @constant   kCCAlgorithmDES     Data Encryption Standard
    @constant   kCCAlgorithm3DES    Triple-DES, three key, EDE configuration
    @constant   kCCAlgorithmCAST    CAST
  @constant   kCCAlgorithmRC4     RC4 stream cipher
  @constant   kCCAlgorithmBlowfish    Blowfish block cipher
*/
enum {
    kCCAlgorithmAES128 = 0,
    kCCAlgorithmAES = 0,
    kCCAlgorithmDES,
    kCCAlgorithm3DES,       
    kCCAlgorithmCAST,       
    kCCAlgorithmRC4,
    kCCAlgorithmRC2,   
    kCCAlgorithmBlowfish    
};
typedef uint32_t CCAlgorithm;

/*!
    @enum       CCOptions
    @abstract   Options flags, passed to CCCryptorCreate().

    @constant   kCCOptionPKCS7Padding   Perform PKCS7 padding. 
    @constant   kCCOptionECBMode        Electronic Code Book Mode. 
    Default is CBC.(就是说 这里使用0x0000 就是CBC了)
*/
enum {
    /* options for block ciphers */
    kCCOptionPKCS7Padding   = 0x0001,
    kCCOptionECBMode        = 0x0002
    /* stream ciphers currently have no options */
};
typedef uint32_t CCOptions;

/*!
    @enum           Key sizes

    @discussion     Key sizes, in bytes, for supported algorithms.  Use these
                    constants to select any keysize variants you wish to use
                    for algorithms that support them (ie AES-128, AES-192, AES-256)

    @constant kCCKeySizeAES128      128 bit AES key size.
    @constant kCCKeySizeAES192      192 bit AES key size.
    @constant kCCKeySizeAES256      256 bit AES key size.
    @constant kCCKeySizeDES         DES key size.
    @constant kCCKeySize3DES        Triple DES key size.
    @constant kCCKeySizeMinCAST     CAST minimum key size.
    @constant kCCKeySizeMaxCAST     CAST maximum key size.
    @constant kCCKeySizeMinRC4      RC4 minimum key size.
    @constant kCCKeySizeMaxRC4      RC4 maximum key size.

    @discussion     DES and TripleDES have fixed key sizes.
                    AES has three discrete key sizes.
                    CAST and RC4 have variable key sizes.
*/
enum {
    kCCKeySizeAES128          = 16,
    kCCKeySizeAES192          = 24,
    kCCKeySizeAES256          = 32,
    kCCKeySizeDES             = 8,
    kCCKeySize3DES            = 24,
    kCCKeySizeMinCAST         = 5,
    kCCKeySizeMaxCAST         = 16,
    kCCKeySizeMinRC4          = 1,
    kCCKeySizeMaxRC4          = 512,
    kCCKeySizeMinRC2          = 1,
    kCCKeySizeMaxRC2          = 128,
    kCCKeySizeMinBlowfish     = 8,
    kCCKeySizeMaxBlowfish     = 56,
};

/*!
    @enum           Block sizes

    @discussion     Block sizes, in bytes, for supported algorithms. 

    @constant kCCBlockSizeAES128    AES block size (currently, only 128-bit 
                                    blocks are supported).
    @constant kCCBlockSizeDES       DES block size.
    @constant kCCBlockSize3DES      Triple DES block size.
    @constant kCCBlockSizeCAST      CAST block size.
*/
enum {
    /* AES */
    kCCBlockSizeAES128        = 16,
    /* DES */
    kCCBlockSizeDES           = 8,
    /* 3DES */
    kCCBlockSize3DES          = 8,
    /* CAST */
    kCCBlockSizeCAST          = 8,
    kCCBlockSizeRC2           = 8,
    kCCBlockSizeBlowfish      = 8,
};

/*!
    @enum       Minimum context sizes
    @discussion Minimum context sizes, for caller-allocated CCCryptorRefs.
                To minimize dynamic allocation memory, a caller can create 
                a CCCryptorRef by passing caller-supplied memory to the 
                CCCryptorCreateFromData() function.

                These constants define the minimum amount of memory, in 
                bytes, needed for CCCryptorRefs for each supported algorithm. 

                Note: these constants are valid for the current version of 
                this library; they may change in subsequent releases, so 
                applications wishing to allocate their own memory for use 
                in creating CCCryptorRefs must be prepared to deal with 
                a kCCBufferTooSmall return from CCCryptorCreateFromData().
                See discussion for the CCCryptorCreateFromData() function.

    @constant kCCContextSizeAES128 - Minimum context size for kCCAlgorithmAES128.
    @constant kCCContextSizeDES    - Minimum context size for kCCAlgorithmDES.
    @constant kCCContextSize3DES   - Minimum context size for kCCAlgorithm3DES.
    @constant kCCContextSizeCAST   - Minimum context size for kCCAlgorithmCAST.
    @constant kCCContextSizeRC4    - Minimum context size for kCCAlgorithmRC4.
*/

enum {
    kCCContextSizeAES128 = 404,
    kCCContextSizeDES  = 240,
    kCCContextSize3DES  = 496,
    kCCContextSizeCAST  = 240,
    kCCContextSizeRC4  = 1072
};

CCCryptorStatus CCCrypt(
 CCOperation op,          // operation: kCCEncrypt加密 or kCCDecrypt解密 
 CCAlgorithm alg,         // algorithm: kCCAlgorithmAES128(AES128加密)...  
 CCOptions options,       // operation: kCCOptionPKCS7Padding...
 const void *key,         // key
 size_t keyLength,        // key length
 const void *iv,          // initialization vector (optional)
 const void *dataIn,      // input data
 size_t dataInLength,     // input data length
 void *dataOut,           // output data buffer
 size_t dataOutAvailable, // output data length available
 size_t *dataOutMoved)    // real output data length generated

AES128加密

//(key和iv向量这里是16位的) 这里是CBC加密模式,安全性更高
- (NSData *)AES128EncryptWithKey:(NSString *)key iv:(NSString *)ivStr {//加密
    char keyPtr[kCCKeySizeAES128 + 1];
    bzero(keyPtr, sizeof(keyPtr));
    //objective c字符串转化c语言字符串
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    char ivPtr[kCCKeySizeAES128+1];
    memset(ivPtr, 0, sizeof(ivPtr));
    //objective c字符串转化c语言字符串
    [ivStr getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
    
    //这里进行核心返回数据的配置
    NSUInteger dataLength = [self length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesEncrypted = 0;
    
    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                          kCCAlgorithmAES128, //AES加密(需要偏移向量)
                                          kCCOptionPKCS7Padding,  //这里是使用PKCS7Padding
                                          keyPtr,
                                          kCCBlockSizeAES128,
                                          ivPtr,
                                          [self bytes],
                                          dataLength,
                                          buffer,
                                          bufferSize,
                                          &numBytesEncrypted);
    if (cryptStatus == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
    }
    free(buffer);
    return nil;
}


- (NSData *)AES128DecryptWithKey:(NSString *)key iv:(NSString *)ivStr{//解密
    char keyPtr[kCCKeySizeAES128+1];
    bzero(keyPtr, sizeof(keyPtr));
    [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
    
    char ivPtr[kCCKeySizeAES128+1];
    memset(ivPtr, 0, sizeof(ivPtr));
    [ivStr getCString:ivPtr maxLength:sizeof(ivPtr) encoding:NSUTF8StringEncoding];
    
    NSUInteger dataLength = [self length];
    size_t bufferSize = dataLength + kCCBlockSizeAES128;
    void *buffer = malloc(bufferSize);
    size_t numBytesDecrypted = 0;
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                          kCCAlgorithmAES128,
                                          kCCOptionPKCS7Padding,
                                          keyPtr,
                                          kCCBlockSizeAES128,
                                          ivPtr,
                                          [self bytes],
                                          dataLength,
                                          buffer,
                                          bufferSize,
                                          &numBytesDecrypted);
    if (cryptStatus == kCCSuccess) {
        return [NSData dataWithBytesNoCopy:buffer length:numBytesDecrypted];
    }
    free(buffer);
    return nil;
}

当然大家 也可以使用网上一个开源的NSData+CommonCrypto,读者看完上面的原理,实际项目中可以直接使用开源的来实现对称加密

非对称加密

引入Security.framework

/*!
    @function SecKeyEncrypt
    @abstract Encrypt a block of plaintext. 
    @param key Public key with which to encrypt the data.
    @param padding See Padding Types above, typically kSecPaddingPKCS1.
    @param plainText The data to encrypt.
    @param plainTextLen Length of plainText in bytes, this must be less
    or equal to the value returned by SecKeyGetBlockSize().
    @param cipherText Pointer to the output buffer.
    @param cipherTextLen On input, specifies how much space is available at
    cipherText; on return, it is the actual number of cipherText bytes written.
    @result A result code. See "Security Error Codes" (SecBase.h).
    @discussion If the padding argument is kSecPaddingPKCS1 or kSecPaddingOAEP,
    PKCS1 (respectively kSecPaddingOAEP) padding will be performed prior to encryption.
    If this argument is kSecPaddingNone, the incoming data will be encrypted "as is".
    kSecPaddingOAEP is the recommended value. Other value are not recommended 
    for security reason (Padding attack or malleability).

    When PKCS1 padding is performed, the maximum length of data that can
    be encrypted is the value returned by SecKeyGetBlockSize() - 11.

    When memory usage is a critical issue, note that the input buffer
    (plainText) can be the same as the output buffer (cipherText). 
 */
OSStatus SecKeyEncrypt(
    SecKeyRef           key,
 SecPadding          padding,
 const uint8_t  *plainText,
 size_t              plainTextLen,
 uint8_t             *cipherText,
 size_t              *cipherTextLen)
    __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);

/*!
    @function SecKeyDecrypt
    @abstract Decrypt a block of ciphertext. 
    @param key Private key with which to decrypt the data.
    @param padding See Padding Types above, typically kSecPaddingPKCS1.
    @param cipherText The data to decrypt.
    @param cipherTextLen Length of cipherText in bytes, this must be less
    or equal to the value returned by SecKeyGetBlockSize().
    @param plainText Pointer to the output buffer.
    @param plainTextLen On input, specifies how much space is available at
    plainText; on return, it is the actual number of plainText bytes written.
    @result A result code. See "Security Error Codes" (SecBase.h).
    @discussion If the padding argument is kSecPaddingPKCS1 or kSecPaddingOAEP,
    the corresponding padding will be removed after decryption. 
    If this argument is kSecPaddingNone, the decrypted data will be returned "as is".

    When memory usage is a critical issue, note that the input buffer
    (plainText) can be the same as the output buffer (cipherText). 
 */
OSStatus SecKeyDecrypt(
    SecKeyRef           key,                /* Private key */对应的密钥
 SecPadding          padding,               /* kSecPaddingNone,
                                               kSecPaddingPKCS1,
                                               kSecPaddingOAEP 
                                               解密方式:一般是kSecPaddingPKCS1
*/
 const uint8_t       *cipherText,                               对应需要解密的字符串
 size_t              cipherTextLen,  /* length of cipherText */ 需要解密的字符串的长度
 uint8_t             *plainText,                                返回的字符串 
 size_t              *plainTextLen)  /* IN/OUT */               返回解密之后的字符串长度
    __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);

RSA的实际应用,可以参考Objective-C-RSA,作者已经封装好了RSA的加密和解密。项目开发 调用对应的encryptData和DecryptData即可

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容