зеркало из https://github.com/microsoft/RIoT.git
Added RIoT test utilities
This commit is contained in:
Родитель
ee09fe471b
Коммит
dff03f0b3f
|
@ -0,0 +1,63 @@
|
|||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
42
LICENSE
42
LICENSE
|
@ -1,21 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
# Robust Internet of Things, Microsoft Research
|
||||
README
|
||||
# Robust Internet of Things, Microsoft Research
|
||||
README
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
Create chains given a CSR (and an alias cert)
|
||||
-dir d:\tmp\Emulator -csr
|
||||
|
||||
|
||||
-nogen -dir d:\tmp\Emulator -e2e
|
Двоичный файл не отображается.
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
||||
</startup>
|
||||
</configuration>
|
|
@ -0,0 +1,468 @@
|
|||
using Org.BouncyCastle.Asn1;
|
||||
using Org.BouncyCastle.Asn1.Utilities;
|
||||
using Org.BouncyCastle.Asn1.Nist;
|
||||
using Org.BouncyCastle.Asn1.Sec;
|
||||
using Org.BouncyCastle.Asn1.X509;
|
||||
using Org.BouncyCastle.Asn1.X9;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Generators;
|
||||
using Org.BouncyCastle.Crypto.Operators;
|
||||
using Org.BouncyCastle.Crypto.Prng;
|
||||
using Org.BouncyCastle.Math;
|
||||
using Org.BouncyCastle.Security;
|
||||
using Org.BouncyCastle.X509;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Pkcs;
|
||||
|
||||
namespace RIoT
|
||||
{
|
||||
class DeviceBundle
|
||||
{
|
||||
internal AsymmetricCipherKeyPair AliasKeyPair;
|
||||
internal X509Certificate AliasCert;
|
||||
internal AsymmetricKeyParameter DeviceIDPublic;
|
||||
}
|
||||
/// <summary>
|
||||
/// A certificate and a key pair
|
||||
/// </summary>
|
||||
internal class CertBundle
|
||||
{
|
||||
internal X509Certificate Certificate;
|
||||
internal AsymmetricCipherKeyPair KeyPair;
|
||||
}
|
||||
|
||||
|
||||
|
||||
class CertMaker
|
||||
{
|
||||
internal CertMaker(string dir)
|
||||
{
|
||||
if(!Directory.Exists(dir))
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
}
|
||||
|
||||
internal void CertifyExisting(int chainLen)
|
||||
{
|
||||
DeviceBundle bundle = new DeviceBundle();
|
||||
|
||||
bundle.AliasCert = (X509Certificate) Helpers.ReadPemObject(ToPath(Program.AliasCert));
|
||||
bundle.DeviceIDPublic = (AsymmetricKeyParameter)Helpers.ReadPemObject(ToPath(Program.DeviceIDPublic));
|
||||
bundle.AliasKeyPair = (AsymmetricCipherKeyPair)Helpers.ReadPemObject(ToPath(Program.AliasKey));
|
||||
|
||||
MakeCertChain(bundle, chainLen, 0);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
internal void MakeNew(int chainLen, bool refresh, int fwidSeed)
|
||||
{
|
||||
var bundle = MakeAliasCert(refresh, fwidSeed);
|
||||
MakeCertChain(bundle, chainLen, fwidSeed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make a new Alias Cert. If refresh=false, a new DevID and Alias are created. If refresh=true
|
||||
/// then just the Alias is created and re-certified using the stored DevID key.
|
||||
/// </summary>
|
||||
/// <param name="refresh"></param>
|
||||
/// <returns></returns>
|
||||
internal DeviceBundle MakeAliasCert(bool refresh, int fwidSeed)
|
||||
{
|
||||
DateTime now = DateTime.Now;
|
||||
byte[] fwid = Helpers.HashData(new byte[1] { (byte) fwidSeed }, 0, 1);
|
||||
|
||||
const int keyStrength = 256;
|
||||
CryptoApiRandomGenerator rg = new CryptoApiRandomGenerator();
|
||||
SecureRandom random = new SecureRandom(rg);
|
||||
KeyGenerationParameters keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
|
||||
var keyPairGenerator = new ECKeyPairGenerator();
|
||||
keyPairGenerator.Init(keyGenerationParameters);
|
||||
|
||||
AsymmetricCipherKeyPair devIdKey = null;
|
||||
if (refresh)
|
||||
{
|
||||
devIdKey = (AsymmetricCipherKeyPair)Helpers.ReadPemObject(ToPath(Program.DeviceIDPrivate));
|
||||
}
|
||||
else
|
||||
{
|
||||
devIdKey = keyPairGenerator.GenerateKeyPair();
|
||||
}
|
||||
|
||||
// test - remove
|
||||
var oids = new List<Object>() { X509Name.UnstructuredName };
|
||||
var values = new List<Object>() { "ljkljljklkjlkjlkjlkjlkjlkjlkjlkjlkjljklkjlkjlkjlkjljk" };
|
||||
X509Name name = new X509Name(oids, values);
|
||||
|
||||
|
||||
|
||||
AsymmetricCipherKeyPair aliasKey = keyPairGenerator.GenerateKeyPair();
|
||||
|
||||
// make a string name based on DevID public. Note that the authoritative information
|
||||
// is encoded in the RIoT-extension: this is just for quick-and-dirty device identification.
|
||||
var pubInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(devIdKey.Public);
|
||||
byte[] pubEncoded = pubInfo.GetDerEncoded();
|
||||
var pubHashed = Helpers.HashData(pubEncoded, 0, pubEncoded.Length);
|
||||
var shortNameBytes = Helpers.CopyArray(pubHashed, 0, 8);
|
||||
var shortNameString = Helpers.Hexify(shortNameBytes);
|
||||
|
||||
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
|
||||
var serialNumber = new byte[8];
|
||||
rg.NextBytes(serialNumber);
|
||||
serialNumber[0] &= 0x7F;
|
||||
certGen.SetSerialNumber(new BigInteger(serialNumber));
|
||||
// The important name-related stuff is encoded in the RIoT extension
|
||||
certGen.SetIssuerDN(new X509Name($"CN=[I]DevID:{shortNameString}, O=MSR_TEST, C=US"));
|
||||
// test REMOVE
|
||||
//certGen.SetSubjectDN(name);
|
||||
certGen.SetSubjectDN(new X509Name($"CN=[S]DevID:{shortNameString}, O=MSR_TEST, C=US"));
|
||||
certGen.SetNotBefore(now);
|
||||
certGen.SetNotAfter(now + new TimeSpan(365 * 10, 0, 0, 0, 0));
|
||||
certGen.SetPublicKey(aliasKey.Public);
|
||||
|
||||
// Add the extensions (todo: not sure about KeyUsage.DigitalSiganture
|
||||
certGen.AddExtension(X509Extensions.ExtendedKeyUsage, true,
|
||||
ExtendedKeyUsage.GetInstance(new DerSequence(KeyPurposeID.IdKPClientAuth)));
|
||||
certGen.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.DigitalSignature));
|
||||
AddRIoTExtension(certGen, fwid, devIdKey);
|
||||
|
||||
// sign it
|
||||
ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA256WITHECDSA", devIdKey.Private, random);
|
||||
var certificate = certGen.Generate(signatureFactory);
|
||||
// and return the bundle
|
||||
DeviceBundle bundle = new DeviceBundle
|
||||
{
|
||||
AliasCert = certificate,
|
||||
DeviceIDPublic = devIdKey.Public,
|
||||
AliasKeyPair = aliasKey
|
||||
};
|
||||
|
||||
// Just the AliasCert
|
||||
Helpers.WritePEMObject(ToPath(Program.AliasCert), bundle.AliasCert);
|
||||
// The Alias Key Pair
|
||||
Helpers.WritePEMObject(ToPath(Program.AliasKey), bundle.AliasKeyPair);
|
||||
// The encoded DevID
|
||||
Helpers.WritePEMObject(ToPath(Program.DeviceIDPublic), bundle.DeviceIDPublic);
|
||||
// DeviceIDPrivate (just for the update demo)
|
||||
Helpers.WritePEMObject(ToPath(Program.DeviceIDPrivate), devIdKey.Private);
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
|
||||
internal void MakeCertChain(DeviceBundle bundle, int chainLen, int fwidSeed)
|
||||
{
|
||||
var aliasCert = bundle.AliasCert;
|
||||
|
||||
DateTime now = DateTime.Now;
|
||||
byte[] fwid = Helpers.HashData(new byte[1] { 0 }, 0, 1);
|
||||
const int keyStrength = 256;
|
||||
CryptoApiRandomGenerator rg = new CryptoApiRandomGenerator();
|
||||
SecureRandom random = new SecureRandom(rg);
|
||||
KeyGenerationParameters keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
|
||||
var keyPairGenerator = new ECKeyPairGenerator();
|
||||
keyPairGenerator.Init(keyGenerationParameters);
|
||||
|
||||
// Starting the loop we have a (not yet certified) DeviceID public key
|
||||
// and the Issuer of the Alias Cert (which we want to be the DevID-cert DN.)
|
||||
|
||||
List<X509Certificate> certChain = new List<X509Certificate>();
|
||||
|
||||
var lastCertIssuer = aliasCert.IssuerDN;
|
||||
var lastPubKey = bundle.DeviceIDPublic; ;
|
||||
AsymmetricCipherKeyPair lastKeyPair = null;
|
||||
for (int j = 0; j < chainLen; j++)
|
||||
{
|
||||
bool rootCert = j == chainLen - 1;
|
||||
bool lastButOne = j == chainLen - 2;
|
||||
AsymmetricCipherKeyPair caKey = keyPairGenerator.GenerateKeyPair();
|
||||
|
||||
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
|
||||
certGen.SetSerialNumber(new BigInteger(new byte[] { 1, 2, 3, 4, 5 }));
|
||||
var issuerDn = lastButOne ?
|
||||
new X509Name($"CN=Vendor Root CA O=MSR_TEST, C=US") :
|
||||
new X509Name($"CN=Vendor Intermediate CA {j}, O=MSR_TEST, C=US");
|
||||
if (rootCert)
|
||||
{
|
||||
issuerDn = lastCertIssuer;
|
||||
}
|
||||
|
||||
certGen.SetIssuerDN(issuerDn);
|
||||
certGen.SetSubjectDN(lastCertIssuer);
|
||||
certGen.SetNotBefore(now);
|
||||
certGen.SetNotAfter(now + new TimeSpan(365 * 10, 0, 0, 0, 0));
|
||||
certGen.SetPublicKey(lastPubKey);
|
||||
|
||||
|
||||
|
||||
int pathLengthConstraint = j + 1;
|
||||
certGen.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(pathLengthConstraint));
|
||||
certGen.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.KeyCertSign));
|
||||
X509Certificate certificate;
|
||||
|
||||
if (rootCert)
|
||||
{
|
||||
ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA256WITHECDSA", lastKeyPair.Private, random);
|
||||
certificate = certGen.Generate(signatureFactory);
|
||||
}
|
||||
else
|
||||
{
|
||||
ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA256WITHECDSA", caKey.Private, random);
|
||||
certificate = certGen.Generate(signatureFactory);
|
||||
}
|
||||
|
||||
|
||||
lastCertIssuer = certificate.IssuerDN;
|
||||
lastPubKey = caKey.Public;
|
||||
lastKeyPair = caKey;
|
||||
certChain.Add(certificate);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Chain including root, but not AliasCert. NOTE, finishes with CA
|
||||
Helpers.WritePEMObjects(ToPath(Program.DeviceCertChain), certChain.ToArray());
|
||||
// cert chain including AliasCert (alias cert is first. CA is last)
|
||||
JoinFiles(Program.AliasCert, Program.DeviceCertChain, Program.DeviceCertChainIncAlias);
|
||||
|
||||
// just the device CA (for the server)
|
||||
Helpers.WritePEMObject(ToPath("DeviceCA.pem"), certChain.Last());
|
||||
|
||||
|
||||
// Now make some certs for the server to use
|
||||
string serverCaName = "CN=Server CA, C=US, O=MSR_TEST";
|
||||
string serverName = "CN=Server Cert, C=US, O=MSR_TEST";
|
||||
|
||||
var serverCA = MakeCertInternal(serverCaName, serverCaName, true, null, null, 1);
|
||||
var serverCert = MakeCertInternal(serverCaName, serverName, false, serverCA.KeyPair, null, 0);
|
||||
|
||||
Helpers.WritePEMObject(ToPath(Program.ServerCA), serverCA.Certificate);
|
||||
Helpers.WritePEMObjects(ToPath(Program.ServerChain), new Object[] { serverCA.Certificate, serverCert.Certificate });
|
||||
//Helpers.WritePEMObject(ToPath("T0_ServerCAKey.PEM", serverCA.KeyPair);
|
||||
Helpers.WritePEMObject(ToPath(Program.ServerCert), serverCert.Certificate);
|
||||
Helpers.WritePEMObject(ToPath(Program.ServerKey), serverCert.KeyPair);
|
||||
|
||||
// OpenSSL needs a file with the Device CA AND the server CA
|
||||
JoinFiles(Program.DeviceCertChain, Program.ServerCA, Program.DeviceCertChainAndServerCA);
|
||||
|
||||
// OpenSSL test scripts -
|
||||
|
||||
// print a cert
|
||||
// openssl x509 -text -in T0_ServerCA.pem
|
||||
|
||||
// Just verify the client chain
|
||||
// openssl verify -purpose sslclient -CAfile T0_DeviceCertChain.PEM DeviceAliasCert.PEM
|
||||
|
||||
// Just verify the server chain
|
||||
// openssl verify -purpose sslserver -CAfile T0_ServerCA.PEM T0_ServerCert.PEM
|
||||
|
||||
|
||||
// openssl s_client -connect localhost: 5556 - cert T0_AliasCert.PEM - key T0_AliasKey.PEM - CAfile T0_DeviceCertChainAndServerCA.PEM
|
||||
// openssl s_client -connect localhost:5556 -cert T0_AliasCert.PEM -key T0_AliasKey.PEM -CAfile T0_DeviceCertChain.PEM
|
||||
// openssl s_server -cert T0_ServerCert.PEM -key T0_ServerKey.PEM -CAfile T0_DeviceCertChainAndServerCA.PEM -status_verbose -verify 10 -rev -accept 5556
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void AddRIoTExtension(X509V3CertificateGenerator certGen, byte[] fwid, AsymmetricCipherKeyPair devIdKey)
|
||||
{
|
||||
DerObjectIdentifier extensionTag = new DerObjectIdentifier(Program.DeviceIdOid);
|
||||
DerOctetString fwId = new DerOctetString(fwid);
|
||||
|
||||
var TaggedDevID = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(devIdKey.Public);
|
||||
|
||||
// {hashAlgId,fwidHash}
|
||||
var TaggedFWID = new DerSequence(
|
||||
new Asn1Encodable[]
|
||||
{
|
||||
NistObjectIdentifiers.IdSha256,
|
||||
new DerOctetString(fwid)
|
||||
});
|
||||
|
||||
// {1, devId, fwId}
|
||||
var EncodedRIoTIdentity = new DerSequence(
|
||||
new Asn1Encodable[]
|
||||
{
|
||||
new DerInteger(1), // version number
|
||||
TaggedDevID,
|
||||
TaggedFWID
|
||||
});
|
||||
|
||||
// {riotOid, encodedIdentity}
|
||||
var TaggedEncodedRIoTID = new DerSequence(
|
||||
new Asn1Encodable[]
|
||||
{
|
||||
new DerObjectIdentifier(Program.DeviceIdOid),
|
||||
EncodedRIoTIdentity
|
||||
});
|
||||
|
||||
|
||||
byte[] ttt = TaggedEncodedRIoTID.GetDerEncoded();
|
||||
Debug.WriteLine(Helpers.Hexify(ttt));
|
||||
|
||||
Debug.WriteLine(Asn1Dump.DumpAsString(TaggedEncodedRIoTID));
|
||||
|
||||
certGen.AddExtension(
|
||||
X509Extensions.SubjectAlternativeName.Id,
|
||||
true,
|
||||
new GeneralNames(
|
||||
new GeneralName(GeneralName.OtherName, TaggedEncodedRIoTID)));
|
||||
}
|
||||
|
||||
|
||||
private static CertBundle MakeCertInternal(
|
||||
string issuerName, string subjectName,
|
||||
bool isCA,
|
||||
AsymmetricCipherKeyPair signerKey = null,
|
||||
AsymmetricCipherKeyPair certKey = null,
|
||||
int pathLengthConstraint = 0)
|
||||
{
|
||||
const int keyStrength = 256;
|
||||
CryptoApiRandomGenerator rg = new CryptoApiRandomGenerator();
|
||||
SecureRandom random = new SecureRandom(rg);
|
||||
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
|
||||
|
||||
// make a new ECC key pair. The pubKey will go in the cert. The private key may or may
|
||||
// not be used for signing, depending on whether the caller provides a signing key.
|
||||
KeyGenerationParameters keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
|
||||
var keyPairGenerator = new ECKeyPairGenerator();
|
||||
keyPairGenerator.Init(keyGenerationParameters);
|
||||
AsymmetricCipherKeyPair newKey = keyPairGenerator.GenerateKeyPair();
|
||||
|
||||
if (certKey != null)
|
||||
{
|
||||
newKey = certKey;
|
||||
}
|
||||
|
||||
certGen.SetPublicKey(newKey.Public);
|
||||
BigInteger serialNumber = new BigInteger(new byte[] { 2, 3, 4 });
|
||||
certGen.SetSerialNumber(serialNumber);
|
||||
certGen.SetIssuerDN(new X509Name(issuerName));
|
||||
certGen.SetSubjectDN(new X509Name(subjectName));
|
||||
|
||||
DateTime notBefore = DateTime.UtcNow.Date;
|
||||
DateTime notAfter = notBefore + new TimeSpan(365, 0, 0, 0);
|
||||
|
||||
certGen.SetNotBefore(notBefore);
|
||||
certGen.SetNotAfter(notAfter);
|
||||
|
||||
if (pathLengthConstraint != 0)
|
||||
{
|
||||
// then we want it to be a CA. Using this constructor sets CA true AND sets the path length
|
||||
certGen.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(pathLengthConstraint));
|
||||
}
|
||||
else
|
||||
{
|
||||
certGen.AddExtension(X509Extensions.ExtendedKeyUsage, true,
|
||||
ExtendedKeyUsage.GetInstance(new DerSequence(KeyPurposeID.IdKPServerAuth)));
|
||||
};
|
||||
|
||||
// Sign the cert
|
||||
var signingKey = (signerKey != null) ? signerKey : newKey;
|
||||
ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA256WITHECDSA", signingKey.Private, random);
|
||||
|
||||
var certificate = certGen.Generate(signatureFactory);
|
||||
|
||||
// and return the bundle
|
||||
var bundle = new CertBundle() { Certificate = certificate, KeyPair = newKey };
|
||||
return bundle;
|
||||
}
|
||||
|
||||
|
||||
string ToPath(string fileName)
|
||||
{
|
||||
return Program.ToPath(fileName);
|
||||
}
|
||||
|
||||
void JoinFiles(string f1, string f2, string opFile)
|
||||
{
|
||||
File.Copy(ToPath(f1), ToPath(opFile), true);
|
||||
File.AppendAllText(ToPath(opFile), File.ReadAllText(ToPath(f2)));
|
||||
return;
|
||||
}
|
||||
|
||||
static void TestCode()
|
||||
{
|
||||
var oids = new List<Object>() { X509Name.UnstructuredName };
|
||||
var values = new List<Object>() { "ljkljljklkjlkjlkjlkjlkjlkjlkjlkjlkjljklkjlkjlkjlkjljk"};
|
||||
X509Name name = new X509Name(oids, values);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
// Make a BCRYPT_ECCKEY_BLOB structure
|
||||
// #define BCRYPT_ECDSA_PRIVATE_P256_MAGIC 0x32534345 // ECS2
|
||||
uint BCRYPT_ECDSA_PRIVATE_P256_MAGIC = 0x32534345;
|
||||
ECPublicKeyParameters aliasPub = (ECPublicKeyParameters)bundle.AliasKeyPair.Public;
|
||||
ECPrivateKeyParameters aliasPriv = (ECPrivateKeyParameters)bundle.AliasKeyPair.Private;
|
||||
|
||||
var xx = aliasPub.Q.AffineXCoord.ToBigInteger().ToByteArrayUnsigned();
|
||||
var yy = aliasPub.Q.AffineYCoord.ToBigInteger().ToByteArrayUnsigned();
|
||||
var dd = aliasPriv.D.ToByteArrayUnsigned();
|
||||
|
||||
byte[] magic = BitConverter.GetBytes(BCRYPT_ECDSA_PRIVATE_P256_MAGIC);
|
||||
byte[] size = BitConverter.GetBytes(3 * 32);
|
||||
byte[] ecBlob = new byte[32 * 3 + 4 + 4];
|
||||
|
||||
Array.Copy(magic, 0, ecBlob, 0, 4);
|
||||
Array.Copy(size, 0, ecBlob, 4, 4);
|
||||
Array.Copy(xx, 0, ecBlob, 8, 32);
|
||||
Array.Copy(yy, 0, ecBlob, 8+32, 32);
|
||||
Array.Copy(dd, 0, ecBlob, 8+64, 32);
|
||||
|
||||
File.WriteAllBytes("P0_EcBlob.BIN", ecBlob);
|
||||
|
||||
Debug.WriteLine("");
|
||||
foreach(var b in ecBlob)
|
||||
{
|
||||
Debug.Write($"0x{b:X},");
|
||||
|
||||
}
|
||||
Debug.WriteLine("");
|
||||
*/
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
using Org.BouncyCastle.Asn1;
|
||||
using Org.BouncyCastle.Asn1.Utilities;
|
||||
using System.Diagnostics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace RIoT
|
||||
{
|
||||
internal class RIoTDeviceInfo
|
||||
{
|
||||
internal byte[] FirmwareID;
|
||||
internal byte[] EncodedDeviceIDKey;
|
||||
internal X509Certificate2 Cert;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Decodes the RIoT Subject Alt Name extension. todo: this uses bouncy castle: port
|
||||
/// to raw c#.
|
||||
/// </summary>
|
||||
class ExtensionDecoder
|
||||
{
|
||||
static string RIoTOid = "1.3.6.1.4.1.311.89.3.1";
|
||||
static string ecPubKeyOID = "1.2.840.10045.2.1";
|
||||
static string prime256v1Oid = "1.2.840.10045.3.1.7";
|
||||
static string sha256Oid = "2.16.840.1.101.3.4.2.1";
|
||||
static Oid SubjectAltNameOID = new Oid("2.5.29.17");
|
||||
|
||||
static internal RIoTDeviceInfo Decode(string certFileName)
|
||||
{
|
||||
var cert = new X509Certificate2(certFileName);
|
||||
return Decode(cert);
|
||||
}
|
||||
|
||||
static internal RIoTDeviceInfo Decode(X509Certificate2 aliasCert)
|
||||
{
|
||||
|
||||
|
||||
AsnEncodedData altNames = null;
|
||||
foreach (var ext in aliasCert.Extensions)
|
||||
{
|
||||
if (ext.Oid.Value != SubjectAltNameOID.Value) 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");
|
||||
|
||||
DerSequence obj3 = (DerSequence)obj2[1];
|
||||
var versionNumber = (DerInteger)obj3[0];
|
||||
if (versionNumber.PositiveValue.IntValue != 1) return ParseError("Wrong version number");
|
||||
|
||||
DerSequence obj4 = (DerSequence)obj3[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)obj3[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;
|
||||
}
|
||||
|
||||
}
|
||||
static RIoTDeviceInfo ParseError(string error)
|
||||
{
|
||||
Helpers.Notify($"Extension parsing error: {error}", true);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If a device presents a "bare" certificate rather than a full certificate chain, we have to
|
||||
/// peform some steps that would normall be performed by the X509 chain builder. Specifically,
|
||||
/// the RIoT extension (above) encodes the DeviceID public key, but we have to check that this
|
||||
/// Alias Cert was actually signed by the corresponding DeviceID private key. To do this we
|
||||
/// parse the Alias Cert to get:
|
||||
/// The "to be signed" region (byte array)
|
||||
/// The OID of the signature scheme
|
||||
/// The signature block
|
||||
///
|
||||
/// The caller of this function can then check that the ALias Cert was indeed signed by the
|
||||
/// device it claims to be from.
|
||||
/// </summary>
|
||||
/// <param name="aliasCert"></param>
|
||||
/// <returns></returns>
|
||||
static internal DecomposedCert Decompose(X509Certificate2 aliasCert)
|
||||
{
|
||||
var rawData = aliasCert.GetRawCertData();
|
||||
try
|
||||
{
|
||||
DerSequence seq = (DerSequence)DerSequence.FromByteArray(rawData);
|
||||
DerSequence tbs = (DerSequence)seq[0];
|
||||
DerSequence sigAlg = (DerSequence)seq[1];
|
||||
var sigAlgOid = (DerObjectIdentifier)sigAlg[0];
|
||||
|
||||
DerBitString sigData = (DerBitString)seq[2];
|
||||
DerSequence sigSequence = (DerSequence) DerSequence.FromByteArray(sigData.GetOctets());
|
||||
var sig1 = (DerInteger)sigSequence[0];
|
||||
var sig2 = (DerInteger)sigSequence[0];
|
||||
|
||||
var sig1Bytes = sig1.Value.ToByteArrayUnsigned();
|
||||
var sig2Bytes = sig1.Value.ToByteArrayUnsigned();
|
||||
|
||||
Debug.Assert(sig1Bytes.Length == 32 && sig1Bytes.Length == 32);
|
||||
|
||||
byte[] SignatureX = new byte[64];
|
||||
Array.Copy(sig1Bytes, 0, SignatureX, 0, 32);
|
||||
Array.Copy(sig2Bytes, 0, SignatureX, 32, 32);
|
||||
|
||||
Debug.WriteLine("");
|
||||
|
||||
DecomposedCert bits = new DecomposedCert
|
||||
{
|
||||
Tbs = tbs.GetDerEncoded(),
|
||||
OID = sigAlgOid.Id,
|
||||
Signature = SignatureX
|
||||
};
|
||||
return bits;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine(e.ToString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
internal class DecomposedCert
|
||||
{
|
||||
internal byte[] Tbs;
|
||||
internal string OID;
|
||||
internal byte[] Signature;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
/*
|
||||
* This will be a RIoT extension decoder that doesn't use BC.
|
||||
* INCOMPLETE
|
||||
*
|
||||
* */
|
||||
|
||||
|
||||
|
||||
namespace RIoT
|
||||
{
|
||||
internal enum DerTags
|
||||
{
|
||||
Integer = 2,
|
||||
BitString = 3,
|
||||
Sequence = 16,
|
||||
Oid = 6
|
||||
}
|
||||
|
||||
internal class ExtensionDecoder2
|
||||
{
|
||||
internal ExtensionDecoder2(byte[] extension)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
internal class DERSequence
|
||||
{
|
||||
byte[] Data;
|
||||
// this describes the data in this sequence
|
||||
int Start;
|
||||
int TotalLength;
|
||||
int PayloadStart;
|
||||
int PayloadLength;
|
||||
int EndPointer;
|
||||
// This is
|
||||
int ParsePointer;
|
||||
|
||||
internal DERSequence(byte[] _x, int _start)
|
||||
{
|
||||
Data = _x;
|
||||
Start = _start;
|
||||
ParsePointer = Start;
|
||||
if (Data[ParsePointer++] != (byte)DerTags.Sequence)
|
||||
{
|
||||
throw new Exception($"Not a sequence: start = {Start}");
|
||||
}
|
||||
PayloadLength = GetDEREncodedInt();
|
||||
PayloadStart = ParsePointer;
|
||||
//TotalLength = PayloadLength + headerLength;
|
||||
EndPointer = Start + TotalLength;
|
||||
if (Start + TotalLength > Data.Length) throw new Exception($"Sequence length of {PayloadLength}, but only {Data.Length - Start}");
|
||||
}
|
||||
internal DERSequence GetSequence()
|
||||
{
|
||||
var s = new DERSequence(Data, ParsePointer);
|
||||
ParsePointer += s.TotalLength;
|
||||
return s;
|
||||
}
|
||||
internal int GetShortInt()
|
||||
{
|
||||
var tag = (byte)Data[ParsePointer++];
|
||||
if (tag != (byte)DerTags.Integer) throw new Exception($"Integer tag expected at {ParsePointer}");
|
||||
int dataLen = GetDEREncodedInt();
|
||||
if (dataLen > 1) throw new NotImplementedException();
|
||||
return (int)Data[ParsePointer++];
|
||||
}
|
||||
internal int GetOctetString()
|
||||
{
|
||||
var tag = (byte)Data[ParsePointer++];
|
||||
if (tag != (byte)DerTags.Integer) throw new Exception($"Integer tag expected at {ParsePointer}");
|
||||
int dataLen = GetDEREncodedInt();
|
||||
if (dataLen > 1) throw new NotImplementedException();
|
||||
return (int)Data[ParsePointer++];
|
||||
}
|
||||
|
||||
|
||||
internal int GetOID()
|
||||
{
|
||||
var tag = Data[ParsePointer++];
|
||||
if (tag != (byte)DerTags.Oid) throw new Exception($"OID tag expected at {ParsePointer}");
|
||||
int intLen = GetDEREncodedInt();
|
||||
ParsePointer += intLen;
|
||||
return 0;
|
||||
}
|
||||
private int GetDEREncodedInt()
|
||||
{
|
||||
if (ParsePointer >= EndPointer) throw new Exception("overflow");
|
||||
uint n = (uint)Data[ParsePointer++];
|
||||
// if n<127, then it's the length
|
||||
if (n < 127) return (int)n;
|
||||
// if n>127 then it's the number of bytes. We only care about
|
||||
// small numbers
|
||||
if (n == 1)
|
||||
{
|
||||
if (ParsePointer >= EndPointer) throw new Exception("e2");
|
||||
return (int)Data[ParsePointer++];
|
||||
}
|
||||
if (n == 2)
|
||||
{
|
||||
if (ParsePointer >= EndPointer) throw new Exception("e2");
|
||||
int t = Data[ParsePointer++] * 256;
|
||||
if (ParsePointer >= EndPointer) throw new Exception("e2");
|
||||
return t + Data[ParsePointer++];
|
||||
}
|
||||
|
||||
throw new Exception("Number too big");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
/*
|
||||
internal class DerHeader
|
||||
{
|
||||
// todo - sequence overflow
|
||||
internal static int GetLength(byte[] x, int pos)
|
||||
{
|
||||
uint n = (uint)x[pos];
|
||||
// if n<127, then it's the length
|
||||
if (n < 127) return (int)n;
|
||||
// if n>127 then it's the number of bytes. We only care about
|
||||
// small numbers
|
||||
if (n == 1) return (int)x[pos + 1];
|
||||
if (n == 2) return x[pos + 1] * 256 + x[pos + 2];
|
||||
throw new Exception("Number too big");
|
||||
}
|
||||
internal static int GetLengthLength(byte[] x, int pos)
|
||||
{
|
||||
uint n = (uint)x[pos];
|
||||
if (n < 127) return 1;
|
||||
return (int)n + 1;
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
|
||||
namespace RIoT
|
||||
{
|
||||
using Org.BouncyCastle.Asn1;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Digests;
|
||||
using Org.BouncyCastle.Crypto.Prng;
|
||||
using Org.BouncyCastle.Pkcs;
|
||||
using Org.BouncyCastle.Security;
|
||||
using Org.BouncyCastle.Utilities.Encoders;
|
||||
using Org.BouncyCastle.X509;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
public class Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Make a copy of a byte array
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] CopyArray(byte[] x)
|
||||
{
|
||||
var d = new byte[x.Length];
|
||||
Array.Copy(x, d, x.Length);
|
||||
return d;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy of part of a byte array
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="startPos"></param>
|
||||
/// <param name="len"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] CopyArray(byte[] x, int startPos, int len)
|
||||
{
|
||||
var d = new byte[len];
|
||||
Array.Copy(x, startPos, d, 0, len);
|
||||
return d;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a byte-array to packed HEX
|
||||
/// </summary>
|
||||
/// <param name="t"></param>
|
||||
/// <returns></returns>
|
||||
public static string Hexify(byte[] t)
|
||||
{
|
||||
return BitConverter.ToString(t).Replace("-", "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SHA256 hash of data-fragment
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="start"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <returns></returns>
|
||||
internal static byte[] HashData(byte[] data, int start, int length)
|
||||
{
|
||||
var d = new Sha256Digest();
|
||||
d.BlockUpdate(data, start, length);
|
||||
var digest = new byte[32];
|
||||
d.DoFinal(digest, 0);
|
||||
return digest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// byte-array equality check
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns></returns>
|
||||
internal static bool ArraysAreEqual(byte[] x, byte[] y) => Enumerable.SequenceEqual(x, x);
|
||||
|
||||
internal static byte[] GetRepeating(int b, int count)
|
||||
{
|
||||
byte[] r = new byte[count];
|
||||
for (int j = 0; j < count; j++) r[j] = (byte)b;
|
||||
return r;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Windows TLS needs the Alias public + private key in a PFX file
|
||||
/// </summary>
|
||||
/// <param name="certFile"></param>
|
||||
/// <param name="keyFile"></param>
|
||||
internal static void MakePFXFile(string certFile, string keyFile, string outputPfxFile, string password)
|
||||
{
|
||||
CryptoApiRandomGenerator rg = new CryptoApiRandomGenerator();
|
||||
var rng = new SecureRandom(rg);
|
||||
// get the cert
|
||||
var parser = new X509CertificateParser();
|
||||
var cert = parser.ReadCertificate(File.ReadAllBytes(certFile));
|
||||
// get the key
|
||||
Org.BouncyCastle.OpenSsl.PemReader pemReader =
|
||||
new Org.BouncyCastle.OpenSsl.PemReader(new StringReader(File.ReadAllText(keyFile)));
|
||||
AsymmetricCipherKeyPair kp = pemReader.ReadObject() as AsymmetricCipherKeyPair;
|
||||
|
||||
|
||||
// Put the key and cert in an PKCS12 store so that the WIndows TLS stack can use it
|
||||
var store = new Pkcs12Store();
|
||||
string friendlyName = cert.SubjectDN.ToString();
|
||||
var certificateEntry = new X509CertificateEntry(cert);
|
||||
store.SetCertificateEntry(friendlyName, certificateEntry);
|
||||
store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(kp.Private), new[] { certificateEntry });
|
||||
|
||||
var stream = new MemoryStream();
|
||||
var pwd = password == null ? null : password.ToCharArray();
|
||||
store.Save(stream, pwd, rng);
|
||||
File.WriteAllBytes(outputPfxFile, stream.ToArray());
|
||||
return;
|
||||
}
|
||||
|
||||
internal static void Notify(string message, bool isError = false)
|
||||
{
|
||||
if (isError)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
}
|
||||
Debug.WriteLine(message);
|
||||
Console.WriteLine(message);
|
||||
Console.ResetColor();
|
||||
return;
|
||||
}
|
||||
|
||||
internal static void WritePEMObjects(string fileName, Object[] pemObjects)
|
||||
{
|
||||
var stream = new StreamWriter(fileName, false);
|
||||
Org.BouncyCastle.OpenSsl.PemWriter writer = new Org.BouncyCastle.OpenSsl.PemWriter(stream);
|
||||
foreach (var o in pemObjects)
|
||||
{
|
||||
writer.WriteObject(o);
|
||||
}
|
||||
|
||||
writer.Writer.Flush();
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
internal static void WritePEMObject(string fileName, Object pemObject)
|
||||
{
|
||||
var stream = new StreamWriter(fileName, false);
|
||||
Org.BouncyCastle.OpenSsl.PemWriter writer = new Org.BouncyCastle.OpenSsl.PemWriter(stream);
|
||||
writer.WriteObject(pemObject);
|
||||
writer.Writer.Flush();
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
internal static Object ReadPemObject(string fileName)
|
||||
{
|
||||
var stream = new StreamReader(fileName);
|
||||
Org.BouncyCastle.OpenSsl.PemReader reader = new Org.BouncyCastle.OpenSsl.PemReader(stream);
|
||||
var o = reader.ReadObject();
|
||||
stream.Close();
|
||||
return o;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete certs that match the provided issuer. Used for cleaning up test certs.
|
||||
/// </summary>
|
||||
/// <param name="matcher"></param>
|
||||
internal static void DeleteCertsByIssuer(string issuerName)
|
||||
{
|
||||
X509Store store = new X509Store(StoreName.CertificateAuthority, StoreLocation.LocalMachine);
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
|
||||
// Get a collection of our certs.
|
||||
var toRemove = new X509Certificate2Collection();
|
||||
foreach (var c in store.Certificates)
|
||||
{
|
||||
var issuer = c.IssuerName.Name;
|
||||
if (issuer.Contains(issuerName))
|
||||
{
|
||||
toRemove.Add(c);
|
||||
}
|
||||
}
|
||||
|
||||
store.RemoveRange(toRemove);
|
||||
store.Close();
|
||||
}
|
||||
|
||||
internal static void InstallCert(X509Certificate2 cert)
|
||||
{
|
||||
X509Store store = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
|
||||
store.Open(OpenFlags.ReadWrite);
|
||||
store.Add(cert);
|
||||
store.Close();
|
||||
}
|
||||
internal static void InstallCert(string certFileName)
|
||||
{
|
||||
var cert = new X509Certificate2(certFileName);
|
||||
InstallCert(cert);
|
||||
return;
|
||||
}
|
||||
|
||||
internal static void SetCertForPort(string certFile, int port)
|
||||
{
|
||||
X509Certificate2 serverCert = new X509Certificate2(certFile);
|
||||
var certHashString = Helpers.Hexify(serverCert.GetCertHash());
|
||||
|
||||
var psi = new ProcessStartInfo("netsh");
|
||||
|
||||
psi.Arguments = $"http add sslcert ccs=5556 certhash={certHashString} appid={{00112233-4455-6677-8899-AABBCCDDEEFF}}";
|
||||
psi.CreateNoWindow = true;
|
||||
psi.UseShellExecute = false;
|
||||
psi.RedirectStandardError = true;
|
||||
var proc = Process.Start(psi); ;
|
||||
string op = proc.StandardError.ReadToEnd();
|
||||
proc.WaitForExit();
|
||||
|
||||
}
|
||||
/*
|
||||
static string ss = "MKADAgECAgUKCwwNDjAPBg0AAAAAAAAADoKAgKg4MGIxEDAOBgGYDAlSSW9UIENvcmUxCTAHBgH0DAJVUzFDMEEGNQCCgICrNIKAgKo4AIKAgK9cgoCAsACCgICrWACCgICvXIKAgLAAgoCAq1gAwIS7GYSb0etHDAhNU1JfVEVTVDAeFw0xNzAxMDEwMDAwMDBaFw0zNzAxMDEwMDAwMDBaMGQxEjAQBgGYDAtSSW9UIERldmljZTEJMAcGAfQMAlVTMUMwQQY1AIKAgKs0goCAqjgAgoCAr1yCgICwAIKAgKtYAIKAgK9cgoCAsACCgICrWADAhLsZhJvR60cMCE1TUl9URVNUMEwwBgYBaAYBkANCAASJ9eWD644UMAuBkFKWAd6Ja/PeEfr+noNBXYPeea17GDVUfqcbpJpwJ25xShKT5lPdRsQeaRcsOK+WLkqKMuuOozAwBgxogd2StG8Ag6rR/SgBAf8EMA==";
|
||||
internal static void CrackCert(string fileName)
|
||||
{
|
||||
var b = Base64.Decode(ss);
|
||||
|
||||
DerSequence seq = (DerSequence)DerSequence.FromByteArray(b);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RIoT
|
||||
{
|
||||
class HttpsListener
|
||||
{
|
||||
|
||||
static internal void StartListener(string _serverCert, string _serverKey, string serverCA, string _clientCert, string _clientKey)
|
||||
{
|
||||
// note that the programmatic cert creation and installation didn't work so did this -
|
||||
// makecert.exe - r - a sha1 - n CN = localhost - sky exchange - pe - b 01 / 01 / 2000 - e 01 / 01 / 2050 - ss my
|
||||
// then this
|
||||
// C:\Repos\RIoT Development\Utlilities\RIoTUtils\bin\Debug\Certs>netsh http add sslcert ipport=0.0.0.0:5556 appid={20a30499-7f02-446f-8716-e85fcdbb0ce4} certhash=360e6b474436076ff6cca4b1281fda021c276dbb
|
||||
// SSL Certificate successfully added
|
||||
|
||||
|
||||
|
||||
// we need to add the server cert to the store for HttpListener to use it
|
||||
string serverPfxFile = "ServerCert.PFX";
|
||||
Helpers.MakePFXFile(_serverCert, _serverKey, serverPfxFile, null);
|
||||
Helpers.DeleteCertsByIssuer("MSR_TEST");
|
||||
|
||||
Helpers.InstallCert(serverCA);
|
||||
Helpers.InstallCert(serverPfxFile);
|
||||
Helpers.SetCertForPort(serverPfxFile, 5556);
|
||||
|
||||
string clientPfxFile = "ClientCert.PFX";
|
||||
Helpers.MakePFXFile(_clientCert, _clientKey, clientPfxFile, null);
|
||||
|
||||
|
||||
|
||||
// 9970e392d44f8d08c158660f1a0b05838f6201f0
|
||||
|
||||
// 360e6b474436076ff6cca4b1281fda021c276dbb
|
||||
SSLValidator.OverrideValidation();
|
||||
|
||||
var listener = new HttpListener();
|
||||
|
||||
listener.Prefixes.Add("https://127.0.0.1:5556/ABC/");
|
||||
listener.Start();
|
||||
Console.WriteLine("Listening...");
|
||||
|
||||
|
||||
SslTcpClient.RunClient(_clientCert, _clientKey);
|
||||
|
||||
/*
|
||||
// make a request
|
||||
//You must change the path to point to your .cer file location.
|
||||
X509Certificate Cert = X509Certificate.CreateFromCertFile("ClientCert.PFX");
|
||||
// Handle any certificate errors on the certificate from the server.
|
||||
// You must change the URL to point to your Web server.
|
||||
HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://127.0.0.1:5556/ABC/123");
|
||||
Request.ClientCertificates.Add(Cert);
|
||||
Request.UserAgent = "Client Cert Sample";
|
||||
Request.Method = "GET";
|
||||
var responseFromServer = Request.GetResponseAsync();
|
||||
//string respx = responseFromServer.Result.ToString();
|
||||
|
||||
*/
|
||||
|
||||
var context = listener.GetContext();
|
||||
HttpListenerRequest request = context.Request;
|
||||
// Obtain a response object.
|
||||
HttpListenerResponse response = context.Response;
|
||||
|
||||
var cert = request.GetClientCertificate();
|
||||
|
||||
// Construct a response.
|
||||
string responseString = "<HTML><BODY> Hello world!</BODY></HTML>";
|
||||
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
|
||||
// Get a response stream and write the response to it.
|
||||
response.ContentLength64 = buffer.Length;
|
||||
System.IO.Stream output = response.OutputStream;
|
||||
output.Write(buffer, 0, buffer.Length);
|
||||
// You must close the output stream.
|
||||
output.Close();
|
||||
listener.Stop();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
public static class SSLValidator
|
||||
{
|
||||
private static RemoteCertificateValidationCallback _orgCallback;
|
||||
|
||||
private static bool OnValidateCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void OverrideValidation()
|
||||
{
|
||||
_orgCallback = ServicePointManager.ServerCertificateValidationCallback;
|
||||
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(OnValidateCertificate);
|
||||
ServicePointManager.Expect100Continue = true;
|
||||
}
|
||||
|
||||
public static void RestoreValidation()
|
||||
{
|
||||
ServicePointManager.ServerCertificateValidationCallback = _orgCallback;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
using System;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Azure.Devices;
|
||||
using Microsoft.Azure.Devices.Client.Exceptions;
|
||||
using Microsoft.Azure.Devices.Shared;
|
||||
|
||||
namespace RIoT
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
class HubControl
|
||||
{
|
||||
string ConnectionStringFile = "c:\\tmp\\ConnectionString.txt";
|
||||
RegistryManager RegMgr;
|
||||
internal HubControl()
|
||||
{
|
||||
}
|
||||
/// <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;
|
||||
}
|
||||
/// <summary>
|
||||
/// List the current devices
|
||||
/// </summary>
|
||||
/// <param name="numDevices"></param>
|
||||
/// <returns></returns>
|
||||
internal List<string> GetDeviceIDs(int numDevices = 100)
|
||||
{
|
||||
var deviceIds = new List<string>();
|
||||
var x = RegMgr.GetDevicesAsync(100);
|
||||
foreach (var d in x.Result)
|
||||
{
|
||||
Debug.WriteLine($"{d.ETag} -- {d.Id}");
|
||||
deviceIds.Add(d.Id);
|
||||
}
|
||||
return deviceIds;
|
||||
}
|
||||
|
||||
/// <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 bool RefreshDevice(string deviceId, int fwVersionNumber, string fwid, string aliasCertThumbprint, bool primaryNotSecondary)
|
||||
{
|
||||
var dev = RegMgr.GetDeviceAsync(deviceId).Result;
|
||||
if (primaryNotSecondary)
|
||||
{
|
||||
dev.Authentication.X509Thumbprint.PrimaryThumbprint = aliasCertThumbprint;
|
||||
}
|
||||
else
|
||||
{
|
||||
dev.Authentication.X509Thumbprint.SecondaryThumbprint = aliasCertThumbprint;
|
||||
}
|
||||
RegMgr.UpdateDeviceAsync(dev).Wait();
|
||||
|
||||
var twin = RegMgr.GetTwinAsync(deviceId).Result;
|
||||
twin.Properties.Desired["FWID"] = fwid;
|
||||
twin.Properties.Desired["VersionNumber"] = fwVersionNumber.ToString();
|
||||
RegMgr.UpdateTwinAsync(deviceId, twin, twin.ETag).Wait();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
internal bool RemoveAllDevices()
|
||||
{
|
||||
var devIds = GetDeviceIDs();
|
||||
foreach(var devId in devIds)
|
||||
{
|
||||
Device dd = RegMgr.GetDeviceAsync(devId).Result;
|
||||
RegMgr.RemoveDeviceAsync(dd);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool FakeDRSServerEnrollOrRefreshDevice(string deviceId, string certThumbprint)
|
||||
{
|
||||
if(!GetDeviceIDs().Contains(deviceId))
|
||||
{
|
||||
// make a ndw device
|
||||
EnrollDevice(deviceId, 0, "", certThumbprint);
|
||||
return true;
|
||||
}
|
||||
// else refresh
|
||||
var dev = RegMgr.GetDeviceAsync(deviceId).Result;
|
||||
|
||||
dev.Authentication.X509Thumbprint.PrimaryThumbprint = certThumbprint;
|
||||
RegMgr.UpdateDeviceAsync(dev).Wait();
|
||||
|
||||
// todo check the attestation is good
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
using Microsoft.Azure.Devices;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using Microsoft.Azure.Devices.Client;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.Azure.Devices.Shared;
|
||||
using System.Drawing;
|
||||
|
||||
namespace RIoT
|
||||
{
|
||||
class IoTDevice
|
||||
{
|
||||
internal bool FirmwareUpdateNeeded;
|
||||
|
||||
DeviceClient Me;
|
||||
X509Certificate2 MyCert;
|
||||
string MyDevId;
|
||||
int CertCount;
|
||||
internal int DeviceNumber;
|
||||
internal bool P0wned = false;
|
||||
|
||||
internal bool HubRefreshNeeded;
|
||||
|
||||
internal int CurrentFwVersionNumber;
|
||||
internal int DesiredFwVersionNumber;
|
||||
bool updateCurrentVersionNumber = true;
|
||||
|
||||
int MessageCount;
|
||||
|
||||
internal IoTDevice(string deviceName, int fwidSeed, int deviceNumber)
|
||||
{
|
||||
FirmwareUpdateNeeded = false;
|
||||
DeviceNumber = deviceNumber;
|
||||
MyDevId = deviceName;
|
||||
CurrentFwVersionNumber = fwidSeed;
|
||||
HubRefreshNeeded = true;
|
||||
RefreshCert();
|
||||
}
|
||||
|
||||
internal void RefreshCert()
|
||||
{
|
||||
Helpers.MakePFXFile(Program.ToPath(Program.AliasCert), Program.ToPath(Program.AliasKey), Program.ToPath(Program.AliasCertPFX), null);
|
||||
MyCert = new X509Certificate2(Program.ToPath(Program.AliasCertPFX));
|
||||
CertCount++;
|
||||
}
|
||||
|
||||
internal bool RegisterWithFakeDRSServer()
|
||||
{
|
||||
SslTcpClient c = new SslTcpClient();
|
||||
bool connected = c.FakeDRSServerHandshake(MyDevId);
|
||||
if(connected)
|
||||
{
|
||||
HubRefreshNeeded = false;
|
||||
}
|
||||
return connected;
|
||||
}
|
||||
void QueryFWStatus()
|
||||
{
|
||||
try
|
||||
{
|
||||
var twin = Me.GetTwinAsync().Result;
|
||||
var myFwid = Helpers.Hexify(Helpers.HashData(new byte[] { (byte)CurrentFwVersionNumber }, 0, 1));
|
||||
var targetFwVersionNumber = twin.Properties.Desired["VersionNumber"];
|
||||
Int64 currentReportedVersionNumber = -1;
|
||||
if (twin.Properties.Reported.Contains("VersionNumber"))
|
||||
{
|
||||
currentReportedVersionNumber = (Int64)twin.Properties?.Reported["VersionNumber"];
|
||||
}
|
||||
// update our version number if hub version number is not current
|
||||
if ( currentReportedVersionNumber < 0 ||
|
||||
CurrentFwVersionNumber != currentReportedVersionNumber ||
|
||||
updateCurrentVersionNumber
|
||||
)
|
||||
{
|
||||
TwinCollection t = new TwinCollection();
|
||||
t["VersionNumber"] = CurrentFwVersionNumber;
|
||||
Me.UpdateReportedPropertiesAsync(t).Wait();
|
||||
updateCurrentVersionNumber = false;
|
||||
}
|
||||
// if the target version number is not current, then flag that we need a FW update
|
||||
if (targetFwVersionNumber != CurrentFwVersionNumber.ToString())
|
||||
{
|
||||
FirmwareUpdateNeeded = true;
|
||||
DesiredFwVersionNumber = targetFwVersionNumber;
|
||||
Debug.WriteLine("Need to update myself");
|
||||
}
|
||||
else
|
||||
{
|
||||
FirmwareUpdateNeeded = false;
|
||||
Debug.WriteLine("Firmware version is good");
|
||||
}
|
||||
|
||||
// am I p0wned? If I'm P0wned I won't update myself
|
||||
if (twin.Properties.Desired.Contains("POwned"))
|
||||
{
|
||||
P0wned = (bool)twin.Properties?.Desired["POwned"];
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine("Error querying status" + e.ToString());
|
||||
throw;
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
Random rr = new Random();
|
||||
internal bool SendMessages(int numMessages, int sleepMilisecs)
|
||||
{
|
||||
var colors = Enum.GetValues(typeof(KnownColor));
|
||||
try
|
||||
{
|
||||
// Note that MQTT is required for the device twin stuff to work.
|
||||
Me = DeviceClient.Create(Program.IotHubUri, new DeviceAuthenticationWithX509Certificate(MyDevId, MyCert),
|
||||
Microsoft.Azure.Devices.Client.TransportType.Mqtt);
|
||||
Me.OpenAsync().Wait();
|
||||
QueryFWStatus();
|
||||
for (byte j = 0; j < numMessages; j++)
|
||||
{
|
||||
int colorVal = (int) colors.GetValue(rr.Next(colors.Length));
|
||||
var t = Me.SendEventAsync(MakeMessage(MyDevId, colorVal, MessageCount++));
|
||||
t.Wait();
|
||||
Thread.Sleep(sleepMilisecs);
|
||||
}
|
||||
Me.CloseAsync().Wait();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error in Create or SendMessages {ex.ToString()}");
|
||||
if(ex.InnerException!=null) Debug.WriteLine($"Error in SendMessages (inner exception is) {ex.InnerException.ToString()}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Microsoft.Azure.Devices.Client.Message MakeMessage(string devId, int colorVal, int messageCount)
|
||||
{
|
||||
var m = new
|
||||
{
|
||||
DevID = devId,
|
||||
ColorVal = colorVal,
|
||||
MessageCount = messageCount
|
||||
};
|
||||
var messageString = JsonConvert.SerializeObject(m);
|
||||
var message = new Microsoft.Azure.Devices.Client.Message(Encoding.ASCII.GetBytes(messageString));
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,323 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RIoT
|
||||
{
|
||||
// Useful utility
|
||||
// http://www.lapo.it/asn1js/
|
||||
|
||||
class Program
|
||||
{
|
||||
// The RIoT utility expects to find all keys and certs in IODir (which can be changed
|
||||
// through the command line.) File names are as listed below, and are not configurable
|
||||
internal static string IODir = "./Certs/";
|
||||
// device certs and keys
|
||||
internal static string AliasCert = "AliasCert.PEM";
|
||||
internal static string AliasKey = "AliasKey.PEM";
|
||||
internal static string DeviceCertChain = "DeviceCertChain.PEM";
|
||||
internal static string DeviceCertChainIncAlias = "DeviceCertChainIncAlias.PEM";
|
||||
internal static string DeviceCA = "DeviceCA.PEM";
|
||||
internal static string DeviceIDPublic = "DeviceIDPublic.PEM";
|
||||
internal static string DeviceIDPrivate = "DeviceIDPrivate.PEM"; // just for the update demo
|
||||
internal static string DeviceIDCSR = "DeviceIDCSR.PEM";
|
||||
// cert+key file that is generated
|
||||
internal static string AliasCertPFX = "AliasCert.PFX";
|
||||
|
||||
// server certs and keys
|
||||
internal static string ServerCA = "ServerCA.PEM";
|
||||
internal static string ServerChain = "ServerChain.PEM";
|
||||
internal static string ServerCert = "ServerCert.PEM";
|
||||
internal static string ServerKey = "ServerKey.PEM";
|
||||
|
||||
// For IoT hub testing
|
||||
internal static string IotHubUri = "pengland.azure-devices.net";
|
||||
|
||||
// to test a bunch of different devices simultaneously
|
||||
static int DeviceNumber = -1;
|
||||
|
||||
|
||||
// ossl command lines (client and server) need a CA file with both client and
|
||||
// server roots
|
||||
internal static string DeviceCertChainAndServerCA = "DeviceCertChainAndServerCA.PEM";
|
||||
|
||||
// modifies the behavior of -e2e
|
||||
internal static bool MakeCerts = true;
|
||||
|
||||
// This is from the MS/MSR arc.
|
||||
internal static string DeviceIdOid = "1.3.6.1.4.1.311.89.3.1";
|
||||
|
||||
static List<CommandLineOption> Parms = new List<CommandLineOption>();
|
||||
internal static string ChainOrBareCert = "C";
|
||||
static List<CommandLineOption> ActiveParms = new List<CommandLineOption>();
|
||||
|
||||
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 == "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 == "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;
|
||||
}
|
||||
static void InitParms()
|
||||
{
|
||||
Parms.Add(new CommandLineOption("help", "Print this text"));
|
||||
Parms.Add(new CommandLineOption("gentest", "Make a set of client and server test certificates"));
|
||||
Parms.Add(new CommandLineOption("certify", "Certify a device given a bare DeviceIDPublic.PEM file"));
|
||||
Parms.Add(new CommandLineOption("csr", "Make certs given a DeviceIDCsr.PEM (AliasKey and AliasCert are assumed to exist)"));
|
||||
Parms.Add(new CommandLineOption("server", "Start a TLS test server using SSLStream"));
|
||||
Parms.Add(new CommandLineOption("server2", "Start a TLS test server using "));
|
||||
Parms.Add(new CommandLineOption("sc", "Start a TLS test server AND launch TlsClient.exe"));
|
||||
Parms.Add(new CommandLineOption("nogen", "Modifies -e2e to skip creation of a new key and cert set"));
|
||||
Parms.Add(new CommandLineOption("e2e", "Make a new set of test certs, start a TLS test server, and launch TlsClient.exe"));
|
||||
Parms.Add(new CommandLineOption("ossl_client", "Print command line parameters for an openssl test client"));
|
||||
Parms.Add(new CommandLineOption("ossl_server", "Print command line parameters for an openssl test server"));
|
||||
Parms.Add(new CommandLineOption("tls_client", "Start the TlsClient test program and wait for it to complete"));
|
||||
Parms.Add(new CommandLineOption("dir", "Set the directory to put and gets certs and keys", 1));
|
||||
Parms.Add(new CommandLineOption("bare", "Present a bare alias certificate rather than a chain (use before other options)"));
|
||||
Parms.Add(new CommandLineOption("demo", "Update demo"));
|
||||
}
|
||||
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;
|
||||
}
|
||||
internal static string ToPath(string fileName)
|
||||
{ if (DeviceNumber < 0)
|
||||
{
|
||||
return IODir + fileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
// hacky way of supporting lots of devices
|
||||
return IODir + $"_{DeviceNumber}_" + fileName;
|
||||
}
|
||||
}
|
||||
internal static void SetDeviceNumber(int deviceNum)
|
||||
{
|
||||
DeviceNumber = deviceNum;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
internal class CommandLineOption
|
||||
{
|
||||
internal string Flag;
|
||||
internal int NumParms;
|
||||
internal string Help;
|
||||
|
||||
internal bool Active = false;
|
||||
internal string Parameter;
|
||||
|
||||
internal CommandLineOption(string _flag, string _help, int _numParms=0)
|
||||
{
|
||||
Flag = _flag;
|
||||
Help = _help;
|
||||
NumParms = _numParms;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("RIoT")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("RIoT")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("20a30499-7f02-446f-8716-e85fcdbb0ce4")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,184 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{20A30499-7F02-446F-8716-E85FCDBB0CE4}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>RIoT</RootNamespace>
|
||||
<AssemblyName>RIoT</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="BouncyCastle.Crypto, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
|
||||
<HintPath>..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="DotNetty.Buffers, Version=0.3.2.0, Culture=neutral, PublicKeyToken=e7a0210a354f294a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DotNetty.Buffers-signed.0.3.2\lib\net45\DotNetty.Buffers.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="DotNetty.Codecs, Version=0.3.2.0, Culture=neutral, PublicKeyToken=e7a0210a354f294a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DotNetty.Codecs-signed.0.3.2\lib\net45\DotNetty.Codecs.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="DotNetty.Codecs.Mqtt, Version=0.3.2.0, Culture=neutral, PublicKeyToken=e7a0210a354f294a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DotNetty.Codecs.Mqtt-signed.0.3.2\lib\net45\DotNetty.Codecs.Mqtt.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="DotNetty.Common, Version=0.3.2.0, Culture=neutral, PublicKeyToken=e7a0210a354f294a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DotNetty.Common-signed.0.3.2\lib\net45\DotNetty.Common.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="DotNetty.Handlers, Version=0.3.2.0, Culture=neutral, PublicKeyToken=e7a0210a354f294a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DotNetty.Handlers-signed.0.3.2\lib\net45\DotNetty.Handlers.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="DotNetty.Transport, Version=0.3.2.0, Culture=neutral, PublicKeyToken=e7a0210a354f294a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DotNetty.Transport-signed.0.3.2\lib\net45\DotNetty.Transport.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.Amqp, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Azure.Amqp.2.0.3\lib\net45\Microsoft.Azure.Amqp.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.Devices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Azure.Devices.1.2.3\lib\net451\Microsoft.Azure.Devices.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.Devices.Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Azure.Devices.Client.1.2.5\lib\net45\Microsoft.Azure.Devices.Client.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.Devices.Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Azure.Devices.Shared.1.0.7\lib\net45\Microsoft.Azure.Devices.Shared.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.KeyVault.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Azure.KeyVault.Core.1.0.0\lib\net40\Microsoft.Azure.KeyVault.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Data.Edm, Version=5.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Data.Edm.5.6.4\lib\net40\Microsoft.Data.Edm.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Data.OData, Version=5.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Data.OData.5.6.4\lib\net40\Microsoft.Data.OData.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Data.Services.Client, Version=5.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Data.Services.Client.5.6.4\lib\net40\Microsoft.Data.Services.Client.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\EnterpriseLibrary.TransientFaultHandling.6.0.1304.0\lib\portable-net45+win+wp8\Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.WindowsAzure.Storage, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\WindowsAzure.Storage.7.0.0\lib\net40\Microsoft.WindowsAzure.Storage.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PCLCrypto, Version=2.0.0.0, Culture=neutral, PublicKeyToken=d4421c8a4786956c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\PCLCrypto.2.0.147\lib\net45\PCLCrypto.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PInvoke.BCrypt, Version=0.3.0.0, Culture=neutral, PublicKeyToken=9e300f9f87f04a7a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\PInvoke.BCrypt.0.3.2\lib\net40\PInvoke.BCrypt.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PInvoke.Kernel32, Version=0.3.0.0, Culture=neutral, PublicKeyToken=9e300f9f87f04a7a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\PInvoke.Kernel32.0.3.2\lib\net40\PInvoke.Kernel32.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PInvoke.NCrypt, Version=0.3.0.0, Culture=neutral, PublicKeyToken=9e300f9f87f04a7a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\PInvoke.NCrypt.0.3.2\lib\net40\PInvoke.NCrypt.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PInvoke.Windows.Core, Version=0.3.0.0, Culture=neutral, PublicKeyToken=9e300f9f87f04a7a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\PInvoke.Windows.Core.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Security.Cryptography, Version=1.7.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Security.Cryptography.1.7.2\lib\net35\Security.Cryptography.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Spatial, Version=5.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Spatial.5.6.4\lib\net40\System.Spatial.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Validation, Version=2.2.0.0, Culture=neutral, PublicKeyToken=2fc06f0d701809a7, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Validation.2.2.8\lib\dotnet\Validation.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CertMaker.cs" />
|
||||
<Compile Include="ExtensionDecoder.cs" />
|
||||
<Compile Include="Helpers.cs" />
|
||||
<Compile Include="HttpsListener.cs" />
|
||||
<Compile Include="HubControl.cs" />
|
||||
<Compile Include="IoTDevice.cs" />
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="TLSClient.cs" />
|
||||
<Compile Include="TLSServer.cs" />
|
||||
<Compile Include="UpdateDemo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,198 @@
|
|||
namespace RIoT
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Collections;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
public class SslTcpClient
|
||||
{
|
||||
static string aliasCert, aliasKey;
|
||||
|
||||
public static void RunClient(string _aliasCert, string _aliasKey)
|
||||
{
|
||||
aliasCert = _aliasCert;
|
||||
aliasKey = _aliasKey;
|
||||
Thread tt = new Thread(new ThreadStart(RunClientX));
|
||||
tt.Start();
|
||||
}
|
||||
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.");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private static string ReadMessage(SslStream sslStream)
|
||||
{
|
||||
byte[] buffer = new byte[2048];
|
||||
StringBuilder messageData = new StringBuilder();
|
||||
int bytes = -1;
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
bytes = sslStream.Read(buffer, 0, buffer.Length);
|
||||
Decoder decoder = Encoding.UTF8.GetDecoder();
|
||||
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
|
||||
decoder.GetChars(buffer, 0, bytes, chars, 0);
|
||||
messageData.Append(chars);
|
||||
// Check for EOF.
|
||||
if (messageData.ToString().IndexOf("<EOF>") != -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (bytes != 0);
|
||||
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
Debug.WriteLine("Stream read error:" + e.ToString());
|
||||
throw;
|
||||
|
||||
}
|
||||
return messageData.ToString();
|
||||
}
|
||||
|
||||
|
||||
public static bool ValidateServerCertificate(
|
||||
object sender,
|
||||
X509Certificate certificate,
|
||||
X509Chain chain,
|
||||
SslPolicyErrors sslPolicyErrors)
|
||||
{
|
||||
if (sslPolicyErrors == SslPolicyErrors.None)
|
||||
return true;
|
||||
// For this test code, any server-cert is allowed
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,476 @@
|
|||
|
||||
namespace RIoT
|
||||
{
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
using Security.Cryptography;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Asn1;
|
||||
using Org.BouncyCastle.Security;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.Asn1.X509;
|
||||
using Org.BouncyCastle.Asn1.X9;
|
||||
using Org.BouncyCastle.X509;
|
||||
using Org.BouncyCastle.Asn1.Nist;
|
||||
using Org.BouncyCastle.Math.EC;
|
||||
|
||||
public sealed class SslTcpServer
|
||||
{
|
||||
static X509Certificate2 ServerCert;
|
||||
static X509Certificate2 DeviceCA;
|
||||
static string DeviceIDPEMFile;
|
||||
|
||||
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 = "passw0rd";
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private static string ReadMessageX(SslStream sslStream)
|
||||
{
|
||||
byte[] buffer = new byte[2048];
|
||||
StringBuilder messageData = new StringBuilder();
|
||||
int bytes = -1;
|
||||
do
|
||||
{
|
||||
bytes = sslStream.Read(buffer, 0, buffer.Length);
|
||||
Decoder decoder = Encoding.UTF8.GetDecoder();
|
||||
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
|
||||
decoder.GetChars(buffer, 0, bytes, chars, 0);
|
||||
messageData.Append(chars);
|
||||
if (messageData.ToString().IndexOf("<EOF>") != -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
} while (bytes != 0);
|
||||
|
||||
return messageData.ToString();
|
||||
}
|
||||
|
||||
|
||||
/// <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)
|
||||
{
|
||||
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.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;
|
||||
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
static Object HubLock = new object();
|
||||
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()}");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal static void SendMessage(SslStream sslStream, string _message)
|
||||
{
|
||||
if (_message.Length > 256) throw new ArgumentException("too long");
|
||||
byte[] message = Encoding.UTF8.GetBytes(_message);
|
||||
try
|
||||
{
|
||||
byte[] len = new byte[] { (byte)message.Length };
|
||||
sslStream.Write(len, 0, 1);
|
||||
sslStream.Write(message, 0, message.Length);
|
||||
sslStream.Flush();
|
||||
} catch(Exception e)
|
||||
{
|
||||
Debug.WriteLine("error writing: " + e.ToString());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,212 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RIoT
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Contains logic for a demo where a bunch of devices (right now just simulated devices, but easy to
|
||||
/// extend to real devices) send messages to the hub and look for update requests, and then update themselves.
|
||||
///
|
||||
/// This test starts a TLS server with backing logic to create IoT device Hub instances. This is the fake-DRS server
|
||||
///
|
||||
/// Devices are created that know the address of the fakeDRS server, but no hub twin.
|
||||
///
|
||||
/// When the devices start they connect to the fake-DRS service, and authenticate themselves usign their device certs
|
||||
///
|
||||
/// If authentication is successful, the fake-DRS server creates the IoT hub device and sets the authentication
|
||||
/// token as the alias cert.
|
||||
///
|
||||
///
|
||||
///
|
||||
/// </summary>
|
||||
class UpdateDemo
|
||||
{
|
||||
IoTDevice[] ClientDevices;
|
||||
internal static HubControl HubController;
|
||||
|
||||
|
||||
internal UpdateDemo()
|
||||
{
|
||||
HubController = new HubControl();
|
||||
HubController.Connect();
|
||||
HubController.RemoveAllDevices();
|
||||
}
|
||||
|
||||
internal void FakeDRSTest()
|
||||
{
|
||||
MakeClientDevicesAndCerts(5);
|
||||
StartFakeDRSServerThread();
|
||||
RunDemo();
|
||||
}
|
||||
/// <summary>
|
||||
/// Make a bunch of IOTDevice objects and give them a name, keys and certs, but do not enroll them
|
||||
/// in the hub
|
||||
/// </summary>
|
||||
/// <param name="numDevices"></param>
|
||||
void MakeClientDevicesAndCerts(int numDevices)
|
||||
{
|
||||
Program.IODir += "MultiDevice/";
|
||||
CertMaker m = new CertMaker(Program.IODir);
|
||||
|
||||
ClientDevices = new IoTDevice[numDevices];
|
||||
// make the devices and enroll them in the hub
|
||||
for (int j = 0; j < numDevices; j++)
|
||||
{
|
||||
string devId = GetDeviceID(j);
|
||||
Program.SetDeviceNumber(j);
|
||||
|
||||
// todo - have the devices chain to the same vendor root.
|
||||
int fwidSeed = 0;
|
||||
m.MakeNew(5, false, fwidSeed);
|
||||
IoTDevice device = new IoTDevice(devId, 0, j);
|
||||
ClientDevices[j] = device;
|
||||
}
|
||||
}
|
||||
|
||||
void StartFakeDRSServerThread()
|
||||
{
|
||||
Task t = new Task(StartServer);
|
||||
t.Start();
|
||||
}
|
||||
void StartServer()
|
||||
{
|
||||
SslTcpServer.RunServer(
|
||||
Program.ToPath(Program.ServerCA),
|
||||
Program.ToPath(Program.ServerCert),
|
||||
Program.ToPath(Program.ServerKey),
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
void RunDemo()
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
// register or re-register
|
||||
foreach (var d in ClientDevices)
|
||||
{
|
||||
Program.SetDeviceNumber(d.DeviceNumber);
|
||||
if (d.HubRefreshNeeded)
|
||||
{
|
||||
d.RegisterWithFakeDRSServer();
|
||||
}
|
||||
}
|
||||
// try to send some messages
|
||||
foreach (var d in ClientDevices)
|
||||
{
|
||||
Program.SetDeviceNumber(d.DeviceNumber);
|
||||
d.SendMessages(1, 10);
|
||||
}
|
||||
// see if anyone needs to be updated
|
||||
foreach (var d in ClientDevices)
|
||||
{
|
||||
Program.SetDeviceNumber(d.DeviceNumber);
|
||||
if(d.FirmwareUpdateNeeded)
|
||||
{
|
||||
int targetFwid = d.DesiredFwVersionNumber;
|
||||
d.CurrentFwVersionNumber = targetFwid;
|
||||
if (!d.P0wned)
|
||||
{
|
||||
CertMaker m = new CertMaker(Program.IODir);
|
||||
m.MakeNew(5, true, targetFwid);
|
||||
d.FirmwareUpdateNeeded = false;
|
||||
d.HubRefreshNeeded = true;
|
||||
d.RefreshCert();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine("I'm powned");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Thread.Sleep(1000);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static internal void DoDemo(int numDevices)
|
||||
{
|
||||
Program.IODir += "MultiDevice/";
|
||||
CertMaker m = new CertMaker(Program.IODir);
|
||||
|
||||
IoTDevice[] deviceList = new IoTDevice[numDevices];
|
||||
HubController = new HubControl();
|
||||
|
||||
// make the devices and enroll them in the hub
|
||||
for (int j = 0; j < numDevices; j++)
|
||||
{
|
||||
string devId = GetDeviceID(j);
|
||||
Program.SetDeviceNumber(j);
|
||||
|
||||
int fwidSeed = 0;
|
||||
m.MakeNew(5, false, fwidSeed);
|
||||
|
||||
HubController.Connect();
|
||||
HubController.RemoveDevice(devId);
|
||||
|
||||
var devInfo = ExtensionDecoder.Decode(Program.ToPath(Program.AliasCert));
|
||||
HubController.EnrollDevice(devId, fwidSeed, Helpers.Hexify(devInfo.FirmwareID), devInfo.Cert.Thumbprint);
|
||||
|
||||
IoTDevice device = new IoTDevice(devId, 0, j);
|
||||
deviceList[j] = device;
|
||||
|
||||
}
|
||||
|
||||
// run through messaging and update
|
||||
bool[] primaryOrSEcondary = new bool[numDevices];
|
||||
int epoch = 0;
|
||||
while (true)
|
||||
{
|
||||
for (int j = 0; j < numDevices; j++)
|
||||
{
|
||||
Program.SetDeviceNumber(j);
|
||||
var device = deviceList[j];
|
||||
string devId = GetDeviceID(j);
|
||||
|
||||
// send messages using current firmware
|
||||
device.RefreshCert();
|
||||
device.SendMessages(1, 30);
|
||||
|
||||
if (device.FirmwareUpdateNeeded)
|
||||
{
|
||||
|
||||
// update the firmware on the device
|
||||
int fwidSeed = device.DesiredFwVersionNumber;
|
||||
m.MakeAliasCert(true, fwidSeed);
|
||||
var devInfo = ExtensionDecoder.Decode(Program.ToPath(Program.AliasCert));
|
||||
|
||||
// and tell the hub
|
||||
HubController.RefreshDevice(devId, fwidSeed, Helpers.Hexify(devInfo.FirmwareID), devInfo.Cert.Thumbprint, primaryOrSEcondary[j]);
|
||||
primaryOrSEcondary[j] = !primaryOrSEcondary[j];
|
||||
device.CurrentFwVersionNumber = fwidSeed;
|
||||
}
|
||||
}
|
||||
Debug.WriteLine($"Epoch == {epoch++}");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
static string GetDeviceID(int deviceNum)
|
||||
{
|
||||
string devId = $"RIoT_Test_{deviceNum}";
|
||||
return devId;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="BouncyCastle" version="1.8.1" targetFramework="net452" />
|
||||
<package id="DotNetty.Buffers-signed" version="0.3.2" targetFramework="net452" />
|
||||
<package id="DotNetty.Codecs.Mqtt-signed" version="0.3.2" targetFramework="net452" />
|
||||
<package id="DotNetty.Codecs-signed" version="0.3.2" targetFramework="net452" />
|
||||
<package id="DotNetty.Common-signed" version="0.3.2" targetFramework="net452" />
|
||||
<package id="DotNetty.Handlers-signed" version="0.3.2" targetFramework="net452" />
|
||||
<package id="DotNetty.Transport-signed" version="0.3.2" targetFramework="net452" />
|
||||
<package id="EnterpriseLibrary.TransientFaultHandling" version="6.0.1304.0" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.Azure.Amqp" version="2.0.3" targetFramework="net452" />
|
||||
<package id="Microsoft.Azure.Devices" version="1.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.Azure.Devices.Client" version="1.2.5" targetFramework="net452" />
|
||||
<package id="Microsoft.Azure.Devices.Shared" version="1.0.7" targetFramework="net452" />
|
||||
<package id="Microsoft.Azure.KeyVault.Core" version="1.0.0" targetFramework="net452" />
|
||||
<package id="Microsoft.Data.Edm" version="5.6.4" targetFramework="net452" />
|
||||
<package id="Microsoft.Data.OData" version="5.6.4" targetFramework="net452" />
|
||||
<package id="Microsoft.Data.Services.Client" version="5.6.4" targetFramework="net452" />
|
||||
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net452" />
|
||||
<package id="PCLCrypto" version="2.0.147" targetFramework="net452" />
|
||||
<package id="PInvoke.BCrypt" version="0.3.2" targetFramework="net452" />
|
||||
<package id="PInvoke.Kernel32" version="0.3.2" targetFramework="net452" />
|
||||
<package id="PInvoke.NCrypt" version="0.3.2" targetFramework="net452" />
|
||||
<package id="PInvoke.Windows.Core" version="0.3.2" targetFramework="net452" />
|
||||
<package id="Security.Cryptography" version="1.7.2" targetFramework="net452" />
|
||||
<package id="System.Spatial" version="5.6.4" targetFramework="net452" />
|
||||
<package id="Validation" version="2.2.8" targetFramework="net452" />
|
||||
<package id="WindowsAzure.Storage" version="7.0.0" targetFramework="net452" />
|
||||
</packages>
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
|
||||
</startup>
|
||||
<system.serviceModel>
|
||||
<extensions>
|
||||
<!-- In this extension section we are introducing all known service bus extensions. User can remove the ones they don't need. -->
|
||||
<behaviorExtensions>
|
||||
<add name="connectionStatusBehavior"
|
||||
type="Microsoft.ServiceBus.Configuration.ConnectionStatusElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="transportClientEndpointBehavior"
|
||||
type="Microsoft.ServiceBus.Configuration.TransportClientEndpointBehaviorElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="serviceRegistrySettings"
|
||||
type="Microsoft.ServiceBus.Configuration.ServiceRegistrySettingsElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
</behaviorExtensions>
|
||||
<bindingElementExtensions>
|
||||
<add name="netMessagingTransport"
|
||||
type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingTransportExtensionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="tcpRelayTransport"
|
||||
type="Microsoft.ServiceBus.Configuration.TcpRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="httpRelayTransport"
|
||||
type="Microsoft.ServiceBus.Configuration.HttpRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="httpsRelayTransport"
|
||||
type="Microsoft.ServiceBus.Configuration.HttpsRelayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="onewayRelayTransport"
|
||||
type="Microsoft.ServiceBus.Configuration.RelayedOnewayTransportElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
</bindingElementExtensions>
|
||||
<bindingExtensions>
|
||||
<add name="basicHttpRelayBinding"
|
||||
type="Microsoft.ServiceBus.Configuration.BasicHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="webHttpRelayBinding"
|
||||
type="Microsoft.ServiceBus.Configuration.WebHttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="ws2007HttpRelayBinding"
|
||||
type="Microsoft.ServiceBus.Configuration.WS2007HttpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="netTcpRelayBinding"
|
||||
type="Microsoft.ServiceBus.Configuration.NetTcpRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="netOnewayRelayBinding"
|
||||
type="Microsoft.ServiceBus.Configuration.NetOnewayRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="netEventRelayBinding"
|
||||
type="Microsoft.ServiceBus.Configuration.NetEventRelayBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add name="netMessagingBinding"
|
||||
type="Microsoft.ServiceBus.Messaging.Configuration.NetMessagingBindingCollectionElement, Microsoft.ServiceBus, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
</bindingExtensions>
|
||||
</extensions>
|
||||
</system.serviceModel>
|
||||
<appSettings>
|
||||
<!-- Service Bus specific app setings for messaging connections -->
|
||||
<add key="Microsoft.ServiceBus.ConnectionString"
|
||||
value="Endpoint=sb://[your namespace].servicebus.windows.net;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=[your secret]"/>
|
||||
</appSettings>
|
||||
</configuration>
|
|
@ -0,0 +1,106 @@
|
|||
namespace RIoTDemo
|
||||
{
|
||||
partial class DeviceStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Component Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.pic = new System.Windows.Forms.PictureBox();
|
||||
this.status = new System.Windows.Forms.TextBox();
|
||||
this.DeviceID = new System.Windows.Forms.Label();
|
||||
this.message = new System.Windows.Forms.TextBox();
|
||||
this.pownMe = new System.Windows.Forms.Button();
|
||||
((System.ComponentModel.ISupportInitialize)(this.pic)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// pic
|
||||
//
|
||||
this.pic.Location = new System.Drawing.Point(19, 45);
|
||||
this.pic.Name = "pic";
|
||||
this.pic.Size = new System.Drawing.Size(187, 137);
|
||||
this.pic.TabIndex = 0;
|
||||
this.pic.TabStop = false;
|
||||
//
|
||||
// status
|
||||
//
|
||||
this.status.Location = new System.Drawing.Point(19, 206);
|
||||
this.status.Name = "status";
|
||||
this.status.Size = new System.Drawing.Size(104, 20);
|
||||
this.status.TabIndex = 1;
|
||||
//
|
||||
// DeviceID
|
||||
//
|
||||
this.DeviceID.AutoSize = true;
|
||||
this.DeviceID.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.DeviceID.Location = new System.Drawing.Point(19, 4);
|
||||
this.DeviceID.Name = "DeviceID";
|
||||
this.DeviceID.Size = new System.Drawing.Size(136, 29);
|
||||
this.DeviceID.TabIndex = 2;
|
||||
this.DeviceID.Text = "UnknownID";
|
||||
//
|
||||
// message
|
||||
//
|
||||
this.message.Location = new System.Drawing.Point(19, 180);
|
||||
this.message.Name = "message";
|
||||
this.message.Size = new System.Drawing.Size(187, 20);
|
||||
this.message.TabIndex = 3;
|
||||
//
|
||||
// pownMe
|
||||
//
|
||||
this.pownMe.Location = new System.Drawing.Point(129, 206);
|
||||
this.pownMe.Name = "pownMe";
|
||||
this.pownMe.Size = new System.Drawing.Size(75, 23);
|
||||
this.pownMe.TabIndex = 4;
|
||||
this.pownMe.Text = "P0wn Me!";
|
||||
this.pownMe.UseVisualStyleBackColor = true;
|
||||
this.pownMe.Click += new System.EventHandler(this.pownMe_Click);
|
||||
//
|
||||
// DeviceStatus
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.pownMe);
|
||||
this.Controls.Add(this.message);
|
||||
this.Controls.Add(this.DeviceID);
|
||||
this.Controls.Add(this.status);
|
||||
this.Controls.Add(this.pic);
|
||||
this.Name = "DeviceStatus";
|
||||
this.Size = new System.Drawing.Size(231, 241);
|
||||
((System.ComponentModel.ISupportInitialize)(this.pic)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.PictureBox pic;
|
||||
private System.Windows.Forms.TextBox status;
|
||||
private System.Windows.Forms.Label DeviceID;
|
||||
private System.Windows.Forms.TextBox message;
|
||||
private System.Windows.Forms.Button pownMe;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace RIoTDemo
|
||||
{
|
||||
internal enum CurrentState
|
||||
{
|
||||
Good,
|
||||
OldFirmware,
|
||||
BadFirmware
|
||||
}
|
||||
|
||||
public partial class DeviceStatus : UserControl
|
||||
{
|
||||
internal string Id="Unknown";
|
||||
internal int MyVersionNumber = 0;
|
||||
internal CurrentState State = CurrentState.BadFirmware;
|
||||
internal Bitmap Picture = new Bitmap(100, 100);
|
||||
String LastMessage;
|
||||
internal DateTime LastMessageTime;
|
||||
internal KnownColor PicColor = KnownColor.Black;
|
||||
|
||||
internal bool AmIPOwned = false;
|
||||
internal bool P0wnedStatusChanged = false;
|
||||
|
||||
|
||||
public DeviceStatus()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
void SetVersionNumber(int newVersionNumber)
|
||||
{
|
||||
if (newVersionNumber == MyVersionNumber) return;
|
||||
MyVersionNumber = newVersionNumber;
|
||||
UpdateGUI();
|
||||
}
|
||||
internal void NotifyNewMessage(string m)
|
||||
{
|
||||
LastMessage = m;
|
||||
LastMessageTime = DateTime.Now;
|
||||
|
||||
}
|
||||
|
||||
internal void UpdateGUI()
|
||||
{
|
||||
if(LastMessage!=null)
|
||||
{
|
||||
message.Text = LastMessage;
|
||||
pic.BackColor = Color.FromKnownColor((KnownColor) PicColor);
|
||||
LastMessage = null;
|
||||
}
|
||||
|
||||
if (AmIPOwned)
|
||||
{
|
||||
pownMe.BackColor = Color.Red;
|
||||
}
|
||||
else
|
||||
{
|
||||
pownMe.BackColor = Color.Green;
|
||||
}
|
||||
|
||||
//this.pic. = Picture;
|
||||
this.DeviceID.Text = Id;
|
||||
this.status.Text = $"Version Number {MyVersionNumber}";
|
||||
switch(State)
|
||||
{
|
||||
case CurrentState.Good:
|
||||
this.BackColor = Color.Green;
|
||||
break;
|
||||
case CurrentState.OldFirmware:
|
||||
this.BackColor = Color.Yellow;
|
||||
break;
|
||||
case CurrentState.BadFirmware:
|
||||
this.BackColor = Color.Red;
|
||||
break;
|
||||
default:
|
||||
this.BackColor = Color.Red;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void pownMe_Click(object sender, EventArgs e)
|
||||
{
|
||||
AmIPOwned = !AmIPOwned;
|
||||
P0wnedStatusChanged = true;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
|
@ -0,0 +1,80 @@
|
|||
namespace RIoTDemo
|
||||
{
|
||||
partial class MainPage
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.timer1 = new System.Windows.Forms.Timer(this.components);
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.VersionNumber = new System.Windows.Forms.Label();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.button1.Location = new System.Drawing.Point(13, 13);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(145, 42);
|
||||
this.button1.TabIndex = 0;
|
||||
this.button1.Text = "StartUpdate";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// VersionNumber
|
||||
//
|
||||
this.VersionNumber.AutoSize = true;
|
||||
this.VersionNumber.Font = new System.Drawing.Font("Microsoft Sans Serif", 14F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.VersionNumber.Location = new System.Drawing.Point(201, 20);
|
||||
this.VersionNumber.Name = "VersionNumber";
|
||||
this.VersionNumber.Size = new System.Drawing.Size(312, 24);
|
||||
this.VersionNumber.TabIndex = 1;
|
||||
this.VersionNumber.Text = "Target Firmware Version Number: 0";
|
||||
//
|
||||
// MainPage
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(805, 504);
|
||||
this.Controls.Add(this.VersionNumber);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Name = "MainPage";
|
||||
this.Text = "Device Status";
|
||||
this.Paint += new System.Windows.Forms.PaintEventHandler(this.MainPage_Paint);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Timer timer1;
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.Label VersionNumber;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
using Microsoft.Azure.Devices;
|
||||
using System.Diagnostics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using RIoT;
|
||||
using System.IO;
|
||||
using Microsoft.Azure.Devices.Shared;
|
||||
using Microsoft.ServiceBus.Messaging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace RIoTDemo
|
||||
{
|
||||
/// <summary>
|
||||
/// Most program logic is driven from timer ticks in MainPage. ChildStatus forms
|
||||
/// contain the per-device UI, but they display what they're told based on waht
|
||||
/// goes on here.
|
||||
/// </summary>
|
||||
public partial class MainPage : Form
|
||||
{
|
||||
bool initialized = false;
|
||||
string DeviceIDMatch = "RIoT_Test";
|
||||
RegistryManager RegMgr;
|
||||
EventHubClient EventClient;
|
||||
EventHubReceiver[] Receivers;
|
||||
DateTime LastUpdateTime = DateTime.Now;
|
||||
string[] D2CPartitions;
|
||||
string ConnectionStringFile = "c:\\tmp\\ConnectionString.txt";
|
||||
IEnumerable<Device> CurrentDeviceList;
|
||||
List<DeviceStatus> StatusPanes = new List<DeviceStatus>();
|
||||
|
||||
int TargetVersionNumber = 0;
|
||||
|
||||
|
||||
public MainPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void MainPage_Paint(object sender, PaintEventArgs e)
|
||||
{
|
||||
if (initialized) return;
|
||||
initialized = true;
|
||||
|
||||
if (!File.Exists(ConnectionStringFile))
|
||||
{
|
||||
Notify($"Missing connection string file {ConnectionStringFile}. This file is omitted from the distribution because it contains passwords. Fix and restart.");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
var connectionString = File.ReadAllText(ConnectionStringFile);
|
||||
RegMgr = RegistryManager.CreateFromConnectionString(connectionString);
|
||||
string iotHubD2cEndpoint = "messages/events";
|
||||
EventClient = EventHubClient.CreateFromConnectionString(connectionString, iotHubD2cEndpoint);
|
||||
D2CPartitions = EventClient.GetRuntimeInformation().PartitionIds;
|
||||
Receivers = new EventHubReceiver[D2CPartitions.Length];
|
||||
for (int j = 0; j < D2CPartitions.Length; j++)
|
||||
{
|
||||
Receivers[j] = EventClient.GetDefaultConsumerGroup().CreateReceiver(D2CPartitions[j], DateTime.UtcNow - new TimeSpan(0, 0, 10));
|
||||
}
|
||||
Debug.Write("");
|
||||
//CancellationTokenSource cts = new CancellationTokenSource();
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Notify($"Failed to CreateFromConnectionString: {ex.ToString()}");
|
||||
return ;
|
||||
}
|
||||
// everything else happens on timer ticks
|
||||
this.timer1.Interval = 1000;
|
||||
this.timer1.Tick += Timer1_Tick;
|
||||
this.timer1.Start();
|
||||
}
|
||||
|
||||
bool executing = false;
|
||||
private async void Timer1_Tick(object sender, EventArgs e)
|
||||
{
|
||||
if (executing) return;
|
||||
executing = true;
|
||||
// do we have changes to the devices we're monitoring?
|
||||
var devices = await RegMgr.GetDevicesAsync(100);
|
||||
var demoDevices = devices.Where(x => x.Id.Contains(DeviceIDMatch));
|
||||
if(DeviceListHasChanged(CurrentDeviceList, demoDevices))
|
||||
{
|
||||
CurrentDeviceList = demoDevices;
|
||||
UpdateDevicePanes();
|
||||
}
|
||||
// else update status of what we have
|
||||
|
||||
// First, get a batch of messages and post them to the controls
|
||||
|
||||
for (int j = 0; j < Receivers.Length; j++)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
|
||||
var mx = Receivers[j].Receive(new TimeSpan(0, 0, 0, 0, 1));
|
||||
if (mx == null) break;
|
||||
var senderDeviceId = mx.SystemProperties["iothub-connection-device-id"];
|
||||
var messageData = mx.GetBytes();
|
||||
var message = JsonConvert.DeserializeObject<MyMessageType>(Encoding.ASCII.GetString(messageData));
|
||||
|
||||
var device = StatusPanes.Where(x => x.Id == senderDeviceId.ToString()).First();
|
||||
string colorString = Enum.GetName(typeof(KnownColor), message.ColorVal);
|
||||
device.NotifyNewMessage($"Count={message.MessageCount}, Color = {colorString}");
|
||||
device.PicColor = (KnownColor)message.ColorVal;
|
||||
device.LastMessageTime = DateTime.Now;
|
||||
Debug.WriteLine(message.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Next 1) update the background color based on what version number the device *claims* it is
|
||||
// 2) If device firmware is wrong AND the last firmware change was >1 min ago, revoke the
|
||||
// creds for the device
|
||||
foreach (var cc in this.Controls)
|
||||
{
|
||||
if (!(cc is DeviceStatus)) continue;
|
||||
var ds = (DeviceStatus)cc;
|
||||
|
||||
string deviceId = ds.Id;
|
||||
var device = demoDevices.Where(x => x.Id == deviceId).First();
|
||||
var twx = await RegMgr.GetTwinAsync(deviceId);
|
||||
CurrentState s = CurrentState.Good;
|
||||
DateTime nowTIme = DateTime.Now;
|
||||
try
|
||||
{
|
||||
if (!twx.Properties.Reported.Contains("VersionNumber")) s = CurrentState.BadFirmware;
|
||||
if (!twx.Properties.Desired.Contains("VersionNumber")) s = CurrentState.BadFirmware;
|
||||
if (s != CurrentState.BadFirmware)
|
||||
{
|
||||
var reported = (twx.Properties.Reported["VersionNumber"]);
|
||||
var desired = (twx.Properties.Desired["VersionNumber"]);
|
||||
if (reported == desired)
|
||||
{
|
||||
s = CurrentState.Good;
|
||||
}
|
||||
else
|
||||
{
|
||||
s = CurrentState.OldFirmware;
|
||||
}
|
||||
// If the device is out-of-date, and the update was requested >1 min ago, revoke the creds
|
||||
if(s== CurrentState.OldFirmware && nowTIme-LastUpdateTime> new TimeSpan(0,1,0))
|
||||
{
|
||||
device.Authentication.X509Thumbprint.PrimaryThumbprint = null;
|
||||
s = CurrentState.BadFirmware;
|
||||
await RegMgr.UpdateDeviceAsync(device);
|
||||
}
|
||||
|
||||
if (twx.Properties.Reported.Contains("VersionNumber"))
|
||||
{
|
||||
var repX = (twx.Properties.Reported["VersionNumber"]);
|
||||
ds.MyVersionNumber = repX;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ee)
|
||||
{
|
||||
Debug.WriteLine($"Error getting twin info {ee.ToString()}");
|
||||
s = CurrentState.BadFirmware;
|
||||
}
|
||||
|
||||
// any messages in the last minute?
|
||||
if (nowTIme - ds.LastMessageTime > new TimeSpan(0, 0, 1, 0))
|
||||
{
|
||||
s = CurrentState.BadFirmware;
|
||||
}
|
||||
|
||||
ds.State = s;
|
||||
ds.UpdateGUI();
|
||||
}
|
||||
|
||||
// Finally, tell devices that they're P0wned
|
||||
foreach (var cc in this.Controls)
|
||||
{
|
||||
if (!(cc is DeviceStatus)) continue;
|
||||
var ds = (DeviceStatus)cc;
|
||||
if (!ds.P0wnedStatusChanged) continue;
|
||||
ds.P0wnedStatusChanged = false;
|
||||
|
||||
var twx = await RegMgr.GetTwinAsync(ds.Id);
|
||||
|
||||
TwinCollection t = new TwinCollection();
|
||||
t["POwned"] = ds.AmIPOwned;
|
||||
twx.Properties.Desired = t;
|
||||
await RegMgr.UpdateTwinAsync(ds.Id, twx, twx.ETag);
|
||||
|
||||
}
|
||||
|
||||
executing = false;
|
||||
|
||||
}
|
||||
|
||||
void UpdateDevicePanes()
|
||||
{
|
||||
// remove current
|
||||
foreach (var s in StatusPanes)
|
||||
{
|
||||
this.Controls.Remove(s);
|
||||
}
|
||||
StatusPanes.Clear();
|
||||
// this.Controls.Clear();
|
||||
// add new
|
||||
int xCount = 0;
|
||||
int yCount = 0;
|
||||
int yGutter = 64;
|
||||
foreach (var d in CurrentDeviceList)
|
||||
{
|
||||
DeviceStatus s = new DeviceStatus();
|
||||
|
||||
s.Id = d.Id;
|
||||
if ((xCount + 1) * (s.Width + 10) + 10 > this.Width)
|
||||
{
|
||||
yCount++;
|
||||
xCount = 0;
|
||||
}
|
||||
s.Location = new Point(xCount * (s.Width + 10) + 10, yCount * (s.Height + 10) + yGutter);
|
||||
xCount++;
|
||||
s.Show();
|
||||
StatusPanes.Add(s);
|
||||
this.Controls.Add(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool DeviceListHasChanged(IEnumerable<Device> current, IEnumerable<Device> newList)
|
||||
{
|
||||
if (current == null) return true;
|
||||
if (current.Count() != newList.Count()) return true;
|
||||
foreach (var c in current)
|
||||
{
|
||||
if (newList.Where(x => x.Id == c.Id).Count() == 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
internal static void Notify(string s)
|
||||
{
|
||||
MessageBox.Show(s);
|
||||
}
|
||||
|
||||
bool updating = false;
|
||||
private async void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (updating) return;
|
||||
updating = true;
|
||||
TargetVersionNumber++;
|
||||
this.VersionNumber.Text = $"Target Version Number {TargetVersionNumber}";
|
||||
|
||||
var devices = await RegMgr.GetDevicesAsync(100);
|
||||
var demoDevices = devices.Where(x => x.Id.Contains(DeviceIDMatch));
|
||||
foreach(var d in demoDevices)
|
||||
{
|
||||
var twx = await RegMgr.GetTwinAsync(d.Id);
|
||||
|
||||
TwinCollection t = new TwinCollection();
|
||||
t["VersionNumber"] = TargetVersionNumber;
|
||||
twx.Properties.Desired = t;
|
||||
await RegMgr.UpdateTwinAsync(d.Id, twx, twx.ETag);
|
||||
|
||||
}
|
||||
LastUpdateTime = DateTime.Now;
|
||||
updating = false;
|
||||
}
|
||||
}
|
||||
/*
|
||||
var m = new
|
||||
{
|
||||
DevID = devId,
|
||||
ColorVal = colorVal,
|
||||
MessageCount = messageCount
|
||||
};
|
||||
|
||||
*/
|
||||
public class MyMessageType
|
||||
{
|
||||
public string DevID;
|
||||
public int ColorVal;
|
||||
public int MessageCount;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="timer1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
</root>
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace RIoTDemo
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new MainPage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("RIoTDemo")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("RIoTDemo")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("70669fd8-b9bb-4ea2-b9bb-6e387b2e5788")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,71 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace RIoTDemo.Properties
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RIoTDemo.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
|
@ -0,0 +1,30 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace RIoTDemo.Properties
|
||||
{
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
|
@ -0,0 +1,217 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{70669FD8-B9BB-4EA2-B9BB-6E387B2E5788}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>RIoTDemo</RootNamespace>
|
||||
<AssemblyName>RIoTDemo</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="BouncyCastle.Crypto, Version=1.8.1.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
|
||||
<HintPath>..\packages\BouncyCastle.1.8.1\lib\BouncyCastle.Crypto.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="DotNetty.Buffers, Version=0.3.2.0, Culture=neutral, PublicKeyToken=e7a0210a354f294a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DotNetty.Buffers-signed.0.3.2\lib\net45\DotNetty.Buffers.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="DotNetty.Codecs, Version=0.3.2.0, Culture=neutral, PublicKeyToken=e7a0210a354f294a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DotNetty.Codecs-signed.0.3.2\lib\net45\DotNetty.Codecs.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="DotNetty.Codecs.Mqtt, Version=0.3.2.0, Culture=neutral, PublicKeyToken=e7a0210a354f294a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DotNetty.Codecs.Mqtt-signed.0.3.2\lib\net45\DotNetty.Codecs.Mqtt.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="DotNetty.Common, Version=0.3.2.0, Culture=neutral, PublicKeyToken=e7a0210a354f294a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DotNetty.Common-signed.0.3.2\lib\net45\DotNetty.Common.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="DotNetty.Handlers, Version=0.3.2.0, Culture=neutral, PublicKeyToken=e7a0210a354f294a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DotNetty.Handlers-signed.0.3.2\lib\net45\DotNetty.Handlers.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="DotNetty.Transport, Version=0.3.2.0, Culture=neutral, PublicKeyToken=e7a0210a354f294a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DotNetty.Transport-signed.0.3.2\lib\net45\DotNetty.Transport.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.Amqp, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Azure.Amqp.2.0.3\lib\net45\Microsoft.Azure.Amqp.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.Devices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Azure.Devices.1.2.3\lib\net451\Microsoft.Azure.Devices.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.Devices.Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Azure.Devices.Client.1.2.5\lib\net45\Microsoft.Azure.Devices.Client.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.Devices.Shared, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Azure.Devices.Shared.1.0.7\lib\net45\Microsoft.Azure.Devices.Shared.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Azure.KeyVault.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Azure.KeyVault.Core.1.0.0\lib\net40\Microsoft.Azure.KeyVault.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Data.Edm, Version=5.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Data.Edm.5.6.4\lib\net40\Microsoft.Data.Edm.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Data.OData, Version=5.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Data.OData.5.6.4\lib\net40\Microsoft.Data.OData.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Data.Services.Client, Version=5.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Data.Services.Client.5.6.4\lib\net40\Microsoft.Data.Services.Client.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\EnterpriseLibrary.TransientFaultHandling.6.0.1304.0\lib\portable-net45+win+wp8\Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.ServiceBus, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\WindowsAzure.ServiceBus.4.0.0\lib\net45-full\Microsoft.ServiceBus.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.WindowsAzure.Storage, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\WindowsAzure.Storage.7.0.0\lib\net40\Microsoft.WindowsAzure.Storage.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PCLCrypto, Version=2.0.0.0, Culture=neutral, PublicKeyToken=d4421c8a4786956c, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\PCLCrypto.2.0.147\lib\net45\PCLCrypto.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PInvoke.BCrypt, Version=0.3.0.0, Culture=neutral, PublicKeyToken=9e300f9f87f04a7a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\PInvoke.BCrypt.0.3.2\lib\net40\PInvoke.BCrypt.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PInvoke.Kernel32, Version=0.3.0.0, Culture=neutral, PublicKeyToken=9e300f9f87f04a7a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\PInvoke.Kernel32.0.3.2\lib\net40\PInvoke.Kernel32.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PInvoke.NCrypt, Version=0.3.0.0, Culture=neutral, PublicKeyToken=9e300f9f87f04a7a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\PInvoke.NCrypt.0.3.2\lib\net40\PInvoke.NCrypt.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="PInvoke.Windows.Core, Version=0.3.0.0, Culture=neutral, PublicKeyToken=9e300f9f87f04a7a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\PInvoke.Windows.Core.0.3.2\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\PInvoke.Windows.Core.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Net.Http.Formatting, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Spatial, Version=5.6.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Spatial.5.6.4\lib\net40\System.Spatial.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.Http, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Validation, Version=2.2.0.0, Culture=neutral, PublicKeyToken=2fc06f0d701809a7, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Validation.2.2.8\lib\dotnet\Validation.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\RIoT\Helpers.cs">
|
||||
<Link>Helpers.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="DeviceStatus.cs">
|
||||
<SubType>UserControl</SubType>
|
||||
</Compile>
|
||||
<Compile Include="DeviceStatus.Designer.cs">
|
||||
<DependentUpon>DeviceStatus.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="MainPage.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainPage.Designer.cs">
|
||||
<DependentUpon>MainPage.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="DeviceStatus.resx">
|
||||
<DependentUpon>DeviceStatus.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="MainPage.resx">
|
||||
<DependentUpon>MainPage.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,31 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="BouncyCastle" version="1.8.1" targetFramework="net452" />
|
||||
<package id="DotNetty.Buffers-signed" version="0.3.2" targetFramework="net452" />
|
||||
<package id="DotNetty.Codecs.Mqtt-signed" version="0.3.2" targetFramework="net452" />
|
||||
<package id="DotNetty.Codecs-signed" version="0.3.2" targetFramework="net452" />
|
||||
<package id="DotNetty.Common-signed" version="0.3.2" targetFramework="net452" />
|
||||
<package id="DotNetty.Handlers-signed" version="0.3.2" targetFramework="net452" />
|
||||
<package id="DotNetty.Transport-signed" version="0.3.2" targetFramework="net452" />
|
||||
<package id="EnterpriseLibrary.TransientFaultHandling" version="6.0.1304.0" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.Azure.Amqp" version="2.0.3" targetFramework="net452" />
|
||||
<package id="Microsoft.Azure.Devices" version="1.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.Azure.Devices.Client" version="1.2.5" targetFramework="net452" />
|
||||
<package id="Microsoft.Azure.Devices.Shared" version="1.0.7" targetFramework="net452" />
|
||||
<package id="Microsoft.Azure.KeyVault.Core" version="1.0.0" targetFramework="net452" />
|
||||
<package id="Microsoft.Data.Edm" version="5.6.4" targetFramework="net452" />
|
||||
<package id="Microsoft.Data.OData" version="5.6.4" targetFramework="net452" />
|
||||
<package id="Microsoft.Data.Services.Client" version="5.6.4" targetFramework="net452" />
|
||||
<package id="Newtonsoft.Json" version="6.0.8" targetFramework="net452" />
|
||||
<package id="PCLCrypto" version="2.0.147" targetFramework="net452" />
|
||||
<package id="PInvoke.BCrypt" version="0.3.2" targetFramework="net452" />
|
||||
<package id="PInvoke.Kernel32" version="0.3.2" targetFramework="net452" />
|
||||
<package id="PInvoke.NCrypt" version="0.3.2" targetFramework="net452" />
|
||||
<package id="PInvoke.Windows.Core" version="0.3.2" targetFramework="net452" />
|
||||
<package id="System.Spatial" version="5.6.4" targetFramework="net452" />
|
||||
<package id="Validation" version="2.2.8" targetFramework="net452" />
|
||||
<package id="WindowsAzure.ServiceBus" version="4.0.0" targetFramework="net452" />
|
||||
<package id="WindowsAzure.Storage" version="7.0.0" targetFramework="net452" />
|
||||
</packages>
|
|
@ -0,0 +1,69 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.25420.1
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RIoT", "RIoT\RIoT.csproj", "{20A30499-7F02-446F-8716-E85FCDBB0CE4}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TlsClient", "TlsClient\TlsClient.vcxproj", "{E144BF89-3BEA-402C-B58A-A3DB2AC68F67}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{590E0BE7-0E85-4923-AD6D-7DA0E2F7FF1C}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
Docs\Notes.txt = Docs\Notes.txt
|
||||
Docs\RiotUtils.docx = Docs\RiotUtils.docx
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RIoTDemo", "RIoTDemo\RIoTDemo.csproj", "{70669FD8-B9BB-4EA2-B9BB-6E387B2E5788}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{20A30499-7F02-446F-8716-E85FCDBB0CE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{20A30499-7F02-446F-8716-E85FCDBB0CE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{20A30499-7F02-446F-8716-E85FCDBB0CE4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{20A30499-7F02-446F-8716-E85FCDBB0CE4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{20A30499-7F02-446F-8716-E85FCDBB0CE4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{20A30499-7F02-446F-8716-E85FCDBB0CE4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{20A30499-7F02-446F-8716-E85FCDBB0CE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{20A30499-7F02-446F-8716-E85FCDBB0CE4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{20A30499-7F02-446F-8716-E85FCDBB0CE4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{20A30499-7F02-446F-8716-E85FCDBB0CE4}.Release|x64.Build.0 = Release|Any CPU
|
||||
{20A30499-7F02-446F-8716-E85FCDBB0CE4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{20A30499-7F02-446F-8716-E85FCDBB0CE4}.Release|x86.Build.0 = Release|Any CPU
|
||||
{E144BF89-3BEA-402C-B58A-A3DB2AC68F67}.Debug|Any CPU.ActiveCfg = Debug|Win32
|
||||
{E144BF89-3BEA-402C-B58A-A3DB2AC68F67}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{E144BF89-3BEA-402C-B58A-A3DB2AC68F67}.Debug|x64.Build.0 = Debug|x64
|
||||
{E144BF89-3BEA-402C-B58A-A3DB2AC68F67}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{E144BF89-3BEA-402C-B58A-A3DB2AC68F67}.Debug|x86.Build.0 = Debug|Win32
|
||||
{E144BF89-3BEA-402C-B58A-A3DB2AC68F67}.Release|Any CPU.ActiveCfg = Release|Win32
|
||||
{E144BF89-3BEA-402C-B58A-A3DB2AC68F67}.Release|x64.ActiveCfg = Release|x64
|
||||
{E144BF89-3BEA-402C-B58A-A3DB2AC68F67}.Release|x64.Build.0 = Release|x64
|
||||
{E144BF89-3BEA-402C-B58A-A3DB2AC68F67}.Release|x86.ActiveCfg = Release|Win32
|
||||
{E144BF89-3BEA-402C-B58A-A3DB2AC68F67}.Release|x86.Build.0 = Release|Win32
|
||||
{70669FD8-B9BB-4EA2-B9BB-6E387B2E5788}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{70669FD8-B9BB-4EA2-B9BB-6E387B2E5788}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{70669FD8-B9BB-4EA2-B9BB-6E387B2E5788}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{70669FD8-B9BB-4EA2-B9BB-6E387B2E5788}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{70669FD8-B9BB-4EA2-B9BB-6E387B2E5788}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{70669FD8-B9BB-4EA2-B9BB-6E387B2E5788}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{70669FD8-B9BB-4EA2-B9BB-6E387B2E5788}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{70669FD8-B9BB-4EA2-B9BB-6E387B2E5788}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{70669FD8-B9BB-4EA2-B9BB-6E387B2E5788}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{70669FD8-B9BB-4EA2-B9BB-6E387B2E5788}.Release|x64.Build.0 = Release|Any CPU
|
||||
{70669FD8-B9BB-4EA2-B9BB-6E387B2E5788}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{70669FD8-B9BB-4EA2-B9BB-6E387B2E5788}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
EnterpriseLibraryConfigurationToolBinariesPathV6 = packages\EnterpriseLibrary.TransientFaultHandling.6.0.1304.0\lib\portable-net45+win+wp8
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,40 @@
|
|||
========================================================================
|
||||
CONSOLE APPLICATION : TlsClient Project Overview
|
||||
========================================================================
|
||||
|
||||
AppWizard has created this TlsClient application for you.
|
||||
|
||||
This file contains a summary of what you will find in each of the files that
|
||||
make up your TlsClient application.
|
||||
|
||||
|
||||
TlsClient.vcxproj
|
||||
This is the main project file for VC++ projects generated using an Application Wizard.
|
||||
It contains information about the version of Visual C++ that generated the file, and
|
||||
information about the platforms, configurations, and project features selected with the
|
||||
Application Wizard.
|
||||
|
||||
TlsClient.vcxproj.filters
|
||||
This is the filters file for VC++ projects generated using an Application Wizard.
|
||||
It contains information about the association between the files in your project
|
||||
and the filters. This association is used in the IDE to show grouping of files with
|
||||
similar extensions under a specific node (for e.g. ".cpp" files are associated with the
|
||||
"Source Files" filter).
|
||||
|
||||
TlsClient.cpp
|
||||
This is the main application source file.
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
Other standard files:
|
||||
|
||||
StdAfx.h, StdAfx.cpp
|
||||
These files are used to build a precompiled header (PCH) file
|
||||
named TlsClient.pch and a precompiled types file named StdAfx.obj.
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
Other notes:
|
||||
|
||||
AppWizard uses "TODO:" comments to indicate parts of the source code you
|
||||
should add to or customize.
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
|
@ -0,0 +1,414 @@
|
|||
// RIoTOsslClient.cpp : Defines the entry point for the console application.
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
/*
|
||||
This test utility is derived from the sample code at
|
||||
https://wiki.openssl.org/index.php/SSL/TLS_Client
|
||||
|
||||
The only substantive changes are marked <PAUL></PAUL>
|
||||
|
||||
*/
|
||||
|
||||
|
||||
int verify_callback(int preverify, X509_STORE_CTX* x509_ctx);
|
||||
void init_openssl_library(void);
|
||||
void print_cn_name(const char* label, X509_NAME* const name);
|
||||
void print_san_name(const char* label, X509* const cert);
|
||||
void print_error_string(unsigned long err, const char* const label);
|
||||
void CheckIsOne(unsigned long err, const char* const label);
|
||||
void CheckIsNotNull(void* p, const char* const label);
|
||||
int Usage();
|
||||
|
||||
/* Cipher suites, https://www.openssl.org/docs/apps/ciphers.html */
|
||||
const char* const PREFERRED_CIPHERS = "HIGH:!aNULL:!kRSA:!SRP:!PSK:!CAMELLIA:!RC4:!MD5:!DSS";
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
BOOL useChain;
|
||||
char dir[128], deviceCertChain[128], aliasKey[128], serverCA[128], aliasCert[128];;
|
||||
|
||||
if (argc == 1) return Usage();
|
||||
if (argc > 3)return Usage();
|
||||
|
||||
if (strcmp(argv[1], "B") == 0)
|
||||
{
|
||||
useChain = FALSE;
|
||||
}
|
||||
else
|
||||
if (strcmp(argv[1], "C")==0)
|
||||
{
|
||||
useChain = TRUE;
|
||||
}
|
||||
else
|
||||
return Usage();
|
||||
|
||||
if (argc == 3)
|
||||
{
|
||||
strcpy(dir, argv[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
strcpy(dir, ".");
|
||||
}
|
||||
|
||||
//strcat(dir, "/");
|
||||
|
||||
strcpy(aliasKey, dir); strcat(aliasKey, "AliasKey.PEM");
|
||||
strcpy(deviceCertChain, dir); strcat(deviceCertChain, "DeviceCertChainIncAlias.PEM");
|
||||
strcpy(serverCA, dir); strcat(serverCA, "ServerCA.PEM");
|
||||
strcpy(aliasCert, dir); strcat(aliasCert, "AliasCert.PEM");
|
||||
|
||||
printf("Attempting to establish a TLS connection on localhost\n");
|
||||
|
||||
|
||||
long res = 1;
|
||||
int ret = 1;
|
||||
unsigned long ssl_err = 0;
|
||||
|
||||
SSL_CTX* ctx = NULL;
|
||||
BIO *web = NULL, *out = NULL;
|
||||
SSL *ssl = NULL;
|
||||
|
||||
do {
|
||||
// for interactive debugging
|
||||
Sleep(3000);
|
||||
|
||||
init_openssl_library();
|
||||
const SSL_METHOD* method = SSLv23_method();
|
||||
CheckIsNotNull((void*) method, "SSLv23_method");
|
||||
|
||||
ctx = SSL_CTX_new(method);
|
||||
CheckIsNotNull((void*)ctx, "SSL_CTX_new");
|
||||
|
||||
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
|
||||
SSL_CTX_set_verify_depth(ctx, 10);
|
||||
|
||||
|
||||
/* Remove the most egregious. Because SSLv2 and SSLv3 have been */
|
||||
/* removed, a TLSv1.0 handshake is used. The client accepts TLSv1.0 */
|
||||
/* and above. An added benefit of TLS 1.0 and above are TLS */
|
||||
/* extensions like Server Name Indicatior (SNI). */
|
||||
const long flags = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
|
||||
long old_opts = SSL_CTX_set_options(ctx, flags);
|
||||
UNUSED(old_opts);
|
||||
|
||||
// <PAUL> - the following three setup lines are the only substantive changes to the
|
||||
// OSSL sample code
|
||||
res = SSL_CTX_use_PrivateKey_file(ctx, aliasKey, SSL_FILETYPE_PEM);
|
||||
CheckIsOne(res, "SSL_CTX_use_PrivateKey_file");
|
||||
if (useChain)
|
||||
{
|
||||
res = SSL_CTX_use_certificate_chain_file(ctx, deviceCertChain);
|
||||
CheckIsOne(res, "SSL_CTX_use_certificate_chain_file");
|
||||
}
|
||||
else
|
||||
{
|
||||
res = SSL_CTX_use_certificate_chain_file(ctx, aliasCert);
|
||||
CheckIsOne(res, "SSL_CTX_use_certificate_chain_file");
|
||||
}
|
||||
res = SSL_CTX_load_verify_locations(ctx, serverCA, NULL);
|
||||
CheckIsOne(res, "SSL_CTX_load_verify_locations");
|
||||
// </PAUL>
|
||||
|
||||
|
||||
web = BIO_new_ssl_connect(ctx);
|
||||
CheckIsNotNull((void*)web, "BIO_new_ssl_connect");
|
||||
res = BIO_set_conn_hostname(web, "localhost:5556");
|
||||
CheckIsOne(res, "BIO_set_conn_hostname");
|
||||
BIO_get_ssl(web, &ssl);
|
||||
res = SSL_set_cipher_list(ssl, PREFERRED_CIPHERS);
|
||||
CheckIsOne(res, "SSL_set_cipher_list");
|
||||
res = SSL_set_tlsext_host_name(ssl, HOST_NAME);
|
||||
CheckIsOne(res, "SSL_set_tlsext_host_name");
|
||||
res = BIO_do_connect(web);
|
||||
CheckIsOne(res, "BIO_do_connect");
|
||||
res = BIO_do_handshake(web);
|
||||
CheckIsOne(res, "BIO_do_handshake");
|
||||
|
||||
|
||||
X509* cert = SSL_get_peer_certificate(ssl);
|
||||
if (cert) { X509_free(cert); } /* Free immediately */
|
||||
CheckIsNotNull(cert, "SSL_get_peer_certificate");
|
||||
/* Error codes: http://www.openssl.org/docs/apps/verify.html */
|
||||
res = SSL_get_verify_result(ssl);
|
||||
|
||||
ASSERT(X509_V_OK == res);
|
||||
if (!(X509_V_OK == res))
|
||||
{
|
||||
/* Hack a code into print_error_string. */
|
||||
print_error_string((unsigned long)res, "SSL_get_verify_results");
|
||||
break; /* failed */
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Step 3: hostname verifcation. */
|
||||
/* An exercise left to the reader. */
|
||||
|
||||
/**************************************************************************************/
|
||||
/**************************************************************************************/
|
||||
/* Now, we can finally start reading and writing to the BIO... */
|
||||
/**************************************************************************************/
|
||||
/**************************************************************************************/
|
||||
|
||||
printf("Connection was successful. Terminating");
|
||||
|
||||
|
||||
BIO_puts(web, "GET " HOST_RESOURCE " HTTP/1.1\r\nHost: " HOST_NAME "\r\nConnection: close\r\n\r\n");
|
||||
BIO_puts(out, "\nFetching: " HOST_RESOURCE "\n\n");
|
||||
|
||||
#ifdef USE_CONNECTION
|
||||
int len = 0;
|
||||
do {
|
||||
char buff[1536] = {};
|
||||
|
||||
/* https://www.openssl.org/docs/crypto/BIO_read.html */
|
||||
len = BIO_read(web, buff, sizeof(buff));
|
||||
|
||||
if (len > 0)
|
||||
BIO_write(out, buff, len);
|
||||
|
||||
/* BIO_should_retry returns TRUE unless there's an */
|
||||
/* error. We expect an error when the server */
|
||||
/* provides the response and closes the connection. */
|
||||
|
||||
} while (len > 0 || BIO_should_retry(web));
|
||||
#endif
|
||||
|
||||
ret = 0;
|
||||
|
||||
} while (0);
|
||||
|
||||
if (out)
|
||||
BIO_free(out);
|
||||
|
||||
if (web != NULL)
|
||||
BIO_free_all(web);
|
||||
|
||||
if (NULL != ctx)
|
||||
SSL_CTX_free(ctx);
|
||||
|
||||
// for interactive debugging
|
||||
Sleep(3000);
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void init_openssl_library(void)
|
||||
{
|
||||
/* https://www.openssl.org/docs/ssl/SSL_library_init.html */
|
||||
(void)SSL_library_init();
|
||||
/* Cannot fail (always returns success) ??? */
|
||||
|
||||
/* https://www.openssl.org/docs/crypto/ERR_load_crypto_strings.html */
|
||||
SSL_load_error_strings();
|
||||
/* Cannot fail ??? */
|
||||
|
||||
/* SSL_load_error_strings loads both libssl and libcrypto strings */
|
||||
/* ERR_load_crypto_strings(); */
|
||||
/* Cannot fail ??? */
|
||||
|
||||
/* OpenSSL_config may or may not be called internally, based on */
|
||||
/* some #defines and internal gyrations. Explicitly call it */
|
||||
/* *IF* you need something from openssl.cfg, such as a */
|
||||
/* dynamically configured ENGINE. */
|
||||
OPENSSL_config(NULL);
|
||||
/* Cannot fail ??? */
|
||||
|
||||
/* Include <openssl/opensslconf.h> to get this define */
|
||||
#if defined (OPENSSL_THREADS)
|
||||
/* TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO */
|
||||
/* https://www.openssl.org/docs/crypto/threads.html */
|
||||
fprintf(stdout, "Warning: thread locking is not implemented\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void print_cn_name(const char* label, X509_NAME* const name)
|
||||
{
|
||||
int idx = -1, success = 0;
|
||||
unsigned char *utf8 = NULL;
|
||||
|
||||
do
|
||||
{
|
||||
if (!name) break; /* failed */
|
||||
|
||||
idx = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
|
||||
if (!(idx > -1)) break; /* failed */
|
||||
|
||||
X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, idx);
|
||||
if (!entry) break; /* failed */
|
||||
|
||||
ASN1_STRING* data = X509_NAME_ENTRY_get_data(entry);
|
||||
if (!data) break; /* failed */
|
||||
|
||||
int length = ASN1_STRING_to_UTF8(&utf8, data);
|
||||
if (!utf8 || !(length > 0)) break; /* failed */
|
||||
|
||||
fprintf(stdout, " %s: %s\n", label, utf8);
|
||||
success = 1;
|
||||
|
||||
} while (0);
|
||||
|
||||
if (utf8)
|
||||
OPENSSL_free(utf8);
|
||||
|
||||
if (!success)
|
||||
fprintf(stdout, " %s: <not available>\n", label);
|
||||
}
|
||||
|
||||
void print_san_name(const char* label, X509* const cert)
|
||||
{
|
||||
int success = 0;
|
||||
GENERAL_NAMES* names = NULL;
|
||||
unsigned char* utf8 = NULL;
|
||||
|
||||
do
|
||||
{
|
||||
if (!cert) break; /* failed */
|
||||
|
||||
names = (GENERAL_NAMES*)X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0);
|
||||
if (!names) break;
|
||||
|
||||
int i = 0, count = sk_GENERAL_NAME_num(names);
|
||||
if (!count) break; /* failed */
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
GENERAL_NAME* entry = sk_GENERAL_NAME_value(names, i);
|
||||
if (!entry) continue;
|
||||
|
||||
if (GEN_DNS == entry->type)
|
||||
{
|
||||
int len1 = 0, len2 = -1;
|
||||
|
||||
len1 = ASN1_STRING_to_UTF8(&utf8, entry->d.dNSName);
|
||||
if (utf8) {
|
||||
len2 = (int)strlen((const char*)utf8);
|
||||
}
|
||||
|
||||
if (len1 != len2) {
|
||||
fprintf(stderr, " Strlen and ASN1_STRING size do not match (embedded null?): %d vs %d\n", len2, len1);
|
||||
}
|
||||
|
||||
/* If there's a problem with string lengths, then */
|
||||
/* we skip the candidate and move on to the next. */
|
||||
/* Another policy would be to fails since it probably */
|
||||
/* indicates the client is under attack. */
|
||||
if (utf8 && len1 && len2 && (len1 == len2)) {
|
||||
fprintf(stdout, " %s: %s\n", label, utf8);
|
||||
success = 1;
|
||||
}
|
||||
|
||||
if (utf8) {
|
||||
OPENSSL_free(utf8), utf8 = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, " Unknown GENERAL_NAME type: %d\n", entry->type);
|
||||
}
|
||||
}
|
||||
|
||||
} while (0);
|
||||
|
||||
if (names)
|
||||
GENERAL_NAMES_free(names);
|
||||
|
||||
if (utf8)
|
||||
OPENSSL_free(utf8);
|
||||
|
||||
if (!success)
|
||||
fprintf(stdout, " %s: <not available>\n", label);
|
||||
|
||||
}
|
||||
|
||||
int verify_callback(int preverify, X509_STORE_CTX* x509_ctx)
|
||||
{
|
||||
/* For error codes, see http://www.openssl.org/docs/apps/verify.html */
|
||||
|
||||
int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
|
||||
int err = X509_STORE_CTX_get_error(x509_ctx);
|
||||
|
||||
X509* cert = X509_STORE_CTX_get_current_cert(x509_ctx);
|
||||
X509_NAME* iname = cert ? X509_get_issuer_name(cert) : NULL;
|
||||
X509_NAME* sname = cert ? X509_get_subject_name(cert) : NULL;
|
||||
|
||||
fprintf(stdout, "verify_callback (depth=%d)(preverify=%d)\n", depth, preverify);
|
||||
|
||||
/* Issuer is the authority we trust that warrants nothing useful */
|
||||
print_cn_name("Issuer (cn)", iname);
|
||||
|
||||
/* Subject is who the certificate is issued to by the authority */
|
||||
print_cn_name("Subject (cn)", sname);
|
||||
|
||||
if (depth == 0) {
|
||||
/* If depth is 0, its the server's certificate. Print the SANs */
|
||||
print_san_name("Subject (san)", cert);
|
||||
}
|
||||
|
||||
if (preverify == 0)
|
||||
{
|
||||
if (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)
|
||||
fprintf(stdout, " Error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY\n");
|
||||
else if (err == X509_V_ERR_CERT_UNTRUSTED)
|
||||
fprintf(stdout, " Error = X509_V_ERR_CERT_UNTRUSTED\n");
|
||||
else if (err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN)
|
||||
fprintf(stdout, " Error = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN\n");
|
||||
else if (err == X509_V_ERR_CERT_NOT_YET_VALID)
|
||||
fprintf(stdout, " Error = X509_V_ERR_CERT_NOT_YET_VALID\n");
|
||||
else if (err == X509_V_ERR_CERT_HAS_EXPIRED)
|
||||
fprintf(stdout, " Error = X509_V_ERR_CERT_HAS_EXPIRED\n");
|
||||
else if (err == X509_V_OK)
|
||||
fprintf(stdout, " Error = X509_V_OK\n");
|
||||
else
|
||||
fprintf(stdout, " Error = %d\n", err);
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
return 1;
|
||||
#else
|
||||
return preverify;
|
||||
#endif
|
||||
}
|
||||
|
||||
void print_error_string(unsigned long err, const char* const label)
|
||||
{
|
||||
const char* const str = ERR_reason_error_string(err);
|
||||
if (str)
|
||||
fprintf(stderr, "%s\n", str);
|
||||
else
|
||||
fprintf(stderr, "%s failed: %lu (0x%lx)\n", label, err, err);
|
||||
|
||||
Sleep(3000);
|
||||
|
||||
}
|
||||
|
||||
void CheckIsOne(unsigned long err, const char* const label)
|
||||
{
|
||||
if (err == 1)return;
|
||||
unsigned long ssl_err = ERR_get_error();
|
||||
fprintf(stderr, "(NotOne) Error number %d -- %s\n", ssl_err, label);
|
||||
Sleep(3000);
|
||||
exit(1);
|
||||
}
|
||||
void CheckIsNotNull(void* p, const char* const label)
|
||||
{
|
||||
if (p != NULL)return;
|
||||
unsigned long ssl_err = ERR_get_error();
|
||||
fprintf(stderr, "(IsNull) Error number %d -- %s\n", ssl_err, label);
|
||||
Sleep(3000);
|
||||
exit(1);
|
||||
}
|
||||
int Usage()
|
||||
{
|
||||
fprintf(stderr, "Usage: TlsClient [B|C] Looks for PEM-encoded certificate files in current directory.");
|
||||
fprintf(stderr, " TlsClient [B|C] dir Looks for PEM-encoded certificate files in dir");
|
||||
fprintf(stderr, " B - Use the bare alias certificate");
|
||||
fprintf(stderr, " C - Use the device certificate chain");
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{E144BF89-3BEA-402C-B58A-A3DB2AC68F67}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>TlsClient</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<OutDir>$(SolutionDir)\bin\$(Configuration)\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>C:\OpenSSL-Win32\include</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>C:\OpenSSL-Win32\lib</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);openssl.lib; libcrypto.lib; libssl.lib</AdditionalDependencies>
|
||||
<OutputFile>$(SolutionDir)\bin\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="ReadMe.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TlsClient.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="ReadMe.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="targetver.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TlsClient.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,8 @@
|
|||
// stdafx.cpp : source file that includes just the standard includes
|
||||
// TlsClient.pch will be the pre-compiled header
|
||||
// stdafx.obj will contain the pre-compiled type information
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
// TODO: reference any additional headers you need in STDAFX.H
|
||||
// and not in this file
|
|
@ -0,0 +1,58 @@
|
|||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
|
||||
#include "targetver.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <tchar.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include <openssl/tls1.h>
|
||||
#include <openssl/x509v3.h>
|
||||
|
||||
// just need this for Sleep() - a debugging aid
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
|
||||
#ifndef UNUSED
|
||||
# define UNUSED(x) ((void)(x))
|
||||
#endif
|
||||
|
||||
#ifndef TRUE
|
||||
# define TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef FALSE
|
||||
# define FALSE 0
|
||||
#endif
|
||||
|
||||
#ifndef HOST_NAME
|
||||
# define HOST_NAME "localhost"
|
||||
#endif
|
||||
|
||||
#ifndef HOST_PORT
|
||||
# define HOST_PORT "5556"
|
||||
#endif
|
||||
|
||||
#ifndef HOST_RESOURCE
|
||||
# define HOST_RESOURCE "/cgi-bin/randbyte?nbytes=32&format=h"
|
||||
#endif
|
||||
|
||||
# define ASSERT(x) { \
|
||||
if(!(x)) { \
|
||||
fprintf(stderr, "Assertion: %s: function %s, line %d\n", (char*)(__FILE__), (char*)(__func__), (int)__LINE__); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
// Including SDKDDKVer.h defines the highest available Windows platform.
|
||||
|
||||
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
|
||||
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
|
||||
|
||||
#include <SDKDDKVer.h>
|
Загрузка…
Ссылка в новой задаче