init
This commit is contained in:
commit
e124a47765
19374 changed files with 9806149 additions and 0 deletions
63
Kreta.Core/Logic/BankszamlaLogic.cs
Normal file
63
Kreta.Core/Logic/BankszamlaLogic.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Kreta.Resources;
|
||||
|
||||
namespace Kreta.Core.Logic
|
||||
{
|
||||
public static class BankszamlaLogic
|
||||
{
|
||||
private static readonly int[] s_sulyok = new int[] { 9, 7, 3, 1, 9, 7, 3, 1 };
|
||||
|
||||
public static string KitoltottsegValidacio(string bankszamlaSzam, int? bankszamlaTulajdonosTipusId, string bankszamlaTulajdonosNeve)
|
||||
{
|
||||
var parList = new List<string>()
|
||||
{
|
||||
bankszamlaSzam,
|
||||
bankszamlaTulajdonosTipusId.ToString(),
|
||||
bankszamlaTulajdonosNeve,
|
||||
};
|
||||
if ((parList.All(x => !string.IsNullOrWhiteSpace(x))) || (parList.All(x => string.IsNullOrWhiteSpace(x))))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return ErrorResource.BankszamlaAdatokKitoltottsegeHibas;
|
||||
}
|
||||
|
||||
public static string CDVValidacio(string bankszamlaSzam)
|
||||
{
|
||||
var bankszamlaNonSpace = bankszamlaSzam.Replace(" ", "");
|
||||
var komponensek = bankszamlaNonSpace.Split("-".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
||||
if ((!komponensek.Skip(1).Any()) || (komponensek.Skip(3).Any()) || komponensek.Any(x => x.Length < 8))
|
||||
{
|
||||
return ErrorResource.NemMegfeleloSzamuSzamjegyuSzamlaszam;
|
||||
}
|
||||
|
||||
var replBankszamlaSzam = bankszamlaNonSpace.Replace("-", "").Select(x => int.Parse(x.ToString())).ToList();
|
||||
int szumProd = replBankszamlaSzam.Take(8).Select((n, i) => n * s_sulyok[i]).Sum();
|
||||
if ((szumProd % 10) != 0)
|
||||
{
|
||||
return ErrorResource.NemMegfeleloFormatumuElsoOktetSzamlaszamban;
|
||||
}
|
||||
szumProd = replBankszamlaSzam.Select((n, i) => n * s_sulyok[i % 8]).Sum();
|
||||
if ((szumProd % 10) != 0)
|
||||
{
|
||||
return string.Format(ErrorResource.NemMegfeleloFormatumXOktetSzamlaszamban, komponensek.Length);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<string> StringSplitByChunkSize(string str, int chunkSize)
|
||||
{
|
||||
return Enumerable.Range(0, str.Length / chunkSize)
|
||||
.Select(i => str.Substring(i * chunkSize, chunkSize)).ToList();
|
||||
}
|
||||
|
||||
public static bool IsBankAccountNumberValid(string bankAccountNumber)
|
||||
{
|
||||
var bankAccountNumberRegex = new Regex(Constants.General.BankAccountNumberRegexPattern);
|
||||
return bankAccountNumberRegex.IsMatch(bankAccountNumber);
|
||||
}
|
||||
}
|
||||
}
|
13
Kreta.Core/Logic/ConfigurationLogic.cs
Normal file
13
Kreta.Core/Logic/ConfigurationLogic.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System.Configuration;
|
||||
|
||||
namespace Kreta.Core.Logic
|
||||
{
|
||||
public static class ConfigurationLogic
|
||||
{
|
||||
public static T GetConfigurationSection<T>(string sectionName) where T : ConfigurationSection
|
||||
{
|
||||
T result = (T)ConfigurationManager.GetSection(sectionName);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
134
Kreta.Core/Logic/DaoLogic.cs
Normal file
134
Kreta.Core/Logic/DaoLogic.cs
Normal file
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Kreta.Core.CustomAttributes;
|
||||
using Kreta.Core.Enum;
|
||||
using Kreta.Core.Exceptions;
|
||||
|
||||
namespace Kreta.Core.Logic
|
||||
{
|
||||
/// <summary>
|
||||
/// Author: Kovács Kornél (DevKornél) Created On: 2019.07.
|
||||
/// </summary>
|
||||
public static class DaoLogic
|
||||
{
|
||||
public static object BoolStringToBool(this object value)
|
||||
{
|
||||
if (Equals(value, "T"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Equals(value, "F"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new BlException(Enum.BlExceptionType.None);
|
||||
}
|
||||
|
||||
public static List<TDao> ToDaoList<TDao>(this DataSet dataSet) where TDao : class, new()
|
||||
{
|
||||
return dataSet.Tables[0].ToDaoList<TDao>();
|
||||
}
|
||||
|
||||
public static List<TDao> ToDaoList<TDao>(this DataTable dataTable) where TDao : class, new()
|
||||
{
|
||||
var response = new List<TDao>();
|
||||
|
||||
foreach (DataRow dataRow in dataTable.Rows)
|
||||
{
|
||||
response.Add(dataRow.ToDao<TDao>());
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public static HashSet<TDao> ToDaoHashSet<TDao>(this DataSet dataSet) where TDao : class, new()
|
||||
{
|
||||
return dataSet.Tables[0].ToDaoHashSet<TDao>();
|
||||
}
|
||||
|
||||
public static HashSet<TDao> ToDaoHashSet<TDao>(this DataTable dataTable) where TDao : class, new()
|
||||
{
|
||||
var response = new HashSet<TDao>();
|
||||
|
||||
foreach (DataRow dataRow in dataTable.Rows)
|
||||
{
|
||||
if (!response.Add(dataRow.ToDao<TDao>()))
|
||||
{
|
||||
throw new BlException(BlExceptionType.DuplikaltKulcs);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
public static TDao ToDao<TDao>(this DataRow dataRow) where TDao : class, new()
|
||||
{
|
||||
PropertyInfo[] properties = typeof(TDao).GetProperties();
|
||||
var dao = new TDao();
|
||||
|
||||
// Case sensitivity miatt van kiszedve külön (A Table.Columns.Contains() nem case-sensitive).
|
||||
IEnumerable<string> dataTableColumns = dataRow.Table.Columns.Cast<DataColumn>().Select(c => c.ColumnName);
|
||||
|
||||
foreach (PropertyInfo property in properties)
|
||||
{
|
||||
if (property.GetCustomAttribute<IgnoreAttribute>() == null)
|
||||
{
|
||||
string[] columnNames = property.GetCustomAttribute<ColumnNameAttribute>()?.ColumnName ?? new string[] { property.Name };
|
||||
if (dataTableColumns.Intersect(columnNames).Any())
|
||||
{
|
||||
foreach (string columnName in columnNames)
|
||||
{
|
||||
if (dataTableColumns.Contains(columnName))
|
||||
{
|
||||
var propertyValue = ParsePropertyValue(dataRow[columnName], property.PropertyType);
|
||||
if (Nullable.GetUnderlyingType(property.PropertyType) == null && propertyValue == null && property.PropertyType != typeof(string))
|
||||
{
|
||||
throw new BlException(BlExceptionType.ValtozoErtekeNemLehetNull);
|
||||
}
|
||||
|
||||
var getterSetter = property.GetSetMethod();
|
||||
getterSetter.Invoke(dao, new[] { propertyValue });
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BlException(BlExceptionType.ElvartErtekNemTalalhato);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dao;
|
||||
}
|
||||
|
||||
private static object ParsePropertyValue(object dataRowValue, Type propertyType)
|
||||
{
|
||||
if (dataRowValue == DBNull.Value)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Nullable.GetUnderlyingType(propertyType) != null)
|
||||
{
|
||||
if (dataRowValue == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
propertyType = propertyType.GenericTypeArguments[0];
|
||||
}
|
||||
|
||||
if (propertyType == typeof(bool))
|
||||
{
|
||||
return Logic.DaoLogic.BoolStringToBool(dataRowValue);
|
||||
}
|
||||
|
||||
return Convert.ChangeType(dataRowValue, propertyType);
|
||||
}
|
||||
}
|
||||
}
|
75
Kreta.Core/Logic/SqlLogic.cs
Normal file
75
Kreta.Core/Logic/SqlLogic.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using Kreta.Core.Enum;
|
||||
using Kreta.Core.Exceptions;
|
||||
|
||||
namespace Kreta.Core.Logic
|
||||
{
|
||||
/// <summary>
|
||||
/// Author: Kovács Kornél (DevKornél) Created On: 2019.09.
|
||||
/// </summary>
|
||||
public static class SqlLogic
|
||||
{
|
||||
public static object ParseListToParameter<T>(ICollection<T> list)
|
||||
{
|
||||
object response;
|
||||
if (list == null)
|
||||
{
|
||||
response = DBNull.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (list.Count < 1)
|
||||
{
|
||||
throw new BlException(BlExceptionType.ListaNemTartalmazElemet);
|
||||
}
|
||||
response = CleanSqlParameter(string.Join(Constants.General.VesszoSeparator, list));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A paraméterben kapott collection elemeit egy sql-template-be menti bele, például: "...NOT IN ({0})"
|
||||
/// </summary>
|
||||
public static string ParseListIntoSqlQuery<T>(string sqlTemplate, ICollection<T> list)
|
||||
{
|
||||
string response;
|
||||
if (list == null)
|
||||
{
|
||||
return sqlTemplate;
|
||||
}
|
||||
|
||||
if (list.Count < 1)
|
||||
{
|
||||
throw new BlException(BlExceptionType.ListaNemTartalmazElemet);
|
||||
}
|
||||
response = CleanSqlParameter(string.Join(Constants.General.VesszoSeparator, list));
|
||||
return string.Format(sqlTemplate, response);
|
||||
}
|
||||
|
||||
public static string CleanSqlParameter(string parameter)
|
||||
{
|
||||
string[] blacklistCharacters = new string[] { "--", ";", "/*", "*/", "@@", "@" };
|
||||
string[] blacklistWords = new string[] {
|
||||
"CHAR", "NCHAR", "VARCHAR", "NVARCHAR", "ALTER", "BEGIN", "CAST",
|
||||
"CREATE", "CURSOR", "DECLARE", "DELETE", "DROP", "END", "EXEC",
|
||||
"EXECUTE", "FETCH", "INSERT", "KILL", "OPEN", "SELECT", "SYS",
|
||||
"SYSOBJECTS", "SYSCOLUMNS", "TABLE", "UPDATE"
|
||||
};
|
||||
|
||||
string result = parameter;
|
||||
|
||||
foreach (var blacklistItem in blacklistCharacters)
|
||||
{
|
||||
result = result.Replace(blacklistItem, "");
|
||||
}
|
||||
foreach (var blacklistItem in blacklistWords)
|
||||
{
|
||||
result = Regex.Replace(result, blacklistItem, "", RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
return result.Trim();
|
||||
}
|
||||
}
|
||||
}
|
546
Kreta.Core/Logic/UrlLogic.cs
Normal file
546
Kreta.Core/Logic/UrlLogic.cs
Normal file
|
@ -0,0 +1,546 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using Jose;
|
||||
using Kreta.Resources;
|
||||
|
||||
namespace Kreta.Core.Logic
|
||||
{
|
||||
public static class UrlLogic
|
||||
{
|
||||
public static string Encrypt(string token, string key)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
throw new ArgumentException(nameof(token));
|
||||
}
|
||||
|
||||
byte[] encryptedData;
|
||||
|
||||
using (SymmetricAlgorithm symmetricAlgorithm = Aes.Create())
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
ICryptoTransform encryptor = symmetricAlgorithm.CreateEncryptor(Encoding.UTF8.GetBytes(key), new byte[16]);
|
||||
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
|
||||
{
|
||||
using (var streamWriter = new StreamWriter(cryptoStream))
|
||||
{
|
||||
streamWriter.Write(token);
|
||||
}
|
||||
|
||||
encryptedData = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string result = WebEncoders.Base64UrlEncode(encryptedData);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string Decrypt(string encryptData, string key)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(encryptData))
|
||||
{
|
||||
throw new ArgumentException(nameof(encryptData));
|
||||
}
|
||||
|
||||
string decryptedData;
|
||||
byte[] encryptDataBytes = WebEncoders.Base64UrlDecode(encryptData);
|
||||
|
||||
using (SymmetricAlgorithm symmetricAlgorithm = Aes.Create())
|
||||
{
|
||||
using (var memoryStream = new MemoryStream(encryptDataBytes))
|
||||
{
|
||||
ICryptoTransform encryptor = symmetricAlgorithm.CreateDecryptor(Encoding.UTF8.GetBytes(key), new byte[16]);
|
||||
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Read))
|
||||
{
|
||||
using (var streamReader = new StreamReader(cryptoStream))
|
||||
{
|
||||
decryptedData = streamReader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return decryptedData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A JweEncrypt egy jose-jwt nevű nuget-et használ és a JSON Web Encryption (JWE) formátumra enrypt-álja a bejövő objektumot.
|
||||
/// A JWE leírása: https://tools.ietf.org/html/rfc7516
|
||||
/// A jose-jwt leírása: https://github.com/dvsekhvalnov/jose-jwt
|
||||
/// Jelenleg az MKB bankos integrációhoz(tanuló bankszámlaigénylés) szükséges, ők ezt a fajta titkosítást használják.
|
||||
/// </summary>
|
||||
/// <param name="publicKeyFileName">A publikus kulcs elérési útvonala.</param>
|
||||
/// <param name="publicKeyFilePassword">A publikus kulcshoz tartozó jelszó(nem kötelező).</param>
|
||||
/// <param name="payload">Az objektum, amit enkódol a megfelelő formátumra a jose-jwt nuget.</param>
|
||||
/// <returns></returns>
|
||||
public static string JweEncrypt(string publicKeyFileName, string publicKeyFilePassword, object payload)
|
||||
{
|
||||
if (!File.Exists(publicKeyFileName))
|
||||
{
|
||||
throw new FileNotFoundException(publicKeyFileName);
|
||||
}
|
||||
|
||||
if (payload == null)
|
||||
{
|
||||
throw new ArgumentException(nameof(payload));
|
||||
}
|
||||
|
||||
RSACryptoServiceProvider key = string.IsNullOrWhiteSpace(publicKeyFilePassword) ?
|
||||
new X509Certificate2(publicKeyFileName).PublicKey.Key as RSACryptoServiceProvider :
|
||||
new X509Certificate2(publicKeyFileName, publicKeyFilePassword).PublicKey.Key as RSACryptoServiceProvider;
|
||||
|
||||
string result = JWT.Encode(payload, key, JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A JweDecrypt egy jose-jwt nevű nuget-et használ és a JSON Web Encryption (JWE) formátumú token-t decrypt-álja string-é, akár json formátumban.
|
||||
/// A JWE leírása: https://tools.ietf.org/html/rfc7516
|
||||
/// A jose-jwt leírása: https://github.com/dvsekhvalnov/jose-jwt
|
||||
/// Jelenleg az MKB bankos integrációhoz(tanuló bankszámlaigénylés) szükséges, ők ezt a fajta titkosítást használják.
|
||||
/// </summary>
|
||||
/// <param name="privateKeyFileName">A privát kulcs elérési útvonala.</param>
|
||||
/// <param name="privateKeyFilePassword">A privát kulcshoz tartozó jelszó(nem kötelező).</param>
|
||||
/// <param name="token">A token, amit dekódol string-é a jose-jwt nuget.</param>
|
||||
/// <param name="isJsonObject">Ha nem json objektumról van szó, akkor a dekódolás benne hagy escape karaktereket(\"), pl. Guid-nál és ezeket kiszedjük, hogy egyszerűbb legyen a típuskoncerzió.</param>
|
||||
/// <returns></returns>
|
||||
public static string JweDecrypt(string privateKeyFileName, string privateKeyFilePassword, string token, bool isJsonObject = false)
|
||||
{
|
||||
if (!File.Exists(privateKeyFileName))
|
||||
{
|
||||
throw new FileNotFoundException(privateKeyFileName);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(token))
|
||||
{
|
||||
throw new ArgumentException(nameof(token));
|
||||
}
|
||||
|
||||
RSACryptoServiceProvider key = string.IsNullOrWhiteSpace(privateKeyFilePassword) ?
|
||||
new X509Certificate2(privateKeyFileName).PrivateKey as RSACryptoServiceProvider :
|
||||
new X509Certificate2(privateKeyFileName, privateKeyFilePassword).PrivateKey as RSACryptoServiceProvider;
|
||||
|
||||
string result = JWT.Decode(token, key);
|
||||
if (!isJsonObject)
|
||||
{
|
||||
result = result.Replace("\"", string.Empty);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string GetEmailKezelesUrl(string intezmenyAzonosito = null, Guid? guid = null)
|
||||
{
|
||||
string url = null;
|
||||
if (!string.IsNullOrWhiteSpace(intezmenyAzonosito) && guid.HasValue)
|
||||
{
|
||||
var data = Encrypt(guid.ToString(), Constants.EncryptionKey);
|
||||
if (!string.IsNullOrWhiteSpace(data))
|
||||
{
|
||||
var builder = new UriBuilder(string.Format(CommonResource.IntezmenyUrl, intezmenyAzonosito))
|
||||
{
|
||||
Path = "/Adminisztracio/EmailKezeles",
|
||||
Query = $"data={HttpUtility.UrlEncode(data)}",
|
||||
Scheme = "https"
|
||||
};
|
||||
url = builder.Uri.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/aspnet/Extensions/blob/5c3ee37fb773db04a1aea6b67e49395e84fda223/shared/Microsoft.Extensions.WebEncoders.Sources/Properties/EncoderResources.cs
|
||||
internal static class WebEncoders
|
||||
{
|
||||
private static readonly byte[] EmptyBytes = new byte[0];
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a base64url-encoded string.
|
||||
/// </summary>
|
||||
/// <param name="input">The base64url-encoded input to decode.</param>
|
||||
/// <returns>The base64url-decoded form of the input.</returns>
|
||||
/// <remarks>
|
||||
/// The input must not contain any whitespace or padding characters.
|
||||
/// Throws <see cref="FormatException"/> if the input is malformed.
|
||||
/// </remarks>
|
||||
public static byte[] Base64UrlDecode(string input)
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
}
|
||||
|
||||
return Base64UrlDecode(input, offset: 0, count: input.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a base64url-encoded substring of a given string.
|
||||
/// </summary>
|
||||
/// <param name="input">A string containing the base64url-encoded input to decode.</param>
|
||||
/// <param name="offset">The position in <paramref name="input"/> at which decoding should begin.</param>
|
||||
/// <param name="count">The number of characters in <paramref name="input"/> to decode.</param>
|
||||
/// <returns>The base64url-decoded form of the input.</returns>
|
||||
/// <remarks>
|
||||
/// The input must not contain any whitespace or padding characters.
|
||||
/// Throws <see cref="FormatException"/> if the input is malformed.
|
||||
/// </remarks>
|
||||
public static byte[] Base64UrlDecode(string input, int offset, int count)
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
}
|
||||
|
||||
ValidateParameters(input.Length, nameof(input), offset, count);
|
||||
|
||||
// Special-case empty input
|
||||
if (count == 0)
|
||||
{
|
||||
return EmptyBytes;
|
||||
}
|
||||
|
||||
// Create array large enough for the Base64 characters, not just shorter Base64-URL-encoded form.
|
||||
var buffer = new char[GetArraySizeRequiredToDecode(count)];
|
||||
|
||||
return Base64UrlDecode(input, offset, buffer, bufferOffset: 0, count: count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes a base64url-encoded <paramref name="input"/> into a <c>byte[]</c>.
|
||||
/// </summary>
|
||||
/// <param name="input">A string containing the base64url-encoded input to decode.</param>
|
||||
/// <param name="offset">The position in <paramref name="input"/> at which decoding should begin.</param>
|
||||
/// <param name="buffer">
|
||||
/// Scratch buffer to hold the <see cref="char"/>s to decode. Array must be large enough to hold
|
||||
/// <paramref name="bufferOffset"/> and <paramref name="count"/> characters as well as Base64 padding
|
||||
/// characters. Content is not preserved.
|
||||
/// </param>
|
||||
/// <param name="bufferOffset">
|
||||
/// The offset into <paramref name="buffer"/> at which to begin writing the <see cref="char"/>s to decode.
|
||||
/// </param>
|
||||
/// <param name="count">The number of characters in <paramref name="input"/> to decode.</param>
|
||||
/// <returns>The base64url-decoded form of the <paramref name="input"/>.</returns>
|
||||
/// <remarks>
|
||||
/// The input must not contain any whitespace or padding characters.
|
||||
/// Throws <see cref="FormatException"/> if the input is malformed.
|
||||
/// </remarks>
|
||||
public static byte[] Base64UrlDecode(string input, int offset, char[] buffer, int bufferOffset, int count)
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
}
|
||||
|
||||
if (buffer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
}
|
||||
|
||||
ValidateParameters(input.Length, nameof(input), offset, count);
|
||||
if (bufferOffset < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(bufferOffset));
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return EmptyBytes;
|
||||
}
|
||||
|
||||
// Assumption: input is base64url encoded without padding and contains no whitespace.
|
||||
|
||||
var paddingCharsToAdd = GetNumBase64PaddingCharsToAddForDecode(count);
|
||||
var arraySizeRequired = checked(count + paddingCharsToAdd);
|
||||
Debug.Assert(arraySizeRequired % 4 == 0, "Invariant: Array length must be a multiple of 4.");
|
||||
|
||||
if (buffer.Length - bufferOffset < arraySizeRequired)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
EncoderResources.WebEncoders_InvalidCountOffsetOrLength,
|
||||
nameof(count),
|
||||
nameof(bufferOffset),
|
||||
nameof(input)),
|
||||
nameof(count));
|
||||
}
|
||||
|
||||
// Copy input into buffer, fixing up '-' -> '+' and '_' -> '/'.
|
||||
var i = bufferOffset;
|
||||
for (var j = offset; i - bufferOffset < count; i++, j++)
|
||||
{
|
||||
var ch = input[j];
|
||||
if (ch == '-')
|
||||
{
|
||||
buffer[i] = '+';
|
||||
}
|
||||
else if (ch == '_')
|
||||
{
|
||||
buffer[i] = '/';
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[i] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the padding characters back.
|
||||
for (; paddingCharsToAdd > 0; i++, paddingCharsToAdd--)
|
||||
{
|
||||
buffer[i] = '=';
|
||||
}
|
||||
|
||||
// Decode.
|
||||
// If the caller provided invalid base64 chars, they'll be caught here.
|
||||
return Convert.FromBase64CharArray(buffer, bufferOffset, arraySizeRequired);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum <c>char[]</c> size required for decoding of <paramref name="count"/> characters
|
||||
/// with the <see cref="Base64UrlDecode(string, int, char[], int, int)"/> method.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of characters to decode.</param>
|
||||
/// <returns>
|
||||
/// The minimum <c>char[]</c> size required for decoding of <paramref name="count"/> characters.
|
||||
/// </returns>
|
||||
public static int GetArraySizeRequiredToDecode(int count)
|
||||
{
|
||||
if (count < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var numPaddingCharsToAdd = GetNumBase64PaddingCharsToAddForDecode(count);
|
||||
|
||||
return checked(count + numPaddingCharsToAdd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes <paramref name="input"/> using base64url encoding.
|
||||
/// </summary>
|
||||
/// <param name="input">The binary input to encode.</param>
|
||||
/// <returns>The base64url-encoded form of <paramref name="input"/>.</returns>
|
||||
public static string Base64UrlEncode(byte[] input)
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
}
|
||||
|
||||
return Base64UrlEncode(input, offset: 0, count: input.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes <paramref name="input"/> using base64url encoding.
|
||||
/// </summary>
|
||||
/// <param name="input">The binary input to encode.</param>
|
||||
/// <param name="offset">The offset into <paramref name="input"/> at which to begin encoding.</param>
|
||||
/// <param name="count">The number of bytes from <paramref name="input"/> to encode.</param>
|
||||
/// <returns>The base64url-encoded form of <paramref name="input"/>.</returns>
|
||||
public static string Base64UrlEncode(byte[] input, int offset, int count)
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
}
|
||||
|
||||
ValidateParameters(input.Length, nameof(input), offset, count);
|
||||
|
||||
// Special-case empty input
|
||||
if (count == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var buffer = new char[GetArraySizeRequiredToEncode(count)];
|
||||
var numBase64Chars = Base64UrlEncode(input, offset, buffer, outputOffset: 0, count: count);
|
||||
|
||||
return new String(buffer, startIndex: 0, length: numBase64Chars);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes <paramref name="input"/> using base64url encoding.
|
||||
/// </summary>
|
||||
/// <param name="input">The binary input to encode.</param>
|
||||
/// <param name="offset">The offset into <paramref name="input"/> at which to begin encoding.</param>
|
||||
/// <param name="output">
|
||||
/// Buffer to receive the base64url-encoded form of <paramref name="input"/>. Array must be large enough to
|
||||
/// hold <paramref name="outputOffset"/> characters and the full base64-encoded form of
|
||||
/// <paramref name="input"/>, including padding characters.
|
||||
/// </param>
|
||||
/// <param name="outputOffset">
|
||||
/// The offset into <paramref name="output"/> at which to begin writing the base64url-encoded form of
|
||||
/// <paramref name="input"/>.
|
||||
/// </param>
|
||||
/// <param name="count">The number of <c>byte</c>s from <paramref name="input"/> to encode.</param>
|
||||
/// <returns>
|
||||
/// The number of characters written to <paramref name="output"/>, less any padding characters.
|
||||
/// </returns>
|
||||
public static int Base64UrlEncode(byte[] input, int offset, char[] output, int outputOffset, int count)
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(input));
|
||||
}
|
||||
|
||||
if (output == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(output));
|
||||
}
|
||||
|
||||
ValidateParameters(input.Length, nameof(input), offset, count);
|
||||
if (outputOffset < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(outputOffset));
|
||||
}
|
||||
|
||||
var arraySizeRequired = GetArraySizeRequiredToEncode(count);
|
||||
if (output.Length - outputOffset < arraySizeRequired)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
EncoderResources.WebEncoders_InvalidCountOffsetOrLength,
|
||||
nameof(count),
|
||||
nameof(outputOffset),
|
||||
nameof(output)),
|
||||
nameof(count));
|
||||
}
|
||||
|
||||
// Special-case empty input.
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Use base64url encoding with no padding characters. See RFC 4648, Sec. 5.
|
||||
|
||||
// Start with default Base64 encoding.
|
||||
var numBase64Chars = Convert.ToBase64CharArray(input, offset, count, output, outputOffset);
|
||||
|
||||
// Fix up '+' -> '-' and '/' -> '_'. Drop padding characters.
|
||||
for (var i = outputOffset; i - outputOffset < numBase64Chars; i++)
|
||||
{
|
||||
var ch = output[i];
|
||||
if (ch == '+')
|
||||
{
|
||||
output[i] = '-';
|
||||
}
|
||||
else if (ch == '/')
|
||||
{
|
||||
output[i] = '_';
|
||||
}
|
||||
else if (ch == '=')
|
||||
{
|
||||
// We've reached a padding character; truncate the remainder.
|
||||
return i - outputOffset;
|
||||
}
|
||||
}
|
||||
|
||||
return numBase64Chars;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the minimum output <c>char[]</c> size required for encoding <paramref name="count"/>
|
||||
/// <see cref="byte"/>s with the <see cref="Base64UrlEncode(byte[], int, char[], int, int)"/> method.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of characters to encode.</param>
|
||||
/// <returns>
|
||||
/// The minimum output <c>char[]</c> size required for encoding <paramref name="count"/> <see cref="byte"/>s.
|
||||
/// </returns>
|
||||
public static int GetArraySizeRequiredToEncode(int count)
|
||||
{
|
||||
var numWholeOrPartialInputBlocks = checked(count + 2) / 3;
|
||||
return checked(numWholeOrPartialInputBlocks * 4);
|
||||
}
|
||||
|
||||
private static int GetNumBase64PaddingCharsToAddForDecode(int inputLength)
|
||||
{
|
||||
switch (inputLength % 4)
|
||||
{
|
||||
case 0:
|
||||
return 0;
|
||||
case 2:
|
||||
return 2;
|
||||
case 3:
|
||||
return 1;
|
||||
default:
|
||||
throw new FormatException(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
EncoderResources.WebEncoders_MalformedInput,
|
||||
inputLength));
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidateParameters(int bufferLength, string inputName, int offset, int count)
|
||||
{
|
||||
if (offset < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
}
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
}
|
||||
|
||||
if (bufferLength - offset < count)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
EncoderResources.WebEncoders_InvalidCountOffsetOrLength,
|
||||
nameof(count),
|
||||
nameof(offset),
|
||||
inputName),
|
||||
nameof(count));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static class EncoderResources
|
||||
{
|
||||
/// <summary>
|
||||
/// Invalid {0}, {1} or {2} length.
|
||||
/// </summary>
|
||||
internal static readonly string WebEncoders_InvalidCountOffsetOrLength = "Invalid {0}, {1} or {2} length.";
|
||||
|
||||
/// <summary>
|
||||
/// Malformed input: {0} is an invalid input length.
|
||||
/// </summary>
|
||||
internal static readonly string WebEncoders_MalformedInput = "Malformed input: {0} is an invalid input length.";
|
||||
|
||||
/// <summary>
|
||||
/// Invalid {0}, {1} or {2} length.
|
||||
/// </summary>
|
||||
internal static string FormatWebEncoders_InvalidCountOffsetOrLength(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, WebEncoders_InvalidCountOffsetOrLength, p0, p1, p2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Malformed input: {0} is an invalid input length.
|
||||
/// </summary>
|
||||
internal static string FormatWebEncoders_MalformedInput(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, WebEncoders_MalformedInput, p0);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue