/// <summary> /// Shares the reference to a message payload with the specified recipient. /// </summary> /// <param name="messageReference">The payload reference to share.</param> /// <param name="recipient">The recipient that should be notified of the message.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task representing the asynchronous operation.</returns> protected virtual async Task<NotificationPostedReceipt> PostPayloadReferenceAsync(PayloadReference messageReference, Endpoint recipient, CancellationToken cancellationToken) { Requires.NotNull(recipient, "recipient"); Requires.NotNull(messageReference, "messageReference"); cancellationToken.ThrowIfCancellationRequested(); // Prepare the payload. var plainTextPayloadStream = new MemoryStream(); var plainTextPayloadWriter = new BinaryWriter(plainTextPayloadStream); // Include the intended recipient's signing certificate so the recipient knows that // the message author intended the recipient to receive it (defeats fowarding and re-encrypting // a message notification with the intent to deceive a victim that a message was intended for them when it was not.) plainTextPayloadWriter.WriteSizeAndBuffer(recipient.SigningKeyPublicMaterial); plainTextPayloadWriter.Write(DateTime.UtcNow.ToBinary()); // Write out the author of this notification (which may be different from the author of the // message itself in the case of a "forward"). plainTextPayloadWriter.SerializeDataContract(this.Endpoint.PublicEndpoint); plainTextPayloadWriter.SerializeDataContract(messageReference); plainTextPayloadWriter.Flush(); this.Log("Message invite plaintext", plainTextPayloadStream.ToArray()); byte[] notificationSignature = WinRTCrypto.CryptographicEngine.Sign(this.Endpoint.SigningKey, plainTextPayloadStream.ToArray()); var signedPlainTextPayloadStream = new MemoryStream((int)plainTextPayloadStream.Length + notificationSignature.Length + 4); ////await signedPlainTextPayloadStream.WriteSizeAndBufferAsync(Encoding.UTF8.GetBytes(this.CryptoServices.HashAlgorithmName), cancellationToken); await signedPlainTextPayloadStream.WriteSizeAndBufferAsync(notificationSignature, cancellationToken).ConfigureAwait(false); plainTextPayloadStream.Position = 0; await plainTextPayloadStream.CopyToAsync(signedPlainTextPayloadStream, 4096, cancellationToken).ConfigureAwait(false); signedPlainTextPayloadStream.Position = 0; var cipherTextStream = new MemoryStream(); var encryptedVariables = await this.CryptoServices.EncryptAsync(signedPlainTextPayloadStream, cipherTextStream, cancellationToken: cancellationToken).ConfigureAwait(false); this.Log("Message invite ciphertext", cipherTextStream.ToArray()); this.Log("Message invite key", encryptedVariables.Key); this.Log("Message invite IV", encryptedVariables.IV); var builder = new UriBuilder(recipient.MessageReceivingEndpoint); var lifetimeInMinutes = (int)(messageReference.ExpiresUtc - DateTime.UtcNow).TotalMinutes; builder.Query += "&lifetime=" + lifetimeInMinutes.ToString(CultureInfo.InvariantCulture); var postContent = new MemoryStream(); var encryptionKey = CryptoSettings.EncryptionAlgorithm.ImportPublicKey( recipient.EncryptionKeyPublicMaterial, CryptoSettings.PublicKeyFormat); var encryptedKey = WinRTCrypto.CryptographicEngine.Encrypt(encryptionKey, encryptedVariables.Key); this.Log("Message invite encrypted key", encryptedKey); await postContent.WriteSizeAndBufferAsync(encryptedKey, cancellationToken).ConfigureAwait(false); await postContent.WriteSizeAndBufferAsync(encryptedVariables.IV, cancellationToken).ConfigureAwait(false); cipherTextStream.Position = 0; await postContent.WriteSizeAndStreamAsync(cipherTextStream, cancellationToken).ConfigureAwait(false); await postContent.FlushAsync().ConfigureAwait(false); postContent.Position = 0; using (var response = await this.HttpClient.PostAsync(builder.Uri, new StreamContent(postContent), cancellationToken).ConfigureAwait(false)) { if (response.Content != null) { // Just to help in debugging. string responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); } response.EnsureSuccessStatusCode(); var receipt = new NotificationPostedReceipt(recipient, response.Headers.Date); return receipt; } }
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> /// Shares the reference to a message payload with the specified recipient. /// </summary> /// <param name="messageReference">The payload reference to share.</param> /// <param name="recipient">The recipient that should be notified of the message.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task representing the asynchronous operation.</returns> protected virtual async Task PostPayloadReferenceAsync(PayloadReference messageReference, Endpoint recipient, CancellationToken cancellationToken) { Requires.NotNull(recipient, "recipient"); Requires.NotNull(messageReference, "messageReference"); cancellationToken.ThrowIfCancellationRequested(); // Prepare the payload. var plainTextPayloadStream = new MemoryStream(); var plainTextPayloadWriter = new BinaryWriter(plainTextPayloadStream); // Include the intended recipient's signing certificate so the recipient knows that // the message author intended the recipient to receive it (defeats fowarding and re-encrypting // a message notification with the intent to deceive a victim that a message was intended for them when it was not.) plainTextPayloadWriter.WriteSizeAndBuffer(recipient.SigningKeyPublicMaterial); plainTextPayloadWriter.Write(DateTime.UtcNow.ToBinary()); // Write out the author of this notification (which may be different from the author of the // message itself in the case of a "forward"). plainTextPayloadWriter.SerializeDataContract(this.Endpoint.PublicEndpoint); plainTextPayloadWriter.SerializeDataContract(messageReference); plainTextPayloadWriter.Flush(); this.Log("Message invite plaintext", plainTextPayloadStream.ToArray()); byte[] notificationSignature = this.CryptoServices.Sign(plainTextPayloadStream.ToArray(), this.Endpoint.SigningKeyPrivateMaterial); var signedPlainTextPayloadStream = new MemoryStream((int)plainTextPayloadStream.Length + notificationSignature.Length + 4); await signedPlainTextPayloadStream.WriteSizeAndBufferAsync(notificationSignature, cancellationToken); plainTextPayloadStream.Position = 0; await plainTextPayloadStream.CopyToAsync(signedPlainTextPayloadStream, 4096, cancellationToken); var encryptedPayload = this.CryptoServices.Encrypt(signedPlainTextPayloadStream.ToArray()); this.Log("Message invite ciphertext", encryptedPayload.Ciphertext); this.Log("Message invite key", encryptedPayload.Key); this.Log("Message invite IV", encryptedPayload.IV); var builder = new UriBuilder(recipient.MessageReceivingEndpoint); var lifetimeInMinutes = (int)(messageReference.ExpiresUtc - DateTime.UtcNow).TotalMinutes; builder.Query += "&lifetime=" + lifetimeInMinutes.ToString(CultureInfo.InvariantCulture); var postContent = new MemoryStream(); var encryptedKey = this.CryptoServices.Encrypt(recipient.EncryptionKeyPublicMaterial, encryptedPayload.Key); this.Log("Message invite encrypted key", encryptedKey); await postContent.WriteSizeAndBufferAsync(encryptedKey, cancellationToken); await postContent.WriteSizeAndBufferAsync(encryptedPayload.IV, cancellationToken); await postContent.WriteSizeAndBufferAsync(encryptedPayload.Ciphertext, cancellationToken); await postContent.FlushAsync(); postContent.Position = 0; using (var response = await this.HttpClient.PostAsync(builder.Uri, new StreamContent(postContent), cancellationToken)) { response.EnsureSuccessStatusCode(); } }