2016-09-10 5 views
0

폴란드 정부 (MF - Ministry of Finance)는 최근 SAF-T (Standard Audit File-Tax/pl : JPK - Jednolity Plik Kontrolny). 이 솔루션의 핵심 부분 중 하나를 올바른 방식으로 구현하는 데 많은 어려움이있었습니다. 이 부분은 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에서 해독 할 수있는 문자열을 암호화하는 데 사용하고 있습니다. 인증서는 'CREATE CERTIFICATE'로 TSQL로 만들어지고 'BACKUP CERTIFICATE ... TO FILE'로 내보내집니다. 파일에 DER 형식이 있으므로 'CryptStringToBinaryA()'를 'Move (cert.Memory ^, derCert [1], cert.Size)'로 바 꾸었습니다. 'reverse()'는 필요하지 않았습니다. – crk