1

我设计了一个存储数据块的系统,由多个客户端压缩和加密发送。

当尝试在特定计算机上获取存储的数据块(我需要解压缩和解密)时,在数据解压缩期间会引发 ZLib“数据错误”异常。很明显,该流尚未正确解密,因此输入流不是有效的 ZLib 流,从而导致此类问题。

经过一番研究,我发现对于同样存储的相同源数据块,这个有问题的块包含完全不同的数据,因此很明显加密或压缩算法存在问题。

然而,我不能轻易地重现问题的事实意味着,如果有错误,它远非显而易见。所以我在这里发布代码,希望有人会发现我没有看到的东西。

首先,数据块AStream被压缩:

function CompressStream(AStream: TMemoryStream;
  ACompressionLevel:
  TCompressionLevel): Boolean;
var
  LTempStream: TMemoryStream;
  LCompressedStream: TCompressionStream;
begin
  LTempStream := TMemoryStream.create;
  try
    try
      AStream.Seek(0, soBeginning);
      LCompressedStream := TCompressionStream.Create(ACompressionLevel, LTempStream);
      try
        try
          LCompressedStream.CopyFrom(AStream, AStream.Size);
        finally
          LCompressedStream.free;
        end;
      finally
        AStream.Clear;
        AStream.CopyFrom(LTempStream, 0);
        AStream.Seek(0, soBeginning);
        Result := True;
      end;
    finally
      LTempStream.free;
    end;
  except
    Result := False;
  end;
end;

然后,它被加密:

function EncryptStream(AStream: TMemoryStream; const AParameters: AnsiString): Boolean;
var
  LModifiedStream: TMemoryStream;
  LRijndaelCipher: TDCP_rijndael;
begin
   try
     LRijndaelCipher := TDCP_rijndael.Create(nil);
     LModifiedStream := TMemoryStream.Create;
     InitCipherWithKey(LRijndaelCipher, AParameters);
     try
        AStream.Seek(0, soBeginning);
        LRijndaelCipher.EncryptStream(AStream, LModifiedStream, AStream.Size);
        TMemoryStream(AStream).Clear;
        AStream.CopyFrom(LModifiedStream, 0);
        Result := True;
     finally
        LRijndaelCipher.Burn;
        LRijndaelCipher.Free;
        LModifiedStream.Free;
     end;
   except
     Result := False;
   end;
end;

...并存储在某个地方。

获取数据块时,首先对其进行解密:

function DecryptStream(AStream: TMemoryStream; const AParameters: AnsiString): Boolean;
var
  LModifiedStream: TMemoryStream;
  LRijndaelCipher: TDCP_rijndael;
begin
   try
     LRijndaelCipher := TDCP_rijndael.Create(nil);
     LModifiedStream := TMemoryStream.Create;
     InitCipherWithKey(LRijndaelCipher, AParameters);
     try
        AStream.Seek(0, soBeginning);
        LRijndaelCipher.DecryptStream(AStream, LModifiedStream, AStream.Size);
        TMemoryStream(AStream).Clear;
        AStream.CopyFrom(LModifiedStream, 0);
        Result := True;
     finally
        LRijndaelCipher.Burn;
        LRijndaelCipher.Free;
        LModifiedStream.Free;
     end;
   except
     Result := False;
   end;
end;

然后解压:

function DecompressStream(AStream: TMemoryStream): Boolean;
var
  LTempStream: TMemoryStream;
  LCompressedStream: TDecompressionStream;
begin
  LTempStream := TMemoryStream.create;
  try
    try
      AStream.Seek(0, soBeginning);
      LCompressedStream := TDecompressionStream.Create(AStream);
      try
        try
          LTempStream.CopyFrom(LCompressedStream, LCompressedStream.size);
        finally
          LCompressedStream.Free;
        end;
      finally
        AStream.Clear;
        AStream.CopyFrom(LTempStream, 0);
        AStream.Seek(0, soBeginning);
        Result := True;
      end;
    finally
      LTempStream.free;
    end;
  except
    Result := False;
  end;
end;

使用该InitCipherWithKey()方法初始化密码。它旨在将 MD5 哈希转换为包含在LMD5Hash变量中的二进制表示(是的,数组长 64 个字节,但只有前 16 个字节将被密码使用,因为我Init()使用 128 值调用(这意味着 128 -bit/16 字节密钥长度):

procedure InitCipherWithKey(ACipher: TDCP_cipher; const AKey: AnsiString);
var
  LMD5Hash: array [0..63] of Byte;
  S: AnsiString;
begin
  //We use a 128 bit key
  ZeroMemory(@LMD5Hash, SizeOf(LMD5Hash));
  S := AKey;
  HexToBin(PAnsiChar(S), LMD5Hash, Length(LMD5Hash) -1);
  ACipher.Init(LMD5Hash[0], 128, nil);
  ZeroMemory(@LMD5Hash, SizeOf(LMD5Hash));
end;

提前致谢。

4

1 回答 1

1

计算/传输密钥时出现问题。AKey首先,您使用 MD5从密码(名称不正确)计算密钥。出于某种原因,您为此使用了 64 字节的缓冲区,即使 MD5 将始终准确输出 16 字节。这已经不安全了,您应该使用基于密码的密钥派生函数(例如 PBKDF2)从密码中派生密钥。

但是,您似乎将 MD5 的二进制输出视为字符。我想你把它的输出InitCipherWithKey放进去AParameters。现在突然将 MD5 的二进制输出视为字符串。不幸的是,这意味着字节被解释为诸如控制字符之类的字符(如果字节具有 value ,则包括空终止字符值00)。

因此,由于密钥本身的数据丢失,很可能解密不成功。这当然取决于键值,使这个方案时不时地失败。请检查您的代码库,看看您是否在方面出错。

于 2013-08-29T08:42:24.633 回答