NanoXLSX.Core 3.0.0-rc.3
Loading...
Searching...
No Matches
LegacyPassword.cs
1/*
2 * NanoXLSX is a small .NET library to generate and read XLSX (Microsoft Excel 2007 or newer) files in an easy and native way
3 * Copyright Raphael Stoeckli © 2025
4 * This library is licensed under the MIT License.
5 * You find a copy of the license in project folder or on: http://opensource.org/licenses/MIT
6 */
7
8using System;
9using System.Collections.Generic;
10using System.Runtime.InteropServices;
11using System.Security;
13using NanoXLSX.Utils;
14
15namespace NanoXLSX
16{
21 {
36
37 private SecureString password;
38
42 public PasswordType Type { get; set; }
43
48 public string PasswordHash { get; set; }
49
55 {
56 this.Type = type;
57 this.PasswordHash = null;
58 }
59
64 public string GetPassword()
65 {
66 if (password != null && password.Length > 0)
67 {
68 return GetPasswordOfSecureString(password);
69 }
70 return null;
71 }
72
77 public void SetPassword(string plainText)
78 {
79 if (string.IsNullOrEmpty(plainText))
80 {
82 return;
83 }
84 else
85 {
86 password = GetSecureString(plainText);
88 }
89 }
90
94 public void UnsetPassword()
95 {
96 if (password != null)
97 {
98 password.Clear();
99 }
100 PasswordHash = null;
101 }
102
107 public bool PasswordIsSet()
108 {
109 return !string.IsNullOrEmpty(PasswordHash);
110 }
111
116 public void CopyFrom(IPassword passwordInstance)
117 {
118 this.PasswordHash = passwordInstance.PasswordHash;
119 this.password = GetSecureString(passwordInstance.GetPassword());
120 if (this.GetType() == passwordInstance.GetType())
121 {
122 this.Type = ((LegacyPassword)passwordInstance).Type;
123 }
124 }
125
132 public static string GenerateLegacyPasswordHash(string password)
133 {
134 if (string.IsNullOrEmpty(password)) { return string.Empty; }
135 int passwordLength = password.Length;
136 int passwordHash = 0;
137 char character;
138 for (int i = passwordLength; i > 0; i--)
139 {
140 character = password[i - 1];
141 passwordHash = ((passwordHash >> 14) & 0x01) | ((passwordHash << 1) & 0x7fff);
142 passwordHash ^= character;
143 }
144 passwordHash = ((passwordHash >> 14) & 0x01) | ((passwordHash << 1) & 0x7fff);
145 passwordHash ^= (0x8000 | ('N' << 8) | 'K');
146 passwordHash ^= passwordLength;
147 return passwordHash.ToString("X", ParserUtils.InvariantCulture);
148 }
149
155 private static SecureString GetSecureString(string plaintextPassword)
156 {
157 char[] chars;
158 if (string.IsNullOrEmpty(plaintextPassword))
159 {
160#pragma warning disable CA1825 // Suppress: 0-length array allocation (suggestion is not .Net 5.0 compatible)
161 chars = new char[0];
162#pragma warning restore CA1825 // Suppress: 0-length array allocation
163 }
164 else
165 {
166 chars = plaintextPassword.ToCharArray();
167 }
168 SecureString str = new SecureString();
169 foreach (char c in chars)
170 {
171 str.AppendChar(c);
172 }
173 return str;
174 }
175
181 private static string GetPasswordOfSecureString(SecureString secureString)
182 {
183 IntPtr unmanagedString = IntPtr.Zero;
184 try
185 {
186 unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureString);
187 return Marshal.PtrToStringUni(unmanagedString);
188 }
189 finally
190 {
191 Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
192 }
193 }
194
200 public override bool Equals(object obj)
201 {
203 return obj is LegacyPassword password &&
204 Comparators.CompareSecureStrings(this.password, password.password) &&
205 Type == password.Type &&
206 PasswordHash == password.PasswordHash;
207 }
208
213 public override int GetHashCode()
214 {
215 // The actual password is not considered since its hash is sufficient
216 unchecked
217 {
218 var hashCode = 1034998357;
219 hashCode = hashCode * -1521134295 + Type.GetHashCode();
220 hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(PasswordHash);
221 hashCode = hashCode * -1521134295 + PasswordIsSet().GetHashCode();
222 return hashCode;
223 }
224 }
225 }
226}
override int GetHashCode()
Gets the hash code of the password instance. Note that this is not the actual password hash.
string GetPassword()
Gets the pain text password.
override bool Equals(object obj)
Returns whether two instances are the same.
void UnsetPassword()
Removes the password form the current instance.
PasswordType Type
Current target type of the password instance.
void SetPassword(string plainText)
Sets the current password and calculates the hash.
LegacyPassword(PasswordType type)
Default constructor with parameter.
void CopyFrom(IPassword passwordInstance)
Copes all data from another class instance.
string PasswordHash
Gets or sets the hashed password.
static string GenerateLegacyPasswordHash(string password)
Method to generate a legacy (Excel internal) password hash, to protect workbooks or worksheets This m...
PasswordType
Target type of the password.
@ WorksheetProtection
Password is used to protect a worksheet.
@ WorkbookProtection
Password is used to protect a workbook.
bool PasswordIsSet()
Gets whether a password was set.
Class providing general comparator methods.
static bool CompareSecureStrings(SecureString value1, SecureString value2)
Compares whether the content of two SecureString instances are equal. The comparison method tries to ...
Class providing static methods to parse string values to specific types or to print object as languag...
static readonly CultureInfo InvariantCulture
Constant for number conversion. The invariant culture (represents mostly the US numbering scheme) ens...
Interface to represent a protection password, either for workbooks or worksheets. The implementations...
Definition IPassword.cs:14
string PasswordHash
Gets or sets the password hash.
Definition IPassword.cs:18
string GetPassword()
Gets the password as plain text.