RSA暗号化プロセスノート

11779 ワード

一、各種の異なる接尾辞名が表す意味


X.509は一般的な一般的な証明書フォーマットです.すべての証明書はPublic Key Infrastructure(PKI)のために制定されたITU-T X 509国際基準に合致している.
  • .pem:Privacy Enhanced Mail,以----BEGIN........で始まる終了、内容はBASE 64符号化
  • .csr:証明書署名要求、これは証明書ではなく、コアコンテンツは公開鍵とユーザー情報
  • である.
  • .crt:署名済み証明書
  • .der:Distinguished Encoding Rules、証明書、バイナリフォーマット、読み取り不可
  • .p 12:=CERファイル+秘密鍵
  • PS:システムはライブラリの公開鍵ファイルのサポートを持参する.derフォーマット、秘密鍵サポート.p 12フォーマット

    二、opensslコマンドrsa秘密鍵共通鍵及び証明書の生成


    コマンドラインを対応するフォルダに切り替え
    $ openssl     // openssl 
    $ genrsa -out rsa_private_key.pem 1024         // 
    $ rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
     writing RSA key           // 
    $ pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt         // PKCS#8 ,  
    $ req -new -key rsa_private_key.pem -out rsa_cert.csr         // , 
    $ x509 -req -days 3650 -in rsa_cert.csr -signkey rsa_private_key.pem -out rsa_cert.crt             // , 10 
    $ x509 -outform der -in rsa_cert.crt -out rsa_cert.der           // -  PEM   DER  
    $ pkcs12 -export -out p.p12 -inkey rsa_private_key.pem -in rsa_cert.crt         // P12 , 
    $ exit       // openssl 
    

    三、暗号化の方式及び起動方法


    暗号化の方式は主に2種類に分けられる
  • 1、システムのうちのSecKeyRef共通鍵は、接尾辞で名前を付けることができる.perのファイル作成は、共通鍵文字列で作成することもできます.秘密鍵は接尾辞で名前を付ける.p 12のファイル作成は、パスワードが必要であるか、秘密鍵文字列で作成することもできる.共通鍵暗号化、秘密鍵復号、秘密鍵署名、共通鍵検証.
  • 2、サードパーティのopensslにおけるRSA共通鍵は、接尾辞で名前を付けることができる.pemのファイル作成は、共通鍵文字列で作成することもできます.秘密鍵は接尾辞で名前を付ける.pemのファイル作成は、秘密鍵文字列で作成することもできます.共通鍵暗号化、秘密鍵復号、秘密鍵署名、共通鍵検証.秘密鍵暗号化、共通鍵復号化も可能である.

  • 四、実現過程で踏んだ穴

  • 1、暗号化されたコンテンツにはエスケープが必要であり、鍵の長さから11文字を差し引くことはできません.長すぎる場合は、自分でセグメント暗号化を切り取る必要があります.
  • 2、SecKeyRef復号中パラメータのピット
  • /*!
     @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);
    
    // 
    - (NSData *)encryptData:(NSData *)data
                          withKeyType:(QKeyType)keyType {
        SecKeyRef keyRef = _pubSecKeyRef;
        if (keyRef == NULL) {
            NSAssert(NO, @" ");
            return nil;
        }
        
        int dataLength = (int)data.length;
        int blockSize = (int)SecKeyGetBlockSize(keyRef) * sizeof(uint8_t);
     int maxLen =  blockSize;
        if (padding == kSecPaddingPKCS1) {
    /**When PKCS1 padding is performed, the maximum length of data that can
     be encrypted is the value returned by SecKeyGetBlockSize() - 11. */
            maxLen -= 11;
        }
        int count = (int)ceil(dataLength * 1.0 / maxLen); //  count ,ceil , 
    // , 
        
        NSMutableData *encryptedData = [[NSMutableData alloc] init] ;
        uint8_t* cipherText = (uint8_t*)malloc(blockSize);
        
        for (int i = 0; i < count; i++) {
            NSUInteger bufferSize = MIN(maxLen, dataLength - i * maxLen);
            NSData *inputData = [data subdataWithRange:NSMakeRange(i * maxLen, bufferSize)];
            bzero(cipherText, blockSize);// 
            size_t outlen = blockSize; // maxLen , ,
            
            OSStatus status = SecKeyEncrypt(keyRef,
                                            kSecPaddingPKCS1,
                                            (const uint8_t *)[inputData bytes],
                                            bufferSize,
                                            cipherText,
                                            &outlen);
            if (status == errSecSuccess) {//errSecSuccess == 0
                [encryptedData appendBytes:cipherText length:outlen];
            }else{
                free(cipherText);
                cipherText = NULL;
                return nil;
            }
        }
        free(cipherText);
        cipherText = NULL;
        
        return encryptedData;
    }
    // 
    - (NSData *)decryptEncryptedData:(NSData *)encryptedData
                                   withKeyType:(QKeyType)keyType {
        SecKeyRef keyRef = _priSecKeyRef;
        if (keyRef == NULL) {
            NSAssert(NO, @" ");
            return nil;
        }
        int dataLength = (int)encryptedData.length;
        int blockSize = (int)SecKeyGetBlockSize(keyRef) * sizeof(uint8_t);
        int maxLen = blockSize ; // 11 
        int count = (int)ceil(dataLength * 1.0 / blockSize);
        
        NSMutableData *decryptedData = [[NSMutableData alloc] init] ;
        UInt8 *outbuf = malloc(blockSize);
        for (int i = 0; i < count; i++) {
            NSUInteger bufferSize = MIN(maxLen, dataLength - i * maxLen);
            NSData *inputData = [encryptedData subdataWithRange:NSMakeRange(i * maxLen, bufferSize)];
            bzero(outbuf, blockSize);// 
            size_t outlen = blockSize;
            
            OSStatus status = SecKeyDecrypt(keyRef,
                                            secPadding(),
                                            (const uint8_t *)[inputData bytes],
                                            bufferSize,
                                            outbuf,
                                            &outlen);
            if (status == errSecSuccess) {
                [decryptedData appendBytes:outbuf length:outlen];
            }else{
                free(outbuf);
                outbuf = NULL;
                return nil;
            }
        }
        
        free(outbuf);
        outbuf = NULL;
        return decryptedData;
    }
    
  • 3、三方RSA類暗号化で遭遇したピット
  • PEM_read_RSAPrivateKey(, , , )
    

    通過するpemファイルがRSAを作成する時、この関数によってパスワードを伝達することができて、cに対して熟知していないで、しかし長い間探してやっと探し当てて、他の人はどのように使います
    int pass_cb(char *buf, int size, int rwflag, void* password) {
        snprintf(buf, size, "%s", (char*) password);
        return (int)strlen(buf);
    }
    

    パスワードはありますpemファイルはRSAの作成に失敗し、その後も努力を続けます.
    - (NSData *)encryptData:(NSData *)data
                          withKeyType:(QKeyType)keyType {
        RSA *rsa = [self rsaForKey:keyType];
        
        if (rsa == NULL) {
            NSAssert(NO, @" ");
            return nil;
        }
        QRSA_PADDING_TYPE type = [self current_PADDING_TYPE];
        NSUInteger length = [self sizeOfRSA_PADDING_TYPE:type andRSA:rsa] * 1.0;
        NSUInteger dataLength = data.length;
        int count = (int)ceil(dataLength * 1.0 / length);
        
        int status;//  
        char *encData = (char *)malloc(length);
        NSMutableData *encryptedData = [[NSMutableData alloc] init] ;
        for (int i = 0; i < count; i++) {
            NSUInteger bufferSize = MIN(length, dataLength - i * length);
            NSData *inputData = [data subdataWithRange:NSMakeRange(i * length, bufferSize)];
            bzero(encData, length);// 
            
            switch (keyType) {
                case QKeyTypePublic:
                    status = RSA_public_encrypt((int)bufferSize,
                                                (unsigned char *)[inputData bytes],
                                                (unsigned char *)encData,
                                                _pubRSA,
                                                type);
                    break;
                    
                case QKeyTypePrivate:
                    status = RSA_private_encrypt((int)bufferSize,
                                                 (unsigned char*)[inputData bytes],
                                                 (unsigned char*)encData,
                                                 _priRSA,
                                                 type);
                    break;
            }
            
            if (status > 0){//  status   -1   >0 
                [encryptedData appendBytes:encData length:status];
               
            }else{
                if (encData) {
                    free(encData);
                }
                return nil;
            }
        }
        if (encData){
            free(encData);
        }
        return encryptedData;
    }
    
  • 4、SecKeyRef署名と検証はMD 5
  • をサポートしていません.

    五、感謝


    同じ鍵セットで作成されたSecKeyRefとRSAは、互いに復号化することができる.
    コードの実現の過程の中で、多くのコードの参考を探して、一部は直接借りて、数日かかりました.本文で書いたコード全体が実現した後、参考にした文章のリンクはここで一つ一つ列挙することができなくて、とても感謝しています.

    暗号化と署名


    PS:補足、2018年3月23日暗号化:データに対して機密性保護を行う;対称暗号化、公開鍵暗号化、秘密鍵暗号化の3つの方法があります.3つの方法は、いずれも許容できない欠点があるため、それらを組み合わせて使用する.主に以下のプロセスを行います.
  • 情報送信者が情報を送信する必要がある場合、まず、送信するメッセージを暗号化する対称鍵を生成する.
  • 情報送信者は、情報受信者の公開鍵で前記対称鍵を暗号化する.
  • 情報送信者は、第1のステップと第2のステップの結果を組み合わせて、デジタル封筒と呼ばれる情報受信者に送信する.
  • 情報受信者は、暗号化された対称鍵を自分の秘密鍵で復号し、この対称鍵で送信者によって暗号化された暗号文を復号し、本物の原文を得る.

  • 署名:主に認証に使用されます.データの整合性、一貫性、およびデータ・ソースの信頼性を保証します.主に以下のプロセスを行います.
  • 情報送信者は、一方向ハッシュ関数(HASH関数)を用いて情報の要約を生成する.
  • 情報送信者は、自分の秘密鍵を使用して情報の要約に署名する.
  • 情報送信者は、情報自体を署名された情報要約とともに送信する.
  • 情報受信者は、情報送信者と同じ一方向ハッシュ関数(HASH関数)を用いて受信した情報そのものについて新たな情報要約を生成し、情報送信者の公開鍵を用いて情報要約を検証して、情報送信者のアイデンティティと情報が修正されたか否かを確認する.暗号化は可逆的であり,署名は不可逆的である.秘密鍵は署名にのみ使用でき、公開鍵は署名を検証するために使用されます.