using Microsoft.AspNetCore.Cryptography.KeyDerivation; using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.IdentityModel.Tokens.Jwt; using System.IO; using System.Security.Claims; using System.Security.Cryptography; using System.Text; namespace TEAMModelOS.SDK.Extension { public class Utils { private static RNGCryptoServiceProvider _random = new RNGCryptoServiceProvider(); public static string HashedPassword(string password, string salt) { byte[] hashBytes = KeyDerivation.Pbkdf2( password: password, salt: Encoding.UTF8.GetBytes(salt), //SHA1鹽(8-20字節),SHA256(32字節) prf: KeyDerivationPrf.HMACSHA1, iterationCount: 10000, //hash次數,越多次代表破解難度變高,但效能會差點 numBytesRequested: 256 / 8 //指定得出結果長度 ); string hashText = BitConverter.ToString(hashBytes).Replace("-", string.Empty); return hashText; } /// /// 建立真隨機字串(CSPRNG),適用密碼、鹽 /// /// 長度 /// 要限制的字元串(長度需大於等於8),如果為null或者小於8,預設為"abcdefghijklmnopqrstuvwxyz1234567890" /// public static string CreatSaltString(int stringLength, string key = null) { ReadOnlySpan span; if (key == null || key.Length < 8) span = "abcdefghijklmnopqrstuvwxyz1234567890"; else span = key.AsSpan(); int length = span.Length; StringBuilder randomString = new StringBuilder(length); for (int i = 0; i < stringLength; ++i) { randomString.Append(span[SetRandomSeeds(length)]); } return randomString.ToString(); } /// /// 建立真隨機整數數字(CSPRNG),適用亂數、隨機編號 /// /// 最大值 public static int CreatSaltInt(int max) { var bytes = new byte[4]; _random.GetBytes(bytes); int value = BitConverter.ToInt32(bytes, 0); value = value % (max + 1); if (value < 0) value = -value; return value; } /// /// 建立真隨機整數數字(CSPRNG),適用亂數、隨機編號 /// /// 最小值 /// 最大值 public static int CreatSaltInt(int min, int max) { int value = CreatSaltInt(max - min) + min; return value; } /// /// 剖析連接字串 /// /// /// public static Dictionary ParseConnectionString(string connectionString) { var d = new Dictionary(); foreach (var item in connectionString.Split(';', StringSplitOptions.RemoveEmptyEntries)) { var a = item.IndexOf('='); d.Add(item.Substring(0, a), item.Substring(a + 1)); } return d; } #region 圖片處理 /// /// 判斷圖片格式 /// /// 傳入Stream,注意請自行釋放 /// public static (bool, string, int) ImageValidateByStream(Stream stream) { int length = 100000; //100 KB byte[] bytes = new byte[length]; BinaryReader br = new BinaryReader(stream); StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < length; i++) { byte tempByte; try { tempByte = br.ReadByte(); bytes[i] = tempByte; stringBuilder.Append(Convert.ToString(tempByte, 16)); stringBuilder.Append(","); } catch { break; //如果發生檔尾損壞異常抱錯,直接跳離並結算stringBuilder } } stream.Position = 0; //指針回歸為0 string fileheader = stringBuilder.ToString().ToUpper(); if (string.IsNullOrWhiteSpace(fileheader)) return (false, "", 0); if (fileheader.StartsWith("FF,D8,") || fileheader.StartsWith("42,4D,")) { int i = 2; int depth; while (true) { int marker = (bytes[i] & 0xff) << 8 | (bytes[i + 1] & 0xff); int size = (bytes[i + 2] & 0xff) << 8 | (bytes[i + 3] & 0xff); if (marker >= 0xffc0 && marker <= 0xffcf && marker != 0xffc4 && marker != 0xffc8) { depth = (bytes[i + 4] & 0xff) * (bytes[i + 9] & 0xff); break; } else i += size + 2; } return (true, "jpg", depth); } if (fileheader.StartsWith("89,50,4E,47,D,A,1A,A,")) { int depth = bytes[24] & 0xff; if ((bytes[25] & 0xff) == 2) depth *= 3; else if ((bytes[25] & 0xff) == 6) depth *= 4; return (true, "png", depth); } if (fileheader.StartsWith("47,49,46,38,39,61,") || fileheader.StartsWith("47,49,46,38,37,61,")) return (true, "gif", 0); if (fileheader.StartsWith("1,0,0,0,")) return (true, "emf", 0); if (fileheader.StartsWith("1,0,9,0,0,3")) return (true, "wmf", 0); //if (fileheader.StartsWith("4D,4D") || fileheader.StartsWith("49,49") || fileheader.StartsWith("46,4F,52,4D")) // return (true, "tif"); return (false, "", 0); } /// /// 轉換EMF為PNG格式 /// /// /// public static string ConvertEMFtoPNG(Stream stream) { using var image = Image.FromStream(stream); //GDI+報錯,Azure Web App沙箱不支持GDI+部分權限 //using var nbase64ms = new MemoryStream(); //image.Save(nbase64ms, ImageFormat.Png); //保存為PNG格式 //byte[] data = nbase64ms.ToArray(); //string base64 = Convert.ToBase64String(data); //return base64; //Azure Web App沙箱不支持GDI+渲染部分EMF指令,可以出圖但會是空白 var pngimage = new Bitmap(image.Width, image.Height); using (var graphics = Graphics.FromImage(pngimage)) { graphics.CompositingQuality = CompositingQuality.HighSpeed; graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.CompositingMode = CompositingMode.SourceCopy; graphics.DrawImage(image, 0, 0, image.Width, image.Height); using var nbase64ms = new MemoryStream(); pngimage.Save(nbase64ms, ImageFormat.Png); //保存為PNG格式 byte[] data = nbase64ms.ToArray(); string base64 = Convert.ToBase64String(data); return base64; } } #endregion #region private private static int SetRandomSeeds(int length) { decimal maxValue = (decimal)long.MaxValue; byte[] array = new byte[8]; _random.GetBytes(array); return (int)(Math.Abs(BitConverter.ToInt64(array, 0)) / maxValue * length); } #endregion } }