2011-02-17 29 views
6

我試圖將CryptoStream與AWS .NET SDk一起使用,因爲CryptoStream不支持seek。我知道內容長度已知的地方應該可以將這些功能添加到CryptoStream中。我想知道如何做到這一點;任何示例代碼也會有用。如何將查找和定位功能添加到CryptoStream

我有一個像這樣的方法與FieStream傳遞並返回一個cryptoStream。我將返回的Stream對象分配給AWS SDk PutObjectRequest對象的InputStream。

public static Stream GetEncryptStream(Stream existingStream, 
    SymmetricAlgorithm cryptoServiceProvider, 
    string encryptionKey, string encryptionIV) 
{ 
    Stream existingStream = this.dataStream; 

    cryptoServiceProvider.Key = ASCIIEncoding.ASCII.GetBytes(encryptionKey); 
    cryptoServiceProvider.IV = ASCIIEncoding.ASCII.GetBytes(encryptionIV); 
    CryptoStream cryptoStream = new CryptoStream(existingStream, 
     cryptoServiceProvider.CreateEncryptor(), CryptoStreamMode.Read); 

    return cryptoStream ; 
} 
+0

嗨,你能告訴我們你在做什麼這麼遠?我相信如果你有一個流媒體閱讀器鏈接到你的CryptoStream,那麼你可以移動位置,並尋求... – 2011-02-17 08:30:05

回答

4

一般用加密還沒有一個1:輸入字節和輸出字節之間的一對一映射,所以爲了尋求向後(尤其是)它會做很多的工作 - 甚至直接回到開始處,並且前進處理數據以消耗來自解密的流的[n]個字節。即使它知道每個字節映射到的位置,加密的狀態依賴於它之前的數據(它不是解碼器環; p),所以再次 - 它必須從開始讀取(和重置回初始化向量),或者它必須跟蹤位置和密碼狀態的快照,並返回到最近的快照,然後向前走。大量的工作和存儲。

這將適用於尋求相對於任何一端。

當前位置向前移動將不會太糟糕,但你又不得不過程數據 - 不只是跳基流的位置。

沒有實現這一點,大多數消費者可以使用方式 - 通常,如果你從CanSeek,意思是「隨機訪問」一true,但效率不高在這種情況下。

解決方法 - 考慮將解密的數據複製到MemoryStream或文件;那麼您可以以隨機訪問的方式訪問完全解密的數據。

+0

只要記住將解密的數據複製到未加密的內存流或文件可以保持數據開放,以便更容易通過不那麼好的窺探人們 – Justin808 2011-02-17 10:20:37

+0

@ Justin808顯然,這取決於它是否打算被保護*在傳輸/存儲*,或者甚至你自己的服務器是否被認爲是敵對的/處於危險中...... – 2011-02-17 10:40:51

1

作爲Mark Gravell的回答的擴展,密碼的可搜索性取決於您用於密碼的Mode Of Operation。大多數操作模式都是不可搜索的,因爲每個密文塊都以某種方式依賴於前一個密文。歐洲央行是可以追求的,但使用它幾乎是個壞主意。 CTR模式是另一種可以隨機訪問的模式,就像CBC一樣。

但是,所有這些模式都有其自身的漏洞,因此,在選擇一種模式之前,您應該仔細閱讀並認真思考並且認真思考(最好諮詢專家)。

2

它非常簡單,只需使用ECB或任何其他您喜歡的加密方法生成與數據流大小相同的長鍵(stream.Position),然後應用XOR。它是可搜索的,非常快速和1對1加密,其輸出長度與輸入長度完全相同。這是有效的內存,你可以在巨大的文件上使用它。我認爲這種方法也用於現代WinZip AES加密。你必須小心的唯一的事情就是:SAL

使用獨特的鹽爲每個數據流否則就沒有加密。 我還沒有測試過它,但請讓我知道如果你認爲它有問題。

public class SeekableAesStream : Stream 
{ 
    private Stream baseStream; 
    private AesManaged aes; 
    private ICryptoTransform encryptor; 
    public bool autoDisposeBaseStream { get; set; } = true; 

    /// <param name="salt">//** WARNING **: MUST be unique for each stream otherwise there is NO security</param> 
    public SeekableAesStream(Stream baseStream, string password, byte[] salt) 
    { 
     this.baseStream = baseStream; 
     using (var key = new PasswordDeriveBytes(password, salt)) 
     { 
      aes = new AesManaged(); 
      aes.KeySize = 128; 
      aes.Mode = CipherMode.ECB; 
      aes.Padding = PaddingMode.None; 
      aes.Key = key.GetBytes(aes.KeySize/8); 
      aes.IV = new byte[16]; //zero buffer is adequate since we have to use new salt for each stream 
      encryptor = aes.CreateEncryptor(aes.Key, aes.IV); 
     } 
    } 

    private void cipher(byte[] buffer, int offset, int count, long streamPos) 
    { 
     //find block number 
     var blockSizeInByte = aes.BlockSize/8; 
     var blockNumber = (streamPos/blockSizeInByte) + 1; 
     var keyPos = streamPos % blockSizeInByte; 

     //buffer 
     var outBuffer = new byte[blockSizeInByte]; 
     var nonce = new byte[blockSizeInByte]; 
     var init = false; 

     for (int i = offset; i < count; i++) 
     { 
      //encrypt the nonce to form next xor buffer (unique key) 
      if (!init || (keyPos % blockSizeInByte) == 0) 
      { 
       BitConverter.GetBytes(blockNumber).CopyTo(nonce, 0); 
       encryptor.TransformBlock(nonce, 0, nonce.Length, outBuffer, 0); 
       if (init) keyPos = 0; 
       init = true; 
       blockNumber++; 
      } 
      buffer[i] ^= outBuffer[keyPos]; //simple XOR with generated unique key 
      keyPos++; 
     } 
    } 

    public override bool CanRead { get { return baseStream.CanRead; } } 
    public override bool CanSeek { get { return baseStream.CanSeek; } } 
    public override bool CanWrite { get { return baseStream.CanWrite; } } 
    public override long Length { get { return baseStream.Length; } } 
    public override long Position { get { return baseStream.Position; } set { baseStream.Position = value; } } 
    public override void Flush() { baseStream.Flush(); } 
    public override void SetLength(long value) { baseStream.SetLength(value); } 
    public override long Seek(long offset, SeekOrigin origin) { return baseStream.Seek(offset, origin); } 

    public override int Read(byte[] buffer, int offset, int count) 
    { 
     var streamPos = Position; 
     var ret = baseStream.Read(buffer, offset, count); 
     cipher(buffer, offset, count, streamPos); 
     return ret; 
    } 

    public override void Write(byte[] buffer, int offset, int count) 
    { 
     cipher(buffer, offset, count, Position); 
     baseStream.Write(buffer, offset, count); 
    } 

    protected override void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      encryptor?.Dispose(); 
      aes?.Dispose(); 
      if (autoDisposeBaseStream) 
       baseStream?.Dispose(); 
     } 

     base.Dispose(disposing); 
    } 
} 

用法:

static void test() 
    { 
     var buf = new byte[255]; 
     for (byte i = 0; i < buf.Length; i++) 
      buf[i] = i; 

     //encrypting 
     var uniqueSalt = new byte[16]; //** WARNING **: MUST be unique for each stream otherwise there is NO security 
     var baseStream = new MemoryStream(); 
     var cryptor = new SeekableAesStream(baseStream, "password", uniqueSalt); 
     cryptor.Write(buf, 0, buf.Length); 

     //decrypting at position 200 
     cryptor.Position = 200; 
     var decryptedBuffer = new byte[50]; 
     cryptor.Read(decryptedBuffer, 0, 50); 

    } 
相關問題