DeflaterOutputStream.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. using System;
  2. using System.IO;
  3. using System.Security.Cryptography;
  4. using ICSharpCode.SharpZipLib.Encryption;
  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. throw new ArgumentNullException("nameof(baseOutputStream)");
  65. }
  66. if (baseOutputStream.CanWrite == false) {
  67. throw new ArgumentException("Must support writing", "nameof(baseOutputStream)");
  68. }
  69. if (deflater == null) {
  70. throw new ArgumentNullException("nameof(deflater)");
  71. }
  72. if (bufferSize < 512) {
  73. throw new ArgumentOutOfRangeException("nameof(bufferSize)");
  74. }
  75. baseOutputStream_ = baseOutputStream;
  76. buffer_ = new byte[bufferSize];
  77. deflater_ = deflater;
  78. }
  79. #endregion
  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. int len = deflater_.Deflate(buffer_, 0, buffer_.Length);
  92. if (len <= 0) {
  93. break;
  94. }
  95. if (cryptoTransform_ != null) {
  96. EncryptBlock(buffer_, 0, len);
  97. }
  98. baseOutputStream_.Write(buffer_, 0, len);
  99. }
  100. if (!deflater_.IsFinished) {
  101. throw new SharpZipBaseException("Can't deflate all input?");
  102. }
  103. baseOutputStream_.Flush();
  104. if (cryptoTransform_ != null) {
  105. if (cryptoTransform_ is ZipAESTransform) {
  106. AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
  107. }
  108. cryptoTransform_.Dispose();
  109. cryptoTransform_ = null;
  110. }
  111. }
  112. /// <summary>
  113. /// Gets or sets a flag indicating ownership of underlying stream.
  114. /// When the flag is true <see cref="Stream.Dispose()" /> will close the underlying stream also.
  115. /// </summary>
  116. /// <remarks>The default value is true.</remarks>
  117. private bool isStreamOwner = true;
  118. public bool IsStreamOwner
  119. {
  120. get { return isStreamOwner;}
  121. set { isStreamOwner = value; }
  122. }
  123. /// <summary>
  124. /// Allows client to determine if an entry can be patched after its added
  125. /// </summary>
  126. public bool CanPatchEntries {
  127. get {
  128. return baseOutputStream_.CanSeek;
  129. }
  130. }
  131. #endregion
  132. #region Encryption
  133. string password;
  134. ICryptoTransform cryptoTransform_;
  135. /// <summary>
  136. /// Returns the 10 byte AUTH CODE to be appended immediately following the AES data stream.
  137. /// </summary>
  138. protected byte[] AESAuthCode;
  139. /// <summary>
  140. /// Get/set the password used for encryption.
  141. /// </summary>
  142. /// <remarks>When set to null or if the password is empty no encryption is performed</remarks>
  143. public string Password {
  144. get {
  145. return password;
  146. }
  147. set {
  148. if ((value != null) && (value.Length == 0)) {
  149. password = null;
  150. } else {
  151. password = value;
  152. }
  153. }
  154. }
  155. /// <summary>
  156. /// Encrypt a block of data
  157. /// </summary>
  158. /// <param name="buffer">
  159. /// Data to encrypt. NOTE the original contents of the buffer are lost
  160. /// </param>
  161. /// <param name="offset">
  162. /// Offset of first byte in buffer to encrypt
  163. /// </param>
  164. /// <param name="length">
  165. /// Number of bytes in buffer to encrypt
  166. /// </param>
  167. protected void EncryptBlock(byte[] buffer, int offset, int length)
  168. {
  169. cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0);
  170. }
  171. /// <summary>
  172. /// Initializes encryption keys based on given <paramref name="password"/>.
  173. /// </summary>
  174. /// <param name="password">The password.</param>
  175. protected void InitializePassword(string password)
  176. {
  177. var pkManaged = new PkzipClassicManaged();
  178. byte[] key = PkzipClassic.GenerateKeys(ZipConstants.ConvertToArray(password));
  179. cryptoTransform_ = pkManaged.CreateEncryptor(key, null);
  180. }
  181. /// <summary>
  182. /// Initializes encryption keys based on given password.
  183. /// </summary>
  184. protected void InitializeAESPassword(ZipEntry entry, string rawPassword,
  185. out byte[] salt, out byte[] pwdVerifier)
  186. {
  187. salt = new byte[entry.AESSaltLen];
  188. // Salt needs to be cryptographically random, and unique per file
  189. if (_aesRnd == null)
  190. _aesRnd = RandomNumberGenerator.Create();
  191. _aesRnd.GetBytes(salt);
  192. int blockSize = entry.AESKeySize / 8; // bits to bytes
  193. cryptoTransform_ = new ZipAESTransform(rawPassword, salt, blockSize, true);
  194. pwdVerifier = ((ZipAESTransform)cryptoTransform_).PwdVerifier;
  195. }
  196. #endregion
  197. #region Deflation Support
  198. /// <summary>
  199. /// Deflates everything in the input buffers. This will call
  200. /// <code>def.deflate()</code> until all bytes from the input buffers
  201. /// are processed.
  202. /// </summary>
  203. protected void Deflate()
  204. {
  205. while (!deflater_.IsNeedingInput) {
  206. int deflateCount = deflater_.Deflate(buffer_, 0, buffer_.Length);
  207. if (deflateCount <= 0) {
  208. break;
  209. }
  210. if (cryptoTransform_ != null) {
  211. EncryptBlock(buffer_, 0, deflateCount);
  212. }
  213. baseOutputStream_.Write(buffer_, 0, deflateCount);
  214. }
  215. if (!deflater_.IsNeedingInput) {
  216. throw new SharpZipBaseException("DeflaterOutputStream can't deflate all input?");
  217. }
  218. }
  219. #endregion
  220. #region Stream Overrides
  221. /// <summary>
  222. /// Gets value indicating stream can be read from
  223. /// </summary>
  224. public override bool CanRead {
  225. get {
  226. return false;
  227. }
  228. }
  229. /// <summary>
  230. /// Gets a value indicating if seeking is supported for this stream
  231. /// This property always returns false
  232. /// </summary>
  233. public override bool CanSeek {
  234. get {
  235. return false;
  236. }
  237. }
  238. /// <summary>
  239. /// Get value indicating if this stream supports writing
  240. /// </summary>
  241. public override bool CanWrite {
  242. get {
  243. return baseOutputStream_.CanWrite;
  244. }
  245. }
  246. /// <summary>
  247. /// Get current length of stream
  248. /// </summary>
  249. public override long Length {
  250. get {
  251. return baseOutputStream_.Length;
  252. }
  253. }
  254. /// <summary>
  255. /// Gets the current position within the stream.
  256. /// </summary>
  257. /// <exception cref="NotSupportedException">Any attempt to set position</exception>
  258. public override long Position {
  259. get {
  260. return baseOutputStream_.Position;
  261. }
  262. set {
  263. throw new NotSupportedException("Position property not supported");
  264. }
  265. }
  266. /// <summary>
  267. /// Sets the current position of this stream to the given value. Not supported by this class!
  268. /// </summary>
  269. /// <param name="offset">The offset relative to the <paramref name="origin"/> to seek.</param>
  270. /// <param name="origin">The <see cref="SeekOrigin"/> to seek from.</param>
  271. /// <returns>The new position in the stream.</returns>
  272. /// <exception cref="NotSupportedException">Any access</exception>
  273. public override long Seek(long offset, SeekOrigin origin)
  274. {
  275. throw new NotSupportedException("DeflaterOutputStream Seek not supported");
  276. }
  277. /// <summary>
  278. /// Sets the length of this stream to the given value. Not supported by this class!
  279. /// </summary>
  280. /// <param name="value">The new stream length.</param>
  281. /// <exception cref="NotSupportedException">Any access</exception>
  282. public override void SetLength(long value)
  283. {
  284. throw new NotSupportedException("DeflaterOutputStream SetLength not supported");
  285. }
  286. /// <summary>
  287. /// Read a byte from stream advancing position by one
  288. /// </summary>
  289. /// <returns>The byte read cast to an int. THe value is -1 if at the end of the stream.</returns>
  290. /// <exception cref="NotSupportedException">Any access</exception>
  291. public override int ReadByte()
  292. {
  293. throw new NotSupportedException("DeflaterOutputStream ReadByte not supported");
  294. }
  295. /// <summary>
  296. /// Read a block of bytes from stream
  297. /// </summary>
  298. /// <param name="buffer">The buffer to store read data in.</param>
  299. /// <param name="offset">The offset to start storing at.</param>
  300. /// <param name="count">The maximum number of bytes to read.</param>
  301. /// <returns>The actual number of bytes read. Zero if end of stream is detected.</returns>
  302. /// <exception cref="NotSupportedException">Any access</exception>
  303. public override int Read(byte[] buffer, int offset, int count)
  304. {
  305. throw new NotSupportedException("DeflaterOutputStream Read not supported");
  306. }
  307. /// <summary>
  308. /// Flushes the stream by calling <see cref="DeflaterOutputStream.Flush">Flush</see> on the deflater and then
  309. /// on the underlying stream. This ensures that all bytes are flushed.
  310. /// </summary>
  311. public override void Flush()
  312. {
  313. deflater_.Flush();
  314. Deflate();
  315. baseOutputStream_.Flush();
  316. }
  317. /// <summary>
  318. /// Calls <see cref="Finish"/> and closes the underlying
  319. /// stream when <see cref="IsStreamOwner"></see> is true.
  320. /// </summary>
  321. protected override void Dispose(bool disposing)
  322. {
  323. if (!isClosed_) {
  324. isClosed_ = true;
  325. try {
  326. Finish();
  327. if (cryptoTransform_ != null) {
  328. GetAuthCodeIfAES();
  329. cryptoTransform_.Dispose();
  330. cryptoTransform_ = null;
  331. }
  332. } finally {
  333. if (IsStreamOwner) {
  334. baseOutputStream_.Dispose();
  335. }
  336. }
  337. }
  338. }
  339. private void GetAuthCodeIfAES()
  340. {
  341. if (cryptoTransform_ is ZipAESTransform) {
  342. AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
  343. }
  344. }
  345. /// <summary>
  346. /// Writes a single byte to the compressed output stream.
  347. /// </summary>
  348. /// <param name="value">
  349. /// The byte value.
  350. /// </param>
  351. public override void WriteByte(byte value)
  352. {
  353. byte[] b = new byte[1];
  354. b[0] = value;
  355. Write(b, 0, 1);
  356. }
  357. /// <summary>
  358. /// Writes bytes from an array to the compressed stream.
  359. /// </summary>
  360. /// <param name="buffer">
  361. /// The byte array
  362. /// </param>
  363. /// <param name="offset">
  364. /// The offset into the byte array where to start.
  365. /// </param>
  366. /// <param name="count">
  367. /// The number of bytes to write.
  368. /// </param>
  369. public override void Write(byte[] buffer, int offset, int count)
  370. {
  371. deflater_.SetInput(buffer, offset, count);
  372. Deflate();
  373. }
  374. #endregion
  375. #region Instance Fields
  376. /// <summary>
  377. /// This buffer is used temporarily to retrieve the bytes from the
  378. /// deflater and write them to the underlying output stream.
  379. /// </summary>
  380. byte[] buffer_;
  381. /// <summary>
  382. /// The deflater which is used to deflate the stream.
  383. /// </summary>
  384. protected Deflater deflater_;
  385. /// <summary>
  386. /// Base stream the deflater depends on.
  387. /// </summary>
  388. protected Stream baseOutputStream_;
  389. bool isClosed_;
  390. #endregion
  391. #region Static Fields
  392. // Static to help ensure that multiple files within a zip will get different random salt
  393. private static RandomNumberGenerator _aesRnd = RandomNumberGenerator.Create();
  394. #endregion
  395. }
  396. }