???????公司接口需要加密,因此需要iOS或android端、.NET服務器端實現同樣的加密解密方式。
???????原本在項目中有DES加密方式的實現,用于保存賬號密碼以便App下次打開時自動登錄,加密解密在本地都無問題。但是使用同樣的加密方法傳輸到服務器,服務器居然無法還原原始字符串。
以下是iOS端加密和解密的方法:
static Byte iv[] = {1,2,3,4,5,6,7,8};
+ (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));
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 =[GTMBase64 stringByEncodingData:data];
}
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;
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;
}
</br>
于是開始走上查找原因的路。
???????首先,由于本地的加密和解密正常能用,懷疑可能是服務器解密函數的問題,于是讓服務器進行加密解密驗證,發現沒有問題。
???????然后對比兩邊的加密結果,對同一字符串使用同一個key加密,服務器的值為aDcUJMile6I=
,而iOS端的值是 CHgXZ9ukWr0=
,值不同但是比較相似。
???????然后查看OC和C#的實現,詳細比對加密函數的各項參數,終于發現了原因所在。DES參數包括工作模式(包括電子密碼本ECB、加密分組鏈接CBC、加密反饋CFB和OFB四種模式)、填充模式、加密密鑰、初始化向量、字符串編碼格式等。
問題的原因就在于初始化向量的不同。
下面分析一下兩端實現中使用的參數。
服務器的C#代碼類似于:
public string Encrypt(string pToEncrypt, string sKey)
{
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
byte[] inputByteArray = Encoding.Default.GetBytes(pToEncrypt);
des.Key = ASCIIEncoding.ASCII.GetBytes(sKey);
des.IV = ASCIIEncoding.ASCII.GetBytes(sKey);
MemoryStream ms = new MemoryStream();
CryptoStream cs = new CryptoStream(ms, des.CreateEncryptor(),CryptoStreamMode.Write);
cs.Write(inputByteArray, 0, inputByteArray.Length);
cs.FlushFinalBlock();
StringBuilder ret = new StringBuilder();
foreach(byte b in ms.ToArray())
{
ret.AppendFormat("{0:X2}", b);
}
ret.ToString();
return ret.ToString();
}
和我本地加密參數相比:編碼格式都采用UTF8編碼,工作模式一致,填充模式都采用 PKCS7方式,加密密鑰也一致,但是初始向量IV不一樣,我本地使用了自定義的字符,而服務器使用密鑰key。
于是,將本地代碼修改,使用密鑰key作為偏移量。再次測試時服務器終于可以正確解密了。
簡而言之,不同平臺協同工作時,采用原理一致的方法和一致的參數非常重要,可以通過查看源代碼來確保這一點。在DES加密中,要著重確定5個方面的參數是否一致,包括:編碼格式、工作模式、填充模式、加密密鑰、初始向量。
</br>
附加修改后的密解和密方法:
+(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));
size_t numBytesEncrypted = 0;
const void *iv = [key UTF8String];
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;
const void *iv = [key UTF8String];
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;
}