【问题标题】:Encrypt in Objective-C / Decrypt in Ruby using anything使用任何东西在 Objective-C 中加密/在 Ruby 中解密
【发布时间】:2011-05-14 16:35:59
【问题描述】:

我们正在使用这段代码在 iPhone 上的 Objective-C 中进行加密:

- (NSMutableData*) EncryptAES: (NSString *) key
{
    char keyPtr[kCCKeySizeAES128+1];
    bzero( keyPtr, sizeof(keyPtr) );

    [key getCString: keyPtr maxLength: sizeof(keyPtr) encoding: NSUTF8StringEncoding];
    size_t numBytesEncrypted = 0;

    NSUInteger dataLength = [self length];

    size_t bufferSize = dataLength + kCCBlockSizeAES128;
        void *buffer = malloc(bufferSize);


    NSMutableData *output = [[NSData alloc] init];


    CCCryptorStatus result = CCCrypt( kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                                     keyPtr, kCCKeySizeAES128,
                                     NULL,
                                     [self mutableBytes], [self length],
                                     buffer, bufferSize,
                                     &numBytesEncrypted );

    output = [NSMutableData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
        if( result == kCCSuccess )
        {
                return output;
        }
    return NULL;
}

并尝试在 Ruby 中使用 OpenSSL 解密:

aes = OpenSSL::Cipher::Cipher.new('AES-128-CBC')
aes.decrypt
aes.padding = 1
aes.iv = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0].pack('c*')
aes.key = key
result = aes.update(d) + aes.final

我愿意使用任何红宝石。我可以控制双方,但我无法让它与 EzCrypto、openssl 或 crypt 一起使用。

有人知道如何让它们一起工作吗?

【问题讨论】:

    标签: objective-c ruby encryption openssl aes


    【解决方案1】:

    您的代码泄漏了输出的第一次分配。

    除此之外,它看起来还不错。

    这是一个完整的端到端实现,使用用户密码短语的 SHA256 哈希(在本例中为“Salamander”)并对输出进行 base64 处理。源代码中有一个 PHP 测试实现,它重构密钥,然后在给出最终输出之前修剪 PKCS7 填充。接下来是 Ruby 中的解密器,PKCS7 填充删除由 OpenSSL::Cipher 自动发生。

    给你:

    // Crypto categories for iOS
    
    #import <CommonCrypto/CommonCryptor.h>
    #import <CommonCrypto/CommonDigest.h>
    
    
    @interface NSData( Crypto )
    
    - (NSData *) aesEncryptedDataWithKey:(NSData *) key;
    - (NSString *) base64Encoding;
    
    @end
    
    
    @interface NSString( Crypto )
    
    - (NSData *) sha256;
    
    @end
    
    
    // --------
    
    
    @implementation NSData( Crypto )
    
    - (NSData *) aesEncryptedDataWithKey:(NSData *) key {
        unsigned char               *buffer = nil;
        size_t                      bufferSize;
        CCCryptorStatus             err;
        NSUInteger                  i, keyLength, plainTextLength;
    
        // make sure there's data to encrypt
        err = ( plainTextLength = [self length] ) == 0;
    
        // pass the user's passphrase through SHA256 to obtain 32 bytes
        // of key data.  Use all 32 bytes for an AES256 key or just the
        // first 16 for AES128.
        if ( ! err ) {
            switch ( ( keyLength = [key length] ) ) {
                case kCCKeySizeAES128:
                case kCCKeySizeAES256:                      break;
    
                // invalid key size
                default:                    err = 1;        break;
            }
        }
    
        // create an output buffer with room for pad bytes
        if ( ! err ) {
            bufferSize = kCCBlockSizeAES128 + plainTextLength + kCCBlockSizeAES128;     // iv + cipher + padding
    
            err = ! ( buffer = (unsigned char *) malloc( bufferSize ) );
        }
    
        // encrypt the data
        if ( ! err ) {
            srandomdev();
    
            // generate a random iv and prepend it to the output buffer.  the
            // decryptor needs to be aware of this.
            for ( i = 0; i < kCCBlockSizeAES128; ++i ) buffer[ i ] = random() & 0xff;
    
            err = CCCrypt( kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
                [key bytes], keyLength, buffer, [self bytes], plainTextLength,
                buffer + kCCBlockSizeAES128, bufferSize - kCCBlockSizeAES128, &bufferSize );
        }
    
        if ( err ) {
            if ( buffer ) free( buffer );
    
            return nil;
        }
    
        // dataWithBytesNoCopy takes ownership of buffer and will free() it
        // when the NSData object that owns it is released.
        return [NSData dataWithBytesNoCopy: buffer length: bufferSize + kCCBlockSizeAES128];
    }
    
    - (NSString *) base64Encoding {
        char                    *encoded, *r;
        const char              eTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        unsigned                i, l, n, t;
        UInt8                   *p, pad = '=';
        NSString                *result;
    
        p = (UInt8 *) [self bytes];
        if ( ! p || ( l = [self length] ) == 0 ) return @"";
        r = encoded = malloc( 4 * ( ( n = l / 3 ) + ( l % 3 ? 1 : 0 ) ) + 1 );
    
        if ( ! encoded ) return nil;
    
        for ( i = 0; i < n; ++i ) {
            t  = *p++ << 16;
            t |= *p++ << 8;
            t |= *p++;
    
            *r++ = eTable[ t >> 18 ];
            *r++ = eTable[ t >> 12 & 0x3f ];
            *r++ = eTable[ t >>  6 & 0x3f ];
            *r++ = eTable[ t       & 0x3f ];
        }
    
        if ( ( i = n * 3 ) < l ) {
            t = *p++ << 16;
    
            *r++ = eTable[ t >> 18 ];
    
            if ( ++i < l ) {
                t |= *p++ << 8;
    
                *r++ = eTable[ t >> 12 & 0x3f ];
                *r++ = eTable[ t >>  6 & 0x3f ];
            } else {
                *r++ = eTable[ t >> 12 & 0x3f ];
                *r++ = pad;
            }
    
            *r++ = pad;
        }
    
        *r = 0;
    
        result = [NSString stringWithUTF8String: encoded];
    
        free( encoded );
    
        return result;
    }
    
    @end
    
    
    @implementation NSString( Crypto )
    
    - (NSData *) sha256 {
        unsigned char               *buffer;
    
        if ( ! ( buffer = (unsigned char *) malloc( CC_SHA256_DIGEST_LENGTH ) ) ) return nil;
    
        CC_SHA256( [self UTF8String], [self lengthOfBytesUsingEncoding: NSUTF8StringEncoding], buffer );
    
        return [NSData dataWithBytesNoCopy: buffer length: CC_SHA256_DIGEST_LENGTH];
    }
    
    @end
    
    
    // -----------------
    
    
    @implementation AppDelegate
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    
        NSData              *plain = [@"This is a test of the emergency broadcast system." dataUsingEncoding: NSUTF8StringEncoding];
        NSData              *key = [NSData dataWithBytes: [[@"Salamander" sha256] bytes] length: kCCKeySizeAES128];
        NSData              *cipher = [plain aesEncryptedDataWithKey: key];
        NSString            *base64 = [cipher base64Encoding];
    
        NSLog( @"cipher: %@", base64 );
    
        // stuff the base64'ed cipher into decrypt.php:
        // http://localhost/~par/decrypt.php?cipher=<base64_output>
    
    /*
    <?php
        header( "content-type: text/plain" );
    
        if ( ! ( $cipher = $_GET[ 'cipher' ] ) ) {
            echo "no cipher parameter found";
            return;
        }
    
        echo "cipher: $cipher\n";
    
        $cipher = base64_decode( $cipher );
        $iv = substr( $cipher, 0, 16 );
        $cipher = substr( $cipher, 16 );
    
        // use the full key (all 32 bytes) for aes256
        $key = substr( hash( "sha256", "Salamander", true ), 0, 16 );
    
        $plainText = mcrypt_decrypt( MCRYPT_RIJNDAEL_128, $key, $cipher, MCRYPT_MODE_CBC, $iv );
        $plainTextLength = strlen( $plainText );
    
        // strip pkcs7 padding
        $padding = ord( $plainText[ $plainTextLength - 1 ] );
        $plainText = substr( $plainText, 0, -$padding );
    
        printf( "plaintext: %s\n", $plainText );
    ?>
    */
    
        return YES;
    }
    
    @end
    

    在 Ruby 中解密上述输出:

    require 'base64'
    require 'openssl'
    
    def decrypt( cipherBase64 )
        cipher = Base64.decode64( cipherBase64 )
    
        aes = OpenSSL::Cipher::Cipher.new( "aes-128-cbc" ).decrypt
        aes.iv = cipher.slice( 0, 16 )
        # don't slice the SHA256 output for AES256
        aes.key = ( Digest::SHA256.digest( 'Salamander' ) ).slice( 0, 16 )
    
        cipher = cipher.slice( 16..-1 )
    
        return aes.update( cipher ) + aes.final
    end
    
    text = '3o4ARWOxwmLEPgq3SJ3A2ws7sUSxMvWSKbbs+oABsOcywk+9qPBoDjhLAfAW/n28pbnsT2w5QMSye6pz3Lz8xmg5BYL8HdfKwbS9EpTbaUc='
    
    print decrypt( text ) + "\n"
    

    【讨论】:

    • 我如何证明这个加密/解密是否有效,在link 在线工具中证明?因为当我尝试使用文本 3o4ARWOxwmLEPgq3SJ3A2ws7sUSxMvWSKbbs+oABsOcywk+9qPBoDjhLAfAW/n28pbnsT2w5QMSye6pz3Lz8xmg5BYL8HdfKwbS9EpTbaUc= ,'Salamander' 作为密钥,方法 Rijndael-128 CBC 时,它没有显示解密
    • 好的,我做到了,但是当我用你提供的代码在 php 中解密时,大多数时候活字符都是垃圾,在 php 中它看起来像正方形 []
    • 您在第一条评论中的输入是正确的(尽管请检查 base64 框),但由于该页面处理密钥的方式,它无法使用在线解密器工具工作。我的代码假设普通用户将使用低熵密钥,例如“love”,四个字符只给了我们 32 位的关键数据(其中每个字节的高位为零,所以实际上只有 28 位的熵)。 AES/Rijndael-128 需要 128 位密钥数据,而 AES-256 需要 256 位。为了提供必要数量的密钥数据,我在密码短语上调用 SHA256,并将其生成的位用作实际密钥。
    • 感谢您的回答,您很好,我已经做到了,并且可以正常工作,关于 base64 编码,我正在使用方法 - (NSString *) base64Encoding ,它在您的代码中,用于NSData 的实现。但是它给了我一些字符'/',我的伙伴告诉我他需要没有'/'的代码,我已经在网上检查了base64编码,没有人给'/'。你知道为什么吗?
    • 我认为您需要让您的合作伙伴修复他的 Base64 解码器实现。几年前我写了那个base64编码器,但它是正确的。请参阅en.wikipedia.org/wiki/Base64 - 特别是显示 Base64 索引表的部分。您会看到它与我的 base64Encoding 方法的 eTable(编码器表)成员 1:1 对应。抱歉我的回复很慢,我目前正忙于一个截止日期很短的项目。
    【解决方案2】:

    我还没有完成您正在做的事情,至少不完全是,但以下可能值得一试。

    require 'crypt/rijndael'
    require 'Base64'
    rijndael = Crypt::Rijndael.new("samepassword")
    decryptedBlock = rijndael.decrypt_block(Base64.decode64("encrypted-b64-encoded-data"))
    

    【讨论】:

      【解决方案3】:

      我认为 Jonas 使用 Rijndael 可能是正确的。我记得使用 Crypt 时需要指定密码。

      【讨论】:

        【解决方案4】:

        您也可以使用以下提供内置 AES-256-CBC 密码和 Base64 编码的库,您可以在两个平台上快速使用:

        红宝石

        https://github.com/Gurpartap/aescrypt

        以下是使用 AESCrypt Ruby gem 的方法:

        message = "top secret message"
        password = "p4ssw0rd"
        
        # Encrypting
        encrypted_data = AESCrypt.encrypt(message, password)
        
        # Decrypting
        message = AESCrypt.decrypt(encrypted_data, password)
        

        目标-C

        https://github.com/Gurpartap/AESCrypt-ObjC

        您将如何使用 AESCrypt Objective-C 类:

        NSString *message = @"top secret message";
        NSString *password = @"p4ssw0rd";
        
        // Encrypting
        NSString *encryptedData = [AESCrypt encrypt:message password:password];
        
        // Decrypting
        NSString *message = [AESCrypt decrypt:encryptedData password:password];
        

        希望对你有帮助!

        【讨论】:

        • 如果您可以发布一个在 IOS 中加密的示例,然后在 ruby​​ 中解密该加密字符串,那将非常有帮助。我发现加密解密本身在每个平台上都可以工作,但当您想跨平台进行时却没有,就像您在此处声称的那样。谢谢
        • @Gurpartap 我们正在使用您的 Ruby 代码进行加密,并使用 obj-c 代码进行解密,但我正在尝试使用您使用 IV 但它们不起作用的方法。如果您使用消除 IV 的方法,您的解决方案将有效。如果您使用使用 IV 的方法,它将不起作用。我做错了什么。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-19
        • 2013-03-01
        • 1970-01-01
        • 2016-07-28
        • 1970-01-01
        相关资源
        最近更新 更多