CSJCurrent en:Perform a QUICK transfer
Introduction
In this article, you'll learn how to perform a Cryptshare transfer that is secured by QUICK Technology using the Cryptshare Java 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. Make sure that the Java API package is available, for example by importing the Jar into your Maven Repository.
import java.nio.file.Path; import java.nio.file.Paths; import java.util.Scanner; class Program { private static final String senderEmailAddress = "john.doe@example.com"; private static final String senderName = "John Doe"; private static final String senderPhoneNumber = "+49 (0) 1234 56789"; private static final String serverUrl = "https://cryptshare.example.com"; // New "client.store" file will be created if it doesn't already exist. private static final Path clientStoreLocation = Paths.get("C:\\\\temp"); private static final String recipientEmailAddress = "jane.doe@example.com"; private static final Path filePath = Paths.get("C:\\\\temp\\\\pdf-sample.pdf"); public static void main(String[] args) { try { // All following code snippets are to be inserted here. } catch (Exception e) { System.err.println("An error has occurred: "); e.printStackTrace(); } finally { System.out.println("Press any key to terminate the program."); new Scanner(System.in).next(); } } }
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.setSenderName(senderName)
- The sender's phone number
transfer.setSenderPhone(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.addRecipient(new Recipient(recipientEmailAddress, true));
- The password mode to be used
transfer.setPasswordMode(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.addFile(new TransferFile(filePath));
- Optional: Specifying whether or not the Cryptshare server should handle the email notification part for both the sender and recipient.
transfer.setSendMails(true); transfer.setNotifySender(true); transfer.setNotifyRecipients(true); transfer.setInformAboutDownload(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.getRecipients().stream().map(Recipient::getEmailAddress).collect(Collectors.toList())); final ZoneId serverTimezone = ZoneOffset.UTC; // Replace this with the timezone the Cryptshare server uses. final ZonedDateTime expirationDate = ZonedDateTime.now(serverTimezone).plusDays(policy.getStorageDuration()); transfer.setExpirationDate(expirationDate.toLocalDate());
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.isUserVerified()) { switch (checkVerificationResult.getVerificationMode()) { case SENDER: System.out.printf("Requesting a verification code for %s...%n", client.getUserEmailAddress()); client.requestSenderVerification(); System.out.println("Please enter the code below and confirm with [Enter]:"); String code = new Scanner(System.in).next().trim(); client.confirmSenderVerification(code); break; case CLIENT: if (checkVerificationResult.isClientVerificationAllowed()) { client.requestClientVerification(); } else { throw new IllegalStateException( "Your client ID is not whitelisted. Please whitelist the following client ID before proceeding: " + client.getClientId()); } 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.isActivationRequired()) { // 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.isQuickEnabled()) { 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.
{ // ... List<String> recipients = transfer.getRecipients().stream().map(Recipient::getEmailAddress).collect(Collectors.toList()); Map<String, QuickConnectionState> quickConnectionStates = client.requestQuickConnectionStates(recipients); if (!isPureQuickTransfer(quickConnectionStates)) { // Not a pure QUICK transfer, password needs to be set. System.out.println("Not a pure QUICK transfer; generating a one-time-password... "); PasswordPolicy passwordPolicy = client.requestPasswordPolicy(); transfer.setPassword(client.requestGeneratedPassword(passwordPolicy.getMinimumLength())); System.out.println(transfer.getPassword()); } // ... } private static boolean isPureQuickTransfer(Map<String, QuickConnectionState> quickConnectionStates) { return quickConnectionStates.values() .stream() .allMatch(quickConnectionState -> quickConnectionState == QuickConnectionState.ESTABLISHED); }
If these prerequisites are considered, the transfer can be performed successfully.
{ // ... client.performTransfer(transfer, null, Program::handleUploadComplete, null, null, 1000); // ... } private static void handleUploadComplete(Map<String, String> urlMappings, Map<String, String> smtpMappings, String serverGenPassword, TransferError transferError, String trackingId) { System.out.println("URL mappings:"); urlMappings.forEach((key, value) -> System.out.printf("%s: %s%n", key, value)); System.out.println("SMTP mappings:"); smtpMappings.forEach((key, value) -> System.out.printf("%s: %s%n", key, value)); System.out.printf("Tracking ID: %s%n", 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 import java.nio.file.Path; import java.nio.file.Paths; import java.util.Scanner; class Program { private static final String senderEmailAddress = "john.doe@example.com"; private static final String senderName = "John Doe"; private static final String senderPhoneNumber = "+49 (0) 1234 56789"; private static final String serverUrl = "https://cryptshare.example.com"; // New "client.store" file will be created if it doesn't already exist. private static final Path clientStoreLocation = Paths.get("C:\\\\temp"); private static final String recipientEmailAddress = "jane.doe@example.com"; private static final Path filePath = Paths.get("C:\\\\temp\\\\pdf-sample.pdf"); public static void main(String[] args) { try { // All following code snippets are to be inserted here. Client client = new Client(senderEmailAddress, new CryptshareConnection(new WebServiceUri(serverUrl)), clientStoreLocation); Transfer transfer = new Transfer(); transfer.setSenderName(senderName); transfer.setSenderPhone(senderPhoneNumber); // Activate QUICK for the given recipient. transfer.addRecipient(new Recipient(recipientEmailAddress, true)); transfer.setPasswordMode(PasswordMode.MANUAL); transfer.addFile(new TransferFile(filePath)); transfer.setSendMails(true); transfer.setNotifySender(true); transfer.setNotifyRecipients(true); transfer.setInformAboutDownload(true); // Query the maximum possible storage duration for the given sender-recipient policy. Policy policy = client.requestPolicy(transfer.getRecipients().stream().map(Recipient::getEmailAddress).collect(Collectors.toList())); final ZoneId serverTimezone = ZoneOffset.UTC; // Replace this with the timezone the Cryptshare server uses. final ZonedDateTime expirationDate = ZonedDateTime.now(serverTimezone).plusDays(policy.getStorageDuration()); transfer.setExpirationDate(expirationDate.toLocalDate()); CheckVerificationResult checkVerificationResult = client.checkVerification(); if (!checkVerificationResult.isUserVerified()) { switch (checkVerificationResult.getVerificationMode()) { case SENDER: System.out.printf("Requesting a verification code for %s...%n", client.getUserEmailAddress()); client.requestSenderVerification(); System.out.println("Please enter the code below and confirm with [Enter]:"); String code = new Scanner(System.in).next().trim(); client.confirmSenderVerification(code); break; case CLIENT: if (checkVerificationResult.isClientVerificationAllowed()) { client.requestClientVerification(); } else { throw new IllegalStateException( "Your client ID is not whitelisted. Please whitelist the following client ID before proceeding: " + client.getClientId()); } break; default: break; } } if (checkVerificationResult.isActivationRequired()) { // 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(); } if (!checkVerificationResult.isQuickEnabled()) { client.enableVerificationForQuick(); } List<String> recipients = transfer.getRecipients().stream().map(Recipient::getEmailAddress).collect(Collectors.toList()); Map<String, QuickConnectionState> quickConnectionStates = client.requestQuickConnectionStates(recipients); if (!isPureQuickTransfer(quickConnectionStates)) { // Not a pure QUICK transfer, password needs to be set. System.out.println("Not a pure QUICK transfer; generating a one-time-password... "); PasswordPolicy passwordPolicy = client.requestPasswordPolicy(); transfer.setPassword(client.requestGeneratedPassword(passwordPolicy.getMinimumLength())); System.out.println(transfer.getPassword()); } client.performTransfer(transfer, null, Program::handleUploadComplete, null, null, 1000); } catch (Exception e) { System.err.println("An error has occurred: "); e.printStackTrace(); } finally { System.out.println("Press any key to terminate the program."); new Scanner(System.in).next(); } } private static void handleUploadComplete(Map<String, String> urlMappings, Map<String, String> smtpMappings, String serverGenPassword, TransferError transferError, String trackingId) { System.out.println("URL mappings:"); urlMappings.forEach((key, value) -> System.out.printf("%s: %s%n", key, value)); System.out.println("SMTP mappings:"); smtpMappings.forEach((key, value) -> System.out.printf("%s: %s%n", key, value)); System.out.printf("Tracking ID: %s%n", trackingId); } private static boolean isPureQuickTransfer(Map<String, QuickConnectionState> quickConnectionStates) { return quickConnectionStates.values() .stream() .allMatch(quickConnectionState -> quickConnectionState == QuickConnectionState.ESTABLISHED); } }