Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
DerEncoder.cs
1using System;
4using System.IO;
5using System.Text;
6using System.Text.RegularExpressions;
7
9{
13 public enum Asn1TypeClass
14 {
18 Universal = 0,
19
23 Application = 1,
24
28 ContextSpecific = 2,
29
33 Private = 3
34 }
35
39 public class DerEncoder : IDisposable
40 {
41 private MemoryStream output = new MemoryStream();
42 private LinkedList<KeyValuePair<byte, MemoryStream>> stack = null;
43
47 public DerEncoder()
48 {
49 }
50
54 public void Dispose()
55 {
56 this.output?.Dispose();
57 this.output = null;
58 }
59
63 public void Clear()
64 {
65 this.output.Position = 0;
66 this.output.SetLength(0);
67 }
68
73 public byte[] ToArray()
74 {
75 if (!(this.stack is null) && !(this.stack.First is null))
76 throw new Exception("DER output not properly closed.");
77
78 return this.output.ToArray();
79 }
80
85 public void BOOLEAN(bool Value)
86 {
87 this.output.WriteByte(1);
88 this.output.WriteByte(1);
89
90 if (Value)
91 this.output.WriteByte(0xff);
92 else
93 this.output.WriteByte(0x00);
94 }
95
100 public void INTEGER(long Value)
101 {
102 this.output.WriteByte(2);
103
104 int Pos = (int)this.output.Position;
105 int c = 8;
106 byte b;
107
108 this.output.WriteByte(0);
109
110 if (Value < 0)
111 {
112 do
113 {
114 b = (byte)(Value >> 56);
115 Value <<= 8;
116 c--;
117 }
118 while (b == 0xff);
119
120 if (b < 0x80)
121 this.output.WriteByte(0xff);
122
123 this.output.WriteByte(b);
124
125 while (c-- > 0)
126 {
127 b = (byte)(Value >> 56);
128 this.output.WriteByte(b);
129 Value <<= 8;
130 }
131 }
132 else if (Value == 0)
133 this.output.WriteByte(0);
134 else
135 {
136 do
137 {
138 b = (byte)(Value >> 56);
139 Value <<= 8;
140 c--;
141 }
142 while (b == 0);
143
144 if (b >= 0x80)
145 this.output.WriteByte(0);
146
147 this.output.WriteByte(b);
148
149 while (c-- > 0)
150 {
151 b = (byte)(Value >> 56);
152 this.output.WriteByte(b);
153 Value <<= 8;
154 }
155 }
156
157 this[Pos] = (byte)(this.output.Position - Pos - 1);
158 }
159
165 public void INTEGER(byte[] Value, bool Negative)
166 {
167 this.output.WriteByte(2);
168
169 if (Negative)
170 {
171 if (Value is null || Value.Length == 0)
172 Value = new byte[] { 0xff };
173 else if (Value[0] < 0x80)
174 {
175 int c = Value.Length;
176 byte[] Value2 = new byte[c + 1];
177 Value2[0] = 0xff;
178 Buffer.BlockCopy(Value, 0, Value2, 1, Value.Length);
179 Value = Value2;
180 }
181 }
182 else
183 {
184 if (Value is null || Value.Length == 0)
185 Value = new byte[] { 0 };
186 else if (Value[0] >= 0x80)
187 {
188 int c = Value.Length;
189 byte[] Value2 = new byte[c + 1];
190 Value2[0] = 0;
191 Buffer.BlockCopy(Value, 0, Value2, 1, Value.Length);
192 Value = Value2;
193 }
194 }
195
196 this.EncodeBinary(Value);
197 }
198
199 private void EncodeBinary(byte[] Bin)
200 {
201 int Len = Bin.Length;
202
203 if (Len >= 0x80)
204 {
205 if (Len <= 0xff)
206 this.output.WriteByte(0x81);
207 else
208 {
209 if (Len <= 0xffff)
210 this.output.WriteByte(0x82);
211 else
212 {
213 if (Len <= 0xffffff)
214 this.output.WriteByte(0x83);
215 else
216 {
217 this.output.WriteByte(0x84);
218 this.output.WriteByte((byte)(Len >> 24));
219 }
220
221 this.output.WriteByte((byte)(Len >> 16));
222 }
223
224 this.output.WriteByte((byte)(Len >> 8));
225 }
226 }
227
228 this.output.WriteByte((byte)Len);
229 this.output.Write(Bin, 0, Bin.Length);
230 }
231
236 public void BITSTRING(BitArray Bits)
237 {
238 this.output.WriteByte(3);
239
240 int NrBits = Bits.Length;
241 int Len = (NrBits + 7) / 8 + 1;
242 int NrUnusedBits = 8 - (NrBits & 7);
243 byte[] Bin = new byte[Len];
244 byte b = 0;
245 int i, j = 0;
246
247 if (NrUnusedBits == 8)
248 NrUnusedBits = 0;
249
250 Bin[j++] = (byte)NrUnusedBits;
251
252 for (i = 0; i < NrBits; i++)
253 {
254 b <<= 1;
255 if (Bits[i])
256 b |= 1;
257
258 if ((i & 7) == 7)
259 Bin[j++] = b;
260 }
261
262 if (NrUnusedBits > 0)
263 {
264 b <<= NrUnusedBits;
265 Bin[j++] = b;
266 }
267
268 this.EncodeBinary(Bin);
269 }
270
271
276 public void BITSTRING(byte[] Bytes)
277 {
278 this.output.WriteByte(3);
279
280 int c = Bytes.Length;
281 byte[] Bytes2 = new byte[c + 1];
282 Bytes2[0] = 0; // Unused bits.
283 Buffer.BlockCopy(Bytes, 0, Bytes2, 1, c);
284
285 this.EncodeBinary(Bytes2);
286 }
287
291 public void StartBITSTRING()
292 {
293 this.Start(3);
294 this.output.WriteByte(0);
295 }
296
300 public void EndBITSTRING()
301 {
302 this.End(3);
303 }
304
309 public void OCTET_STRING(byte[] Value)
310 {
311 this.output.WriteByte(4);
312 this.EncodeBinary(Value);
313 }
314
318 public void StartOCTET_STRING()
319 {
320 this.Start(4);
321 }
322
326 public void EndOCTET_STRING()
327 {
328 this.End(4);
329 }
330
334 public void NULL()
335 {
336 this.output.WriteByte(5);
337 this.output.WriteByte(0);
338 }
339
344 public void OBJECT_IDENTIFIER(string OID)
345 {
346 string[] s = OID.Split('.');
347 int i, c = s.Length;
348 uint[] OID2 = new uint[c];
349
350 for (i = 0; i < c; i++)
351 {
352 if (!uint.TryParse(s[i], out OID2[i]))
353 throw new ArgumentException("Invalid object identifier.", nameof(OID));
354 }
355
356 this.OBJECT_IDENTIFIER(OID2);
357 }
358
363 public void OBJECT_IDENTIFIER(uint[] OID)
364 {
365 int i, c = OID.Length;
366 uint j;
367
368 if (c < 2)
369 throw new ArgumentException("Invalid length of OID.", nameof(OID));
370
371 if (OID[1] >= 40)
372 throw new ArgumentException("Invalid second integer in OID.", nameof(OID));
373
374 j = OID[0] * 40;
375 if (j > 255 || j + OID[1] > 255)
376 throw new ArgumentException("Invalid first integer in OID.", nameof(OID));
377
378 j += OID[1];
379
380 using (MemoryStream Bin = new MemoryStream())
381 {
382 Bin.WriteByte((byte)j);
383
384 for (i = 2; i < c; i++)
385 {
386 j = OID[i];
387
388 if (j >= 0x10000000)
389 Bin.WriteByte((byte)(128 + (j >> 28)));
390
391 if (j >= 0x200000)
392 Bin.WriteByte((byte)(128 + (j >> 21)));
393
394 if (j >= 0x4000)
395 Bin.WriteByte((byte)(128 + (j >> 14)));
396
397 if (j >= 0x80)
398 Bin.WriteByte((byte)(128 + (j >> 7)));
399
400 Bin.WriteByte((byte)(j & 127));
401 }
402
403 this.output.WriteByte(6);
404 this.EncodeBinary(Bin.ToArray());
405 }
406 }
407
412 public void UNICODE_STRING(string Value)
413 {
414 this.output.WriteByte(0x1e);
415 this.EncodeBinary(Encoding.BigEndianUnicode.GetBytes(Value));
416 }
417
422 public void IA5_STRING(string Value)
423 {
424 this.output.WriteByte(0x16);
425 this.EncodeBinary(Encoding.ASCII.GetBytes(Value));
426 }
427
432 public void PRINTABLE_STRING(string Value)
433 {
434 if (!IsPrintable(Value))
435 throw new ArgumentException("Not a printable string.", nameof(Value));
436
437 this.output.WriteByte(0x13);
438 this.EncodeBinary(Encoding.ASCII.GetBytes(Value));
439 }
440
446 public static bool IsPrintable(string Value)
447 {
448 return PrintableString.IsMatch(Value);
449 }
450
451 private static readonly Regex PrintableString = new Regex("^[A-Za-z0-9 '()+,-./:=?]*$", RegexOptions.Singleline | RegexOptions.Compiled);
452
457 public void UTF8_STRING(string Value)
458 {
459 this.output.WriteByte(0x0c);
460 this.EncodeBinary(Encoding.UTF8.GetBytes(Value));
461 }
462
466 public void StartSEQUENCE()
467 {
468 this.Start(0x30);
469 }
470
471 private void Start(byte Expected)
472 {
473 if (this.stack is null)
474 this.stack = new LinkedList<KeyValuePair<byte, MemoryStream>>();
475
476 this.stack.AddLast(new KeyValuePair<byte, MemoryStream>(Expected, this.output));
477
478 this.output = new MemoryStream();
479 }
480
484 public void EndSEQUENCE()
485 {
486 this.End(0x30);
487 }
488
489 private void End(byte Type)
490 {
491 if (this.stack is null || this.stack.Last is null)
492 throw new Exception("Not properly started.");
493
494 if (Type != this.stack.Last.Value.Key)
495 throw new Exception("Start/End type mismatch.");
496
497 byte[] Bin = this.output.ToArray();
498 this.output.Dispose();
499
500 this.output = this.stack.Last.Value.Value;
501 this.stack.RemoveLast();
502
503 this.output.WriteByte(Type);
504 this.EncodeBinary(Bin);
505 }
506
510 public void StartSET()
511 {
512 this.Start(0x31);
513 }
514
518 public void EndSET()
519 {
520 this.End(0x31);
521 }
522
527 public void Content(Asn1TypeClass Class)
528 {
529 this.output.WriteByte((byte)((((int)Class) << 6) | 0x20));
530 this.output.WriteByte(0);
531 }
532
537 public void StartContent(Asn1TypeClass Class)
538 {
539 this.Start((byte)((((int)Class) << 6) | 0x20));
540 }
541
546 public void EndContent(Asn1TypeClass Class)
547 {
548 this.End((byte)((((int)Class) << 6) | 0x20));
549 }
550
555 public void Raw(byte[] DerEncodedBytes)
556 {
557 this.output.Write(DerEncodedBytes, 0, DerEncodedBytes.Length);
558 }
559
563 public int Position
564 {
565 get { return (int)this.output.Position; }
566 }
567
573 public byte this[int Index]
574 {
575 get
576 {
577 long PosBak = this.output.Position;
578 byte Result;
579
580 this.output.Position = Index;
581 Result = (byte)this.output.ReadByte();
582 this.output.Position = PosBak;
583
584 return Result;
585 }
586
587 set
588 {
589 long PosBak = this.output.Position;
590
591 this.output.Position = Index;
592 this.output.WriteByte(value);
593 this.output.Position = PosBak;
594 }
595 }
596
597 }
598}
Encodes data using the Distinguished Encoding Rules (DER), as defined in X.690
Definition: DerEncoder.cs:40
void Raw(byte[] DerEncodedBytes)
Adds DER-encoded bytes to the output.
Definition: DerEncoder.cs:555
byte[] ToArray()
Converts the generated output to a byte arary.
Definition: DerEncoder.cs:73
void Clear()
Clears the output buffer.
Definition: DerEncoder.cs:63
void Content(Asn1TypeClass Class)
Encodes content to the output.
Definition: DerEncoder.cs:527
void EndSET()
Ends the current SET.
Definition: DerEncoder.cs:518
void StartOCTET_STRING()
Starts a OCTET_STRING.
Definition: DerEncoder.cs:318
void StartBITSTRING()
Starts a BITSTRING.
Definition: DerEncoder.cs:291
void Dispose()
IDisposable.Dispose
Definition: DerEncoder.cs:54
void EndSEQUENCE()
Ends the current SEQUENCE.
Definition: DerEncoder.cs:484
void INTEGER(byte[] Value, bool Negative)
Encodes an INTEGER value.
Definition: DerEncoder.cs:165
void BITSTRING(byte[] Bytes)
Encodes an BITSTRING value.
Definition: DerEncoder.cs:276
void EndContent(Asn1TypeClass Class)
Ends the current Content section.
Definition: DerEncoder.cs:546
DerEncoder()
Encodes data using the Distinguished Encoding Rules (DER), as defined in X.690
Definition: DerEncoder.cs:47
void UNICODE_STRING(string Value)
Encodes a UNICODE STRING (BMPString) value.
Definition: DerEncoder.cs:412
static bool IsPrintable(string Value)
Checks if a string is a printable string.
Definition: DerEncoder.cs:446
void OBJECT_IDENTIFIER(string OID)
Encodes an OBJECT IDENTIFIER value.
Definition: DerEncoder.cs:344
void NULL()
Encodes an NULL value.
Definition: DerEncoder.cs:334
void BOOLEAN(bool Value)
Encodes an BOOLEAN value.
Definition: DerEncoder.cs:85
void EndOCTET_STRING()
Ends the current OCTET_STRING.
Definition: DerEncoder.cs:326
void IA5_STRING(string Value)
Encodes an IA5 STRING value.
Definition: DerEncoder.cs:422
void BITSTRING(BitArray Bits)
Encodes an BITSTRING value.
Definition: DerEncoder.cs:236
void OBJECT_IDENTIFIER(uint[] OID)
Encodes an OBJECT IDENTIFIER value.
Definition: DerEncoder.cs:363
void StartContent(Asn1TypeClass Class)
Starts a content section.
Definition: DerEncoder.cs:537
void UTF8_STRING(string Value)
Encodes an UTF-8 STRING value.
Definition: DerEncoder.cs:457
int Position
Current output position.
Definition: DerEncoder.cs:564
void INTEGER(long Value)
Encodes an INTEGER value.
Definition: DerEncoder.cs:100
void EndBITSTRING()
Ends the current BITSTRING.
Definition: DerEncoder.cs:300
void PRINTABLE_STRING(string Value)
Encodes an PRINTABLE STRING value.
Definition: DerEncoder.cs:432
void StartSET()
Starts a SET.
Definition: DerEncoder.cs:510
void StartSEQUENCE()
Starts a SEQUENCE.
Definition: DerEncoder.cs:466
void OCTET_STRING(byte[] Value)
Encodes an OCTET STRING value.
Definition: DerEncoder.cs:309
Asn1TypeClass
Type class
Definition: DerEncoder.cs:14