DelphiのOpenSSLライブラリを介したRSA暗号化





開発者の義務により、タスクはPEM形式の公開キーと秘密キーを使用してRSAアルゴリズムでテキスト文字列を暗号化することでした。 この問題を研究するとき、選択はOpenSSLライブラリの使用に依存していました。 Delphiでの暗号化機能の実装例を共有したいと思います。 アクションは、Windows、Borland Delphi 7開発環境で実行されました。



どこから始めますか?



まず、opensslパッケージ、またはlibeay32.dllライブラリが必要です。 ライブラリを接続するには、プロジェクトに接続する必要のある既製のユニットlibeay32.pasが必要です。 私の例では、プロジェクトからキーを生成する機能はありません。これを行う方法は、記事の下部にあるリンクの資料で読むことができます。 キーペアを生成するには、cmdから次のコマンドを使用して自分でopensslを使用しました。

秘密鍵を生成し、そこから公開鍵を抽出します

openssl genrsa 1024 > private.pem openssl rsa -in private.pem -pubout > public.pem
      
      





ここで、1024はキービットサイズです。 暗号化のための文字列の長さは、キーのビット数にも依存することに注意してください。 キーが1024ビットの場合、キーのサイズと同じサイズである128バイトのみを暗号化できます。 以下に、RSAキー構造のサイズを決定する関数を示します。 しかし、それだけではありません。 暗号化中にパディングパラメータを指定すると、このバッファは11バイト減少します。 パディングパラメータは、データのアライメントを担当します-PKCS#1



2048ビットのキーを生成すると、256バイトを暗号化できます。 これにより、1バイトしか暗号化されていなくても、出力暗号文のサイズが大きくなります。



私のタスクでは、117バイトで十分でした。カードデータが収まり、データベース内の行のサイズはもちろん大幅に増加しました。 しかし、それにはセキュリティが必要です。



主な機能



暗号化に使用する主なものは次のとおりです。



暗号化機能の初期化


 OpenSSL_add_all_algorithms; OpenSSL_add_all_ciphers; OpenSSL_add_all_digests; ERR_load_crypto_strings; ERR_load_RSA_strings;
      
      





破壊する



 EVP_cleanup; ERR_free_strings;
      
      





読書キー


 //     PEM,   RSA // bp  ,   RSA   x, cb –      . function PEM_read_bio_PrivateKey(bp: pBIO; var x: pEVP_PKEY; cb: TPWCallbackFunction; u: pointer): pEVP_PKEY; cdecl; //     PEM,   RSA function PEM_read_bio_PUBKEY(bp: pBIO; var x: pEVP_PKEY; cb: TPWCallbackFunction; u: pointer): pEVP_PKEY; cdecl;
      
      





暗号化/復号化機能


 /// flen   from   _to   RSA       // padding.   /   -1   //   function RSA_public_encrypt(flen: integer; from: PCharacter; _to: PCharacter; rsa: pRSA; padding: integer): integer; cdecl; //   function RSA_private_encrypt(flen: integer; from: PCharacter; _to: PCharacter; rsa: pRSA; padding: integer): integer; cdecl; //   function RSA_public_decrypt(flen: integer; from: PCharacter; _to: PCharacter; rsa: pRSA; padding: integer): integer; cdecl; //   function RSA_private_decrypt(flen: integer; from: PCharacter; _to: PCharacter; rsa: pRSA; padding: integer): integer; cdecl;
      
      





さあ始めましょう



したがって、生成されたキーがあり、dllはプロジェクトフォルダーにあり、liblea32.pasはuseに接続されています。 openssl初期化手順を呼び出します。



 procedure LoadSSL; begin OpenSSL_add_all_algorithms; OpenSSL_add_all_ciphers; OpenSSL_add_all_digests; ERR_load_crypto_strings; ERR_load_RSA_strings; end; procedure FreeSSL; begin EVP_cleanup; ERR_free_strings; end;
      
      





キー読み込み関数を作成します。


KeyFile-キーに任せる( 'C:\ key.pem'):



 function LoadPublicKey(KeyFile: string) :pEVP_PKEY ; var mem: pBIO; k: pEVP_PKEY; begin k:=nil; mem := BIO_new(BIO_s_file()); //BIO   BIO_read_filename(mem, PAnsiChar(KeyFile)); //     BIO try result := PEM_read_bio_PUBKEY(mem, k, nil, nil); // BIO   pEVP_PKEY,    nil,        finally BIO_free_all(mem); end; end; function LoadPrivateKey(KeyFile: string) :pEVP_PKEY; var mem: pBIO; k: pEVP_PKEY; begin k := nil; mem := BIO_new(BIO_s_file()); BIO_read_filename(mem, PAnsiChar(KeyFile)); try result := PEM_read_bio_PrivateKey(mem, k, nil, nil); finally BIO_free_all(mem); end; end;
      
      





キー読み取り関数の呼び出しとエラー処理


 var FPublicKey: pEVP_PKEY; FPrivateKey: pEVP_PKEY; err: Cardinal; … FPublicKey := LoadPublicKey('C:\public.key'); FPrivateKey := LoadPrivateKey('C:\private.key'); //if FPrivateKey = nil then //    ,    if FPublicKey = nil then begin err := ERR_get_error; repeat log.Lines.Add(string(ERR_error_string(err, nil))); err := ERR_get_error; until err = 0; end;
      
      





暗号化(公開鍵)


 var rsa: pRSA; //  RSA size: Integer; FCryptedBuffer: pointer; //   b64, mem: pBIO; str, data: AnsiString; len, b64len: Integer; penc64: PAnsiChar; size: Integer; err: Cardinal begin rsa := EVP_PKEY_get1_RSA(FPrivateKey); //  RSA  EVP_PKEY_free(FPrivateKey); //  pEVP_PKEY size := RSA_size(rsa); //    GetMem(FCryptedBuffer, size); //     str := AnsiString('Some text to encrypt'); //    // len := RSA_public_encrypt(Length(str), //     PAnsiChar(str), //   FCryptedBuffer, //   rsa, //   RSA_PKCS1_PADDING //   ); if len > 0 then //     begin //       base64 b64 := BIO_new(BIO_f_base64); // BIO  base64 mem := BIO_push(b64, BIO_new(BIO_s_mem)); // Stream try BIO_write(mem, FCryptedBuffer, len); //   Stream    BIO_flush(mem); b64len := BIO_get_mem_data(mem, penc64); //    base64 SetLength(data, b64len); //     Move(penc64^, PAnsiChar(data)^, b64len); //    data   base64 finally BIO_free_all(mem); end; end else begin //  ,     -1 err := ERR_get_error; repeat log.Lines.Add(string(ERR_error_string(err, nil))); err := ERR_get_error; until err = 0; end; RSA_free(rsa); end;
      
      





復号化(秘密鍵)


 var rsa: pRSA; out_: AnsiString; str, data: PAnsiChar; len, b64len: Integer; penc64: PAnsiChar; b64, mem, bio_out, bio: pBIO; size: Integer; err: Cardinal; begin //ACryptedData : string; //   base64 rsa := EVP_PKEY_get1_RSA(FPublicKey); size := RSA_size(rsa); GetMem(data, size); //       GetMem(str, size); //        base64 //Decode base64 b64 := BIO_new(BIO_f_base64); mem := BIO_new_mem_buf(PAnsiChar(ACryptedData), Length(ACryptedData)); BIO_flush(mem); mem := BIO_push(b64, mem); BIO_read(mem, str , Length(ACryptedData)); //       BIO_free_all(mem); //  len := RSA_private_decrypt(size, PAnsiChar(str), data, rsa, RSA_PKCS1_PADDING); if len > 0 then begin //   data    «»  , ,    out_         data SetLength(out_, len); Move(data^, PAnsiChar(out_ )^, len); end else begin //  ,     -1 err := ERR_get_error; repeat log.Lines.Add(string(ERR_error_string(err, nil))); err := ERR_get_error; until err = 0; end; end;
      
      





結論として、アプリケーションで「有線」キーを読み取る例


この例は、秘密鍵とその読み取りを示しています。これは「縫い合わせ」られており、公開鍵はPEM_read_bio_PUBKEY関数によって読み取られます

 var mem, keybio: pBIO; k: pEVP_PKEY; keystring: AnsiString; begin keystring := '-----BEGIN RSA PRIVATE KEY-----' + #10 + 'MIICXgIBAAKBgQCfydli2u2kJfb2WetkOekjzQIg7bIuU7AzAlBUPuA72UYXWnQ/' + #10 + 'XcdSzEEMWSBLP7FO1vyVXR4Eb0/WqthF0ZViOK5bCN9CnR/1GMMiSqmIdByv/gUe' + #10 + 'Z/UjGrKmxeQOoa2Yt0MJC64cNXgnKmYC7ui3A12LlvNdBBEF3WpcDbv+PQIDAQAB' + #10 + 'AoGBAJnxukKHchSHjxthHmv9byRSyw42c0g20LcUL5g6y4Zdmi29s+moy/R1XOYs' + #10 + 'p/RXdNfkQI0WnWjgZScIij0Z4rSs39uh7eQ5qxK+NH3QIWeR2ZNIno9jAXPn2bkQ' + #10 + 'odS8FPzbZM9wHhpRvKW4FNPXqTc3ZkTcxi4zOwOdlECf9G+BAkEAzsJHgW1Isyac' + #10 + 'I61MDu2qjMUwOdOBYS8GwEBfi/vbn/duwZIBXG/BZ7Pn+cBwImfksEXwx0MTkgF3' + #10 + 'gyaChUSu+QJBAMXX3d94TwcF7lG9zkzc+AR/Onl4Z5UAb1GmUV57oYIFVgW1RIOk' + #10 + 'vqynXWrTjTOg9C9j+VEpBG67LcnkwU16JmUCQH7pukKz9kAhnw43PcycDmhCUgvs' + #10 + 'zCn/V8GCwiOHAZT7qLyhBrzazHj/cZFYknxMEZAyHk3x2n1w8Q9MACoVsuECQQDF' + #10 + 'U7cyara31IyM7vlS5JpjMdrKyPLXRKXDFFXYHQtLubLA4rlBbBHZ9txP7kzJj+G9' + #10 + 'WsOS1YxcPUlAM28xrYGZAkEArVKJHX4dF8UUtfvyv78muXJZNXTwmaaFy02xjtR5' + #10 + 'uXWT1QjVN2a6jv6AW7ukXiSoE/spgfvdoriMk2JSs88nUw==' + #10 + '-----END RSA PRIVATE KEY-----' ; k := nil; keybio := BIO_new_mem_buf(Pchar(keystring), -1); mem := BIO_new(BIO_s_mem()); BIO_read(mem, PAnsiChar(keystring), length(PAnsiChar(keystring))); try result := PEM_read_bio_PrivateKey(keybio, k, nil, nil); finally BIO_free_all(mem); end; end;
      
      





これが私の実装例の終わりです。 プロジェクトソースはGithubで入手できます。



イトチニキ:






UPD

コメントの議論から追加します: 公開鍵で必要な文字列を暗号化する場合、 秘密でのみ解読され、その逆も同様です-秘密であれば、公開鍵でのみ解読できます。 私の場合、クライアントはデータを暗号化する公開鍵を持ち、サーバー上でのみ秘密鍵で復号化できます。



All Articles