JPK-CryptoAPI RSA 256 / ECB / PKCS#1一次秘密密码加密,使用证书中的公钥(解决方案)

内华达州

波兰政府(MF-财政部)最近实施了SAF-T(标准审计档案税/ pl:JPK-Jednolity Plik Kontrolny)。我很难以正确的方式实现此解决方案的关键部分之一。这部分是使用从MF服务的证书文件加载的公钥,使用MS CryptoAPI的RSA 256 / ECB / PKCS#1算法对用户生成的密码进行加密,该密码用于加密发送到Azure云存储的文件。

内华达州

我对此的有效解决方案是(使用JEDI API库和安全代码库):

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;

希望这可以对某人有所帮助,再见。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

不同的加密Android与纯Java-RSA / ECB / OAEPWithMD5AndMGF1Padding

RSA / ECB / OAEPWithSHA-256AndMGF1Padding但使用SHA-256的MGF1吗?

分解RSA / ECB / OAEPWITHSHA-256ANDMGF1PADDING

Node.js中的Java的RSA / ECB / OAEPWithSHA-256AndMGF1Padding等效项

使用SHA256摘要进行OpenSSL RSA签名

.NET下的原始RSA加密(又名ECB / NoPadding)?

使用SHA-256AndMGF1Padding Swift加密RSA / ECB / OAEP

如何在Elixir中进行AES-256-ECB加密?

使用C#中的RSA / ECB / OAEPWithSHA-1AndMGF1Padding加密

C#中使用AES 256位ECB的意外加密字符串

如何使用.net中的充气城堡通过RSA / ECB / OAEPWithSHA256AndMGF1Padding进行加密?

使用Windows CryptoAPI使用AES 256位密钥解密的简单代码有什么问题?

SonarQube:确保此处加密数据安全。AES / GCM /无填充,RSA / ECB / PKCS1填充

CryptoJS AES 256 ECB解密

C#Bouncy Castle中的RSA / ECB / OAEPWITHSHA-256ANDMGF1PADDING-输入太大,无法使用RSA密码

获取此“ RSA / ECB / OAEPWithSHA-256AndMGF1Padding”组合用于红宝石

具有DECRYPT_MODE的RSA / ECB / PKCS1填充

无法将用php openssl生成的RSA密钥导入Windows CryptoAPI

错误的填充例外-pkcs11中的RSA / ECB / OAEPWITHSHA-256ANDMGF1PADDING

iOS上的RSA加密(RSA / ECB / PKCS1Padding)

NET的Java RSA / ECB / PKCS1Padding加密

使用模量和指数的C#中的RSA / ECB / PKCS1填充解密

CryptoAPI-如何从私钥中提取RSA公钥

PHP 7.2 中的 RSA/ECB/PKCS1Padding 解密

我正在使用 AES ECB 256 填充 - 加密发送数据,但我无法实现解密

带有 rsa/ecb/oaepwithsha-256andmgf1padding 的 SubtleCrypto

在 Java 中使用 AES-256-ECB 解密

PHP 加密/解密 - AES-256-ECB

使用 Sha256 RSA 验证 SignatureValue 和 DigestValue