| | | 1 | | using System.Runtime.InteropServices; |
| | | 2 | | using System.Security; |
| | | 3 | | using System.Security.Cryptography; |
| | | 4 | | using System.Text; |
| | | 5 | | |
| | | 6 | | namespace LOCKnet.Core.Crypto; |
| | | 7 | | |
| | | 8 | | /// <summary> |
| | | 9 | | /// Implementierung von <see cref="ISecureStringService"/>. |
| | | 10 | | /// Alle Konvertierungen pinnen den Speicher so kurz wie möglich. |
| | | 11 | | /// </summary> |
| | | 12 | | public sealed class SecureStringService : ISecureStringService |
| | | 13 | | { |
| | | 14 | | /// <inheritdoc/> |
| | | 15 | | public byte[] ToByteArray(SecureString secureString) |
| | 141 | 16 | | { |
| | 141 | 17 | | ArgumentNullException.ThrowIfNull(secureString); |
| | 140 | 18 | | if (secureString.Length == 0) |
| | 1 | 19 | | return []; |
| | | 20 | | |
| | | 21 | | // SecureString intern als UTF-16 — wir holen den Pointer |
| | | 22 | | // und konvertieren direkt, ohne einen verwalteten String zu erzeugen. |
| | 139 | 23 | | var ptr = IntPtr.Zero; |
| | | 24 | | try |
| | 139 | 25 | | { |
| | 139 | 26 | | ptr = Marshal.SecureStringToGlobalAllocUnicode(secureString); |
| | | 27 | | |
| | | 28 | | // Länge in UTF-16-Zeichen ist secureString.Length |
| | 139 | 29 | | var utf16 = new char[secureString.Length]; |
| | 139 | 30 | | Marshal.Copy(ptr, utf16, 0, utf16.Length); |
| | | 31 | | |
| | | 32 | | // UTF-16 → UTF-8 (was die Krypto-Layer erwartet) |
| | 139 | 33 | | var bytes = Encoding.UTF8.GetBytes(utf16); |
| | | 34 | | |
| | | 35 | | // char-Array sofort überschreiben |
| | 139 | 36 | | Array.Clear(utf16, 0, utf16.Length); |
| | | 37 | | |
| | 139 | 38 | | return bytes; |
| | | 39 | | } |
| | | 40 | | finally |
| | 139 | 41 | | { |
| | 139 | 42 | | if (ptr != IntPtr.Zero) |
| | 139 | 43 | | Marshal.ZeroFreeGlobalAllocUnicode(ptr); |
| | 139 | 44 | | } |
| | 140 | 45 | | } |
| | | 46 | | |
| | | 47 | | /// <inheritdoc/> |
| | | 48 | | public void ZeroMemory(byte[] data) |
| | 205 | 49 | | { |
| | 205 | 50 | | if (data is { Length: > 0 }) |
| | 203 | 51 | | CryptographicOperations.ZeroMemory(data); |
| | 205 | 52 | | } |
| | | 53 | | |
| | | 54 | | /// <inheritdoc/> |
| | | 55 | | public SecureString FromByteArray(byte[] data) |
| | 7 | 56 | | { |
| | 7 | 57 | | ArgumentNullException.ThrowIfNull(data); |
| | | 58 | | |
| | 6 | 59 | | var secure = new SecureString(); |
| | | 60 | | try |
| | 6 | 61 | | { |
| | 6 | 62 | | var chars = Encoding.UTF8.GetChars(data); |
| | | 63 | | try |
| | 6 | 64 | | { |
| | 148 | 65 | | foreach (var c in chars) |
| | 65 | 66 | | secure.AppendChar(c); |
| | 6 | 67 | | } |
| | | 68 | | finally |
| | 6 | 69 | | { |
| | 6 | 70 | | Array.Clear(chars, 0, chars.Length); |
| | 6 | 71 | | } |
| | | 72 | | |
| | 6 | 73 | | secure.MakeReadOnly(); |
| | 6 | 74 | | return secure; |
| | | 75 | | } |
| | 0 | 76 | | catch |
| | 0 | 77 | | { |
| | 0 | 78 | | secure.Dispose(); |
| | 0 | 79 | | throw; |
| | | 80 | | } |
| | | 81 | | finally |
| | 6 | 82 | | { |
| | 6 | 83 | | ZeroMemory(data); |
| | 6 | 84 | | } |
| | 6 | 85 | | } |
| | | 86 | | } |