This commit is contained in:
pengland 2017-04-07 09:40:31 -07:00
Родитель ee09fe471b
Коммит dff03f0b3f
43 изменённых файлов: 5236 добавлений и 23 удалений

63
.gitattributes поставляемый Normal file
Просмотреть файл

@ -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
Просмотреть файл

@ -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

6
Tools/Docs/Notes.txt Normal file
Просмотреть файл

@ -0,0 +1,6 @@
Create chains given a CSR (and an alias cert)
-dir d:\tmp\Emulator -csr
-nogen -dir d:\tmp\Emulator -e2e

Двоичные данные
Tools/Docs/RiotUtils.docx Normal file

Двоичный файл не отображается.

6
Tools/RIoT/App.config Normal file
Просмотреть файл

@ -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>

468
Tools/RIoT/CertMaker.cs Normal file
Просмотреть файл

@ -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;
}
}
*/
}

230
Tools/RIoT/Helpers.cs Normal file
Просмотреть файл

@ -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;
}
*/
}
}

110
Tools/RIoT/HttpsListener.cs Normal file
Просмотреть файл

@ -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;
}
}
}

181
Tools/RIoT/HubControl.cs Normal file
Просмотреть файл

@ -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;
}
}
}

157
Tools/RIoT/IoTDevice.cs Normal file
Просмотреть файл

@ -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;
}
}
}

323
Tools/RIoT/Main.cs Normal file
Просмотреть файл

@ -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")]

184
Tools/RIoT/RIoT.csproj Normal file
Просмотреть файл

@ -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>

198
Tools/RIoT/TLSClient.cs Normal file
Просмотреть файл

@ -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;
}
}
}

476
Tools/RIoT/TLSServer.cs Normal file
Просмотреть файл

@ -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;
}
}
}
}

212
Tools/RIoT/UpdateDemo.cs Normal file
Просмотреть файл

@ -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>

52
Tools/RIoTDemo/App.config Normal file
Просмотреть файл

@ -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>

106
Tools/RIoTDemo/DeviceStatus.Designer.cs сгенерированный Normal file
Просмотреть файл

@ -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>

80
Tools/RIoTDemo/MainPage.Designer.cs сгенерированный Normal file
Просмотреть файл

@ -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;
}
}

292
Tools/RIoTDemo/MainPage.cs Normal file
Просмотреть файл

@ -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>

22
Tools/RIoTDemo/Program.cs Normal file
Просмотреть файл

@ -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")]

71
Tools/RIoTDemo/Properties/Resources.Designer.cs сгенерированный Normal file
Просмотреть файл

@ -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>

30
Tools/RIoTDemo/Properties/Settings.Designer.cs сгенерированный Normal file
Просмотреть файл

@ -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>

69
Tools/RIoTUtils.sln Normal file
Просмотреть файл

@ -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

58
Tools/TlsClient/stdafx.h Normal 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>