A set of extension methods that make it easy to mask a string (to protect account numbers or other personal data). For example, you could mask a SSN of 123456789 to be ******789.
using System;
using System.Text;
/// <summary>
/// An enumeration of the types of masking styles for the Mask() extension method
/// of the string class.
/// </summary>
public enum MaskStyle
{
/// <summary>
/// Masks all characters within the masking region, regardless of type.
/// </summary>
All,
/// <summary>
/// Masks only alphabetic and numeric characters within the masking region.
/// </summary>
AlphaNumericOnly,
}
/// <summary>
/// Utility class for string manipulation.
/// </summary>
public static class StringExtensions
{
/// <summary>
/// Default masking character used in a mask.
/// </summary>
public static readonly char DefaultMaskCharacter = '*';
/// <summary>
/// Returns true if the string is non-null and at least the specified number of characters.
/// </summary>
/// <param name="value">The string to check.</param>
/// <param name="length">The minimum length.</param>
/// <returns>True if string is non-null and at least the length specified.</returns>
/// <exception>throws ArgumentOutOfRangeException if length is not a non-negative number.</exception>
public static bool IsLengthAtLeast(this string value, int length)
{
if (length < 0)
{
throw new ArgumentOutOfRangeException("length", length,
"The length must be a non-negative number.");
}
return value != null
? value.Length >= length
: false;
}
/// <summary>
/// Mask the source string with the mask char except for the last exposed digits.
/// </summary>
/// <param name="sourceValue">Original string to mask.</param>
/// <param name="maskChar">The character to use to mask the source.</param>
/// <param name="numExposed">Number of characters exposed in masked value.</param>
/// <param name="style">The masking style to use (all characters or just alpha-nums).</param>
/// <returns>The masked account number.</returns>
public static string Mask(this string sourceValue, char maskChar, int numExposed, MaskStyle style)
{
var maskedString = sourceValue;
if (sourceValue.IsLengthAtLeast(numExposed))
{
var builder = new StringBuilder(sourceValue.Length);
int index = maskedString.Length - numExposed;
if (style == MaskStyle.AlphaNumericOnly)
{
CreateAlphaNumMask(builder, sourceValue, maskChar, index);
}
else
{
builder.Append(maskChar, index);
}
builder.Append(sourceValue.Substring(index));
maskedString = builder.ToString();
}
return maskedString;
}
/// <summary>
/// Mask the source string with the mask char except for the last exposed digits.
/// </summary>
/// <param name="sourceValue">Original string to mask.</param>
/// <param name="maskChar">The character to use to mask the source.</param>
/// <param name="numExposed">Number of characters exposed in masked value.</param>
/// <returns>The masked account number.</returns>
public static string Mask(this string sourceValue, char maskChar, int numExposed)
{
return Mask(sourceValue, maskChar, numExposed, MaskStyle.All);
}
/// <summary>
/// Mask the source string with the mask char.
/// </summary>
/// <param name="sourceValue">Original string to mask.</param>
/// <param name="maskChar">The character to use to mask the source.</param>
/// <returns>The masked account number.</returns>
public static string Mask(this string sourceValue, char maskChar)
{
return Mask(sourceValue, maskChar, 0, MaskStyle.All);
}
/// <summary>
/// Mask the source string with the default mask char except for the last exposed digits.
/// </summary>
/// <param name="sourceValue">Original string to mask.</param>
/// <param name="numExposed">Number of characters exposed in masked value.</param>
/// <returns>The masked account number.</returns>
public static string Mask(this string sourceValue, int numExposed)
{
return Mask(sourceValue, DefaultMaskCharacter, numExposed, MaskStyle.All);
}
/// <summary>
/// Mask the source string with the default mask char.
/// </summary>
/// <param name="sourceValue">Original string to mask.</param>
/// <returns>The masked account number.</returns>
public static string Mask(this string sourceValue)
{
return Mask(sourceValue, DefaultMaskCharacter, 0, MaskStyle.All);
}
/// <summary>
/// Mask the source string with the mask char.
/// </summary>
/// <param name="sourceValue">Original string to mask.</param>
/// <param name="maskChar">The character to use to mask the source.</param>
/// <param name="style">The masking style to use (all characters or just alpha-nums).</param>
/// <returns>The masked account number.</returns>
public static string Mask(this string sourceValue, char maskChar, MaskStyle style)
{
return Mask(sourceValue, maskChar, 0, style);
}
/// <summary>
/// Mask the source string with the default mask char except for the last exposed digits.
/// </summary>
/// <param name="sourceValue">Original string to mask.</param>
/// <param name="numExposed">Number of characters exposed in masked value.</param>
/// <param name="style">The masking style to use (all characters or just alpha-nums).</param>
/// <returns>The masked account number.</returns>
public static string Mask(this string sourceValue, int numExposed, MaskStyle style)
{
return Mask(sourceValue, DefaultMaskCharacter, numExposed, style);
}
/// <summary>
/// Mask the source string with the default mask char.
/// </summary>
/// <param name="sourceValue">Original string to mask.</param>
/// <param name="style">The masking style to use (all characters or just alpha-nums).</param>
/// <returns>The masked account number.</returns>
public static string Mask(this string sourceValue, MaskStyle style)
{
return Mask(sourceValue, DefaultMaskCharacter, 0, style);
}
/// <summary>
/// Masks all characters for the specified length.
/// </summary>
/// <param name="buffer">String builder to store result in.</param>
/// <param name="source">The source string to pull non-alpha numeric characters.</param>
/// <param name="mask">Masking character to use.</param>
/// <param name="length">Length of the mask.</param>
private static void CreateAlphaNumMask(StringBuilder buffer, string source, char mask, int length)
{
for (int i = 0; i < length; i++)
{
buffer.Append(char.IsLetterOrDigit(source[i])
? mask
: source[i]);
}
}
}
//
// Example
//
var someInput = "123-45-6789"
var maskedId = someInput.Mask('X', 3);
// outputs: XXXXXXXX789
Console.WriteLine(maskedId);
var maskedWithDashes = someInput.Mask('*', 3, MaskStyle.AlphaNumericOnly);
// outputs: ***-**-*789
Console.WriteLine(maskedWithDashes);