L'utilisation de SSL et SslStream pour l'authentification peer to peer?
-
22-08-2019 - |
Question
Je dois fournir une communication sécurisée entre les différents processus qui utilisent des sockets TCP / IP pour la communication. Je veux l'authentification et le chiffrement. Plutôt que de réinventer la roue, je voudrais vraiment utiliser SSL et la classe SslStream et certificats auto-signés. Ce que je veux faire est de valider le certificat du processus à distance contre une copie connue dans ma demande locale. (Il n'a pas besoin d'être une autorité de certification parce que je compte pour les certificats à copier manuellement autour).
Pour ce faire, je veux que l'application soit en mesure de générer automatiquement un nouveau certifiate la première fois qu'il est exécuté. En plus de MakeCert.exe, il ressemble à ce lien montre un moyen de générer automatiquement des certificats auto-signés, de sorte que c'est un début.
Je l'ai regardé les méthodes AuthenticateAsServer et AuthenticateAsClient de SslStream. Vous pouvez fournir des rappels pour la vérification, il semble que cela est possible. Mais maintenant que je suis dans les détails, je ne pense pas qu'il soit possible de le faire.
Vais-je dans la bonne direction? Y at-il une meilleure alternative? Quelqu'un at-il fait quelque chose comme ça avant (essentiellement SSL peer-to-peer plutôt que client-serveur)?
La solution
Étape 1: Création d'un certificat auto-signé:
- J'ai téléchargé le class Certificate.cs posté par Doug Cook,
-
J'ai utilisé ce code pour générer un fichier de certificat .pfx:
byte[] c = Certificate.CreateSelfSignCertificatePfx( "CN=yourhostname.com", //host name DateTime.Parse("2000-01-01"), //not valid before DateTime.Parse("2010-01-01"), //not valid after "mypassword"); //password to encrypt key file using (BinaryWriter binWriter = new BinaryWriter( File.Open(@"testcert.pfx", FileMode.Create))) { binWriter.Write(c); }
Étape 2: Chargement du certificat
X509Certificate cert = new X509Certificate2(
@"testcert.pfx",
"mypassword");
Étape 3: Mettre ensemble
- Je me suis basé sur ce très simple exemple de SslStream
- Vous obtiendrez une erreur de compilation sur l'énumération SslProtocolType. Il suffit de changer que de SslProtocolType.Default à SslProtocols.Default
- Il y avait 3 avertissements sur les fonctions dépréciées. Je les ai remplacé tous les remplacements proposés.
-
Je l'ai remplacé cette ligne dans le serveur de fichiers Program.cs avec la ligne de l'étape 2:
X509Certificate cert = getServerCert ();
-
Dans le fichier Program.cs client, assurez-vous que vous définissez serverName = yourhostname.com (et qu'il correspond au nom dans le certificat)
- Dans la fonction client Program.cs, le CertificateValidationCallback échoue parce que sslPolicyErrors contient un RemoteCertificateChainErrors. Si vous creusez un peu plus profond, cela est parce que l'autorité d'émission qui a signé le certificat n'est pas une racine de confiance.
- Je n `veux entrer dans ayant les certificats d'importation utilisateur dans le magasin racine, etc., donc je fait un cas particulier pour cela, et je vérifie que certificate.GetPublicKeyString () est égale à la clé publique que je avoir sur fichier pour ce serveur. Si elle correspond, je retourne vrai de cette fonction. Cela semble fonctionner.
Étape 4: Authentification Client
Voici comment mon client authentifie (c'est un peu différent de celui du serveur):
TcpClient client = new TcpClient();
client.Connect(hostName, port);
SslStream sslStream = new SslStream(client.GetStream(), false,
new RemoteCertificateValidationCallback(CertificateValidationCallback),
new LocalCertificateSelectionCallback(CertificateSelectionCallback));
bool authenticationPassed = true;
try
{
string serverName = System.Environment.MachineName;
X509Certificate cert = GetServerCert(SERVER_CERT_FILENAME, SERVER_CERT_PASSWORD);
X509CertificateCollection certs = new X509CertificateCollection();
certs.Add(cert);
sslStream.AuthenticateAsClient(
serverName,
certs,
SslProtocols.Default,
false); // check cert revokation
}
catch (AuthenticationException)
{
authenticationPassed = false;
}
if (authenticationPassed)
{
//do stuff
}
Le CertificateValidationCallback est le même que dans le cas du serveur, mais notez comment AuthenticateAsClient prend une collection de certificats, non seulement un certificat. Donc, vous devez ajouter un LocalCertificateSelectionCallback, comme celui-ci (dans ce cas, je ne dispose que d'un client cert donc je reviens juste le premier dans la collection):
static X509Certificate CertificateSelectionCallback(object sender,
string targetHost,
X509CertificateCollection localCertificates,
X509Certificate remoteCertificate,
string[] acceptableIssuers)
{
return localCertificates[0];
}
Autres conseils
vous pouvez regarder aussi cet exemple Client / Mise en œuvre du serveur Exemple Asynchronous SslStream http: // blogs.msdn.com/joncole/archive/2007/06/13/sample-asynchronous-sslstream-client-server-implementation.aspx
si le certificat ne se produit pas correctement, vous pouvez obtenir exception Le mode serveur SSL doit utiliser un certificat avec la clé privée associée.
Exemple de certificat de base
makecert -SR LocalMachine -ss Mon -n CN = Test échange -sky -sk 123456
ou
comme fichier externe
makecert -SR LocalMachine -ss Mon échange -n CN = Test -sky -sk 123456 c: \ Test.cer
Certificat outil de création (Makecert.exe)
http://msdn.microsoft.com/en- nous / bibliothèque / bfsktky3% 28VS.80% 29.aspx
Qu'est-ce que vous proposez sonne bien pour moi, sauf que ça sonne comme vous êtes à la recherche d'attendre jusqu'à ce que le rappel est invoqué afin de générer le certificat. Je ne pense pas que cela va voler; Autant que je sache, vous devez fournir un certificat valide lorsque vous invoquez AuthenticateAsX
.
Cependant, ces classes sont Overridable; donc en théorie, vous pouvez créer une classe dérivée qui vérifie d'abord si un certificat doit être généré, génère si besoin, puis appelle la méthode AuthenticateAsX
mère.