using System.Net; using System.Net.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Xml.Linq; namespace AmtScanner.Api.Services; public interface IAmtWsmanService { Task GetUuidAsync(string ip, string username, string password); Task GetSystemInfoAsync(string ip, string username, string password); } public class AmtSystemInfo { public string? Uuid { get; set; } public string? Hostname { get; set; } public string? Manufacturer { get; set; } public string? Model { get; set; } public string? SerialNumber { get; set; } } public class AmtWsmanService : IAmtWsmanService { private readonly ILogger _logger; private const int AMT_PORT = 16992; private const int AMT_TLS_PORT = 16993; public AmtWsmanService(ILogger logger) { _logger = logger; } public async Task GetUuidAsync(string ip, string username, string password) { try { var info = await GetSystemInfoAsync(ip, username, password); return info?.Uuid; } catch (Exception ex) { _logger.LogError(ex, "获取UUID失败: {Ip}", ip); return null; } } public async Task GetSystemInfoAsync(string ip, string username, string password) { // 构建WS-Management请求获取CIM_ComputerSystemPackage var soapEnvelope = BuildGetUuidRequest(ip, AMT_PORT); try { // 先尝试非TLS端口 var response = await SendWsmanRequestWithDigestAsync(ip, AMT_PORT, username, password, soapEnvelope, false); if (response != null) { _logger.LogDebug("非TLS连接成功,响应长度: {Length}", response.Length); return ParseSystemInfo(response); } } catch (Exception ex) { _logger.LogDebug("非TLS连接失败,尝试TLS: {Error}", ex.Message); } try { // 尝试TLS端口 soapEnvelope = BuildGetUuidRequest(ip, AMT_TLS_PORT); var response = await SendWsmanRequestWithDigestAsync(ip, AMT_TLS_PORT, username, password, soapEnvelope, true); if (response != null) { _logger.LogDebug("TLS连接成功,响应长度: {Length}", response.Length); return ParseSystemInfo(response); } } catch (Exception ex) { _logger.LogError(ex, "TLS连接也失败: {Ip}", ip); } return null; } /// /// 使用 Digest 认证发送 WS-Man 请求 /// private async Task SendWsmanRequestWithDigestAsync(string ip, int port, string username, string password, string soapEnvelope, bool useTls) { var protocol = useTls ? "https" : "http"; var url = $"{protocol}://{ip}:{port}/wsman"; _logger.LogDebug("尝试连接: {Url}", url); // 创建 HttpClientHandler,配置 Digest 认证 var handler = new HttpClientHandler { Credentials = new CredentialCache { { new Uri($"{protocol}://{ip}:{port}"), "Digest", new NetworkCredential(username, password) } }, PreAuthenticate = false, ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true }; using var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(15) }; var content = new StringContent(soapEnvelope, Encoding.UTF8, "application/soap+xml"); try { var response = await client.PostAsync(url, content); _logger.LogDebug("响应状态码: {StatusCode}", response.StatusCode); if (response.IsSuccessStatusCode) { var responseContent = await response.Content.ReadAsStringAsync(); _logger.LogDebug("响应内容: {Content}", responseContent.Length > 500 ? responseContent[..500] + "..." : responseContent); return responseContent; } var errorContent = await response.Content.ReadAsStringAsync(); _logger.LogWarning("WS-Management请求失败: {StatusCode}, 响应: {Response}", response.StatusCode, errorContent); return null; } catch (HttpRequestException ex) { _logger.LogWarning("HTTP请求异常: {Message}", ex.Message); throw; } } private static string BuildGetUuidRequest(string ip, int port) { // 使用 Intel AMT SDK 的 GET_XML 格式 var toAddress = $"http://{ip}:{port}/wsman"; var messageId = $"uuid:{Guid.NewGuid()}"; return $@" {toAddress} http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystemPackage http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous http://schemas.xmlsoap.org/ws/2004/09/transfer/Get 153600 {messageId} PT60.000S "; } private AmtSystemInfo? ParseSystemInfo(string xmlResponse) { try { var doc = XDocument.Parse(xmlResponse); // 查找UUID(PlatformGUID) var uuid = FindElementValue(doc, "PlatformGUID"); // 如果没找到PlatformGUID,尝试其他方式 if (string.IsNullOrEmpty(uuid)) { // 尝试从CIM_Chassis获取UUID uuid = FindElementValue(doc, "UUID"); } if (string.IsNullOrEmpty(uuid)) { // 尝试从Tag字段获取 uuid = FindElementValue(doc, "Tag"); } // 格式化UUID(如果需要) if (!string.IsNullOrEmpty(uuid)) { uuid = FormatUuid(uuid); } return new AmtSystemInfo { Uuid = uuid, Hostname = FindElementValue(doc, "Name") ?? FindElementValue(doc, "ElementName"), Manufacturer = FindElementValue(doc, "Manufacturer"), Model = FindElementValue(doc, "Model"), SerialNumber = FindElementValue(doc, "SerialNumber") }; } catch (Exception ex) { _logger.LogError(ex, "解析WS-Management响应失败"); return null; } } private static string? FindElementValue(XDocument doc, string localName) { var element = doc.Descendants() .FirstOrDefault(e => e.Name.LocalName.Equals(localName, StringComparison.OrdinalIgnoreCase)); return element?.Value; } private static string FormatUuid(string uuid) { // 移除可能的前缀和格式化 uuid = uuid.Replace("uuid:", "").Replace("-", "").Trim(); if (uuid.Length == 32) { // 格式化为标准UUID格式 return $"{uuid[..8]}-{uuid[8..12]}-{uuid[12..16]}-{uuid[16..20]}-{uuid[20..]}".ToLower(); } return uuid.ToLower(); } }