DeflaterOutputStream.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. using ICSharpCode.SharpZipLib.Encryption;
  2. using System;
  3. using System.IO;
  4. using System.Security.Cryptography;
  5. namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
  6. {
  7. /// <summary>
  8. /// A special stream deflating or compressing the bytes that are
  9. /// written to it. It uses a Deflater to perform actual deflating.<br/>
  10. /// Authors of the original java version : Tom Tromey, Jochen Hoenicke
  11. /// </summary>
  12. public class DeflaterOutputStream : Stream
  13. {
  14. #region Constructors
  15. /// <summary>
  16. /// Creates a new DeflaterOutputStream with a default Deflater and default buffer size.
  17. /// </summary>
  18. /// <param name="baseOutputStream">
  19. /// the output stream where deflated output should be written.
  20. /// </param>
  21. public DeflaterOutputStream(Stream baseOutputStream)
  22. : this(baseOutputStream, new Deflater(), 512)
  23. {
  24. }
  25. /// <summary>
  26. /// Creates a new DeflaterOutputStream with the given Deflater and
  27. /// default buffer size.
  28. /// </summary>
  29. /// <param name="baseOutputStream">
  30. /// the output stream where deflated output should be written.
  31. /// </param>
  32. /// <param name="deflater">
  33. /// the underlying deflater.
  34. /// </param>
  35. public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater)
  36. : this(baseOutputStream, deflater, 512)
  37. {
  38. }
  39. /// <summary>
  40. /// Creates a new DeflaterOutputStream with the given Deflater and
  41. /// buffer size.
  42. /// </summary>
  43. /// <param name="baseOutputStream">
  44. /// The output stream where deflated output is written.
  45. /// </param>
  46. /// <param name="deflater">
  47. /// The underlying deflater to use
  48. /// </param>
  49. /// <param name="bufferSize">
  50. /// The buffer size in bytes to use when deflating (minimum value 512)
  51. /// </param>
  52. /// <exception cref="ArgumentOutOfRangeException">
  53. /// bufsize is less than or equal to zero.
  54. /// </exception>
  55. /// <exception cref="ArgumentException">
  56. /// baseOutputStream does not support writing
  57. /// </exception>
  58. /// <exception cref="ArgumentNullException">
  59. /// deflater instance is null
  60. /// </exception>
  61. public DeflaterOutputStream(Stream baseOutputStream, Deflater deflater, int bufferSize)
  62. {
  63. if (baseOutputStream == null)
  64. {
  65. throw new ArgumentNullException(nameof(baseOutputStream));
  66. }
  67. if (baseOutputStream.CanWrite == false)
  68. {
  69. throw new ArgumentException("Must support writing", nameof(baseOutputStream));
  70. }
  71. if (bufferSize < 512)
  72. {
  73. throw new ArgumentOutOfRangeException(nameof(bufferSize));
  74. }
  75. baseOutputStream_ = baseOutputStream;
  76. buffer_ = new byte[bufferSize];
  77. deflater_ = deflater ?? throw new ArgumentNullException(nameof(deflater));
  78. }
  79. #endregion Constructors
  80. #region Public API
  81. /// <summary>
  82. /// Finishes the stream by calling finish() on the deflater.
  83. /// </summary>
  84. /// <exception cref="SharpZipBaseException">
  85. /// Not all input is deflated
  86. /// </exception>
  87. public virtual void Finish()
  88. {
  89. deflater_.Finish();
  90. while (!deflater_.IsFinished)
  91. {
  92. int len = deflater_.Deflate(buffer_, 0, buffer_.Length);
  93. if (len <= 0)
  94. {
  95. break;
  96. }
  97. if (cryptoTransform_ != null)
  98. {
  99. EncryptBlock(buffer_, 0, len);
  100. }
  101. baseOutputStream_.Write(buffer_, 0, len);
  102. }
  103. if (!deflater_.IsFinished)
  104. {
  105. throw new SharpZipBaseException("Can't deflate all input?");
  106. }
  107. baseOutputStream_.Flush();
  108. if (cryptoTransform_ != null)
  109. {
  110. if (cryptoTransform_ is ZipAESTransform)
  111. {
  112. AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
  113. }
  114. cryptoTransform_.Dispose();
  115. cryptoTransform_ = null;
  116. }
  117. }
  118. /// <summary>
  119. /// Gets or sets a flag indicating ownership of underlying stream.
  120. /// When the flag is true <see cref="Stream.Dispose()" /> will close the underlying stream also.
  121. /// </summary>
  122. /// <remarks>The default value is true.</remarks>
  123. public bool IsStreamOwner { get; set; } = true;
  124. /// <summary>
  125. /// Allows client to determine if an entry can be patched after its added
  126. /// </summary>
  127. public bool CanPatchEntries
  128. {
  129. get
  130. {
  131. return baseOutputStream_.CanSeek;
  132. }
  133. }
  134. #endregion Public API
  135. #region Encryption
  136. private string password;
  137. private ICryptoTransform cryptoTransform_;
  138. /// <summary>
  139. /// Returns the 10 byte AUTH CODE to be appended immediately following the AES data stream.
  140. /// </summary>
  141. protected byte[] AESAuthCode;
  142. /// <summary>
  143. /// Get/set the password used for encryption.
  144. /// </summary>
  145. /// <remarks>When set to null or if the password is empty no encryption is performed</remarks>
  146. public string Password
  147. {
  148. get
  149. {
  150. return password;
  151. }
  152. set
  153. {
  154. if ((value != null) && (value.Length == 0))
  155. {
  156. password = null;
  157. }
  158. else
  159. {
  160. password = value;
  161. }
  162. }
  163. }
  164. /// <summary>
  165. /// Encrypt a block of data
  166. /// </summary>
  167. /// <param name="buffer">
  168. /// Data to encrypt. NOTE the original contents of the buffer are lost
  169. /// </param>
  170. /// <param name="offset">
  171. /// Offset of first byte in buffer to encrypt
  172. /// </param>
  173. /// <param name="length">
  174. /// Number of bytes in buffer to encrypt
  175. /// </param>
  176. protected void EncryptBlock(byte[] buffer, int offset, int length)
  177. {
  178. cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0);
  179. }
  180. /// <summary>
  181. /// Initializes encryption keys based on given <paramref name="password"/>.
  182. /// </summary>
  183. /// <param name="password">The password.</param>
  184. protected void InitializePassword(string password)
  185. {
  186. var pkManaged = new PkzipClassicManaged();
  187. byte[] key = PkzipClassic.GenerateKeys(ZipStrings.ConvertToArray(password));
  188. cryptoTransform_ = pkManaged.CreateEncryptor(key, null);
  189. }
  190. /// <summary>
  191. /// Initializes encryption keys based on given password.
  192. /// </summary>
  193. protected void InitializeAESPassword(ZipEntry entry, string rawPassword,
  194. out byte[] salt, out byte[] pwdVerifier)
  195. {
  196. salt = new byte[entry.AESSaltLen];
  197. // Salt needs to be cryptographically random, and unique per file
  198. if (_aesRnd == null)
  199. _aesRnd = RandomNumberGenerator.Create();
  200. _aesRnd.GetBytes(salt);
  201. int blockSize = entry.AESKeySize / 8; // bits to bytes
  202. cryptoTransform_ = new ZipAESTransform(rawPassword, salt, blockSize, true);
  203. pwdVerifier = ((ZipAESTransform)cryptoTransform_).PwdVerifier;
  204. }
  205. #endregion Encryption
  206. #region Deflation Support
  207. /// <summary>
  208. /// Deflates everything in the input buffers. This will call
  209. /// <code>def.deflate()</code> until all bytes from the input buffers
  210. /// are processed.
  211. /// </summary>
  212. protected void Deflate()
  213. {
  214. Deflate(false);
  215. }
  216. private void Deflate(bool flushing)
  217. {
  218. while (flushing || !deflater_.IsNeedingInput)
  219. {
  220. int deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length);
  221. if (deflateCount <= 0)
  222. {
  223. break;
  224. }
  225. if (cryptoTransform_ != null)
  226. {
  227. EncryptBlock(buffer_, 0, deflateCount);
  228. }
  229. baseOutputStream_.Write(buffer_, 0, deflateCount);
  230. }
  231. if (!deflater_.IsNeedingInput)
  232. {
  233. throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?");
  234. }
  235. }
  236. #endregion Deflation Support
  237. #region Stream Overrides
  238. /// <summary>
  239. /// Gets value indicating stream can be read from
  240. /// </summary>
  241. public override bool CanRead
  242. {
  243. get
  244. {
  245. return false;
  246. }
  247. }
  248. /// <summary>
  249. /// Gets a value indicating if seeking is supported for this stream
  250. /// This property always returns false
  251. /// </summary>
  252. public override bool CanSeek
  253. {
  254. get
  255. {
  256. return false;
  257. }
  258. }
  259. /// <summary>
  260. /// Get value indicating if this stream supports writing
  261. /// </summary>
  262. public override bool CanWrite
  263. {
  264. get
  265. {
  266. return baseOutputStream_.CanWrite;
  267. }
  268. }
  269. /// <summary>
  270. /// Get current length of stream
  271. /// </summary>
  272. public override long Length
  273. {
  274. get
  275. {
  276. return baseOutputStream_.Length;
  277. }
  278. }
  279. /// <summary>
  280. /// Gets the current position within the stream.
  281. /// </summary>
  282. /// <exception cref="NotSupportedException">Any attempt to set position</exception>
  283. public override long Position
  284. {
  285. get
  286. {
  287. return baseOutputStream_.Position;
  288. }
  289. set
  290. {
  291. throw new NotSupportedException("Position property not supported");
  292. }
  293. }
  294. /// <summary>
  295. /// Sets the current position of this stream to the given value. Not supported by this class!
  296. /// </summary>
  297. /// <param name="offset">The offset relative to the <paramref name="origin"/> to seek.</param>
  298. /// <param name="origin">The <see cref="SeekOrigin"/> to seek from.</param>
  299. /// <returns>The new position in the stream.</returns>
  300. /// <exception cref="NotSupportedException">Any access</exception>
  301. public override long Seek(long offset, SeekOrigin origin)
  302. {
  303. throw new NotSupportedException("DeflaterOutputStream Seek not supported");
  304. }
  305. /// <summary>
  306. /// Sets the length of this stream to the given value. Not supported by this class!
  307. /// </summary>
  308. /// <param name="value">The new stream length.</param>
  309. /// <exception cref="NotSupportedException">Any access</exception>
  310. public override void SetLength(long value)
  311. {
  312. throw new NotSupportedException("DeflaterOutputStream SetLength not supported");
  313. }
  314. /// <summary>
  315. /// Read a byte from stream advancing position by one
  316. /// </summary>
  317. /// <returns>The byte read cast to an int. THe value is -1 if at the end of the stream.</returns>
  318. /// <exception cref="NotSupportedException">Any access</exception>
  319. public override int ReadByte()
  320. {
  321. throw new NotSupportedException("DeflaterOutputStream ReadByte not supported");
  322. }
  323. /// <summary>
  324. /// Read a block of bytes from stream
  325. /// </summary>
  326. /// <param name="buffer">The buffer to store read data in.</param>
  327. /// <param name="offset">The offset to start storing at.</param>
  328. /// <param name="count">The maximum number of bytes to read.</param>
  329. /// <returns>The actual number of bytes read. Zero if end of stream is detected.</returns>
  330. /// <exception cref="NotSupportedException">Any access</exception>
  331. public override int Read(byte[] buffer, int offset, int count)
  332. {
  333. throw new NotSupportedException("DeflaterOutputStream Read not supported");
  334. }
  335. /// <summary>
  336. /// Flushes the stream by calling <see cref="Flush">Flush</see> on the deflater and then
  337. /// on the underlying stream. This ensures that all bytes are flushed.
  338. /// </summary>
  339. public override void Flush()
  340. {
  341. deflater_.Flush();
  342. Deflate(true);
  343. baseOutputStream_.Flush();
  344. }
  345. /// <summary>
  346. /// Calls <see cref="Finish"/> and closes the underlying
  347. /// stream when <see cref="IsStreamOwner"></see> is true.
  348. /// </summary>
  349. protected override void Dispose(bool disposing)
  350. {
  351. if (!isClosed_)
  352. {
  353. isClosed_ = true;
  354. try
  355. {
  356. Finish();
  357. if (cryptoTransform_ != null)
  358. {
  359. GetAuthCodeIfAES();
  360. cryptoTransform_.Dispose();
  361. cryptoTransform_ = null;
  362. }
  363. }
  364. finally
  365. {
  366. if (IsStreamOwner)
  367. {
  368. baseOutputStream_.Dispose();
  369. }
  370. }
  371. }
  372. }
  373. /// <summary>
  374. /// Get the Auth code for AES encrypted entries
  375. /// </summary>
  376. protected void GetAuthCodeIfAES()
  377. {
  378. if (cryptoTransform_ is ZipAESTransform)
  379. {
  380. AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
  381. }
  382. }
  383. /// <summary>
  384. /// Writes a single byte to the compressed output stream.
  385. /// </summary>
  386. /// <param name="value">
  387. /// The byte value.
  388. /// </param>
  389. public override void WriteByte(byte value)
  390. {
  391. byte[] b = new byte[1];
  392. b[0] = value;
  393. Write(b, 0, 1);
  394. }
  395. /// <summary>
  396. /// Writes bytes from an array to the compressed stream.
  397. /// </summary>
  398. /// <param name="buffer">
  399. /// The byte array
  400. /// </param>
  401. /// <param name="offset">
  402. /// The offset into the byte array where to start.
  403. /// </param>
  404. /// <param name="count">
  405. /// The number of bytes to write.
  406. /// </param>
  407. public override void Write(byte[] buffer, int offset, int count)
  408. {
  409. deflater_.SetInput(buffer, offset, count);
  410. Deflate();
  411. }
  412. #endregion Stream Overrides
  413. #region Instance Fields
  414. /// <summary>
  415. /// This buffer is used temporarily to retrieve the bytes from the
  416. /// deflater and write them to the underlying output stream.
  417. /// </summary>
  418. private byte[] buffer_;
  419. /// <summary>
  420. /// The deflater which is used to deflate the stream.
  421. /// </summary>
  422. protected Deflater deflater_;
  423. /// <summary>
  424. /// Base stream the deflater depends on.
  425. /// </summary>
  426. protected Stream baseOutputStream_;
  427. private bool isClosed_;
  428. #endregion Instance Fields
  429. #region Static Fields
  430. // Static to help ensure that multiple files within a zip will get different random salt
  431. private static RandomNumberGenerator _aesRnd = RandomNumberGenerator.Create();
  432. #endregion Static Fields
  433. }
  434. }