iOS Objective-C 與 PHP DES 加解密演算法實作


原文:http://blog.toright.com/archives/2657

在使用 php-mcrypt 函式庫進行 DES 加解密時,常常會碰到在其他平台無法正確解密資料>,是因為 PHP Mcrypt Library 沒有提供將明文訊息轉換為標準 PKCS#7 的格式 (定義在 RFC 5652) 所造成。以下程式碼說明使用 PHP mcrypt_encrypt function 依據 PKCS#7 規範

對字串進行 DES 加密,並且使用 Base64 編碼後輸出後再使用 DES 解密後的結果 (包含 iOS 的 DES 解密實作)。遵循標準的 PKCS#7 規範實作,密文在其他的程式語言中才能正確>地進行解密。以下為 PHP DES 加解密的程式碼,後面會介紹 Object-C 的加解密實作。

<?php
/**
 * PHP DES 加密程式
 *
 * @param $key 密鑰(八個字元內)
 * @param $encrypt 要加密的明文
 * @return string 密文
 */
function encrypt ($key, $encrypt)
{
    // 根據 PKCS#7 RFC 5652 Cryptographic Message Syntax (CMS) 修正 Message 加入 Padding
    $block = mcrypt_get_block_size(MCRYPT_DES, MCRYPT_MODE_ECB);
    $pad = $block - (strlen($encrypt) % $block);
    $encrypt .= str_repeat(chr($pad), $pad);

    // 不需要設定 IV 進行加密
    $passcrypt = mcrypt_encrypt(MCRYPT_DES, $key, $encrypt, MCRYPT_MODE_ECB);
    return base64_encode($passcrypt);
}

/**
 * PHP DES 解密程式
 *
 * @param $key 密鑰(八個字元內)
 * @param $decrypt 要解密的密文
 * @return string 明文
 */
function decrypt ($key, $decrypt)
{
    // 不需要設定 IV
    $str = mcrypt_decrypt(MCRYPT_DES, $key, base64_decode($decrypt), MCRYPT_MODE_ECB);

    // 根據 PKCS#7 RFC 5652 Cryptographic Message Syntax (CMS) 修正 Message 移除 Padding
    $pad = ord($str[strlen($str) - 1]);
    return substr($str, 0, strlen($str) - $pad);
}

$key = 'uuid';
$plain = '0123ABCD!@#$中文';
$encrypt = encrypt($key, $plain);
$decrypt = decrypt($key, $encrypt);

echo 'plain = '.$plain."\r\n";
echo 'encrypt = '.$encrypt."\r\n";
echo 'decrypt = '.$decrypt."\r\n";
?>

PHP 執行結果如下:

plain = 0123ABCD!@#$中文
encrypt = fDDkBvvC7KJAcmU9IB6aptxMZ35cn4ZX
decrypt = 0123ABCD!@#$中文

那在 iOS 中該如何進行解密呢?我們可以透過 iOS 內建的 CommonCryptor 函式庫進行 DES 加解密,使用時記得加入以下程式碼來引入需要的 Header。

#include <CommonCrypto/CommonCryptor.h>

在 iOS 對於 Base64 編碼 (解碼) 的實作,我們可以引用 GTM Library GTMBase64 Class 來進行,這時需要 GTMBase64.h, GTMBase64.m, GTMDefines.h 這三支檔案,程式可以在 GTM Source Code 下載。GTM 全名為 Google Toolbox for MAC,其中實作了許多好用的函式
庫,並且使用 Apache License 2.0 作為授權。經過比較發現 GTMBase64 在實作上確實比>較完整,因此我們採用 GTMBase64 作為編碼函式。Objective-C 實作 DES 解密,程式碼如
下:

-(NSString*) decryptUseDES:(NSString*)cipherText key:(NSString*)key {
    // 利用 GTMBase64 解碼 Base64 字串
    NSData* cipherData = [GTMBase64 decodeString:cipherText];
    unsigned char buffer[1024];
    memset(buffer, 0, sizeof(char));
    size_t numBytesDecrypted = 0;

    // IV 偏移量不需使用
    CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                          kCCAlgorithmDES,
                                          kCCOptionPKCS7Padding | kCCOptionECBMode,
                                          [key UTF8String],
                                          kCCKeySizeDES,
                                          nil,
                                          [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] autorelease];
    }
    return plainText;
}

接下來我們將剛剛 PHP 輸出的 Base64 密文進行解密,參考以下程式碼:

// 設定 key
NSString* key = @"skey";
NSString* encrypt = @"fDDkBvvC7KJAcmU9IB6aptxMZ35cn4ZX";
NSString* decrypt = [self decryptUseDES:encrypt key:key];

// 輸出
NSLog(@"encrypt = %@", encrypt);
NSLog(@"decrypt = %@", decrypt);

執行結果輸出到 console,輸出資料如下:

2012-06-26 16:50:28.092 test[15666:13403] encrypt = fDDkBvvC7KJAcmU9IB6aptxMZ35cn4ZX
2012-06-26 16:50:28.093 test[15666:13403] decrypt = 0123ABCD!@#$中文

最後補充一下網友(waxdoll)所提供的 Object-C DES 加密程式,程式碼如下:

-(NSString *) encryptUseDES:(NSString *)clearText key:(NSString *)key
{
    NSData *data = [clearText dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
    unsigned char buffer[1024];
    memset(buffer, 0, sizeof(char));
    size_t numBytesEncrypted = 0;

    CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                          kCCAlgorithmDES,
                                          kCCOptionPKCS7Padding | kCCOptionECBMode,
                                          [key UTF8String],
                                          kCCKeySizeDES,
                                          nil,
                                          [data bytes],
                                          [data length],
                                          buffer,
                                          1024,
                                          &numBytesEncrypted);

    NSString* plainText = nil;
    if (cryptStatus == kCCSuccess) {
        NSData *dataTemp = [NSData dataWithBytes:buffer length:(NSUInteger)numBytesEncrypted];
        plainText = [GTMBase64 stringByEncodingData:dataTemp];
    }else{
        NSLog(@"DES加密失败");
    }
    return plainText;
}

參考資料

  • PHP Manual – Mcrypt
  • RFC 5652 – Cryptographic Message Syntax (CMS)
  • Google Toolbox for Mac
  • Google Toolbox for Mac – Source Code

你可能感兴趣的:(iOS Objective-C 與 PHP DES 加解密演算法實作)