CSDNCurrent en:Perform a QUICK transfer

Aus Cryptshare Documentation
Version vom 7. März 2022, 07:50 Uhr von Maintenance script (Diskussion | Beiträge) (Imported from text file)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Wechseln zu:Navigation, Suche



Introduction

General information about QUICK Technology
For general information about how QUICK Technology works and in order to get a better understanding of the terminology in use, please take a look at the article Introduction to QUICK.

In this article, you'll learn how to perform a Cryptshare transfer that is secured by QUICK Technology using the Cryptshare .NET API. Alternatively, you may skip to the Summary section and view the complete source code.

Implementation

In this section, we'll go through a step-by-step instruction in order to build a program that is capable of performing a QUICK transfer.

Preparing the program

We prepare a simple console application and define important bits of information that we'll need along the way. In order to use the `Cryptshare.*` namespace, please make sure that you have successfully added the `Cryptshare.API.dll` to the project references.

using Cryptshare.API;
using System;
using System.Collections.Generic;
using System.Linq;

namespace QuickTransfer
{
	class Program
	{
		private static readonly string SenderEmailAddress = "john.doe@example.com";
		private static readonly string SenderName = "John Doe";
		private static readonly string SenderPhoneNumber = "+49 (0) 1234 56789";
		private static readonly string ServerUrl = "https://cryptshare.example.com";
		// New "client.store" file will be created if it doesn't already exist.
		private static readonly string ClientStoreLocation = @"C:\temp"; 
		private static readonly string RecipientEmailAddress = "jane.doe@example.com";
		private static readonly string FilePath = @"C:\temp\pdf-sample.pdf";

		static void Main(string[] args)
		{
			try
			{
				// All following code snippets are to be inserted here.
			}
			catch (Exception e)
			{
				Console.Error.WriteLine("An error has occurred: " + e);
			}
			finally
			{
				Console.WriteLine("Press any key to terminate the program.");
				Console.ReadKey();
			}
		}
	}
}

Creating the `Client` instance

Similar to performing regular transfers, it's important to create a `Client` instance representing the sender. The following information is needed:

  • Email address of the sender
  • URL of the Cryptshare Server
  • Path to access the verification store
Client client = new Client(
	SenderEmailAddress,
	new CryptshareConnection(new WebServiceUri(ServerUrl)),
	ClientStoreLocation
);

Creating and preparing the `Transfer` instance

Populating the `Transfer` instance with information is just as important as creating a `Client` instance and an integral part of every Cryptshare transfer.

Transfer transfer = new Transfer();

A successful transfer requires the following minimum amount of information:

  • The sender's full name
transfer.SenderName = SenderName;
  • The sender's phone number
transfer.SenderPhone = SenderPhoneNumber;
  • At least one recipient (the second parameter defines whether or not QUICK should be enabled for this recipient)
// Activate QUICK for the given recipient.
transfer.Recipients.Add(new Recipient(RecipientEmailAddress, true));
  • The password mode to be used
transfer.PasswordMode = PasswordMode.Manual;
  • At least one file to be sent
    • Please note that you always need to specify a file, even if you only intend to send a confidential message. Support for sending transfers that only contain a confidential message is planned for a future release.
transfer.Files = new List<string>() { FilePath };
  • Optional: Specifying whether or not the Cryptshare server should handle the email notification part for both the sender and recipient.
transfer.SendEmails = true;
transfer.NotifySender = true;
transfer.NotifyRecipients = true;
transfer.InformAboutDownload = true;
  • Expiration date of the transfer.Hint: Because we're relying on a policy query in order to set the expiration date, we're required to have a valid verification. Thus, the maximum storage duration should only be queried if a valid verification is ensured beforehand. Please note the order of calls in the Summary section.
// Query the maximum possible storage duration for the given sender-recipient policy.
Policy policy = client.RequestPolicy(transfer.Recipients.ConvertAll(recipient => recipient.EmailAddress));
transfer.ExpirationDate = DateTime.Now.AddDays(policy.StorageDuration);

Further requirements for a successful transfer

Our `Client` and `Transfer` instances now fulfill all necessary requirements for a transfer. However, additional requirements imposed by the Cryptshare server need to be considered and handled. More specifically, it is required that:

  • the sender email address is verified.
    • If no, the program should ensure a valid sender verification as seen in the example below.
CheckVerificationResult checkVerificationResult = client.CheckVerification();
if (!checkVerificationResult.UserVerified)
{
    switch (checkVerificationResult.VerificationMode)
    {
        case VerificationMode.Sender:
            Console.WriteLine($"Requesting a verification code for {client.UserEmailAddress}...");
            client.RequestSenderVerification();
            Console.WriteLine($"Please enter the code below and confirm with [Enter]:");
            string code = Console.ReadLine().Trim();
            client.ConfirmSenderVerification(code);
            break;
        case VerificationMode.Client:
            if (checkVerificationResult.ClientVerificationAllowed)
            {
                client.RequestClientVerification();
            }
            else
            {
                throw new UnauthorizedAccessException(
                    $"Your client ID is not whitelisted. Please whitelist the following client ID before proceeding: {client.ClientId}"
                );
            }
            break;
        default:
            break;
    }
  • no QUICK activation is required - for example, a QUICK activation may be required if the given sender email address already has QUICK access credentials on the server, which may be the case if QUICK has been used previously on another device.
    • If yes, the sender's own QUICK access will be reset.
    • Hint: Make sure you're aware of the consequences before resetting the QUICK connection. In general, QUICK access can be activated programmatically as is described in the article Activate QUICK access.
if (checkVerificationResult.ActivationRequired)
{
    // WARNING: Only reset the QUICK state if you are aware of the consequences. When in doubt, please
    // consult the Wiki article about QUICK Technology: http://cryptshare.click/webapp-quick-activation-en
    client.ResetQuickState();
}
  • the existing verification is enabled for the usage of QUICK.
if (!checkVerificationResult.QuickEnabled)
{
    client.EnableVerificationForQuick();
}
  • a password is set if no QUICK connection has been established to at least one recipient.
    • For the recipient to whom there is no established QUICK connection, this transfer also acts as an invitation to establish a QUICK connection with the sender.
{
	// ...
    HashSet<string> recipients = new HashSet<string>(transfer.Recipients.Select(r => r.EmailAddress));
    IDictionary<string, QuickConnectionState> quickConnectionStates = client.RequestQuickConnectionStates(recipients);
    if (!IsPureQuickTransfer(quickConnectionStates))
    {
        // Not a pure QUICK transfer, password needs to be set.
        Console.Write("Not a pure QUICK transfer; generating a one-time-password... ");
        PasswordPolicy passwordPolicy = client.RequestPasswordPolicy();
        transfer.Password = client.RequestGeneratedPassword(passwordPolicy.MinimumLength);
        Console.WriteLine(transfer.Password);
    }
	// ...
}

private static bool IsPureQuickTransfer(IDictionary<string, QuickConnectionState> quickConnectionStates)
{
	return quickConnectionStates
		.Select(entry => entry.Value)
		.All(recipient => recipient == QuickConnectionState.Established);
}

If these prerequisites are considered, the transfer can be performed successfully.

{
    // ...
    client.PerformTransfer(transfer, null, HandleUploadComplete, null, null);
    // ...
}

private static void HandleUploadComplete(IDictionary<string, string> urlMappings, IDictionary<string, string> smtpMappings, string serverGenPassword, TransferError transferError, string trackingID)
{
	Console.WriteLine("URL mappings:");
	foreach (var mapping in urlMappings)
	{
		Console.WriteLine($"{mapping.Key}: {mapping.Value}");
	}
	Console.WriteLine("SMTP mappings:");
	foreach (var mapping in smtpMappings)
	{
		Console.WriteLine($"{mapping.Key}: {mapping.Value}");
	}
	Console.WriteLine($"Tracking ID: {trackingID}");

Summary

If you have followed the article step-by-step, you should end up with a program that looks as follows:

Expand to view the source code...  Expand source

using Cryptshare.API;
using System;
using System.Collections.Generic;
using System.Linq;

namespace QuickTransfer
{
	class Program
	{
		private static readonly string SenderEmailAddress = "john.doe@example.com";
		private static readonly string SenderName = "John Doe";
		private static readonly string SenderPhoneNumber = "+49 (0) 1234 56789";
		private static readonly string ServerUrl = "https://cryptshare.example.com";
        // New "client.store" file will be created if it doesn't already exist.
		private static readonly string ClientStoreLocation = @"C:\temp";
		private static readonly string RecipientEmailAddress = "jane.doe@example.com";
		private static readonly string FilePath = @"C:\temp\pdf-sample.pdf";

		static void Main(string[] args)
		{
			try
			{
				Client client = new Client(
					SenderEmailAddress,
					new CryptshareConnection(new WebServiceUri(ServerUrl)),
					ClientStoreLocation
				);
				Transfer transfer = new Transfer();
				transfer.SenderName = SenderName;
				transfer.SenderPhone = SenderPhoneNumber;
				// Activate QUICK for the given recipient.
				transfer.Recipients.Add(new Recipient(RecipientEmailAddress, true));
				transfer.PasswordMode = PasswordMode.Manual;
				transfer.Files = new List<string>() { FilePath };
				// Optional: Let the Cryptshare Server handle the email sending process.
				transfer.SendEmails = true;
				transfer.NotifySender = true;
				transfer.NotifyRecipients = true;
				transfer.InformAboutDownload = true;

 				CheckVerificationResult checkVerificationResult = client.CheckVerification();
				if (!checkVerificationResult.UserVerified)
				{
					switch (checkVerificationResult.VerificationMode)
					{
						case VerificationMode.Sender:
							Console.WriteLine($"Requesting a verification code for {client.UserEmailAddress}...");
							client.RequestSenderVerification();
							Console.WriteLine($"Please enter the code below and confirm with [Enter]:");
							string code = Console.ReadLine().Trim();
							client.ConfirmSenderVerification(code);
							break;
						case VerificationMode.Client:
							if (checkVerificationResult.ClientVerificationAllowed)
							{
								client.RequestClientVerification();
							}
							else
							{
								throw new UnauthorizedAccessException(
									$"Your client ID is not whitelisted. Please whitelist the following client ID before proceeding: {client.ClientId}"
								);
							}
							break;
						default:
							break;
					}
				}

				// Query the maximum possible storage duration for the given sender-recipient policy.
				Policy policy = client.RequestPolicy(transfer.Recipients.ConvertAll(recipient => recipient.EmailAddress));
				transfer.ExpirationDate = DateTime.Now.AddDays(policy.StorageDuration);

				// If the user already has a personal key on this server, reset the QUICK state. We could activate the
				// QUICK access using the API, but choose to reset the QUICK state for the sake of brevity.
				if (checkVerificationResult.ActivationRequired)
				{
					// WARNING: Only reset the QUICK state if you are aware of the consequences. When in doubt, please
					// consult the Wiki article about QUICK Technology: http://cryptshare.click/webapp-quick-activation-en
					client.ResetQuickState();
				}

				// A regular verification needs to be enabled for QUICK, if not already done.
				if (!checkVerificationResult.QuickEnabled)
				{
					client.EnableVerificationForQuick();
				}

				// We must define a password if there is at least one recipient who has not yet accepted the QUICK invitation.
				HashSet<string> recipients = new HashSet<string>(transfer.Recipients.Select(r => r.EmailAddress));
				IDictionary<string, QuickConnectionState> quickConnectionStates = client.RequestQuickConnectionStates(recipients);
				if (!IsPureQuickTransfer(quickConnectionStates))
				{
					// Not a pure QUICK transfer, password needs to be set.
					Console.Write("Not a pure QUICK transfer; generating a one-time-password... ");
					PasswordPolicy passwordPolicy = client.RequestPasswordPolicy();
					transfer.Password = client.RequestGeneratedPassword(passwordPolicy.MinimumLength);
					Console.WriteLine(transfer.Password);
				}

				client.PerformTransfer(transfer, null, HandleUploadComplete, null, null);
			}
			catch (Exception e)
			{
				Console.Error.WriteLine("An error has occurred: " + e);
			}
			finally
			{
				Console.WriteLine("Press any key to terminate the program.");
				Console.ReadKey();
			}
		}

		private static bool IsPureQuickTransfer(IDictionary<string, QuickConnectionState> quickConnectionStates)
		{
			return quickConnectionStates
				.Select(entry => entry.Value)
				.All(recipient => recipient == QuickConnectionState.Established);
		}

		private static void HandleUploadComplete(IDictionary<string, string> urlMappings, IDictionary<string, string> smtpMappings, string serverGenPassword, TransferError transferError, string trackingID)
		{
			Console.WriteLine("URL mappings:");
			foreach (var mapping in urlMappings)
			{
				Console.WriteLine($"{mapping.Key}: {mapping.Value}");
			}
			Console.WriteLine("SMTP mappings:");
			foreach (var mapping in smtpMappings)
			{
				Console.WriteLine($"{mapping.Key}: {mapping.Value}");
			}
			Console.WriteLine($"Tracking ID: {trackingID}");
		}
	}
}