|
1 | 1 | using System; |
2 | 2 | using System.Collections.Generic; |
3 | | -using System.Linq; |
4 | 3 |
|
5 | 4 | namespace SysadminsLV.Asn1Parser; |
6 | 5 | static class StringToBinaryFormatter { |
7 | 6 | static readonly Char[] _delimiters = [' ', '-', ':', '\t', '\n', '\r']; |
8 | 7 |
|
| 8 | + /// <summary> |
| 9 | + /// Converts the specified string, which encodes binary data as base-64 digits, to |
| 10 | + /// an equivalent 8-bit unsigned integer array. |
| 11 | + /// </summary> |
| 12 | + /// <param name="input">The string to convert.</param> |
| 13 | + /// <returns>An array of 8-bit unsigned integers that is equivalent to <strong>input</strong>.</returns> |
9 | 14 | public static Byte[]? FromBase64(String input) { |
10 | 15 | try { |
11 | 16 | return Convert.FromBase64String(input.Trim()); |
12 | 17 | } catch { |
13 | 18 | return null; |
14 | 19 | } |
15 | 20 | } |
16 | | - // accept any header, not only certificate |
17 | | - public static Byte[]? FromBase64Header(String input) { |
18 | | - const String header = "-----BEGIN "; |
19 | | - const String footer = "-----END "; |
20 | 21 |
|
21 | | - return FromBase64Header(input, header, footer, true); |
22 | | - } |
23 | | - public static Byte[]? FromBase64Header(String input, String header, String footer, Boolean skipHeaderValidation = false) { |
24 | | - if (skipHeaderValidation && (!input.ToUpper().Contains(header) || !input.Contains(footer))) { |
| 22 | + /// <summary> |
| 23 | + /// Attempts to convert the specified string, which encodes binary data as base-64 digits with PEM header and footer, |
| 24 | + /// to an equivalent 8-bit unsigned integer array. |
| 25 | + /// </summary> |
| 26 | + /// <param name="input">The string to convert.</param> |
| 27 | + /// <param name="headerName"> |
| 28 | + /// Specifies the header name. This parameter MUS NOT include 5-dash sequence and BEGIN/END constants. |
| 29 | + /// This parameter is ignored if <strong>skipHeaderValidation</strong> parameter is set to <c>true</c>. |
| 30 | + /// </param> |
| 31 | + /// <param name="skipHeaderValidation"> |
| 32 | + /// Skips strict PEM header and footer check. By default, this method checks for exact PEM header and footer |
| 33 | + /// specified in <strong>header</strong> parameter, which includes only name, without fixed (5-dash sequences, |
| 34 | + /// BEGIN and END constants). |
| 35 | + /// <para> |
| 36 | + /// When this parameter is set to <c>true</c>, then <strong>headerName</strong> parameter is ignored and method will |
| 37 | + /// attempt to find any properly PEM formatted header and footer, even if PEM header and footer names don't match. |
| 38 | + /// For example, the following sequence would also be considered valid: |
| 39 | + /// <code> |
| 40 | + /// -----BEGIN CERTIFICATE----- |
| 41 | + /// MIIDBjCCAm8CAQAwcTERMA8GA1UEAxMIcXV1eC5jb20xDzANBgNVBAsTBkJyYWlu |
| 42 | + /// czEWMBQGA1UEChMNRGV2ZWxvcE1lbnRvcjERMA8GA1UEBxMIVG9ycmFuY2UxEzAR |
| 43 | + /// BgNVBAgTCkNhbGlmb3JuaWExCzAJBgNVBAYTAlVTMIGfMA0GCSqGSIb3DQEBAQUA |
| 44 | + /// <...> |
| 45 | + /// -----END X509 CRL----- |
| 46 | + /// </code> |
| 47 | + /// </para> |
| 48 | + /// </param> |
| 49 | + /// <returns>An array of 8-bit unsigned integers that is equivalent to input, or <c>null</c> if no valid PEM header was found.</returns> |
| 50 | + public static Byte[]? FromBase64Header(String input, String headerName, Boolean skipHeaderValidation = false) { |
| 51 | + String header, footer; |
| 52 | + if (skipHeaderValidation) { |
| 53 | + header = "-----BEGIN "; |
| 54 | + footer = "-----END "; |
| 55 | + } else { |
| 56 | + header = $"-----BEGIN {headerName.Trim()}-----"; |
| 57 | + footer = $"-----END {headerName.Trim()}-----"; |
| 58 | + } |
| 59 | + // search for header (uppercase) start position |
| 60 | + Int32 start = input.IndexOf(header, StringComparison.Ordinal); |
| 61 | + if (start < 0) { |
| 62 | + // if we don't find header, then it is not valid PEM header. End here. |
25 | 63 | return null; |
26 | 64 | } |
27 | | - Int32 start = input.IndexOf(header, StringComparison.Ordinal) + 10; |
28 | | - Int32 headerEndPos = input.IndexOf("-----", start, StringComparison.Ordinal) + 5; |
29 | | - Int32 footerStartPos = input.IndexOf(footer, StringComparison.Ordinal); |
| 65 | + Int32 headerEndPos = skipHeaderValidation |
| 66 | + // 10 is the length of mandatory '-----BEGIN' header part. Seek after mandatory part and look where |
| 67 | + // terminating 5-dash sequence begins and append the length of 5-dash. This is the end of header and |
| 68 | + // where body begins. |
| 69 | + ? input.IndexOf("-----", start + 10, StringComparison.Ordinal) + 5 |
| 70 | + : start + header.Length; |
| 71 | + // search for footer starting with header end position. Empty |
| 72 | + Int32 footerStartPos = input.IndexOf(footer, headerEndPos, StringComparison.Ordinal); |
| 73 | + if (footerStartPos < 0) { |
| 74 | + // we found PEM header, but no PEM footer, or the order is wrong (footer defined before header) |
| 75 | + return null; |
| 76 | + } |
| 77 | + |
30 | 78 | try { |
31 | 79 | return Convert.FromBase64String(input.Substring(headerEndPos, footerStartPos - headerEndPos)); |
32 | 80 | } catch { |
33 | 81 | return null; |
34 | 82 | } |
35 | 83 | } |
36 | | - public static Byte[]? FromBase64Request(String input) { |
37 | | - String header; |
38 | | - String footer; |
39 | | - if (input.ToUpper().Contains(PemHeader.PEM_HEADER_REQ_NEW.GetHeader())) { |
40 | | - header = PemHeader.PEM_HEADER_REQ_NEW.GetHeader(); |
41 | | - footer = PemHeader.PEM_HEADER_REQ_NEW.GetFooter(); |
42 | | - } else if (input.ToUpper().Contains(PemHeader.PEM_HEADER_REQ.GetHeader())) { |
43 | | - header = PemHeader.PEM_HEADER_REQ.GetHeader(); |
44 | | - footer = PemHeader.PEM_HEADER_REQ.GetFooter(); |
45 | | - } else { |
46 | | - return null; |
47 | | - } |
48 | | - |
49 | | - return FromBase64Header(input, header, footer, true); |
50 | | - } |
| 84 | + /// <summary> |
| 85 | + /// Attempts to convert input string into a 8bit byte array. This method converts each character of input |
| 86 | + /// string to its 8bit numerical representation. |
| 87 | + /// </summary> |
| 88 | + /// <param name="input">String to convert.</param> |
| 89 | + /// <returns>Decoded byte array. This method returns <c>null</c> if input string contains non-8bit characters.</returns> |
51 | 90 | public static Byte[]? FromBinary(String input) { |
52 | 91 | Byte[] rawBytes = new Byte[input.Length]; |
53 | 92 | for (Int32 i = 0; i < input.Length; i++) { |
@@ -321,11 +360,29 @@ static class StringToBinaryFormatter { |
321 | 360 | return bytes.ToArray(); |
322 | 361 | } |
323 | 362 |
|
| 363 | + /// <summary> |
| 364 | + /// Attempts to convert the specified string, which encodes binary data as base64 digits with |
| 365 | + /// or without PEM header and footer, to an equivalent 8-bit unsigned integer array. |
| 366 | + /// </summary> |
| 367 | + /// <param name="input">Base64 formatted string with optional PEM header and footer.</param> |
| 368 | + /// <returns> |
| 369 | + /// An array of 8-bit unsigned integers that is equivalent to input, or <c>null</c> if input string doesn't represent |
| 370 | + /// valid Base64 or PEM formatted string. |
| 371 | + /// </returns> |
324 | 372 | public static Byte[]? FromBase64Any(String input) { |
325 | | - return FromBase64Header(input) ?? FromBase64(input); |
| 373 | + if (input.Contains("-----BEGIN ")) { |
| 374 | + // if input string contains PEM begin sequence, then try only Base64 with header and footer |
| 375 | + // without strict validation. |
| 376 | + return FromBase64Header(input, String.Empty, true); |
| 377 | + } |
| 378 | + // if there is no PEM found, then try raw base64. |
| 379 | + return FromBase64(input); |
326 | 380 | } |
327 | 381 | public static Byte[] FromStringAny(String input) { |
328 | | - return FromBase64Header(input) ?? FromBase64(input) ?? input.Select(Convert.ToByte).ToArray(); |
| 382 | + if (input.Contains("-----BEGIN ")) { |
| 383 | + return FromBase64Header(input, String.Empty, true); |
| 384 | + } |
| 385 | + return FromBase64(input) ?? FromBinary(input); |
329 | 386 | } |
330 | 387 | public static Byte[]? FromHexAny(String input) { |
331 | 388 | return FromHexAddr(input) ?? |
|
0 commit comments