/// <summary> /// Downloads a <see cref="PayloadReference"/> that is referenced from an incoming inbox item. /// </summary> /// <param name="inboxItem">The inbox item that referenced the <see cref="PayloadReference"/>.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task representing the asynchronous operation.</returns> protected virtual async Task<PayloadReference> DownloadPayloadReferenceAsync(IncomingList.IncomingItem inboxItem, CancellationToken cancellationToken) { Requires.NotNull(inboxItem, "inboxItem"); var responseMessage = await this.HttpClient.GetAsync(inboxItem.Location, cancellationToken).ConfigureAwait(false); if (responseMessage.StatusCode == HttpStatusCode.NotFound) { // delete inbox item and move on. await this.DeletePayloadReferenceAsync(inboxItem.Location, cancellationToken).ConfigureAwait(false); this.Log("Missing payload reference.", null); return null; } responseMessage.EnsureSuccessStatusCode(); var responseStream = await responseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false); var responseStreamCopy = new MemoryStream(); await responseStream.CopyToAsync(responseStreamCopy, 4096, cancellationToken).ConfigureAwait(false); responseStreamCopy.Position = 0; var encryptedKey = await responseStreamCopy.ReadSizeAndBufferAsync(cancellationToken).ConfigureAwait(false); var key = WinRTCrypto.CryptographicEngine.Decrypt(this.Endpoint.EncryptionKey, encryptedKey); var iv = await responseStreamCopy.ReadSizeAndBufferAsync(cancellationToken).ConfigureAwait(false); var ciphertextStream = await responseStreamCopy.ReadSizeAndStreamAsync(cancellationToken).ConfigureAwait(false); var encryptedVariables = new SymmetricEncryptionVariables(key, iv); var plainTextPayloadStream = new MemoryStream(); await this.CryptoServices.DecryptAsync(ciphertextStream, plainTextPayloadStream, encryptedVariables, cancellationToken).ConfigureAwait(false); plainTextPayloadStream.Position = 0; AsymmetricAlgorithm? signingHashAlgorithm = null; //// Encoding.UTF8.GetString(await plainTextPayloadStream.ReadSizeAndBufferAsync(cancellationToken)); byte[] signature = await plainTextPayloadStream.ReadSizeAndBufferAsync(cancellationToken).ConfigureAwait(false); long payloadStartPosition = plainTextPayloadStream.Position; var signedBytes = new byte[plainTextPayloadStream.Length - plainTextPayloadStream.Position]; await plainTextPayloadStream.ReadAsync(signedBytes, 0, signedBytes.Length).ConfigureAwait(false); plainTextPayloadStream.Position = payloadStartPosition; var plainTextPayloadReader = new BinaryReader(plainTextPayloadStream); var recipientPublicSigningKeyBuffer = plainTextPayloadReader.ReadSizeAndBuffer(); var creationDateUtc = DateTime.FromBinary(plainTextPayloadReader.ReadInt64()); var notificationAuthor = Utilities.DeserializeDataContract<Endpoint>(plainTextPayloadReader); var messageReference = Utilities.DeserializeDataContract<PayloadReference>(plainTextPayloadReader); messageReference.ReferenceLocation = inboxItem.Location; if (messageReference.HashAlgorithmName == null) { messageReference.HashAlgorithmName = Utilities.GuessHashAlgorithmFromLength(messageReference.Hash.Length).GetHashAlgorithmName(); } if (!CryptoProviderExtensions.VerifySignatureWithTolerantHashAlgorithm(notificationAuthor.SigningKeyPublicMaterial, signedBytes, signature, signingHashAlgorithm)) { throw new InvalidMessageException(); } if (!Utilities.AreEquivalent(recipientPublicSigningKeyBuffer, this.Endpoint.PublicEndpoint.SigningKeyPublicMaterial)) { throw new InvalidMessageException(Strings.MisdirectedMessage); } return messageReference; }
public async Task ECAsymmetricSigningAndEncryption() { var bob = new ECDsaCng(521); var bobPublic = CngKey.Import(bob.Key.Export(CngKeyBlobFormat.EccPublicBlob), CngKeyBlobFormat.EccPublicBlob); var alice = new ECDsaCng(521); var alicePublic = CngKey.Import(alice.Key.Export(CngKeyBlobFormat.EccPublicBlob), CngKeyBlobFormat.EccPublicBlob); // Bob formulates request. var bobRequest = new MemoryStream(); var bobDH = ECDiffieHellman.Create(); { byte[] bobPublicDH = bobDH.PublicKey.ToByteArray(); byte[] bobSignedDH = bob.SignData(bobPublicDH); await bobRequest.WriteSizeAndBufferAsync(bobPublicDH, CancellationToken.None); await bobRequest.WriteSizeAndBufferAsync(bobSignedDH, CancellationToken.None); bobRequest.Position = 0; } // Alice reads request. var aliceResponse = new MemoryStream(); byte[] aliceKeyMaterial; var aliceDH = new ECDiffieHellmanCng(); { byte[] bobPublicDH = await bobRequest.ReadSizeAndBufferAsync(CancellationToken.None); byte[] bobSignedDH = await bobRequest.ReadSizeAndBufferAsync(CancellationToken.None); var bobDsa = new ECDsaCng(bobPublic); Assert.IsTrue(bobDsa.VerifyData(bobPublicDH, bobSignedDH)); var bobDHPK = ECDiffieHellmanCngPublicKey.FromByteArray(bobPublicDH, CngKeyBlobFormat.EccPublicBlob); aliceKeyMaterial = aliceDH.DeriveKeyMaterial(bobDHPK); await aliceResponse.WriteSizeAndBufferAsync(aliceDH.PublicKey.ToByteArray(), CancellationToken.None); await aliceResponse.WriteSizeAndBufferAsync(alice.SignData(aliceDH.PublicKey.ToByteArray()), CancellationToken.None); // Alice also adds a secret message. using (var aes = SymmetricAlgorithm.Create()) { using (var encryptor = aes.CreateEncryptor(aliceKeyMaterial, new byte[aes.BlockSize / 8])) { var cipherText = new MemoryStream(); using (var cryptoStream = new CryptoStream(cipherText, encryptor, CryptoStreamMode.Write)) { cryptoStream.Write(new byte[] { 0x1, 0x3, 0x2 }, 0, 3); cryptoStream.FlushFinalBlock(); cipherText.Position = 0; await aliceResponse.WriteSizeAndStreamAsync(cipherText, CancellationToken.None); } } } aliceResponse.Position = 0; } // Bob reads response byte[] bobKeyMaterial; { byte[] alicePublicDH = await aliceResponse.ReadSizeAndBufferAsync(CancellationToken.None); byte[] aliceSignedDH = await aliceResponse.ReadSizeAndBufferAsync(CancellationToken.None); var aliceDsa = new ECDsaCng(alicePublic); Assert.IsTrue(aliceDsa.VerifyData(alicePublicDH, aliceSignedDH)); var aliceDHPK = ECDiffieHellmanCngPublicKey.FromByteArray(alicePublicDH, CngKeyBlobFormat.EccPublicBlob); bobKeyMaterial = bobDH.DeriveKeyMaterial(aliceDHPK); // And Bob reads Alice's secret message. using (var aes = SymmetricAlgorithm.Create()) { using (var decryptor = aes.CreateDecryptor(aliceKeyMaterial, new byte[aes.BlockSize / 8])) { var plaintext = new MemoryStream(); var substream = await aliceResponse.ReadSizeAndStreamAsync(CancellationToken.None); using (var cryptoStream = new CryptoStream(substream, decryptor, CryptoStreamMode.Read)) { await cryptoStream.CopyToAsync(plaintext); plaintext.Position = 0; byte[] secretMessage = new byte[1024]; int readBytes = plaintext.Read(secretMessage, 0, secretMessage.Length); } } } } CollectionAssert.AreEqual(aliceKeyMaterial, bobKeyMaterial); }
/// <summary> /// Downloads a <see cref="PayloadReference"/> that is referenced from an incoming inbox item. /// </summary> /// <param name="inboxItem">The inbox item that referenced the <see cref="PayloadReference"/>.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task representing the asynchronous operation.</returns> protected virtual async Task<PayloadReference> DownloadPayloadReferenceAsync(IncomingList.IncomingItem inboxItem, CancellationToken cancellationToken) { Requires.NotNull(inboxItem, "inboxItem"); var responseMessage = await this.HttpClient.GetAsync(inboxItem.Location, cancellationToken); if (responseMessage.StatusCode == HttpStatusCode.NotFound) { // delete inbox item and move on. await this.DeletePayloadReferenceAsync(inboxItem.Location, cancellationToken); this.Log("Missing payload reference.", null); return null; } responseMessage.EnsureSuccessStatusCode(); var responseStream = await responseMessage.Content.ReadAsStreamAsync(); var responseStreamCopy = new MemoryStream(); await responseStream.CopyToAsync(responseStreamCopy, 4096, cancellationToken); responseStreamCopy.Position = 0; var encryptedKey = await responseStreamCopy.ReadSizeAndBufferAsync(cancellationToken); var key = this.CryptoServices.Decrypt(this.Endpoint.EncryptionKeyPrivateMaterial, encryptedKey); var iv = await responseStreamCopy.ReadSizeAndBufferAsync(cancellationToken); var ciphertext = await responseStreamCopy.ReadSizeAndBufferAsync(cancellationToken); var encryptedPayload = new SymmetricEncryptionResult(key, iv, ciphertext); var plainTextPayloadBuffer = this.CryptoServices.Decrypt(encryptedPayload); var plainTextPayloadStream = new MemoryStream(plainTextPayloadBuffer); var signature = await plainTextPayloadStream.ReadSizeAndBufferAsync(cancellationToken); long payloadStartPosition = plainTextPayloadStream.Position; var signedBytes = new byte[plainTextPayloadStream.Length - plainTextPayloadStream.Position]; await plainTextPayloadStream.ReadAsync(signedBytes, 0, signedBytes.Length); plainTextPayloadStream.Position = payloadStartPosition; var plainTextPayloadReader = new BinaryReader(plainTextPayloadStream); var recipientPublicSigningKeyBuffer = plainTextPayloadReader.ReadSizeAndBuffer(); var creationDateUtc = DateTime.FromBinary(plainTextPayloadReader.ReadInt64()); var notificationAuthor = Utilities.DeserializeDataContract<Endpoint>(plainTextPayloadReader); var messageReference = Utilities.DeserializeDataContract<PayloadReference>(plainTextPayloadReader); messageReference.ReferenceLocation = inboxItem.Location; if (!this.CryptoServices.VerifySignature(notificationAuthor.SigningKeyPublicMaterial, signedBytes, signature)) { throw new InvalidMessageException(); } if (!Utilities.AreEquivalent(recipientPublicSigningKeyBuffer, this.Endpoint.PublicEndpoint.SigningKeyPublicMaterial)) { throw new InvalidMessageException(Strings.MisdirectedMessage); } return messageReference; }