在看公司的项目代码的时候,注意到公司在请求数据,获取数据的时候,客户端都做了加密和解密,因为之前并没有在实际开发中使用过,因此也认真去查看了一下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即可