開発者の義務により、タスクは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で入手できます。
イトチニキ:
- Vladimir Meshkovによる記事「OpenSSLライブラリのツールを使用して暗号化データを保護しています」
- ファイル暗号化の例
- 作者libeay32.pasからのパスワードリクエストでキーを読み取る例
- キーペアの作成、SHA1フィンガープリントなどのチェック
UPD
コメントの議論から追加します: 公開鍵で必要な文字列を暗号化する場合、 秘密でのみ解読され、その逆も同様です-秘密であれば、公開鍵でのみ解読できます。 私の場合、クライアントはデータを暗号化する公開鍵を持ち、サーバー上でのみ秘密鍵で復号化できます。