Skip to content

Commit 24fb30b

Browse files
committed
Added more common PEM header support and drafted unit tests for ASN binary/string formatter
1 parent a934d09 commit 24fb30b

14 files changed

Lines changed: 598 additions & 316 deletions

Asn1Parser.sln

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
1010
.editorconfig = .editorconfig
1111
EndProjectSection
1212
EndProject
13+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{CA656832-2531-426B-ACF5-724FA0A6EDC7}"
14+
EndProject
15+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Asn1Parser.Tests", "tests\Asn1Parser.Tests\Asn1Parser.Tests.csproj", "{739EADA6-013A-4BA3-BF41-70D667B9682A}"
16+
EndProject
1317
Global
1418
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1519
Debug|Any CPU = Debug|Any CPU
@@ -20,10 +24,17 @@ Global
2024
{F18F1812-FD65-4206-8869-B1DBB4D59AC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
2125
{F18F1812-FD65-4206-8869-B1DBB4D59AC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
2226
{F18F1812-FD65-4206-8869-B1DBB4D59AC8}.Release|Any CPU.Build.0 = Release|Any CPU
27+
{739EADA6-013A-4BA3-BF41-70D667B9682A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
28+
{739EADA6-013A-4BA3-BF41-70D667B9682A}.Debug|Any CPU.Build.0 = Debug|Any CPU
29+
{739EADA6-013A-4BA3-BF41-70D667B9682A}.Release|Any CPU.ActiveCfg = Release|Any CPU
30+
{739EADA6-013A-4BA3-BF41-70D667B9682A}.Release|Any CPU.Build.0 = Release|Any CPU
2331
EndGlobalSection
2432
GlobalSection(SolutionProperties) = preSolution
2533
HideSolutionNode = FALSE
2634
EndGlobalSection
35+
GlobalSection(NestedProjects) = preSolution
36+
{739EADA6-013A-4BA3-BF41-70D667B9682A} = {CA656832-2531-426B-ACF5-724FA0A6EDC7}
37+
EndGlobalSection
2738
GlobalSection(ExtensibilityGlobals) = postSolution
2839
SolutionGuid = {B1F6FDE1-A4A9-4257-82D9-2CFC87B32B09}
2940
EndGlobalSection

Asn1Parser.sln.DotSettings

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BMP/@EntryIndexedValue">BMP</s:String>
33
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DH/@EntryIndexedValue">DH</s:String>
44
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DHX/@EntryIndexedValue">DHX</s:String>
5+
<s:Boolean x:Key="/Default/UserDictionary/Words/=ECDSA/@EntryIndexedValue">True</s:Boolean>
6+
<s:Boolean x:Key="/Default/UserDictionary/Words/=PKCS/@EntryIndexedValue">True</s:Boolean>
57
<s:Boolean x:Key="/Default/UserDictionary/Words/=Teletex/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

Asn1Parser/Asn1Parser.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<TargetFrameworks>netstandard2.0;net472</TargetFrameworks>
44
<LangVersion>latest</LangVersion>
55
<Title>High-performance and robust .NET ASN.1 DER parser</Title>
6-
<Version>1.2.11</Version>
6+
<Version>1.2.12</Version>
77
<Authors>Vadims Podans</Authors>
88
<Company>PKI Solutions LLC</Company>
99
<Description>ASN.1 binary parser and utility set for binary data encoded with Distinguished Encoding Rules (DER).</Description>

Asn1Parser/AsnFormatter.cs

Lines changed: 67 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ public static String BinaryToString(Byte[] rawData, EncodingType encoding = Enco
4141
if (rawData == null || rawData.Length == 0) {
4242
return String.Empty;
4343
}
44+
if (PemHeader.ContainsEncoding(encoding)) {
45+
return BinaryToStringFormatter.ToBase64(rawData, encoding, format, start, count);
46+
}
4447
switch (encoding) {
4548
case EncodingType.Base64:
46-
case EncodingType.Base64Header:
47-
case EncodingType.Base64CrlHeader:
48-
case EncodingType.Base64RequestHeader:
4949
return BinaryToStringFormatter.ToBase64(rawData, encoding, format, start, count);
5050
case EncodingType.Hex:
5151
return BinaryToStringFormatter.ToHex(rawData, format, start, count, forceUpperCase);
@@ -90,13 +90,20 @@ public static String BinaryToString(Byte[] rawData, EncodingType encoding = Enco
9090
/// </list>
9191
/// </remarks>
9292
public static String BinaryToString(Asn1Reader asn, EncodingType encoding = EncodingType.HexRaw, EncodingFormat format = EncodingFormat.CRLF, Boolean forceUpperCase = false) {
93-
if (asn == null) { throw new ArgumentNullException(nameof(asn)); }
94-
if (asn.PayloadLength == 0) { return String.Empty; }
93+
if (asn == null) {
94+
throw new ArgumentNullException(nameof(asn));
95+
}
96+
if (asn.PayloadLength == 0) {
97+
return String.Empty;
98+
}
99+
if ((Int32)encoding > 20 && (Int32)encoding < 45) {
100+
return BinaryToStringFormatter.ToBase64(asn.GetRawData(), encoding, format, asn.PayloadStartOffset, asn.PayloadLength);
101+
}
102+
if (PemHeader.ContainsEncoding(encoding)) {
103+
return BinaryToStringFormatter.ToBase64(asn.GetRawData(), encoding, format, asn.PayloadStartOffset, asn.PayloadLength);
104+
}
95105
switch (encoding) {
96106
case EncodingType.Base64:
97-
case EncodingType.Base64Header:
98-
case EncodingType.Base64CrlHeader:
99-
case EncodingType.Base64RequestHeader:
100107
return BinaryToStringFormatter.ToBase64(asn.GetRawData(), encoding, format, asn.PayloadStartOffset, asn.PayloadLength);
101108
case EncodingType.Hex:
102109
return BinaryToStringFormatter.ToHex(asn.GetRawData(), format, asn.PayloadStartOffset, asn.PayloadLength, forceUpperCase);
@@ -120,53 +127,56 @@ public static String BinaryToString(Asn1Reader asn, EncodingType encoding = Enco
120127
/// <exception cref="ArgumentException">And invalid encoding is specified.</exception>
121128
/// <exception cref="InvalidDataException">The string cannot be decoded.</exception>
122129
/// <returns>Original byte array.</returns>
123-
/// <remarks>This method may not be fully compatible with
124-
/// <see cref="BinaryToString(Byte[],EncodingType,EncodingFormat,Int32,Int32,Boolean)">BinaryToString</see>
125-
/// method.
130+
/// <remarks>
131+
/// This method may not be fully compatible with
132+
/// <see cref="BinaryToString(Byte[],EncodingType,EncodingFormat,Int32,Int32,Boolean)">BinaryToString</see>
133+
/// method.
134+
/// <para>
135+
/// If encoding parameter is set to <strong>Base64Header</strong>, the method will accept any properly formatted PEM header
136+
/// and footer.
137+
/// </para>
126138
/// </remarks>
127139
public static Byte[] StringToBinary(String input, EncodingType encoding = EncodingType.Base64) {
128140
Byte[] rawData;
129-
switch (encoding) {
130-
case EncodingType.Binary:
131-
rawData = StringToBinaryFormatter.FromBinary(input);
132-
break;
133-
case EncodingType.Base64:
134-
rawData = StringToBinaryFormatter.FromBase64(input);
135-
break;
136-
case EncodingType.Base64Header:
137-
rawData = StringToBinaryFormatter.FromBase64Header(input);
138-
break;
139-
case EncodingType.Base64CrlHeader:
140-
rawData = StringToBinaryFormatter.FromBase64Crl(input);
141-
break;
142-
case EncodingType.Base64RequestHeader:
143-
rawData = StringToBinaryFormatter.FromBase64Request(input);
144-
break;
145-
case EncodingType.Base64Any:
146-
rawData = StringToBinaryFormatter.FromBase64Any(input);
147-
break;
148-
case EncodingType.StringAny:
149-
rawData = StringToBinaryFormatter.FromStringAny(input);
150-
break;
151-
case EncodingType.Hex:
152-
case EncodingType.HexRaw:
153-
rawData = StringToBinaryFormatter.FromHex(input);
154-
break;
155-
case EncodingType.HexAddress:
156-
rawData = StringToBinaryFormatter.FromHexAddr(input);
157-
break;
158-
case EncodingType.HexAscii:
159-
rawData = StringToBinaryFormatter.FromHexAscii(input);
160-
break;
161-
case EncodingType.HexAsciiAddress:
162-
rawData = StringToBinaryFormatter.FromHexAddrAscii(input);
163-
break;
164-
case EncodingType.HexAny:
165-
rawData = StringToBinaryFormatter.FromHexAny(input);
166-
break;
167-
default:
168-
throw new ArgumentException("Invalid encoding type is specified.");
141+
if (PemHeader.ContainsEncoding(encoding)) {
142+
var pemHeader = PemHeader.GetHeader(encoding);
143+
rawData = StringToBinaryFormatter.FromBase64Header(input, pemHeader.GetHeader(), pemHeader.GetFooter());
144+
} else {
145+
switch (encoding) {
146+
case EncodingType.Binary:
147+
rawData = StringToBinaryFormatter.FromBinary(input);
148+
break;
149+
case EncodingType.Base64:
150+
rawData = StringToBinaryFormatter.FromBase64(input);
151+
break;
152+
case EncodingType.Base64Any:
153+
rawData = StringToBinaryFormatter.FromBase64Any(input);
154+
break;
155+
case EncodingType.StringAny:
156+
rawData = StringToBinaryFormatter.FromStringAny(input);
157+
break;
158+
case EncodingType.Hex:
159+
case EncodingType.HexRaw:
160+
rawData = StringToBinaryFormatter.FromHex(input);
161+
break;
162+
case EncodingType.HexAddress:
163+
rawData = StringToBinaryFormatter.FromHexAddr(input);
164+
break;
165+
case EncodingType.HexAscii:
166+
rawData = StringToBinaryFormatter.FromHexAscii(input);
167+
break;
168+
case EncodingType.HexAsciiAddress:
169+
rawData = StringToBinaryFormatter.FromHexAddrAscii(input);
170+
break;
171+
case EncodingType.HexAny:
172+
rawData = StringToBinaryFormatter.FromHexAny(input);
173+
break;
174+
default:
175+
throw new ArgumentException("Invalid encoding type is specified.");
176+
}
169177
}
178+
179+
170180
if (rawData == null) {
171181
throw new InvalidDataException("The data is invalid.");
172182
}
@@ -180,13 +190,12 @@ public static Byte[] StringToBinary(String input, EncodingType encoding = Encodi
180190
/// Resolved input string format. If format cannot be determined, <string>Binary</string> type is returned.
181191
/// </returns>
182192
public static EncodingType TestInputString(String input) {
183-
Byte[] rawBytes = StringToBinaryFormatter.FromBase64Crl(input);
184-
if (rawBytes != null) {
185-
return EncodingType.Base64CrlHeader;
186-
}
187-
rawBytes = StringToBinaryFormatter.FromBase64Request(input);
188-
if (rawBytes != null) {
189-
return EncodingType.Base64RequestHeader;
193+
Byte[] rawBytes;
194+
foreach (PemHeader pemHeader in PemHeader.GetPemHeaders()) {
195+
rawBytes = StringToBinaryFormatter.FromBase64Header(input, pemHeader.GetHeader(), pemHeader.GetFooter());
196+
if (rawBytes != null) {
197+
return pemHeader.Encoding;
198+
}
190199
}
191200
rawBytes = StringToBinaryFormatter.FromBase64Header(input);
192201
if (rawBytes != null) {

Asn1Parser/BinaryToStringFormatter.cs

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,8 @@ public static String ToHexAscii(Byte[] rawData, EncodingFormat format, Int32 sta
105105
}
106106
// handle last byte to complete partial ASCII panel.
107107
if (n == count) {
108-
Int32 remainder = n % 16;
109-
if (remainder > 7) {
110-
sb.Append(new String(' ', (17 - remainder) * 3 - 1));
111-
sb.Append(ascii);
112-
} else {
113-
sb.Append(new String(' ', (17 - remainder) * 3));
114-
sb.Append(ascii);
115-
}
108+
sb.Append(getAsciiPadding(n));
109+
sb.Append(ascii);
116110
}
117111
}
118112

@@ -156,14 +150,8 @@ public static String ToHexAddressAndAscii(IReadOnlyList<Byte> rawData, EncodingF
156150
}
157151
// handle last byte to complete partial ASCII panel.
158152
if (n + 1 == count) {
159-
Int32 remainder = (index + 1) % 16;
160-
if (remainder > 7) {
161-
sb.Append(new String(' ', (17 - remainder) * 3 - 1));
162-
sb.Append(ascii);
163-
} else {
164-
sb.Append(new String(' ', (17 - remainder) * 3));
165-
sb.Append(ascii);
166-
}
153+
sb.Append(getAsciiPadding(index + 1));
154+
sb.Append(ascii);
167155
}
168156
n++;
169157
}
@@ -208,21 +196,12 @@ public static String ToBase64(IReadOnlyCollection<Byte> rawData, EncodingType en
208196

209197
static void finalizeBase64WithHeader(StringBuilder sb, EncodingType encoding, String splitter) {
210198
Func<String> header, footer;
211-
switch (encoding) {
212-
case EncodingType.Base64Header:
213-
header = PemHeaders.GetCertHeader;
214-
footer = PemHeaders.GetCertFooter;
215-
break;
216-
case EncodingType.Base64RequestHeader:
217-
header = PemHeaders.GetCertReqNewHeader;
218-
footer = PemHeaders.GetCertReqNewFooter;
219-
break;
220-
case EncodingType.Base64CrlHeader:
221-
header = PemHeaders.GetCrlHeader;
222-
footer = PemHeaders.GetCrlFooter;
223-
break;
224-
default:
225-
throw new ArgumentException("Specified encoding is not valid Base64 encoding.");
199+
if (PemHeader.ContainsEncoding(encoding)) {
200+
PemHeader pemHeader = PemHeader.GetHeader(encoding);
201+
header = pemHeader.GetHeader;
202+
footer = pemHeader.GetFooter;
203+
} else {
204+
throw new ArgumentException("Specified encoding is not valid Base64 encoding.");
226205
}
227206
sb.Insert(0, header.Invoke() + splitter);
228207
sb.Append(splitter + footer.Invoke());
@@ -242,6 +221,14 @@ static String finalizeBinaryToString(StringBuilder sb, EncodingFormat format) {
242221

243222
#region helper methods
244223

224+
static String getAsciiPadding(Int32 index) {
225+
Int32 remainder = index % 16;
226+
if (remainder > 7) {
227+
return new String(' ', (17 - remainder) * 3 - 1);
228+
}
229+
230+
return new String(' ', (17 - remainder) * 3);
231+
}
245232
static Int32 getCount(Int32 size, Int32 start, Int32 count) {
246233
if (start < 0 || start >= size) {
247234
throw new OverflowException();

Asn1Parser/EncodingFormat.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ public enum EncodingFormat : UInt32 {
1414
/// <summary>
1515
/// Do not append any new line characters to the encoded string. The default behavior is to use a carriage return/line
1616
/// feed (CR/LF) pair (0x0D/0x0A) to represent a new line.
17-
/// <para><strong>Windows Server 2003 and Windows XP:</strong> This value is not supported.</para>
1817
/// </summary>
18+
/// <remarks>
19+
/// This flag is ignored when encoding binary to string using <strong>HexAddress</strong>,
20+
/// <strong>HexAscii</strong> or <strong>HexAsciiAddress</strong> encoding type.
21+
/// </remarks>
1922
NOCRLF = 0x40000000,
2023
/// <summary>
2124
/// Only use the line feed (LF) character (0x0A) for a new line. The default behavior is to use a CR/LF pair

0 commit comments

Comments
 (0)