//static internal int TargetFirmwareVersionNumber;

        internal static void RunServer(string _serverCA, string serverCert, string serverKey, string deviceCA, string deviceIDPublic)
        {
            if (deviceCA != null)
            {
                DeviceCA = new X509Certificate2(deviceCA);
            }
            DeviceIDPEMFile = deviceIDPublic;

            // Windows likes PFX files so make one out of the cert and key PEM files
            string serverPFXFile = "TO_ServerKey.pfx";
            string password      = "******";

            Helpers.MakePFXFile(serverCert, serverKey, serverPFXFile, password);

            ServerCert = new X509Certificate2(serverPFXFile, password);
            TcpListener listener = new TcpListener(IPAddress.Any, 5556);

            Helpers.Notify("SSL Server starting on localhost:5556");
            listener.Start();
            while (true)
            {
                Helpers.Notify("Waiting for a client to connect...");
                TcpClient client = listener.AcceptTcpClient();
                if (deviceIDPublic != null)
                {
                    ProcessClient(client, false);
                    return;
                }
                else
                {
                    ProcessClient(client, true);
                    // and wait for the next one
                }
            }
        }
 internal static string ReadMessage(SslStream sslStream)
 {
     byte[] buf = new byte[1024];
     try
     {
         int numRead = sslStream.Read(buf, 0, 1);
         if (numRead != 1)
         {
             Helpers.Notify("Got a bad message from the server");
         }
         int pos  = 0;
         int lenX = (int)buf[0];
         while (true)
         {
             numRead = sslStream.Read(buf, pos, lenX - pos);
             pos    += numRead;
             if (pos == lenX)
             {
                 break;
             }
         }
         string serverMessage = Encoding.UTF8.GetString(buf, 0, lenX);
         return(serverMessage);
     }
     catch (Exception e)
     {
         Debug.WriteLine("error reading: " + e.ToString());
         throw;
     }
 }
예제 #3
0
        /// <summary>
        /// Enroll a new device
        /// </summary>
        /// <param name="deviceId"></param>
        /// <param name="fwVersionNumber"></param>
        /// <param name="fwId"></param>
        /// <param name="aliasCertThumbprint"></param>
        /// <returns></returns>
        internal Device EnrollDevice(string deviceId, int fwVersionNumber, string fwId, string aliasCertThumbprint)
        {
            if (GetDeviceIDs().Contains(deviceId))
            {
                Helpers.Notify($"Device {deviceId} already exists");
                return(null);
            }
            Device newDevice = null;
            Device dTemplate = new Device(deviceId);

            dTemplate.Authentication = new AuthenticationMechanism();
            dTemplate.Authentication.X509Thumbprint = new X509Thumbprint();
            dTemplate.Authentication.X509Thumbprint.PrimaryThumbprint = aliasCertThumbprint;
            try
            {
                // create the device
                newDevice = RegMgr.AddDeviceAsync(dTemplate).Result;
                // and set the FWID property in the twin
                var props = new TwinCollection();
                props["FWID"]          = fwId;
                props["VersionNumber"] = fwVersionNumber.ToString();

                var twin = RegMgr.GetTwinAsync(deviceId).Result;
                twin.Properties.Desired = props;
                RegMgr.UpdateTwinAsync(deviceId, twin, twin.ETag).Wait();

                return(newDevice);
            }
            catch (Exception e)
            {
                Helpers.Notify($"Failed to add device. Error is {e.ToString()}");
                return(null);
            }
        }
        internal void CertifyExistingFromCsr(int chainLen)
        {
            var req = (Pkcs10CertificationRequest)Helpers.ReadPemObject(ToPath(Program.DeviceIDCSR));

            //var req = (Pkcs10CertificationRequest)Helpers.ReadPemObject(fileName);

            if (!req.Verify())
            {
                Helpers.Notify("PKCS10 csr is not properly self-signed");
                return;
            }
            // todo: should propagate the subject in the CSR into the DeviceID certificate.

            var info = req.GetCertificationRequestInfo();
            AsymmetricKeyParameter deviceIdKey = PublicKeyFactory.CreateKey(info.SubjectPublicKeyInfo);

            DeviceBundle bundle = new DeviceBundle();

            bundle.AliasCert      = (X509Certificate)Helpers.ReadPemObject(ToPath(Program.AliasCert));
            bundle.DeviceIDPublic = (AsymmetricKeyParameter)deviceIdKey;
            bundle.AliasKeyPair   = (AsymmetricCipherKeyPair)Helpers.ReadPemObject(ToPath(Program.AliasKey));

            if (bundle.AliasCert.IssuerDN.ToString() != info.Subject.ToString())
            {
                Helpers.Notify("CSR Subject Name does not match Alias Certificate Issuer Name: Chain will not build.", true);
                return;
            }
            // todo (maybe).  Check that the Alias extension claimed DeviceID matches the DeviceID CSR key.

            MakeCertChain(bundle, chainLen, 0);
        }
예제 #5
0
        /// <summary>
        /// If the device is not "vendor certified" it will only present the Alias Certificate, which is
        /// validated in this routine.  The essential security checks are:
        ///     1) Does it have a RIoT extension containing the DeviceID?
        ///     2) Is the certificate signed by the corresponding private DeviceID?
        ///     3) Is the DeviceID "authorized."  In the simple case, is it exactly the device DeviceID
        ///             indicated by the code that instantiated the TLSServer object.
        /// </summary>
        /// <param name="certificate"></param>
        /// <returns></returns>
        internal static bool ValidateBareCertificate(X509Certificate2 certificate)
        {
            Helpers.Notify($"Device presented a bare certificate");

            var deviceInfo = ExtensionDecoder.Decode(certificate);

            // check we have a good extension
            if (deviceInfo == null)
            {
                Helpers.Notify("Certificate does not have well-formed RIoT extension", true);
                return(false);
            }
            var devIdPubKeyDEREncoded = deviceInfo.EncodedDeviceIDKey;

            if (devIdPubKeyDEREncoded.Length != 65)
            {
                Helpers.Notify("Public key in extension has incorrect length", true);
                return(false);
            }

            // validating the certificate is signed with the public key encoded in the extension.
            // This is a critical security check.
            // Note: this uses the Bouncy Castle libraries
            var                   bcCert     = new X509CertificateParser().ReadCertificate(certificate.GetRawCertData());
            X9ECParameters        p          = NistNamedCurves.GetByName("P-256");
            ECDomainParameters    parameters = new ECDomainParameters(p.Curve, p.G, p.N, p.H);
            var                   pt         = parameters.Curve.DecodePoint(deviceInfo.EncodedDeviceIDKey);
            ECPublicKeyParameters bcPubKey   = new ECPublicKeyParameters(pt, parameters);

            try
            {
                bcCert.Verify(bcPubKey);
            }
            catch (Exception e)
            {
                Helpers.Notify($"Certificate is not signed using key in extension {e.ToString()}", true);
                return(false);
            }
            if (DeviceIDPEMFile != null)
            {
                // Is this key one of the keys registered with DRS (this test code only has one.)
                ECPublicKeyParameters authorizedDevice = (ECPublicKeyParameters)Helpers.ReadPemObject(DeviceIDPEMFile);
                // todo: there are probably better equality tests than this!
                bool keyIsRecognized =
                    (authorizedDevice.Q.XCoord.ToString() == bcPubKey.Q.XCoord.ToString()) &&
                    (authorizedDevice.Q.YCoord.ToString() == bcPubKey.Q.YCoord.ToString());

                if (!keyIsRecognized)
                {
                    Helpers.Notify($"DeviceID is not known", true);
                    return(false);
                }
                return(true);
            }
            // this code supports the "FakeDRSServer.  Here, any device that connects to us is presumed good

            return(true);
        }
예제 #6
0
        static void Help()
        {
            var progName = Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().MainModule.FileName);

            Helpers.Notify($"Usage:\n{progName} options");
            foreach (var p in Parms)
            {
                var px = p.NumParms == 0 ? "" : "[string parm]";
                Helpers.Notify($"     -{p.Flag} {px}            {p.Help}");
            }
        }
예제 #7
0
        public static void RunClientX()
        {
            Thread.Sleep(3000);

            // The PFX file is created by this utility, but it's just a re-packaging
            // of the Alias Key pair and the the certificate generated by RIoT

            string tempCertFile = "AliasCert.PFX";
            string password     = "";

            Helpers.MakePFXFile(aliasCert, aliasKey, tempCertFile, password);
            var clientCert = new X509Certificate2(tempCertFile);

            var certs = new X509Certificate2Collection(new X509Certificate2[] {
                clientCert
            });

            // connect to server
            TcpClient client = new TcpClient("127.0.0.1", 5556);
            //Helpers.NotifyClient("Client connected.");

            // Create an SSL stream and connect.
            SslStream sslStream = new SslStream(client.GetStream(), false,
                                                new RemoteCertificateValidationCallback(ValidateServerCertificate), null);

            try
            {
                sslStream.AuthenticateAsClient("RIoT Server CA", certs, SslProtocols.Tls, false);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine("Exception: {0}", e.Message);
                if (e.InnerException != null)
                {
                    Helpers.Notify($"Inner exception: {e.InnerException.Message}", true);
                }
                Helpers.Notify("Authentication failed - closing the connection.");
                client.Close();
                return;
            }

            byte[] messsage = Encoding.UTF8.GetBytes("GET /ABC/123");
            sslStream.Write(messsage);
            sslStream.Flush();

            // Read message from the server.
            string serverMessage = ReadMessage(sslStream);

            Helpers.Notify($"Client received: {serverMessage}");

            client.Close();
            Helpers.Notify("Client closed.");
        }
예제 #8
0
        static bool ParseParms(string[] parms)
        {
            if (parms.Length == 0)
            {
                parms = new string[] { "help" };
            }

            int j = 0;

            while (true)
            {
                if (j == parms.Length)
                {
                    break;
                }
                var  parm      = parms[j++];
                var  parmL     = parm.ToLower();
                bool processed = false;
                foreach (var p in Parms)
                {
                    if ("-" + p.Flag == parmL)
                    {
                        p.Active = true;
                        if (p.NumParms != 0)
                        {
                            if (j == parms.Length - 1)
                            {
                                Helpers.Notify($"Missing paramter for flag: {p.Flag}", true);
                                return(false);
                            }
                            p.Parameter = parms[j++];
                        }
                        ActiveParms.Add(p);
                        processed = true;
                        continue;
                    }
                }
                if (!processed)
                {
                    Helpers.Notify($"Unknown command line parameter: {parm}", true);
                    Help();
                    return(false);
                }
            }
            return(true);
        }
예제 #9
0
        internal static bool ValidateBareCertificateWithBcrypt(X509Certificate2 certificate)
        {
            var deviceInfo = ExtensionDecoder.Decode(certificate);

            // check we have a good extension
            if (deviceInfo == null)
            {
                Helpers.Notify("Certificate does not have well-formed RIoT extension", true);
                return(false);
            }
            var devIdPubKeyDEREncoded = deviceInfo.EncodedDeviceIDKey;

            if (devIdPubKeyDEREncoded.Length != 65)
            {
                Helpers.Notify("Public key in extension has incorrect length", true);
                return(false);
            }

            // We need to convert to the Windows key format before we can import
            // #define BCRYPT_ECDSA_PUBLIC_P256_MAGIC  0x31534345  // ECS1
            // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375520(v=vs.85).aspx
            byte[] windowsEncodedKey = new byte[32 * 2 + 4 + 4];
            // todo - endianess
            byte[] magic = BitConverter.GetBytes((uint)0x31534345);
            byte[] len   = BitConverter.GetBytes((uint)32);

            Array.Copy(magic, 0, windowsEncodedKey, 0, 4);
            Array.Copy(len, 0, windowsEncodedKey, 4, 4);
            Array.Copy(devIdPubKeyDEREncoded, 1, windowsEncodedKey, 8, 32);
            Array.Copy(devIdPubKeyDEREncoded, 32 + 1, windowsEncodedKey, 8 + 32, 32);

            var devIdPubKey = CngKey.Import(windowsEncodedKey, CngKeyBlobFormat.EccPublicBlob);

            ECDsaCng verifier = new ECDsaCng(devIdPubKey);

            ECDsaCng testSigner = new ECDsaCng(256);
            var      sig        = testSigner.SignData(new byte[] { 1, 2, 3, 4 });
            bool     okx        = testSigner.VerifyData(new byte[] { 1, 2, 3, 4 }, sig);

            var  bits    = ExtensionDecoder.Decompose(certificate);
            var  tbsHash = Helpers.HashData(bits.Tbs, 0, bits.Tbs.Length);
            bool ok      = verifier.VerifyHash(tbsHash, bits.Signature);


            return(true);
        }
예제 #10
0
 internal int GetTargetVersionNumber()
 {
     try
     {
         /*
          * /////////////////////////////////////////////////
          *  Look at Notes.txt for what's going on'
          * /////////////////////////////////////////////////
          */
         var twin = RegMgr.GetTwinAsync(ControlDevice).Result;
         int targetVersionNumber = (int)twin.Properties.Desired["VersionNumber"];
         return(targetVersionNumber);
     }
     catch (Exception ex)
     {
         Helpers.Notify($"Failed to get control device twin. Error is {ex.ToString()}");
         return(0);
     }
 }
예제 #11
0
        public static void ValidateEmulatorChain(string alias, string deviceID, string root)
        {
            try
            {
                X509Certificate2 aliasCert = new X509Certificate2();
                X509Certificate2 devIDCert = null;
                X509Certificate2 rootCert  = new X509Certificate2();

                rootCert.Import(Helpers.GetBytesFromPEM(root, "CERTIFICATE"));

                aliasCert = new X509Certificate2(Helpers.GetBytesFromPEM(alias, "CERTIFICATE"));
                devIDCert = new X509Certificate2(Helpers.GetBytesFromPEM(deviceID, "CERTIFICATE"));
                rootCert  = new X509Certificate2(Helpers.GetBytesFromPEM(root, "CERTIFICATE"));

                var chain = new X509Chain
                {
                    ChainPolicy =
                    {
                        RevocationMode    = X509RevocationMode.NoCheck,
                        RevocationFlag    = X509RevocationFlag.ExcludeRoot,
                        VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority
                    }
                };

                //chain.ChainPolicy.ExtraStore.Add(devIDCert);
                chain.ChainPolicy.ExtraStore.Add(rootCert);

                bool chainBuildSucceeded = chain.Build(aliasCert as X509Certificate2 ?? new X509Certificate2(aliasCert.Export(X509ContentType.Cert)));

                if (!chainBuildSucceeded)
                {
                    foreach (var err in chain.ChainStatus)
                    {
                        Helpers.Notify($"Error:{err.StatusInformation.ToString()}", true);
                    }
                }
            }
            catch (Exception e)
            {
                Helpers.Notify($"ValidateEmulatorChain error {e.ToString()}");
            }
        }
예제 #12
0
 internal bool RemoveDevice(string devId)
 {
     // quick try to see if it exists
     if (!GetDeviceIDs().Contains(devId))
     {
         // nothing to do.
         return(false);
     }
     // else try to delete
     try
     {
         RegMgr.RemoveDeviceAsync(devId);
     }
     catch (Exception e)
     {
         Helpers.Notify($"Failed to remove device. Error is {e.ToString()}");
         return(false);
     }
     return(true);
 }
예제 #13
0
        /// <summary>
        /// Make an administrative connection to the hub
        /// </summary>
        /// <returns></returns>
        internal bool Connect()
        {
            if (!File.Exists(ConnectionStringFile))
            {
                Helpers.Notify($"Missing connection string file {ConnectionStringFile}.  This file is omitted from the distribution because it contains passwords.", true);
                Helpers.Notify($"The file should contain somehting like this:", true);
                Helpers.Notify($"HostName=pengland.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=0frrKXXXSomeStuffXXXXvWQ=", true);
                return(false);
            }
            var connectionString = File.ReadAllText(ConnectionStringFile);

            try
            {
                RegMgr = RegistryManager.CreateFromConnectionString(connectionString);
            }
            catch (Exception e)
            {
                Helpers.Notify($"Failed to CreateFromConnectionString: {e.ToString()}");
                return(false);
            }
            return(true);
        }
예제 #14
0
        static void ProcessFakeDRSMessage(SslStream s)
        {
            lock (HubLock)
            {
                try
                {
                    // clients send us their Id
                    string messageFromClient = ReadMessage(s);

                    X509Certificate2 cert = new X509Certificate2(s.RemoteCertificate);
                    UpdateDemo.HubController.FakeDRSServerEnrollOrRefreshDevice(messageFromClient, cert.Thumbprint);
                    Debug.Write($"Device {messageFromClient} connected");

                    SendMessage(s, "OK");
                    Thread.Sleep(30);
                }
                catch (Exception e)
                {
                    Helpers.Notify($"ProcessFakeDRSMessage error {e.ToString()}");
                }
            }
        }
예제 #15
0
 static RIoTDeviceInfo ParseError(string error)
 {
     Helpers.Notify($"Extension parsing error: {error}", true);
     return(null);
 }
예제 #16
0
        static void Main(string[] args)
        {
            // This invokes testing using WeClient, etc.  Not yet working.
            //HttpsListener.StartListener(IODir + ServerCert, IODir + ServerKey, IODir + ServerCA, IODir+AliasCert, IODir+AliasKey);

            InitParms();
            bool ok = ParseParms(args);

            if (!ok)
            {
                return;
            }

            foreach (var action in ActiveParms)
            {
                if (action.Flag == "dir")
                {
                    IODir = action.Parameter;
                    if (!IODir.EndsWith("\\"))
                    {
                        IODir += "\\";
                    }
                    continue;
                }

                if (action.Flag == "gentest")
                {
                    CertMaker m = new CertMaker(IODir);
                    m.MakeNew(5, false, 0);
                    continue;
                }

                if (action.Flag == "bare")
                {
                    ChainOrBareCert = "B";
                    continue;
                }

                if (action.Flag == "certify")
                {
                    CertMaker m = new CertMaker(IODir);
                    m.CertifyExisting(5);
                    continue;
                }

                if (action.Flag == "certifyj")
                {
                    CertMaker m = new CertMaker(IODir);
                    m.CertifyExistingForJava(5);
                    continue;
                }
                if (action.Flag == "csr")
                {
                    CertMaker m = new CertMaker(IODir);
                    m.CertifyExistingFromCsr(5);
                    continue;
                }

                if (action.Flag == "server")
                {
                    SslTcpServer.RunServer(
                        ToPath(Program.ServerCA),
                        ToPath(Program.ServerCert),
                        ToPath(Program.ServerKey),
                        ToPath(Program.DeviceCA),
                        ToPath(Program.DeviceIDPublic)
                        );
                    continue;
                }

                if (action.Flag == "testemu")
                {
                    SslTcpServer.ValidateEmulatorChain(@"AliasCert.pem", @"DeviceIDCrt.pem", @"r00tcrt.pem");
                    continue;
                }

                if (action.Flag == "sc")
                {
                    Helpers.Notify("Starting TLSClient...");
                    var psi = new ProcessStartInfo("TlsClient.exe");
                    psi.Arguments       = ChainOrBareCert + " " + IODir;
                    psi.UseShellExecute = true;
                    var proc = Process.Start(psi);;

                    SslTcpServer.RunServer(
                        ToPath(Program.ServerCA),
                        ToPath(Program.ServerCert),
                        ToPath(Program.ServerKey),
                        ToPath(Program.DeviceCA),
                        ToPath(Program.DeviceIDPublic)
                        );
                    proc.WaitForExit();
                    continue;
                }

                if (action.Flag == "nogen")
                {
                    MakeCerts = false;
                    continue;
                }

                if (action.Flag == "e2e")
                {
                    if (MakeCerts)
                    {
                        Helpers.Notify("Making a new certificate set");
                        CertMaker m = new CertMaker(IODir);
                        m.MakeNew(5, false, 0);
                        //m.MakeNew(5, true, 1);
                    }

                    Helpers.Notify("Starting TLSClient...");
                    var psi = new ProcessStartInfo("TlsClient.exe");
                    psi.Arguments       = ChainOrBareCert + " " + IODir;
                    psi.UseShellExecute = true;
                    var proc = Process.Start(psi);;

                    SslTcpServer.RunServer(
                        ToPath(Program.ServerCA),
                        ToPath(Program.ServerCert),
                        ToPath(Program.ServerKey),
                        ToPath(Program.DeviceCA),
                        ToPath(Program.DeviceIDPublic)
                        );
                    proc.WaitForExit();
                    continue;
                }

                if (action.Flag == "ossl_server")
                {
                    Helpers.Notify("OpenSSL s_server parameters for TLS test server (start in directory with certificates and files)");
                    Helpers.Notify($"openssl s_server -cert {ToPath(ServerCert)} -key {ToPath(ServerKey)} -CAfile {ToPath(DeviceCertChainAndServerCA)} -status_verbose -verify 10 -rev -accept 5556");
                    continue;
                }
                if (action.Flag == "ossl_client")
                {
                    Helpers.Notify("OpenSSL s_client parameters for TLS test client (start in directory with certificates and files)");
                    Helpers.Notify($"openssl s_client -connect localhost:5556 -cert {ToPath(AliasCert)} -key {ToPath(AliasKey)} -CAfile {ToPath(DeviceCertChainAndServerCA)}");
                    continue;
                }
                if (action.Flag == "tls_client")
                {
                    Helpers.Notify("Starting TLSClient...");
                    var psi = new ProcessStartInfo("TlsClient.exe");
                    psi.Arguments             = ChainOrBareCert + " " + IODir;
                    psi.CreateNoWindow        = true;
                    psi.UseShellExecute       = false;
                    psi.RedirectStandardError = true;
                    var    proc = Process.Start(psi);;
                    string op   = proc.StandardError.ReadToEnd();
                    proc.WaitForExit();
                    Helpers.Notify(op);
                    continue;
                }

                if (action.Flag == "demo")
                {
                    var demo = new UpdateDemo();
                    demo.FakeDRSTest();
                }
            }

            if (System.Diagnostics.Debugger.IsAttached)
            {
                Thread.Sleep(3000);
            }

            return;
        }
예제 #17
0
        internal bool FakeDRSServerHandshake(string devId)
        {
            string tempCertFile = "AliasCert.PFX";
            string password     = "";

            Helpers.MakePFXFile(Program.ToPath(Program.AliasCert), Program.ToPath(Program.AliasKey), tempCertFile, password);
            var clientCert = new X509Certificate2(tempCertFile);

            var certs = new X509Certificate2Collection(new X509Certificate2[] { clientCert });
            // connect to server
            TcpClient client = new TcpClient("127.0.0.1", 5556);
            // Create an SSL stream and connect.
            SslStream sslStream = new SslStream(client.GetStream(), false,
                                                new RemoteCertificateValidationCallback(ValidateServerCertificate), null);

            try
            {
                sslStream.AuthenticateAsClient("RIoT Server CA", certs, SslProtocols.Tls, false);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine("Exception: {0}", e.Message);
                if (e.InnerException != null)
                {
                    Helpers.Notify($"Inner exception: {e.InnerException.Message}", true);
                }
                Helpers.Notify("Authentication failed - closing the connection.");
                client.Close();
                return(false);
            }

            sslStream.ReadTimeout  = 10000;
            sslStream.WriteTimeout = 10000;


            SslTcpServer.SendMessage(sslStream, devId);
            string messageFromServer = SslTcpServer.ReadMessage(sslStream);

            /*
             * byte[] message = Encoding.UTF8.GetBytes(devId);
             * byte[] len = new byte[] { (byte) message.Length };
             * sslStream.Write(len, 0, 1);
             * sslStream.Write(message,0, message.Length);
             * sslStream.Flush();
             * byte[] buf = new byte[1024];
             * int numRead = sslStream.Read(buf, 0, 1);
             * if(numRead!=1)
             * {
             *  Helpers.Notify("TLSClient got a bad message from the server");
             * }
             * int pos = 0;
             * int lenX = (int) buf[0];
             * while (true)
             * {
             *  numRead = sslStream.Read(buf, pos, lenX - pos);
             *  pos += numRead;
             *  if (pos == lenX) break;
             * }
             * string serverMessage = Encoding.UTF8.GetString(buf, 0, lenX);
             */
            Helpers.Notify($"Client received: {messageFromServer}");
            Thread.Sleep(30);

            client.Close();
            Helpers.Notify("Client closed.");
            return(true);
        }
예제 #18
0
        /// <summary>
        /// This callback validates a RIoT certificate chain or a bare Alias certificate.
        /// We generally don't want to validate a chain against the user or machine cert store,
        /// so we do it (semi) manually with X509Chain.  If the device only presents the Alias
        /// Certificate (no chain) we call it a "bare certificate.)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="_certificate"></param>
        /// <param name="inChain"></param>
        /// <param name="sslPolicyErrors"></param>
        /// <returns></returns>
        public static bool ValidateDeviceCertificate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate _certificate, X509Chain inChain,
                                                     SslPolicyErrors sslPolicyErrors)
        {
            //ValidateBareCertificateWithBcrypt(new X509Certificate2(_certificate));


            if (_certificate == null)
            {
                // not sure that this can happen
                Helpers.Notify($"No certificate presented by client", true);
                return(false);
            }

            // This is the leaf (alias) certificate
            X509Certificate2 certificate = new X509Certificate2(_certificate);

            // Did the device just send one certificate?  If so, we don't check the chain: we just
            // check that the certificate was signed by a known DeviceID in the routine below.
            // Note that if the built-in chain builder found certificates in the system store to build
            // chain, then inChain will contain those and the processing will follow the "chain" rules
            // rather than the bare-certificate validation rules.
            if (inChain.ChainElements.Count == 1)
            {
                Helpers.Notify($"count==1", true);
                return(ValidateBareCertificate(certificate));
            }

            // Else we have a chain...
            // We need at least 3 certificates:
            //      Alias
            //      DeviceID (issued by vendor)
            //      [zero or any number of intermediate CA]
            //      Device Vendor CA

            int chainLength = inChain.ChainElements.Count;

            if (chainLength < 3)
            {
                Helpers.Notify($"Chain length too short: {chainLength}", true);
                return(false);
            }
            Helpers.Notify($"Device presented a certificate chain of length {inChain.ChainElements.Count}");

            // Put the device-provided chain in a new X509Chain so that we can validate it
            X509Chain chain = new X509Chain(false);

            // todo: this seems like a reasonable starting point for the flags
            chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
            chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
            // Note: this flag seems to be ignored.  In any case, we don't use the
            // machine or user-context cert store: we check the root against DeviceCA
            // provided as a parameter during class instantiation (or a database of authorized
            // CAs in the final service.)
            chain.ChainPolicy.VerificationFlags |= X509VerificationFlags.AllowUnknownCertificateAuthority;

            // Add the intermediate and root-CA certs that came out of the TLS session.
            foreach (var c in inChain.ChainElements)
            {
                chain.ChainPolicy.ExtraStore.Add(c.Certificate);
            }

            // Can we build a chain using the leaf-cert and the rest of the provided certs?
            bool valid = chain.Build(new X509Certificate2(certificate));

            if (!valid)
            {
                Helpers.Notify($"Chain building failed: {chainLength}", true);
                foreach (var err in chain.ChainStatus)
                {
                    Helpers.Notify($"        Error:{err.StatusInformation.ToString()}", true);
                }
                return(false);
            }
            // Get the chain we built.  Regardless of what the client sent, we
            // want a chain of 3 or more certificates (alias, DevID, [...], CA) in our
            // chain.
            var deviceCertChain = chain.ChainElements;

            if (deviceCertChain.Count < 3)
            {
                Helpers.Notify($"Chain length too short: {deviceCertChain.Count}", true);
                return(false);
            }

            // Is the root one of the registered CAs for this DRS instance?
            // Here we just recognize a single root
            var thisDeviceRootCert = deviceCertChain[deviceCertChain.Count - 1];

            if (thisDeviceRootCert.Certificate.Thumbprint != DeviceCA.Thumbprint)
            {
                Helpers.Notify($"Device not certified by a known/authorized CA", true);
                return(false);
            }

            // Next, extract the RIoT extension, and see if it is present/well-formed
            var aliasCert    = deviceCertChain[0].Certificate;
            var deviceIDCert = deviceCertChain[1].Certificate;

            var deviceInfo = ExtensionDecoder.Decode(aliasCert);

            // check we have a good extension
            if (deviceInfo == null)
            {
                Helpers.Notify("Certificate does not have well-formed RIoT extension", true);
                return(false);
            }
            // Check that the DevID claimed in the RIoT extension matches that defined by the
            // cert chain.  This is a *critical* security check if the server just wants to authenticate
            // based on the Alias (leaf) certificate rather than the DeviceID cert and the Alias Cert.
            var encodedDeviceID = deviceIDCert.PublicKey.EncodedKeyValue;

            if (!Helpers.ArraysAreEqual(encodedDeviceID.RawData, deviceInfo.EncodedDeviceIDKey))
            {
                Helpers.Notify("Alias Certificate DeviceID does not match DeviceID certificate", true);
                return(false);
            }
            Helpers.Notify("All RIoT Certificate checks passed");
            return(true);
        }
예제 #19
0
        private static void ProcessClient(TcpClient client, bool chat)
        {
            SslStream sslStream = new SslStream(client.GetStream(), false, ValidateDeviceCertificate);

            try
            {
                // authenticate as server and request client cert.  This invokes the ValidateDeviceCertificate
                // callback.  If the callback returns false (or there are other errors), AuthenticateAsServer
                // throws an exception
                sslStream.AuthenticateAsServer(ServerCert, true, SslProtocols.Tls, false);
                // if we get to here, all TLS+RIoT checks have succeeded.
                // Print info about the connected device
                var deviceCert = sslStream.RemoteCertificate;
                var devCert2   = new X509Certificate2(deviceCert);
                var info       = ExtensionDecoder.Decode(devCert2);
                if (info == null)
                {
                    // should not happen since the cert has already been
                    // validated in the callback..
                    Helpers.Notify("Unexpected missing or malformed RIoT device certificate", true);
                    return;
                }
                // we have a good device: tell the world
                Helpers.Notify($"RIoT Device Connected:");
                Helpers.Notify($"            DeviceID:{Helpers.Hexify(info.EncodedDeviceIDKey).Substring(0, 60) + "..."}");
                Helpers.Notify($"                FWID:{Helpers.Hexify(info.FirmwareID)}");

                if (chat)
                {
                    // Read a message from the client.
                    sslStream.ReadTimeout  = 10000;
                    sslStream.WriteTimeout = 10000;
                    Helpers.Notify("Waiting for client message...");
                    if (DeviceIDPEMFile != null)
                    {
                        string messageData = ReadMessageX(sslStream);
                        Helpers.Notify($"Server received: {messageData}");
                        // Write a message to the client.
                        byte[] message = Encoding.UTF8.GetBytes("Hello from the server.<EOF>");
                        Helpers.Notify("Sending hello message.");
                        sslStream.Write(message);
                    }
                    else
                    {
                        ProcessFakeDRSMessage(sslStream);
                    }
                }
                // give the client some time to process before closing the stream
                Thread.Sleep(30);
            }
            catch (AuthenticationException e)
            {
                Helpers.Notify($"Exception in AuthenticateAs server block: {e.Message}", true);
                if (e.InnerException != null)
                {
                    Helpers.Notify($"Inner exception: {e.InnerException.Message}", true);
                }
                Helpers.Notify("Authentication failed - closing the connection.", true);
                sslStream.Close();
                client.Close();
                return;
            }
            finally
            {
                Helpers.Notify("Client has disconnected. Stream is closing");
                sslStream.Close();
                client.Close();
            }
        }
예제 #20
0
        static internal RIoTDeviceInfo Decode(X509Certificate2 aliasCert)
        {
            AsnEncodedData altNames = null;

            foreach (var ext in aliasCert.Extensions)
            {
                if (ext.Oid.Value != RIoTOid)
                {
                    continue;
                }
                altNames = new AsnEncodedData(ext.Oid, ext.RawData);
            }
            // an AltName is mandatory
            if (altNames == null)
            {
                Helpers.Notify("Certificate does not have an altName field", true);
                return(null);
            }
            // parse the extension: this is a collection of nested thus -

            /*
             *  DER Sequence
             *      ObjectIdentifier(1.2.3.4.5.6)                               <- RIoT Composite ID OID
             *      DER Sequence
             *          Integer(1)                                              <- Version number
             *          DER Sequence                                            <- DeviceID public key
             *              DER Sequence                                            (same encoding as in DeviceID cert)
             *                  ObjectIdentifier(1.2.840.10045.2.1)                 EC pubkey
             *                  ObjectIdentifier(1.2.840.10045.3.1.7)               prime256
             *              DER Bit String[65, 0]                                   key value
             *          DER Sequence                                            <-  Encoded FWID
             *              ObjectIdentifier(2.16.840.1.101.3.4.2.1)                sha256
             *              DER Octet String[32]                                    FWID hash value
             *
             *
             * */

            try
            {
                DerSequence seq = (DerSequence)DerSequence.FromByteArray(altNames.RawData);
                //DerTaggedObject obj = (DerTaggedObject)seq[0];
                //DerSequence obj2 = (DerSequence)obj.GetObject();
                //var oid = (DerObjectIdentifier)obj2[0];
                //if (oid.Id != RIoTOid) return ParseError("Incorrect RIoT OID");


                var versionNumber = (DerInteger)seq[0];
                if (versionNumber.PositiveValue.IntValue != 1)
                {
                    return(ParseError("Wrong version number"));
                }

                DerSequence obj4    = (DerSequence)seq[1];
                DerSequence obj5    = (DerSequence)obj4[0];
                var         keyAlg1 = (DerObjectIdentifier)obj5[0];
                var         keyAlg2 = (DerObjectIdentifier)obj5[1];
                if (keyAlg1.Id != ecPubKeyOID)
                {
                    return(ParseError("Bad ECPubKey OID"));
                }
                if (keyAlg2.Id != prime256v1Oid)
                {
                    return(ParseError("Bad curve OID"));
                }
                var key     = (DerBitString)obj4[1];
                var obj4b   = (DerSequence)seq[2];
                var hashAlg = (DerObjectIdentifier)obj4b[0];
                if (hashAlg.Id != sha256Oid)
                {
                    return(ParseError("Bad fwid hash OID"));
                }
                var            hash       = (DerOctetString)obj4b[1];
                RIoTDeviceInfo deviceInfo = new RIoTDeviceInfo()
                {
                    FirmwareID         = hash.GetOctets(),
                    EncodedDeviceIDKey = key.GetBytes(),
                    Cert = aliasCert
                };

                return(deviceInfo);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.ToString());
                return(null);
            }
        }