LockBox 3 使用公共证书加密 RSA

问题描述

在 Delphi 10.4 中,我尝试使用证书 (*.cer) 中的公钥使用 RSA 加密字符串。 问题是,我不知道如何将此证书传递给 RSA 函数我有一种感觉,LockBox 3 有自己的格式来将公钥和私钥存储在一个文件中。

我是这样写的:

type
  TGen = class(TObject)
    private
      keyRSA: TBytes;
      fileKey: TBytes;
      cryptoLib: TCryptographicLibrary;

      procedure readKeyWithoutHeader();

    public
      constructor Create();
      destructor Destroy(); override;

      function getRSA(const inputS: string): string;
  end;

constructor TGen.Create();
var
  path: string;
  tmp: TFileStream;
begin
  inherited;

  cryptoLib := TCryptographicLibrary.Create(nil);
  
  tmp := TFileStream.Create(path,fmOpenRead);
  tmp.Position := 0;
  SetLength(fileKey,tmp.Size);
  plik.Read(fileKey,tmp.Size);
  tmp.Free();

  readKeyWithoutHeader();
end;

function TGen.getRSA(const inputS: string): string;
var
  key: TSymetricKey;
  rsa: TCodec;
  t: TStream;
begin
  try
    t := TMemoryStream.Create();
    Base64_to_stream(keyRSA,t);
    t.Position := 0;

    rsa := TCodec.Create(nil);
    rsa.Reset();
    rsa.CryptoLibrary := cryptoLib;
    rsa.StreamCipherId := uTPLb_Constants.RSA_ProgId;
    //rsa.ChainModeId := uTPLb_Constants.ECB_ProgId;
    //rsa.AsymetricKeySizeInBits := 2048;


    key := rsa.Asymetric_Engine.CreateFromStream(t,[partPublic]);
    rsa.InitFromKey(key);

    rsa.EncryptString(inputS,Result,TEncoding.UTF8);

  finally
    rsa.Burn();
    FreeAndNil(rsa);
    
    FreeAndNil(t);
  end;
end;

procedure TGen.readKeyWithoutHeader();
    var
  pozStart: integer;
  pozKoniec: integer;
  i: integer;
  poz: integer;
begin
  pozStart := 0;
  pozKoniec := 0;
  SetLength(kluczRSA,Length(plikKlucza));

  //znajdź koniec --- begin * ---
  //wyszukaj pierwszej #13#10,ale Nowa linia może mieć tylko #13 lub #10
  for i := 0 to Length(plikKlucza) - 1 do
  begin
    if plikKlucza[i] in [13,10] then pozStart := i
    else if pozStart > 0 then break;
  end;

  //teraz znajdź początek --- end * ----
  //przygotwany klucz może mieć jedną dodatkową linię,inne pliki obecnie nie są wspierane
  for i := Length(plikKlucza) - 4 downto 0 do
  begin
    if plikKlucza[i] in [13,10] then pozKoniec := i
    else if pozKoniec > 0 then break;         
  end;

  if (pozStart = 0) or (pozKoniec = 0) then
    raise Exception.Create('Błędny plik z kluczem - nie zawiera nagłówków');

  if pozStart >= pozKoniec then
    raise Exception.Create('Błąd przy pobieraniu wartości klucza - nie rozpoznano nagłówków');

  //teraz usuń wszystkie Nowe linie,to co jest zakodowane w base64 między nagłówkami ma zostać
  poz := 0;
  for i := pozStart to pozKoniec do
  begin
    if not (plikKlucza[i] in [13,10]) then
    begin
      kluczRSA[poz] := plikKlucza[i];
      Inc(poz);
    end;
  end;

  SetLength(kluczRSA,poz);

end;

但我收到错误:流读取错误

我使用 openssl 从公共证书中提取公钥:

openssl x509 -pubkey -noout -in cert.cer  > pubkey.pem

输出为:

-----开始公钥----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr5qMxLWtgkId2oRUfnPf 6MX+UouBQKOzyfG0J9LW9yya8Nr+ilPTSPp+hSBL/TD1ijUZ2RClyegnrojOKHS7 kp1ZFDQJwmKSW660NKeLbyu2fbcJFBuDmSVK8XwRsUaIpf4eixqx5wAZg8q64kJ9 R9e07WPqrC2+8p2F/7zlKsZ263CWZ/xE0M6I4RiKSA24iaiGVrppnIrX1oX2v/dq UNaQL3uIgH1WWtf4apnDA7MVei2Iz2NjFzLJ569wxzO92XBUrcEkqA7Xx0or6xij h0oFKxsygNqHzK3qf56McRi/xy1VFrGQsiZL1u4+cdIAu5/tWaecLFl0UOBBbYxz 9QIDAQAB -----结束公钥-----

因为这个证书是公开的,并且可以从网站上广泛访问,我在这里粘贴示例:

-----BEGIN CERTIFICATE----- MIIFGTCCBAGAwIBAgITYwABPJqEcB3zrmJ2agABAAE8mjANBgkqhkiG9w0BAQwF ADBdMRIweAYKCZImiZPyLGQBGRYCcGwxEzARBgoJkiaJk/IsZAEZFgNnb3YxEjAQ BgoJkiaJk/IsZAEZFgJtZjEeMBwGA1UEAxMVUMVzb3J0IEZpbmFuc293IEkxIENB MB4XDTIwMDcyMzExMjgyM1oXDTIyMDcyMzExMjgyM1owgZExCzAJBgNVBAYTALBM MRQwEgYDVQQIEwttYXpvd2llY2tpZTERMA8GA1UEBxMIV2Fyc3phd2ExHzAdBgNV BAoMFk1pbmlzdGVyc3R3byBGaW5hbnPDs3cxHDAaBgNVBAsTE0FwbglrYWNqZSBL cnl0eWN6bmUxGjAYBgNVBAMTEWV0dy10c3QubWYuZ292LnBsMIIBIjANBgkqhkiG 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr5qMxLWtgkId2oRUfnPf6MX+UouBQKOzyfG0 J9LW9yya8Nr+ilPTSPp+hSBL/TD1ijUZ2RClyegnrojOKHS7kp1ZFDQJwmKSW660 NKeLbyu2fbcJFBuDmSVK8XwRsUaIpf4eixqx5wAZg8q64kJ9R9e07WPqrC2+8p2F /7zlKsZ263CWZ/xE0M6I4RiKSA24iaiGVrppnIrX1oX2v/dqUNaQL3uIgH1WWtf4 apnDA7MVei2Iz2NjFzLJ569wxzO92XBUrcEkqA7Xx0or6xijh0oFKxsygNqHzK3q f56McRi/xy1VFrGQsiZL1u4+cdIAu5/tWaecLFl0UOBBbYxz9QIDAQABo4IBmzCC AZcwDgYDVR0PAQH/BA​​QDAgWgMD4GCSsGAQQBgjcVBwQxMC8GJysGAQQBgjcVCIS3 mFWE7NgBhrmTDYO18X6DrtM2gUKEotdOhMuPDgIBZAIBDDAdBgNVHQ4EFgQUnFcp uSFJZYUBUkIplCvJERbNN98wHwYDVR0jBBgwFoAUfR+t1nzLSol4VWy6EpltM9Yx us0wRgYDVR0fBD8wPTA7oDmgN4Y1aHR0cDovL3BraS5tZi5nb3YucGwvY2RwL3Jl c29ydF9maW5hbnNvd19pMV9jYSgxKS5jcmwwdQYIKwYBBQUHAQEEaTBnMD4GCCsG AQUFBzAChjJodHRwOi8vcGtpLm1mlmdvdi5wbC9haWEvcmVzb3J0X2ZpbmFuc293 X2kxX2NhLmNydDAlBggrBgEFBQcwAYYZaHR0cDovL3BraS5tZi5nb3YucGwvb2Nz cDAdBgNVHUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwJwYJKwYBBAGCNxUKBBow GDAKBggrBgEFBQcDATAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQwFAAOCAQEAHatd TuoQDgaydindZFPvbvnqO0wmD/sH54LkXnf49hXHcGQRjhBc7xsSDbKzxHEORJ08 wNWWJa0H8cbKCLytBq49FQnENYoi5sgMetM5XGqGSYHwFLTFyAf8A9af8rpqfAEY HwmL6iIjz6NmSD52i3owOaMcop4pyC+UU5CZDqBrYd10JY4bWRm5NPdjIga0+mDm hQYEkoW/AEQQam+VmyLsJFGtBl/kr5+EFIUFw8X2tFAubNTuxQsveLo91Q0lHtSt BMBJ7MYnJeFCGDeuPM65nJtCxexDfWfWSTZvGrdJSUooD5iFSZodCkReP7ZLiQmB dPB1oIhW3y/VUem3+A== -----结束证书-----

错误错误读取流”在起作用:

function TRSAKeyPair.LoadHugeCardinal_IfNotAlready(
  StoreStream: TStream; var Number: IHugeCardinalWrap): boolean;
  // virtual method.
var
  L: cardinal;
  ValueStream: TMemoryStream;
begin
result := not assigned( Number);
if not result then exit; // Only load if we are not already loaded.
StoreStream.ReadBuffer( L,SizeOf( L));    // <-- L have very big value for example 882233221
ValueStream := TMemoryStream.Create;
try
ValueStream.Size := L;
if L > 0 then
  StoreStream.ReadBuffer( ValueStream.Memory^,L);   // <--- error throw
ValueStream.Position := 0;
Number := NewWrap( THugeCardinal.CreateFromStreamIn(
                   L*8,LittleEndien,ValueStream,FPool))
finally
ValueStream.Free
end;
if Number.isZero then
  Number := nil
end;

解决方法

现在这段代码告诉我你不知道你在做什么:

tmp := TStringList.Create();
tmp.LoadFromFile(path,TEncoding.UTF8);
tmp.Delete(0);
tmp.Delete(tmp.Count - 1);
cert := TEncoding.UTF8.GetBytes(stringReplace(tmp.Text,sLineBreak,'',[rfReplaceAll]));
  1. 您想读取 ASCII 文本文件。如果它包含 UTF-8,那么它就根本无效。将其用作文本编码会遗漏任何一点。
  2. 您删除了第一行和最后一行,因为您甚至没有检查就希望它们是 -----BEGIN something----------END something-----。但是这些行并不是为了好玩而存在的:它们清楚地标记了数据应该从哪里开始以及应该在哪里结束,而在这些标记之前和之后的行其他文本可能出现。一个文件可能包含多个密钥,您可以自行决定要使用哪个密钥。
  3. 您确定删除了所有换行符吗?你真的检查过cert内容了吗?
  4. 您忽略了在 ASCII 上下文中二进制数据很可能是 Base64 编码的。一切几乎只由拉丁字母和阿拉伯数字组成,这难道不是在你的头上留下问号吗?然而,您将它们视为字节(或 UTF-8)?
  5. 为什么不看LockBox's own example?它在一个常量上使用它自己的 Base64_to_stream(),该常量的内容与您的公钥数据非常相似。但是实例化 TStringStream 可能会再次操纵解码为字节的内容,因为现在您出于未知原因再次将其视为文本。

查看您的文本文件并按原样阅读,以行分隔。从您想要的那些行中删除任何空格(即换行符、空格等),因为 Base64 不知道这些 - 换行符是为了使其与电子邮件兼容。看看你现在得到了什么 - 它必须是一个长 String,必须全部在 ['A'.. 'Z','a'.. 'z','0'.. '9','+','/'] 中,没有别的。现在将此 Base64 转换为原始字节,最好使用 TMemoryStream.Create() 实例。

并通过查看它发生在代码中的哪个点以及在 LockBox 的代码中引发它来调试“流读取错误”。然后再试一次,这次更仔细地查看异常发生的时间或访问 Stream 的时间点。以及该位置的 Stream 包含的内容。

,

我找到了更好的解决方案。刚刚从 TP LockBox 2 或 3 辞职,我从 https://github.com/lminuti/Delphi-OpenSSL 下载了包装器,我正在使用 OpenSSL 在 RSA 或 AES 中进行加密。

function TGeneratorSzyfrow.zaszyfrujRSA(const wejscie: TBytes): string;
var
  rsa: TRSAUtil;
  certyfikat: TX509Cerificate;
  bufWejscie: TMemoryStream;
  bufWyjscie: TMemoryStream;
  bajty: TBytes;
begin
  rsa := TRSAUtil.Create();
  certyfikat := TX509Cerificate.Create();
  bufWejscie := TMemoryStream.Create();
  bufWyjscie := TMemoryStream.Create();

  try
    certyfikat.LoadFromFile(sciezkaDoPlikuRSA);
    rsa.PublicKey.LoadFromCertificate(certyfikat);

    bufWejscie.write(wejscie,Length(wejscie));
    bufWejscie.Position := 0;

    rsa.PublicEncrypt(bufWejscie,bufWyjscie,rpPKCS);

    setLength(bajty,bufWyjscie.Size);
    bufWyjscie.Position := 0;
    bufWyjscie.Read(bajty,bufWyjscie.Size);

    Result := kodujBase64(bajty);
  finally
    FreeAndNil(rsa);
    FreeAndNil(certyfikat);
    FreeAndNil(bufWejscie);
    FreeAndNil(bufWyjscie);
  end;

end;