Created
October 19, 2018 08:31
-
-
Save acid-chicken/e4b01c1b21a15f8e20e8499e017ad211 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Security.Cryptography; | |
| public static class PEM | |
| { | |
| const string | |
| _header = "-----BEGIN RSA PRIVATE KEY-----", | |
| _footer = "-----END RSA PRIVATE KEY-----"; | |
| public static RSAParameters ParseToRSAParameters(ReadOnlySpan<char> s) | |
| { | |
| var ss = s.IndexOf(_header, StringComparison.Ordinal) + _header.Length; | |
| var sl = s.IndexOf(_footer, StringComparison.Ordinal) - ss; | |
| Span<byte> key = stackalloc byte[sl << 1]; | |
| key = Convert.TryFromBase64Chars(s.Slice(ss, sl), key, out var written) ? | |
| key.Slice(0, written) : | |
| throw new FormatException($"{nameof(s)} is not a valid BASE64 string."); | |
| var cursor = 0; | |
| byte[] ReadParameter(ReadOnlySpan<byte> source) | |
| { | |
| var id = source[cursor++]; | |
| var tag = id & 0b_0001_1111; | |
| var lfr = source[cursor++]; | |
| var lfs = lfr & 0b_0111_1111; | |
| var lem = lfr == lfs; | |
| Span<byte> lsb = stackalloc byte[4]; | |
| if (lem) | |
| lsb[0] = lfr; | |
| else | |
| for (var i = --lfs; i >= 0; i--) | |
| lsb[i] = source[cursor++]; | |
| var length = BitConverter.ToInt32(lsb); | |
| switch (tag) | |
| { | |
| case 0b_0000_0010: | |
| case 0b_0000_0100: | |
| break; | |
| case 0b_0000_0011: | |
| var first = source[cursor++]; | |
| if (first != 0b_0000_0000) | |
| throw new NotSupportedException("The feature of specifing unused bits is not supported."); | |
| length--; | |
| break; | |
| case 0b_0000_0101: | |
| case 0b_0001_0000: | |
| return Array.Empty<byte>(); | |
| default: | |
| throw new NotSupportedException($"Tag #{tag} is not supported."); | |
| } | |
| var content = source.Slice(cursor, length); | |
| cursor += length; | |
| return content.ToArray(); | |
| } | |
| ReadParameter(key); | |
| var version = | |
| ReadParameter(key); | |
| if (version.Length != 1 || version[0] != 0) | |
| throw new NotSupportedException($"Version {version} is not supported."); | |
| return new RSAParameters | |
| { | |
| Modulus = ReadParameter(key), | |
| Exponent = ReadParameter(key), | |
| D = ReadParameter(key), | |
| P = ReadParameter(key), | |
| Q = ReadParameter(key), | |
| DP = ReadParameter(key), | |
| DQ = ReadParameter(key), | |
| InverseQ = ReadParameter(key) | |
| }; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using Xunit; | |
| public class PEMTest | |
| { | |
| [Fact] | |
| public void ParseToRSAParametersTest() | |
| { | |
| var separators = new [] { ':', '\n' }; | |
| IEnumerable<string> L(byte[] source) => | |
| source.Select(x => x.ToString("x2")); | |
| IEnumerable<string> R(string source) => | |
| source.Split(separators, StringSplitOptions.RemoveEmptyEntries); | |
| var key = @" | |
| -----BEGIN RSA PRIVATE KEY----- | |
| MIIEogIBAAKCAQEAvy2lvWbzv/MGBkIaCQ6bwpzzyP92xYT7bzjodk9OhfCspKiM | |
| hF4HHOwMQvyIQ94h+Si03mHtePnsKDbtxwegxWkZ9sed+OEXiwNb0SywXoiGsys5 | |
| Nsqv/VudjumOVjpVd65NcrHLq30XIuHy33jEZn7qn+Zo6XAFx1c7Pxr6OHK1D+38 | |
| Qb7tnvIEXYeeeGACnyyiLeGdGXIoXAKgWMDdSJufHRjuBktqEtW5iWd3tChHJl0D | |
| ejh4UEjKIk/SVC6mOaYmJE4OJaBKbyBwsuiyHs5CcZfOM7xh1e5i/amMamFqpzbr | |
| XR15FnyDy7WAz/6F9XdClOzj+Irn9htk3qi8CwIDAQABAoIBAFQUaFs3ZyZZZKHl | |
| +ntXQGvECXex2vOdu9M7rQkzce54XgWA12Pz0p8GtZHUbL2keT6Sh5FycjWNfS5m | |
| kgbBtRR9V9zwB+sIXAlYbc4+IEdDNjKgZOZTGDmOTGopD9+egi5Dq24xAcknF8DQ | |
| rLdZ7s7BLMEsXaGlEfWMyNLFM50VGipvuTIwCIkmNxagW4RMAThMfTYuDXuambdS | |
| ChZoFubUnn3aUSna0E2QusCDr+Q2REuNF+DHnLkBCjdjhyUycYa430f7Vad54lVN | |
| +rOUXtnFq43STbwOdfIZAD2KQL8eK22ej3kDtZwev8O00rECLIcvn1rJwaEuww6E | |
| eMDvZDECgYEA9Tf8p/fNU7JOPiLHaX0hEy+7JGJfQ6lEdLFDHZxkU0h5q75pMztZ | |
| vLZ5RjBf1gMCrAFtuDoMMwXBoBRono8nhD+O8Qi/ADNcn+ZHxlXhI2RNuwGesqZW | |
| +khMfIdX6RFMtwDrpTGQOFDKRqmFuzafwfqAIgV+EM/pVMS+w46VU1MCgYEAx5Vr | |
| RUwSxvih46gCvT15BsqwR/lmKVruuh/vydt0f9luFSJfMsH0tFjutpJpQ/1sH3wS | |
| Ej1TjyxeFYHEt+XOKtboBLiOjSu+Few0R/mLPDcivAEe4sbVPDSDUTUqorSHUQTk | |
| 5tYyMz8tonY/Ye3YnDIREOy3qSX7HDf7o3VlVWkCgYBwExudpUspwqeyDHE5jGAO | |
| hdUxhuhlYzqPXuj+4piT298IGKm6KZkVAA0jgD588LlK5ghAl/81Xp8lS86ZEXKN | |
| JgNttIKfU9o0lqodQuj4JQLFwrLGkfHUyDB1BeKu+iImzfvlb2ar5njcnOQrMYcI | |
| wDXJ1trMUkohXR6XAFbNUwKBgBpvz2LBfec/Pepy8dHxV5uvs4QFJCQsOF0NJ+0c | |
| FaVtvqgsAmIt0OUmtpAWer0Xz3+oJpil6PCZFulQZCdb2GBSUS925uMKPUaYICC8 | |
| jFXwk7hFibrOTaaI6jASk9Azi40O0edFziZ9ouTXNvQY1k1yUFJmmLleH5IQVFPF | |
| lCOpAoGAXGvdcTvDzB1n9fq6bN6vfzUTezSZNClFhd//18IG32qSwnf2Z6CTdjLG | |
| eqRxURb5LSR7Q0T4PYBLR0zEEadysFvEoJqH4nQJohmpQIeEadf7zwl5TIDhPrbo | |
| qgrHJTtB832Hl0mhIAQNVIed2+QTH/SJReyRTW7/J5Lht9tjQlo= | |
| -----END RSA PRIVATE KEY-----".AsSpan(1); | |
| var rsa = PEM.ParseToRSAParameters(key); | |
| Assert.Equal(L(rsa.Modulus), R(@" | |
| 00:bf:2d:a5:bd:66:f3:bf:f3:06:06:42:1a:09:0e: | |
| 9b:c2:9c:f3:c8:ff:76:c5:84:fb:6f:38:e8:76:4f: | |
| 4e:85:f0:ac:a4:a8:8c:84:5e:07:1c:ec:0c:42:fc: | |
| 88:43:de:21:f9:28:b4:de:61:ed:78:f9:ec:28:36: | |
| ed:c7:07:a0:c5:69:19:f6:c7:9d:f8:e1:17:8b:03: | |
| 5b:d1:2c:b0:5e:88:86:b3:2b:39:36:ca:af:fd:5b: | |
| 9d:8e:e9:8e:56:3a:55:77:ae:4d:72:b1:cb:ab:7d: | |
| 17:22:e1:f2:df:78:c4:66:7e:ea:9f:e6:68:e9:70: | |
| 05:c7:57:3b:3f:1a:fa:38:72:b5:0f:ed:fc:41:be: | |
| ed:9e:f2:04:5d:87:9e:78:60:02:9f:2c:a2:2d:e1: | |
| 9d:19:72:28:5c:02:a0:58:c0:dd:48:9b:9f:1d:18: | |
| ee:06:4b:6a:12:d5:b9:89:67:77:b4:28:47:26:5d: | |
| 03:7a:38:78:50:48:ca:22:4f:d2:54:2e:a6:39:a6: | |
| 26:24:4e:0e:25:a0:4a:6f:20:70:b2:e8:b2:1e:ce: | |
| 42:71:97:ce:33:bc:61:d5:ee:62:fd:a9:8c:6a:61: | |
| 6a:a7:36:eb:5d:1d:79:16:7c:83:cb:b5:80:cf:fe: | |
| 85:f5:77:42:94:ec:e3:f8:8a:e7:f6:1b:64:de:a8: | |
| bc:0b")); | |
| Assert.Equal(L(rsa.Exponent), R(@" | |
| 01:00:01")); | |
| Assert.Equal(L(rsa.D), R(@" | |
| 54:14:68:5b:37:67:26:59:64:a1:e5:fa:7b:57:40: | |
| 6b:c4:09:77:b1:da:f3:9d:bb:d3:3b:ad:09:33:71: | |
| ee:78:5e:05:80:d7:63:f3:d2:9f:06:b5:91:d4:6c: | |
| bd:a4:79:3e:92:87:91:72:72:35:8d:7d:2e:66:92: | |
| 06:c1:b5:14:7d:57:dc:f0:07:eb:08:5c:09:58:6d: | |
| ce:3e:20:47:43:36:32:a0:64:e6:53:18:39:8e:4c: | |
| 6a:29:0f:df:9e:82:2e:43:ab:6e:31:01:c9:27:17: | |
| c0:d0:ac:b7:59:ee:ce:c1:2c:c1:2c:5d:a1:a5:11: | |
| f5:8c:c8:d2:c5:33:9d:15:1a:2a:6f:b9:32:30:08: | |
| 89:26:37:16:a0:5b:84:4c:01:38:4c:7d:36:2e:0d: | |
| 7b:9a:99:b7:52:0a:16:68:16:e6:d4:9e:7d:da:51: | |
| 29:da:d0:4d:90:ba:c0:83:af:e4:36:44:4b:8d:17: | |
| e0:c7:9c:b9:01:0a:37:63:87:25:32:71:86:b8:df: | |
| 47:fb:55:a7:79:e2:55:4d:fa:b3:94:5e:d9:c5:ab: | |
| 8d:d2:4d:bc:0e:75:f2:19:00:3d:8a:40:bf:1e:2b: | |
| 6d:9e:8f:79:03:b5:9c:1e:bf:c3:b4:d2:b1:02:2c: | |
| 87:2f:9f:5a:c9:c1:a1:2e:c3:0e:84:78:c0:ef:64: | |
| 31")); | |
| Assert.Equal(L(rsa.P), R(@" | |
| 00:f5:37:fc:a7:f7:cd:53:b2:4e:3e:22:c7:69:7d: | |
| 21:13:2f:bb:24:62:5f:43:a9:44:74:b1:43:1d:9c: | |
| 64:53:48:79:ab:be:69:33:3b:59:bc:b6:79:46:30: | |
| 5f:d6:03:02:ac:01:6d:b8:3a:0c:33:05:c1:a0:14: | |
| 68:9e:8f:27:84:3f:8e:f1:08:bf:00:33:5c:9f:e6: | |
| 47:c6:55:e1:23:64:4d:bb:01:9e:b2:a6:56:fa:48: | |
| 4c:7c:87:57:e9:11:4c:b7:00:eb:a5:31:90:38:50: | |
| ca:46:a9:85:bb:36:9f:c1:fa:80:22:05:7e:10:cf: | |
| e9:54:c4:be:c3:8e:95:53:53")); | |
| Assert.Equal(L(rsa.Q), R(@" | |
| 00:c7:95:6b:45:4c:12:c6:f8:a1:e3:a8:02:bd:3d: | |
| 79:06:ca:b0:47:f9:66:29:5a:ee:ba:1f:ef:c9:db: | |
| 74:7f:d9:6e:15:22:5f:32:c1:f4:b4:58:ee:b6:92: | |
| 69:43:fd:6c:1f:7c:12:12:3d:53:8f:2c:5e:15:81: | |
| c4:b7:e5:ce:2a:d6:e8:04:b8:8e:8d:2b:be:15:ec: | |
| 34:47:f9:8b:3c:37:22:bc:01:1e:e2:c6:d5:3c:34: | |
| 83:51:35:2a:a2:b4:87:51:04:e4:e6:d6:32:33:3f: | |
| 2d:a2:76:3f:61:ed:d8:9c:32:11:10:ec:b7:a9:25: | |
| fb:1c:37:fb:a3:75:65:55:69")); | |
| Assert.Equal(L(rsa.DP), R(@" | |
| 70:13:1b:9d:a5:4b:29:c2:a7:b2:0c:71:39:8c:60: | |
| 0e:85:d5:31:86:e8:65:63:3a:8f:5e:e8:fe:e2:98: | |
| 93:db:df:08:18:a9:ba:29:99:15:00:0d:23:80:3e: | |
| 7c:f0:b9:4a:e6:08:40:97:ff:35:5e:9f:25:4b:ce: | |
| 99:11:72:8d:26:03:6d:b4:82:9f:53:da:34:96:aa: | |
| 1d:42:e8:f8:25:02:c5:c2:b2:c6:91:f1:d4:c8:30: | |
| 75:05:e2:ae:fa:22:26:cd:fb:e5:6f:66:ab:e6:78: | |
| dc:9c:e4:2b:31:87:08:c0:35:c9:d6:da:cc:52:4a: | |
| 21:5d:1e:97:00:56:cd:53")); | |
| Assert.Equal(L(rsa.DQ), R(@" | |
| 1a:6f:cf:62:c1:7d:e7:3f:3d:ea:72:f1:d1:f1:57: | |
| 9b:af:b3:84:05:24:24:2c:38:5d:0d:27:ed:1c:15: | |
| a5:6d:be:a8:2c:02:62:2d:d0:e5:26:b6:90:16:7a: | |
| bd:17:cf:7f:a8:26:98:a5:e8:f0:99:16:e9:50:64: | |
| 27:5b:d8:60:52:51:2f:76:e6:e3:0a:3d:46:98:20: | |
| 20:bc:8c:55:f0:93:b8:45:89:ba:ce:4d:a6:88:ea: | |
| 30:12:93:d0:33:8b:8d:0e:d1:e7:45:ce:26:7d:a2: | |
| e4:d7:36:f4:18:d6:4d:72:50:52:66:98:b9:5e:1f: | |
| 92:10:54:53:c5:94:23:a9")); | |
| Assert.Equal(L(rsa.InverseQ), R(@" | |
| 5c:6b:dd:71:3b:c3:cc:1d:67:f5:fa:ba:6c:de:af: | |
| 7f:35:13:7b:34:99:34:29:45:85:df:ff:d7:c2:06: | |
| df:6a:92:c2:77:f6:67:a0:93:76:32:c6:7a:a4:71: | |
| 51:16:f9:2d:24:7b:43:44:f8:3d:80:4b:47:4c:c4: | |
| 11:a7:72:b0:5b:c4:a0:9a:87:e2:74:09:a2:19:a9: | |
| 40:87:84:69:d7:fb:cf:09:79:4c:80:e1:3e:b6:e8: | |
| aa:0a:c7:25:3b:41:f3:7d:87:97:49:a1:20:04:0d: | |
| 54:87:9d:db:e4:13:1f:f4:89:45:ec:91:4d:6e:ff: | |
| 27:92:e1:b7:db:63:42:5a")); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment