首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Xcode 12:函数'SecKeyEncrypt‘的隐式声明在C99中无效

Xcode 12:函数'SecKeyEncrypt‘的隐式声明在C99中无效
EN

Stack Overflow用户
提问于 2020-10-08 20:26:13
回答 1查看 1.4K关注 0票数 1

升级到Xcode 12.0.1之后,我的用于文件解密的命令行Mac应用程序(用Swift编写)在试图构建时遇到以下错误:

  • 函数'SecKeyEncrypt‘的隐式声明在C99中无效
  • 函数'SecKeyRawSign‘的隐式声明在C99中无效
  • 函数'SecKeyDecrypt‘的隐式声明在C99中无效

en-/de代码(用目标C编写)取自https://github.com/ideawu/Objective-C-RSA,它在Xcode 11中工作得很好。

代码语言:javascript
复制
#import <Security/Security.h>

有一行

代码语言:javascript
复制
#include <Security/SecKey.h>

在这个文件中,这些方法声明如下:

代码语言:javascript
复制
#if SEC_OS_IPHONE
/*!
 @function SecKeyRawSign
 @abstract Given a private key and data to sign, generate a digital
 signature.
 @param key Private key with which to sign.
 @param padding See Padding Types above, typically kSecPaddingPKCS1SHA1.
 @param dataToSign The data to be signed, typically the digest of the
 actual data.
 @param dataToSignLen Length of dataToSign in bytes.
 @param sig Pointer to buffer in which the signature will be returned.
 @param sigLen IN/OUT maximum length of sig buffer on input, actualy
 length of sig on output.
 @result A result code. See "Security Error Codes" (SecBase.h).
 @discussion If the padding argument is kSecPaddingPKCS1, PKCS1 padding
 will be performed prior to signing. If this argument is kSecPaddingNone,
 the incoming data will be signed "as is".

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

 NOTE: The behavior this function with kSecPaddingNone is undefined if the
 first byte of dataToSign is zero; there is no way to verify leading zeroes
 as they are discarded during the calculation.

 If you want to generate a proper PKCS1 style signature with DER encoding
 of the digest type - and the dataToSign is a SHA1 digest - use
 kSecPaddingPKCS1SHA1.
 */
OSStatus SecKeyRawSign(
                       SecKeyRef           key,
                       SecPadding          padding,
                       const uint8_t       *dataToSign,
                       size_t              dataToSignLen,
                       uint8_t             *sig,
                       size_t              *sigLen)
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);


/*!
 @function SecKeyRawVerify
 @abstract Given a public key, data which has been signed, and a signature,
 verify the signature.
 @param key Public key with which to verify the signature.
 @param padding See Padding Types above, typically kSecPaddingPKCS1SHA1.
 @param signedData The data over which sig is being verified, typically
 the digest of the actual data.
 @param signedDataLen Length of signedData in bytes.
 @param sig Pointer to the signature to verify.
 @param sigLen Length of sig in  bytes.
 @result A result code. See "Security Error Codes" (SecBase.h).
 @discussion If the padding argument is kSecPaddingPKCS1, PKCS1 padding
 will be checked during verification. If this argument is kSecPaddingNone,
 the incoming data will be compared directly to sig.

 If you are verifying a proper PKCS1-style signature, with DER encoding
 of the digest type - and the signedData is a SHA1 digest - use
 kSecPaddingPKCS1SHA1.
 */
OSStatus SecKeyRawVerify(
                         SecKeyRef           key,
                         SecPadding          padding,
                         const uint8_t       *signedData,
                         size_t              signedDataLen,
                         const uint8_t       *sig,
                         size_t              sigLen)
__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_2_0);


/*!
 @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 */
                       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);

#endif // SEC_OS_IPHONE

引发错误的解密方法如下所示:

代码语言:javascript
复制
+ (NSData *)decryptData:(NSData *)data withKeyRef:(SecKeyRef) keyRef{
    const uint8_t *srcbuf = (const uint8_t *)[data bytes];
    size_t srclen = (size_t)data.length;
    
    size_t block_size = SecKeyGetBlockSize(keyRef) * sizeof(uint8_t);
    UInt8 *outbuf = malloc(block_size);
    size_t src_block_size = block_size;
    
    NSMutableData *ret = [[NSMutableData alloc] init];
    for(int idx=0; idx<srclen; idx+=src_block_size){
        //NSLog(@"%d/%d block_size: %d", idx, (int)srclen, (int)block_size);
        size_t data_len = srclen - idx;
        if(data_len > src_block_size){
            data_len = src_block_size;
        }
        
        size_t outlen = block_size;
        OSStatus status = noErr;
        status = SecKeyDecrypt(keyRef,   // <<<<<<<<<<<<<<<<<<<<<< This raises the error
                               kSecPaddingNone,
                               srcbuf + idx,
                               data_len,
                               outbuf,
                               &outlen
                               );
        if (status != 0) {
            NSLog(@"SecKeyEncrypt fail. Error Code: %d", status);
            ret = nil;
            break;
        }else{
            //the actual decrypted data is in the middle, locate it!
            int idxFirstZero = -1;
            int idxNextZero = (int)outlen;
            for ( int i = 0; i < outlen; i++ ) {
                if ( outbuf[i] == 0 ) {
                    if ( idxFirstZero < 0 ) {
                        idxFirstZero = i;
                    } else {
                        idxNextZero = i;
                        break;
                    }
                }
            }
            
            [ret appendBytes:&outbuf[idxFirstZero+1] length:idxNextZero-idxFirstZero-1];
        }
    }
    
    free(outbuf);
    CFRelease(keyRef);
    return ret;
}

看来,不能再直接调用en/ directly函数了。我不知道这里发生了什么变化--问题是因为Xcode的改变吗?更重要的是:如何解决这个问题?(我在卡塔莉娜10.15.6号上)任何帮助都是非常感谢的!(如果缺少一些信息,请告诉我。)

EN

回答 1

Stack Overflow用户

发布于 2020-10-09 17:39:08

正如菲利普·米尔斯所指出的,在Security/SecKey.h包含中有一个#if SEC_OS_IPHONE条件,实际上,如果我创建一个iOS应用程序而不是Mac,那么这个项目就可以构建得没有错误。因此,我假设条件是在Xcode 12中引入的,并且像SecKeyEncrypt和SecKeyDecrypt这样的函数不能再在macOS上调用了(除非Mac催化剂)--也许从来没有正式支持过。

无论如何,我现在已经通过CocoaPods添加了CocoaPods并使解密部分正常工作。如果您感兴趣,我的头文件RSACryptoOpenSSL.h如下所示:

代码语言:javascript
复制
#import <Foundation/Foundation.h>
#import <openssl/bio.h>
#import <openssl/pem.h>

NS_ASSUME_NONNULL_BEGIN

@interface RSACryptoOpenSSL : NSObject

+ (NSString *)decryptMacOsString:(NSString *)str privateKey:(NSString *)privKey;

@end

NS_ASSUME_NONNULL_END

和这样的实现文件

代码语言:javascript
复制
#import "RSACryptoOpenSSL.h"

@implementation RSACryptoOpenSSL

+ (NSString *)decryptMacOsString:(NSString *)str privateKey:(NSString *)privKey
{
    NSData *data = [[NSData alloc] initWithBase64EncodedString:str options:NSDataBase64DecodingIgnoreUnknownCharacters];
    
    // load private key
    const char *private_key = [privKey UTF8String];
    BIO *bio = BIO_new_mem_buf((void*)private_key, (int)strlen(private_key));
    RSA *rsa_privatekey = PEM_read_bio_RSAPrivateKey(bio, NULL, 0, NULL);
    BIO_free(bio);
    
    int maxSize = RSA_size(rsa_privatekey);
    unsigned char *output = (unsigned char *) malloc(maxSize * sizeof(char));
    int bytes __unused = RSA_private_decrypt((int)[data length], [data bytes], output, rsa_privatekey, RSA_PKCS1_PADDING);
        
    NSString *ret = [NSString stringWithUTF8String:(char *)output];
    return ret;
}

@end

感谢狗帽timburks提供的宝贵信息。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64270139

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档