mirrored from https://www.bouncycastle.org/repositories/bc-csharp
-
Notifications
You must be signed in to change notification settings - Fork 602
Expand file tree
/
Copy pathCMSCompressedDataStreamGenerator.cs
More file actions
160 lines (138 loc) · 6.65 KB
/
Copy pathCMSCompressedDataStreamGenerator.cs
File metadata and controls
160 lines (138 loc) · 6.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using System;
using System.IO;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Utilities.IO;
namespace Org.BouncyCastle.Cms
{
/// <summary>
/// Streaming generator for CMS CompressedData messages. Call <see cref="Open(Stream)"/> to obtain a
/// <see cref="Stream"/> to which the content to be compressed is written; closing that stream finalizes the
/// CMS structure. Only ZLIB compression (<see cref="ZLib"/>) is supported.
/// </summary>
/// <remarks>
/// The returned stream must be closed (disposed) to finalize the CMS structure. Closing the returned stream
/// does <b>not</b> close the underlying stream passed to <c>Open</c>; callers are responsible for closing the
/// underlying stream separately.
/// <para>A simple example of usage:</para>
/// <code>
/// CmsCompressedDataStreamGenerator gen = new CmsCompressedDataStreamGenerator();
/// using (Stream cOut = gen.Open(outputStream, CmsCompressedDataStreamGenerator.ZLib))
/// {
/// cOut.Write(data, 0, data.Length);
/// }
/// </code>
/// </remarks>
public class CmsCompressedDataStreamGenerator
{
/// <summary>The OID for ZLIB compression, the only algorithm supported by this generator.</summary>
public static readonly string ZLib = CmsObjectIdentifiers.ZlibCompress.Id;
private int m_bufferSize;
/// <summary>Creates a generator instance.</summary>
public CmsCompressedDataStreamGenerator()
{
}
/// <summary>
/// Sets the buffer size used for the OCTET STRING segments holding the encapsulated content.
/// </summary>
/// <param name="bufferSize">The length, in bytes, of the octet strings used to buffer the data.</param>
public void SetBufferSize(int bufferSize)
{
m_bufferSize = bufferSize;
}
/// <summary>
/// Opens a stream for generating a CMS CompressedData object using ZLIB compression and content type "data".
/// </summary>
/// <param name="outStream">The stream the CMS object is written to.</param>
/// <returns>A stream the content to be compressed is written to; close it to finalize the structure.</returns>
public Stream Open(Stream outStream)
{
return Open(outStream, CmsObjectIdentifiers.Data.Id, ZLib);
}
/// <summary>
/// Opens a stream for generating a CMS CompressedData object with content type "data".
/// </summary>
/// <param name="outStream">The stream the CMS object is written to.</param>
/// <param name="compressionOid">The compression algorithm OID; must be <see cref="ZLib"/>.</param>
/// <returns>A stream the content to be compressed is written to; close it to finalize the structure.</returns>
public Stream Open(Stream outStream, string compressionOid)
{
return Open(outStream, CmsObjectIdentifiers.Data.Id, compressionOid);
}
/// <summary>
/// Opens a stream for generating a CMS CompressedData object with the given encapsulated content type.
/// </summary>
/// <param name="outStream">The stream the CMS object is written to.</param>
/// <param name="contentOid">The OID of the content type being compressed.</param>
/// <param name="compressionOid">The compression algorithm OID; must be <see cref="ZLib"/>.</param>
/// <returns>A stream the content to be compressed is written to; close it to finalize the structure.</returns>
/// <exception cref="ArgumentException">Thrown if <paramref name="compressionOid"/> is not ZLIB.</exception>
public Stream Open(Stream outStream, string contentOid, string compressionOid)
{
if (ZLib != compressionOid)
throw new ArgumentException("Unsupported compression algorithm: " + compressionOid,
nameof(compressionOid));
// ContentInfo
BerSequenceGenerator sGen = new BerSequenceGenerator(outStream);
sGen.AddObject(CmsObjectIdentifiers.CompressedData);
// CompressedData
BerSequenceGenerator cGen = new BerSequenceGenerator(sGen.GetRawOutputStream(), 0, true);
cGen.AddObject(DerInteger.Zero);
cGen.AddObject(new AlgorithmIdentifier(CmsObjectIdentifiers.ZlibCompress));
// EncapsulatedContentInfo
BerSequenceGenerator eciGen = new BerSequenceGenerator(cGen.GetRawOutputStream());
eciGen.AddObject(new DerObjectIdentifier(contentOid));
// eContent [0] EXPLICIT OCTET STRING OPTIONAL
BerOctetStringGenerator ecGen = new BerOctetStringGenerator(eciGen.GetRawOutputStream(), 0, true);
Stream ecStream = ecGen.GetOctetOutputStream(m_bufferSize);
var compressedStream = Utilities.IO.Compression.ZLib.CompressOutput(ecStream, -1);
return new CmsCompressedOutputStream(compressedStream, sGen, cGen, eciGen, ecGen);
}
private class CmsCompressedOutputStream
: BaseOutputStream
{
private Stream m_out;
private BerSequenceGenerator m_sGen;
private BerSequenceGenerator m_cGen;
private BerSequenceGenerator m_eciGen;
private BerOctetStringGenerator m_ecGen;
internal CmsCompressedOutputStream(Stream outStream, BerSequenceGenerator sGen, BerSequenceGenerator cGen,
BerSequenceGenerator eciGen, BerOctetStringGenerator ecGen)
{
m_out = outStream;
m_sGen = sGen;
m_cGen = cGen;
m_eciGen = eciGen;
m_ecGen = ecGen;
}
public override void Write(byte[] buffer, int offset, int count)
{
m_out.Write(buffer, offset, count);
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
public override void Write(ReadOnlySpan<byte> buffer)
{
m_out.Write(buffer);
}
#endif
public override void WriteByte(byte value)
{
m_out.WriteByte(value);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
m_out.Dispose();
// TODO Parent context(s) should really be be closed explicitly
m_ecGen.Dispose();
m_eciGen.Dispose();
m_cGen.Dispose();
m_sGen.Dispose();
}
base.Dispose(disposing);
}
}
}
}