Hallo
Umgebung:
Ein C# Programm (.exe) soll auf einem Windows Rechner ausgeführt werden und sich mit einem REST Service verbinden. Zur Anmeldung am REST soll ein Benutzername und Passwort eingegeben werden. Das Programm soll jetzt in Zukunft für diesen Rechner freigegeben sein und auch nach jedem Neustart ohne weitere Passworteingabe sich mit dem REST verbinden, solange nicht auf einen abmelden Button geklickt wird.
Problem:
Es soll sowas wie eine Lizenz für das Programm auf diesem einen Rechner vergeben werden. Wenn der Benutzer z.B. auf nur 3 Rechner das Programm nutzen darf, soll nach dem Anmelden auf einem 4ten Rechner die Meldung erscheinen, das der Benutzer nur für 3 Rechner eine Lizenz hat. Sollte einer der freigegebenen Rechner kein Internet haben, soll auch nach einem Neustart das Programm auf dem Rechner lizensiert bleiben. Es soll sowas wie ein JWT Token benötigt werden, der nur alle x offline Tage, neu angefordert werden muss.
Teilprobleme:
- Wie kann ich per C# einen Rechner eindeutig identifizieren?
- Wie kann ich vom REST Service sowas wie eine Lizenz oder Zertifikat für den Rechner vergeben?
- Wie gebe ich die Lizenz nach der Deinstallation oder abmelden wieder frei?
- Wie kann ich remote eine Lizenz wiederufen?
- Wie verhindere ich, dass ein Benutzer sich selber eine Lizenz bastelt?
Weitere Fragen:
- Gibt es zu dem Thema schon fertige Bibliotheken?
- Habt ihr in der Richtung schon mal etwas entwickelt?
- Gibt es noch einfachere Wege so eine Lizensierung umzusetzen?
Beste Grüße
Cornflake
Falscher Forenbereich, daher verschoben.
Gibt es zu dem Thema schon fertige Bibliotheken?
Das ist ja simple Recherche, die Du selbst übernehmen kannst.
Gibt es noch einfachere Wege so eine Lizensierung umzusetzen?
Auch Recherche, die Du selbst machen kannst. Schau Dir Lizenzierungsangebote an, vergleich die mit Deinen Anforderungen und fertig.
Du kannst Dir hier auch Mechanismen abschauen, die womöglich bereits etabliert sind.
Das Programm soll jetzt in Zukunft für diesen Rechner freigegeben sein und auch nach jedem Neustart ohne weitere Passworteingabe sich mit dem REST verbinden, solange nicht auf einen abmelden Button geklickt wird.
Das hört sich so an, dass Du/ihr nicht verstanden habt, was REST ist und wie respektive HTTP und Autorisierung im Web/in APIs funktioniert. Auch in Kombination mit euren JWT Ideen empfehl ich, sich nochmal das Thema API Security durchzulesen; hier die Standards beachten und auch zwischen Authentifizierung und Autorisierung zu unterscheiden - die Basis jeder API Sicherheit.
Wie kann ich per C# einen Rechner eindeutig identifizieren?
C# ist eine Sprache, existiert zur Laufzeit daher natürlich nicht. C# kann sowas also nicht identifizieren.
.NET ist die Runtime - und die liefert Dir natürlich keine fertige Funktion für sowas. Du musst selbst entscheiden anhand welchen Parametern eine Hardware eindeutig identifziert werden kann.
Siehe dazu als Basis möglicherweise Windows Hardware ID - beachte die Fallstricke.
Overall: eure Idee wird keine absolute Sicherheit bieten. Kann sie konzeptionell so auch gar nicht.
Alles, was an irgendeine API gesendet wird, kann spätestens durch SSL Interception abgefangen und analysiert - und on the fly manipuliert - werden.
Womöglich wäre auch eine Zertifikatsbasierte Lösung etwas für euch.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Nachdem Du ja nun schon weißt, was und warum alles nicht geht und hier mal bisschen was konstruktiveres... Bitte beachten, dass es durchaus Einschränkungen für die Funktion dieses Codes geben kann (virtuelle PCs etc.) - inwieweit das für dich nutzbar ist, musst Du selbst entscheiden - hängt auch davon ab, ob und wie Du die 'Umgebung' in der das genutzt werden soll unter 'Kontrolle' hast.
Wie kann ich per C# einen Rechner eindeutig identifizieren?
using System.Management;
using System.Net.NetworkInformation;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
namespace SystemInfo;
[SupportedOSPlatform("windows")]
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
SystemInfo ifo = new();
Console.WriteLine("CPU: "+ifo.IDCpu);
Console.WriteLine("MB: "+ifo.IDMotherboard);
Console.WriteLine("MAC: "+ifo.IDMac);
Console.WriteLine("DRIVE: "+ifo.IDSystemDrive);
Console.WriteLine(ifo.UniqueID);
Console.Read();
}
}
/// <summary>
/// Informationen zum Computer, auf dem die Anwendung gerade ausgeführt wird.
/// </summary>
public class SystemInfo
{
private string? _cpuid = null;
private string? _macid = null;
private string? _mbid = null;
private string? _systemserial = null;
private string? _uniqueid = null;
/// <summary>
/// ID des Prozessors des PCs
/// </summary>
[SupportedOSPlatform("windows")]
public string IDCpu
{
get
{
if (_cpuid != null) return _cpuid;
using ManagementClass mc = new("Win32_Processor");
using ManagementObjectCollection moc = mc.GetInstances();
foreach (var mo in moc)
{
_cpuid = mo.Properties["ProcessorId"].Value.ToString();
break;
}
_cpuid ??= "";
return _cpuid;
}
}
/// <summary>
/// ID des Motherboards des PCs
/// </summary>
[SupportedOSPlatform("windows")]
public string IDMotherboard
{
get
{
if (_mbid != null) return _mbid;
using ManagementClass mc = new("Win32_BaseBoard");
using ManagementObjectCollection moc = mc.GetInstances();
foreach (var mo in moc)
{
_mbid = mo.Properties["SerialNumber"].Value.ToString();
break;
}
_mbid ??= "";
return _mbid;
}
}
/// <summary>
/// MAC-Adresse der ersten Netzwerkkarte/der ersten Netzwerkschnittstelle des PCs
/// </summary>
public string IDMac
{
get
{
if (_macid != null) return _macid;
NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface ni in interfaces)
{
if (ni.Supports(NetworkInterfaceComponent.IPv4) == false &&
ni.Supports(NetworkInterfaceComponent.IPv6) == false)
continue;
PhysicalAddress pa = ni.GetPhysicalAddress();
_macid = pa.ToString();
break;
}
_macid ??= "";
return _macid;
}
}
/// <summary>
/// Volume-ID des Systemlaufwerks
///
/// Seriennummer des Laufwerkes, auf dem sich das Betriebssystem befindet.
/// </summary>
public string IDSystemDrive
{
get
{
if (_systemserial != null) return _systemserial;
_systemserial = VolumeSerial(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.System)));
return _systemserial;
}
}
/// <summary>
/// Liefert die VolumeInformationen zu einem Laufwerk
/// </summary>
/// <param name="PathName">Name des Pfades/Laufwerks</param>
/// <param name="VolumeNameBuffer"></param>
/// <param name="VolumeNameSize"></param>
/// <param name="VolumeSerialNumber"></param>
/// <param name="MaximumComponentLength"></param>
/// <param name="FileSystemFlags"></param>
/// <param name="FileSystemNameBuffer"></param>
/// <param name="FileSystemNameSize"></param>
/// <returns></returns>
[DllImport("kernel32.dll")]
public static extern long GetVolumeInformation(string PathName, StringBuilder VolumeNameBuffer, uint VolumeNameSize, ref uint VolumeSerialNumber, ref uint MaximumComponentLength, ref uint FileSystemFlags, StringBuilder FileSystemNameBuffer, uint FileSystemNameSize);
/// <summary>
/// Ermittelt die Seriennummer eines Laufwerkes als Hex-String
/// </summary>
/// <param name="dir">DirectoryInfo dessen VolumeSerial ermittelt werden soll</param>
/// <returns>Seriennummer als Hex-String (0x wenn ein Fehler auftrat)</returns>
public static string VolumeSerial(DirectoryInfo dir)
{
uint serNum = 0;
uint maxCompLen = 0;
string ret = "";
if (dir.FullName.Substring(1, 1) == ":")
{
StringBuilder VolLabel = new(256); // Label
uint VolFlags = new();
StringBuilder FSName = new(256); // File System Name
string drive = dir.FullName.Substring(0, 1) + ":\\";
GetVolumeInformation(drive, VolLabel, (uint)VolLabel.Capacity, ref serNum, ref maxCompLen, ref VolFlags, FSName, (uint)FSName.Capacity);
ret = Convert.ToString(serNum, 16);
}
else
{
throw new InvalidDataException($"{dir.FullName} is not located on a physical drive.");
}
return ret;
}
/// <summary>
/// eindeutige ID des Computers
///
/// Die eindeutige ID wird aus der ID der CPU,
/// der ID der Motherboards und der MAC-Adresse,
/// auf dem sich das Betriebssystem befindet gebildet
/// und dann verschlüsselt. Der so erstellte HashCode kann als eindeutige
/// ID des PCs verwendet.
///
/// Diese ID kann sich ändern, wenn das MotherBoard, die CPU, die Festplatte oder die Netzwerkkarte getauscht werden.
/// </summary>
[SupportedOSPlatform("windows")]
public string UniqueID
{
get
{
if (_uniqueid != null) return _uniqueid;
string ID = IDCpu + IDMac + IDMotherboard + IDSystemDrive;
using HMACSHA1 hmac = new();
hmac.Key = Encoding.ASCII.GetBytes(IDMotherboard);
hmac.ComputeHash(Encoding.ASCII.GetBytes(ID));
// Hash zu Hex-String konvertieren
StringBuilder sb = new();
foreach (var bt in hmac.Hash!)
sb.Append(bt.ToString("X2"));
_uniqueid = sb.ToString();
return _uniqueid;
}
}
}