2016-09-10 16 views
0

ポーランド政府(財務省)は最近SAF-T(Standard Audit File-Tax/pl: JPK - Jednolity Plik Kontrolny)。このソリューションの重要な部分の1つを適切な方法で実装するのは非常に面倒でした。この部分は、MFが提供する証明書ファイルから読み込まれた公開鍵を使用して、MS CryptoAPIのRSA 256/ECB/PKCS#1アルゴリズムを使用して、Azure Cloud Storageに送信されたファイルの暗号化に使用されるユーザ生成パスワードを暗号化することです。これにJPK - CryptoAPI RSA 256/ECB/PKCS#1証明書(公開鍵)で公開鍵を使用した一回の秘密パスワード暗号化

答えて

2

私の作業溶液は、(JEDI API Library & Security Code Libraryを使用しています)されています

unit CryptoAPI_RSA; 
// The MIT License (MIT) 
// 
// Copyright (c) 2016 Grzegorz Molenda 
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy 
// of this software and associated documentation files (the "Software"), to deal 
// in the Software without restriction, including without limitation the rights 
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
// copies of the Software, and to permit persons to whom the Software is 
// furnished to do so, subject to the following conditions: 
// 
// The above copyright notice and this permission notice shall be included in all 
// copies or substantial portions of the Software. 
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
// SOFTWARE. 
interface 

uses 
    SysUtils, 
    Classes; 

function CryptoAPI_Encrypt_RSA(const Input: TBytes; const cert: TMemoryStream): String; 

implementation 

uses 
    Windows, 
    StrUtils, 
    JwaWinCrypt, 
    JwaWinError, 
    EncdDecd; 

type 
    ERSAEncryptionError = class(Exception); 

function WinError(const RetVal: BOOL; const FuncName: String): BOOL; 
var 
    dwResult: Integer; 
begin 
    Result:=RetVal; 
    if not RetVal then begin 
    dwResult:=GetLastError(); 
    raise ERSAEncryptionError.CreateFmt('Error [x%x]: %s failed.'#13#10'%s', [dwResult, FuncName, SysErrorMessage(dwResult)]); 
    end; 
end; 

procedure reverse(var p: TBytes; len: Integer); 
var 
    i, j: Integer; 
    temp: Byte; 
begin 
    i:=0; 
    j:=len - 1; 
    while i < j do begin 
    temp:=p[i]; 
    p[i]:=p[j]; 
    p[j]:=temp; 
    Inc(i); 
    Dec(j); 
    end; 
end; 

function CryptoAPI_Encrypt_RSA(const Input: TBytes; const cert: TMemoryStream): String; 
var 
    derCert: AnsiString; 
    derCertLen: Cardinal; 
    hProv: HCRYPTPROV; 
    certContext: PCCERT_CONTEXT; 
    certPubKey: HCRYPTKEY; 
    len: LongWord; 
    rsa: TBytes; 
    ins: TMemoryStream; 
    ous: TStringStream; 
begin 
    Result:=''; 
    if (cert <> Nil) and (cert.Size > 0) then begin 
    SetLength(derCert, 4096); 
    FillChar(derCert[1], 4096, 0); 
    // Convert from PEM format to DER format - removes header and footer and decodes from base64 
    WinError(CryptStringToBinaryA(PAnsiChar(cert.Memory), cert.Size, CRYPT_STRING_BASE64HEADER, @derCert[1], derCertLen, Nil), 'CryptStringToBinaryA'); 
    SetLength(derCert, derCertLen); 
    try 
     // Get the certificate context structure from a certificate. 
     certContext:=CertCreateCertificateContext(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, @derCert[1], derCertLen); 
     WinError(certContext <> Nil, 'CertCreateCertificateContext'); 
     try 
     hProv:=0; 
     WinError(CryptAcquireContext(hProv, Nil, Nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT), 'CryptAcquireContext'); // flag CRYPT_VERIFYCONTEXT - for backward compatibility with win2003server (and probably with win10pro+) 
     try 
      // Get the public key information for the certificate. 
      certPubKey:=0; 
      WinError(CryptImportPublicKeyInfo(hProv, X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, 
              @certContext.pCertInfo.SubjectPublicKeyInfo, certPubKey), 'CryptImportPublicKeyInfo'); 
      len:=Length(Input); 
      if len > 0 then begin 
      SetLength(rsa, len + 512); 
      FillChar(rsa, len + 512, 0); 
      try 
       CopyMemory(@rsa[0], @Input[0], len); 
       // encrypt our Input buffer 
       WinError(CryptEncrypt(certPubKey, 0, True, 0, @rsa[0], len, len + 512), 'CryptEncrypt'); 
       SetLength(rsa, len); 
       // IMPORTANT !!! 
       // .Net RSA algorithm is BIG-ENDIAN, 
       // CryptoAPI is LITTLE-ENDIAN, 
       // so reverse output before sending to Azure Cloud Storage 
       reverse(rsa, len); 
       ins:=TMemoryStream.Create; 
       try 
       ins.Write(rsa[0], len); 
       ins.Position:=0; 
       ous:=TStringStream.Create; 
       try 
        EncodeStream(ins, ous); 
        ous.Position:=0; 
        Result:=ous.DataString; 
        Result:=ReplaceStr(Result, #13#10, ''); 
       finally 
        ous.Free; 
       end; 
       finally 
       ins.Free; 
       end; 
      finally 
       SetLength(rsa, 0); 
      end; 
      end; 
     finally 
      WinError(CryptReleaseContext(hProv, 0), 'CryptReleaseContext'); 
     end; 
     finally 
     CertFreeCertificateContext(certContext); 
     end; 
    finally 
     SetLength(derCert, 0); 
    end; 
    end; 
end; 

end. 

使い方は次のようになります。これは、誰かを助けるかもしれないことを

var 
    cf: TMemoryStream; 
    input: TBytes; 
    output: String; 
begin 
    if Edit1.Text = '' then 
    Exit; 
    Memo1.Clear; 
    cf:=TMemoryStream.Create; 
    try 
    cf.LoadFromFile('cert.pem'); // certificate with public key 
    input:=TEncoding.Default.GetBytes(Edit1.Text); 
    try 
     output:=CryptoAPI_Encrypt_RSA(input, cf); 
    finally 
     SetLength(input, 0); 
    end; 
    Memo1.Lines.Text:=output; 
    finally 
    cf.Free; 
    end; 
end; 

希望、屋を参照してください。

+0

「FillChar(rsa、len + 512,0)」を「FillChar(rsa [0]、len + 512,0)」に変更して機能させる必要がありました。 SQL Serverで復号化できる文字列を暗号化するために使用しています。証明書はTSQLで 'CREATE CERTIFICATE'で作成され、 'BACKUP CERTIFICATE ... TO FILE'でエクスポートされます。ファイルはDER形式ですので、 'CryptStringToBinaryA()'を 'Move(cert.Memory ^、derCert [1]、cert.Size)'に置き換えました。 'reverse()'は必要ありませんでした。 – crk

関連する問題