CSJCurrent de:Migration von Java API v3 auf v5.0

Aus Cryptshare Documentation
Wechseln zu:Navigation, Suche



Einleitung

In diesem Artikel erfahren Sie, wie Sie ein bestehendes Projekt, das die Cryptshare Java API Version 3 auf die neueste Version der Cryptshare Java API migrieren können. Dieser Artikel ist ein technischer Artikel, in dem die Änderungen für Personen, die mit der Verwendung der bisherigen Java API Version vertraut sind, aufgeführt werden. Allgemeine Informationen zu den Änderungen, die in Version 5 eingeführt wurden, erfahren Sie in den Updatehinweisen (Link TBD).

Vorbereitung

Installation der neuen Java API Version

Informationen zur Installation der neuen Java API Version in Ihr Entwicklungssystem erhalten Sie hier weitere Informationen.

Migration bestehender Projekte

Ersetzen der Abhängigkeit in der Dependencyverwaltung

Die GroupID und die ArtifactID der Java API haben sich verändert. Bitte ersetzen Sie zunächst den Dependency-Eintrag in Ihrer Dependencyverwaltung, z.B. in der pom.xml wie folgt:

Alt

<dependency>
   <groupId>com.befinesolutions.cryptshare</groupId> 
   <artifactId>java-api-ng</artifactId> 
   <version>3.X.X</version>
</dependency>
 
Neu

<dependency>
	<groupId>com.cryptshare.api</groupId>
	<artifactId>cryptshare-api</artifactId>
	<version>5.0.1.190</version>
</dependency>

Inkompatibilität mit bestehender client.store Der Aufbau der `client.store`-Datei wurde grundlegend überarbeitet. Somit ist es leider nicht möglich, die bestehende `client.store` mitsamt ihrer Verifizierungen weiter zu verwenden. Bevor Sie Ihr Projekt mit der neuen Version der Java API testen, löschen Sie zuvor die bestehende `client.store`-Datei.

com.cryptshare.api.ClientException: Can't read from the client store!
	at com.cryptshare.api.Client.openStore(Client.java:810)
	at com.cryptshare.api.Client.<init>(Client.java:126)
	at com.cryptshare.api.Client.<init>(Client.java:86)
	at com.befinesolutions.examples.api.ExampleMain.connectWithStore(ExampleMain.java:351)
	at com.befinesolutions.examples.api.ExampleMain.main(ExampleMain.java:48)
Caused by: com.cryptshare.api.ClientException: Unknown error
	at com.cryptshare.api.DpApiJNAWrapper.unprotect(DpApiJNAWrapper.java:57)
	at com.cryptshare.api.DpApiProtectionService.unprotect(DpApiProtectionService.java:74)
	at com.cryptshare.api.ProtectedFileStore.load(ProtectedFileStore.java:130)
	at com.cryptshare.api.ProtectedFileStore.open(ProtectedFileStore.java:108)
	at com.cryptshare.api.ProtectedFileStore.open(ProtectedFileStore.java:83)
	at com.cryptshare.api.Client.openStore(Client.java:808)
	... 4 more
Caused by: com.sun.jna.platform.win32.Win32Exception: Wrong parameter.
	at com.sun.jna.platform.win32.Crypt32Util.cryptUnprotectData(Crypt32Util.java:144)
	at com.cryptshare.api.DpApiJNAWrapper.unprotect(DpApiJNAWrapper.java:55)
	... 9 more

Änderung des Paketnamens Der Paketname der verwendeten Klassen hat sich verändert. Bitte ersetzen Sie als nächstes in den Import-Statements Ihrer Klassen den Paketnamen von

Alt

com.befinesolutions.cryptshare.aping
 
Neu

com.cryptshare.api

Änderungen von geworfenen Exceptions

Um die Quelle von möglichen Problemen bei der Verwendung der API besser aufzeigen zu können, werden nun in manchen Fällen CryptshareServerExceptions anstelle von ClientExceptions geworfen. Eine ClientException wird meist in den Fällen geworfen, wenn bei der Vorbereitung einer Operation fehlgeschlagen ist. CryptshareServerExceptions treten auf, wenn die Operation selbst auf dem Cryptshare-Server fehlgeschlagen ist, wenn fehlerhafte Angaben festgestellt wurden oder Verarbeitungs- oder sonstige Fehler aufgetreten sind.

Änderungen am Verifizierungsprozess

Client-Initialisierung

Bei der Erstellung eines `Client`-Objekts wurde bisher ein String als Pfadangabe zur `client.store`-Datei benötigt. Stattdessen wird nun ein `Path`-Objekt, das den Pfad zur `client.store` beinhaltet, benötigt, z.B. durch `Paths.get(stringPath)`.

Verwendung eines Bytearrays als Client Store

Im Falle, dass statt einer `client.store`-Datei ein Bytearray verwendet wurde, muss nun eine eigene Implementierung der neuen `IStore` verwendet werden. Mehr Informationen zu eigenen Implementierungen eines IStores erfahren Sie im Artikel Eigene Client Store bereitstellen.

Beispielimplementierung

Java API 3

// Get client store from your custom store.
byte[] clientStoreBytes = // TODO: Load bytes from your custom store

CryptshareConnection connection = new CryptshareConnection(new WebServiceUri(SERVER_URL))
Client client = new Client("sender@domain.com", connection, clientStoreBytes);

// do something like verifications, transfers, ...

// Write encrypted modified client store back into the custom store.
byte[] modifiedClientStoreBytes = client.getStore().save();
// TODO: Save modifiedClientStoreBytes to your custom store

Die Möglichkeit, ein Bytearray bei der Erstellung eines Client-Objekts anzugeben, wurde entfernt. Stattdessen wird eine Implementierung des IStore-Interfaces benötigt. Im nachfolgenden Beispiel wurde eine Minimalimplementierung umgesetzt. Bitte beachten Sie die Stellen, die mit TODO markiert sind. Diese Stellen kennzeichnen Punkte, an denen Sie prüfen müssten, ob Ihr verwendetes Betriebssystem dem Beispiel entspricht oder an denen Sie Ihre eigenen Lade- und Speichermethoden implementieren müssen, die Sie bisher bereits in Ihrem Programm mit der Cryptshare Java API v3 verwendet haben.

Java API 5 CustomStore

import com.cryptshare.api.*;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * Implementation of {@link IStore} that persists to a custom source, and encrypts the store.
 */
public class CustomProtectedStore implements IStore {

	private final Map<String, String> storeValues;
	private final IProtectionService protectionService;

	private CustomProtectedStore(IProtectionService protectionService) {
		this.protectionService = protectionService;
		this.storeValues = new HashMap<>();
	}

	/**
	 * A default {@link IProtectionService} is used to protect the store:
	 * AES encryption, using {@link AesProtectionService} with {@link LinuxKeySourceProvider}.
	 *
	 * @return An instance of a {@link CustomProtectedStore}, with all values loaded.
	 * @throws ClientException If opening fails.
	 */
	public static CustomProtectedStore open() throws ClientException {
		// TODO: When your program is run under Windows instead, use the DpApiProtectionService instead of the {@link AesProtectionService}.
		IProtectionService protectionService = new AesProtectionService(new LinuxKeySourceProvider());
		CustomProtectedStore protectedFileStore = new CustomProtectedStore(protectionService);
		protectedFileStore.load();

		return protectedFileStore;
	}

	/**
	 * Read from client store
	 */
	private void load() throws ClientException {
		try {
			byte[] protectedContent = loadStore();
			if (protectedContent.length == 0) {
				return;
			}

			byte[] unprotectedContent = protectionService.unprotect(protectedContent);
			String storeContent = new String(unprotectedContent, StandardCharsets.UTF_8);

			String[] entries = storeContent.split(";");

			for (String entry : entries) {
				String key = entry.substring(0, entry.indexOf('='));
				String value = entry.substring(entry.indexOf('=') + 1);
				storeValues.put(new String(Hex.decodeHex(key)), value);
			}

		} catch (DecoderException e) {
			throw new ClientException(ClientException.CODE_STORE_ACCESS_FAILED, "Could not read from the client store!", e);
		}
	}

	/**
	 * Gets a stored value from the store.
	 *
	 * @param key Unique key to identify the value. If null, the method returns null.
	 * @return The value that corresponds to the given key, or null, if the key was null or not found.
	 */
	@Override
	public synchronized String getValue(String key) {
		if (key == null) {
			return null;
		}
		return storeValues.get(key);
	}

	/**
	 * Sets a value in the store.
	 *
	 * @param key   Unique key for the value. If null, the method call has no effect.
	 * @param value Value to be stored. If null, the method call has no effect.
	 */
	@Override
	public synchronized void setValue(String key, String value) {
		if (key == null || value == null) {
			return;
		}
		storeValues.put(key, value);
	}

	/**
	 * Removes a specific value from the store.
	 *
	 * @param key Unique key to identify the value. If null, the method call has no effect.
	 */
	@Override
	public synchronized void removeValue(String key) {
		if (key == null) {
			return;
		}
		storeValues.remove(key);
	}

	/**
	 * Saves this store object to the custom store.
	 */
	@Override
	public synchronized void persist() throws ClientException {
		StringBuilder content = new StringBuilder();
		storeValues.forEach((k, v) -> {
			content.append(Hex.encodeHexString(k.getBytes()));
			content.append("=");
			content.append(v);
			content.append(";");
		});

		byte[] unprotectedContent = content.toString().getBytes(StandardCharsets.UTF_8);
		byte[] protectedContent = protectionService.protect(unprotectedContent);

		saveStore(protectedContent);
	}

	private byte[] loadStore() {
		// TODO: Load bytes from your custom store, e.g. from a file or database.
	}

	private void saveStore(byte[] bytes) {
		// TODO: Save bytes to your custom store, e.g. to a file or database.
	}
}
Java API 5

// Get client store from your custom store.
CryptshareConnection connection = new CryptshareConnection(new WebServiceUri(SERVER_URL))
Client client = new Client("sender@domain.com", Locale.ENGLISH, connection, CustomProtectedStore.open());

// do something like verifications, transfers, ...

// Write encrypted modified client store back into the custom store.
// Info: This step is not necessary anymore.

Verifizierung

Die Prüfung auf den aktuellen Verifizierungsstatus hat sich verändert. Die Methode `Client#checkVerification` liefert nun ein `CheckVerificationResult`-Objekt zurück. Ob der Client verifiziert ist, wird statt mit `VerificationStatus#isVerified` nun mit dem `CheckVerificationResult#isUserVerified` überprüft. Den Verifizierungsmodus erhält man statt `VerificationStatus#isSenderVerification` mit `CheckVerificationResult#getVerificationMode`. Unten stehend sehen Sie den Verifizierungsprozess im Detail für die beiden Verifizierungsarten:

Client Verifizierung

private final String SENDER = "john.doe@example.com";
private final String SERVER_URL = "https://cryptshare.mydomain.com";
private final String CLIENT_STORE_PATH = "client.store";

CryptshareConnection connection = new CryptshareConnection(new WebServiceUri(SERVER_URL));
Client client = new Client(SENDER, connection, Paths.get(CLIENT_STORE_PATH));
System.out.println("Checking whether the client is verified...");
CheckVerificationResult result = client.checkVerification();
if (result.isUserVerified()) {
	System.out.println("The client is verified; nothing to do.");
} else {
	if (result.getVerificationMode() == VerificationMode.SENDER) {
		System.out.println("Verification mode is set to 'Sender', please set it to 'Client' and add the following Client ID: " + client.getClientId());
    	return;
	}
	try {
		client.RequestClientVerification();
		System.out.println("The client verification has been requested. Please rerun the program and check whether your client is verified.");
	} catch (CryptshareServerException e) {
		System.out.println("The verification mode is set to 'Client', but your Client ID is not whitelisted. " +
                     	   "Please add your Client ID '" + client.getClientId() + "' to the list of allowed clients.");
		return;
	}
}

Die Methode `Client#checkVerification` liefert nun ein anderes Objekt zurück, bei dem auch die Prüfung, ob der Client bereits verifiziert ist, verändert wurde. Anstatt dass `Client#requestVerification` aufgerufen wird, um eine Verifizierung anzufragen, muss nun explizit eine Client Verification angefragt werden mit `Client#requestClientVerification`. Im Falle eines noch nicht verifizierten Clients wirft `Client#requestClientVerification` nun Fall eine Exception geworfen, die gefangen werden muss.

Absender Verifizierung

private final String SENDER = "john.doe@example.com";
private final String SERVER_URL = "https://cryptshare.mydomain.com";
private final String CLIENT_STORE_PATH = "client.store";

CryptshareConnection connection = new CryptshareConnection(new WebServiceUri(SERVER_URL));
Client client = new Client(SENDER, connection, Paths.get(CLIENT_STORE_PATH));
System.out.println("Checking whether the client is verified...");
CheckVerificationResult result = client.checkVerification();
if (result.isUserVerified()) {
	System.out.println("The client is verified; nothing to do.");
} else {
	if (result.getVerificationMode() == VerificationMode.CLIENT) {
		System.out.println("Verification mode is set to 'Client', please set it to 'Sender'.");
    	return;
	}
	client.RequestSenderVerification();
	System.out.println("Please type in the verification code:");
	BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
	String vericode = reader.readLine();
	reader.close();

	// now tell the client to store the verification code in the verification store
	client.confirmSenderVerification(vericode.trim());
}

Die Methode `Client#checkVerification` liefert nun ein anderes Objekt zurück, bei dem auch die Prüfung, ob der Client bereits verifiziert ist, verändert wurde. Anstatt dass `Client#requestVerification` aufgerufen wird, um eine Verifizierung anzufragen, muss nun explizit eine Absender Verifizierung angefragt werden mit `Client#requestSenderVerification`. Das Speichern des Verification Codes geschieht nun nicht mehr über `Client#storeVerification`, sondern über die Methode `Client#confirmSenderVerification`.

Änderungen am Transferprozess

Einen Transfer vorbereiten

Einige Methoden beim Vorbereiten eines Transfers besitzen nun einen neuen Namen oder benötigen einen anderen Typen als Parameter:

Beschreibung Alte Methode Neue Methode Änderung
Soll beim Download einer Datei durch einen Empfänger eine Benachrichtigung an den Absender geschickt werden? Transfer#setDownloadNotification Transfer#setInformAboutDownload Neuer Methodenname
Setzen der Absendersprache für E-Mails an den Absender Transfer#setSenderLanguage Client#setUserLanguage Neuer Methodenname und Setzen der Sprache auf den Client statt auf den Transfer.
Setzen von Empfängern eines Transfers Transfer#addRecipient

Transfer#setRecipients Transfer#addRecipients


Diese Methoden benötigen nun `Recipient`-Objekte anstatt von Strings, z.B.

`transfer.addRecipient("john.doe@cryptshare.com")` wird ersetzt durch `transfer.addRecipient(new Recipient("john.doe@cryptshare,com"))`

Setzen des Ablaufdatums Transfer#setExpirationDate(Date) Transfer#setExpirationDate(LocalDate) Die Methode benötigt nun ein LocalDate- anstelle eines Date-Objekts, z.B.

LocalDate.of(2020, 12, 24);

Setzen der Empfängersprache Transfer#setRecipientLanguage(String) Transfer#setRecipientLanguage(Locale) Die Methode benötigt nun ein Locale-Objekt anstelle eines Strings, z.B. Locale.GERMAN.
Generieren eines automatisch generierten Passworts Client#requestPassword(int)

Client#requestPassword(int, boolean)

Client#requestGeneratedPassword(int) Neuer Methodenname.

Die lokale Generierung eines Passwortes ist nicht mehr möglich.

Setzen eines generierten Passwortes Transfer#setGeneratedPassword Transfer#setPassword Automatisch generierte Passwörter werden nun wie manuelle Passwörter gespeichert.
Erstellen von Objekten für Transferdateien TransferFile(String) TransferFile(Path) Zum Erstellen von TransferFile-Objekten wird nun ein Path anstelle eines Strings benötigt, z.B. Paths.get(pathString).
Hinzufügen von Transferdateien zu einem Transfer Transfer#setFiles Transfer#setTransferFiles Neuer Methodenname

Einen Transfer durchführen

Die Durchführung von asynchronen und synchronen Transfers wurde angepasst, indem nun nicht nur eine Klasse benötigt wird, die sich um alle möglichen Zustandsänderungen des Transfers kümmert wie Fortschrittsänderung beim Upload, Upload beendet, Upload unterbrochen und Transfer abgebrochen. Stattdessen kann nun für jede dieser Zustandsänderungen ein eigener Handler definiert werden. Diese leiten ihrem Namen entsprechend von den folgenden Interfaces ab: IUploadProgressChangedHandler, IUploadCompleteHandler, IUploadInterruptedHandler, IUploadCancelledHandler. Diese erfordern jeweils eine zu implementierende Methode. Die zu implementierende Methode stellt direkt alle verfügbaren Informationen bereit, sodass diese nicht mehr aus einem Event-Objekt extrahiert werden müssen. Wird ein Handler nicht benötigt, so kann beim Starten eines Transfers null für diesen Handler angegeben werden. Beispiele für Handler:

public class UploadProgressChangedHandler implements IUploadProgressChangedHandler {
	@Override
	public void progressChanged(String currentFileNo, String currentFileName, double bytesUploaded, double bytesTotal, long bytesPerSecond) {
		// do something
	}
}

public class UploadCompleteHandler implements IUploadCompleteHandler {
	@Override
	public void uploadComplete(Map<String, String> urlMappings, Map<String, String> smtpMappings, String serverGenPassword, TransferError transferError,
			String trackingId) {
		// do something
	}
}

public class UploadInterruptedHandler implements IUploadInterruptedHandler {
	@Override
	public void uploadInterrupted(CryptshareException cryptshareException) {
		// do something
	}
}

public class UploadCancelledHeader implements IUploadCancelledHandler {
	@Override
	public void cancelled() {
		// do something
	}
}

Verwendung der Handler beim Starten eines Transfers:

// Asynchroner Transfer
client.beginTransfer(transfer, new UploadProgressChangedHandler(), new UploadCompleteHandler(), new UploadInterruptedHandler(), new UploadCancelledHeader(), pollingTimer);

// Synchroner Transfer
client.performTransfer(transfer, new UploadProgressChangedHandler(), new UploadCompleteHandler(), new UploadInterruptedHandler(), new UploadCancelledHeader(), pollingTimer);

Der neue Integer-Parameter pollingTimer gibt in Millisekunden an, wie oft der Server nach dem aktuellen Zustand des laufenden Transfers befragt werden soll, z.B. alle 1000 Millisekunden (= 1 Sekunde).

Änderungen weiterer Methoden

Standardservereinstellungen abrufen

Client#requestTransferData wurde entfernt. Verwenden Sie stattdessen Client#requestPolicy, um die für Ihre Absender-Empfänger-Kombination gültigen Einstellungen abzurufen.

List<String> recipients = new ArrayList<>();
recipients.add("user001@cryptshare.com");
recipients.add("user002@cryptshare.com");
Policy policy = client.requestPolicy(recipients);

Policyregeln

Die Namen und Dateitypen einzelner Policy-Informationen haben sich verändert:

Beschreibung Alte Methode Neue Methode Änderung
Erlaubte Passwortverfahren Policy#getPasswordMode Policy#getAllowedStandardPasswordModes Neuer Methodenname und Rückgabe eines `Set` statt einer `List`.
Empfänger, die für den gewählten Absender nicht zugelassen sind Policy#getFailedAddresses Policy#getFailedEmailAddresses Neuer Methodenname und Rückgabe eines `Set` statt einer `List`.

Sprachressourcen

Der Abruf definierter Sprachpaketversionen ist nicht mehr möglich. Verwenden Sie stattdessen jene Methoden, die keine Angabe über die Sprachpaketversion machen, z.B. Client#requestLanguagePacks() und Client#requestLanguagePackFile(String, Locale).

Beschreibung Alte Methode Neue Methode Änderung
Bestimmung der Sprachpaketversion LanguagePack#getLanguagePackVersion LanguagePack#getVersion Neuer Methodenname.
Bestimmung der letzten Änderung am Sprachpaket LanguagePack#getLastUpdate Rückgabe eines ZoneDateTime-Objekts anstatt eines Unix-Timestamps in Long-Format.

Nutzungsbestimmungen

Der Rückgabetyp der Nutzungsbestimmungen mittels Client#requestTermsOfUse hat sich verändert. Diese Methode gibt nun ein TermsOfUse-Objekt zurück. Die für eine Sprache spezifischen Nutzungsbestimmungen erhalten Sie über termsOfUse#getTextByLanguage(Locale).

Lizenzinformationen

Der Datentyp einzelner Felder innerhalb des Objekts LicenseInfo, das über Client#requestLicenseInfo abgefragt wird, haben sich von String zu LocalDate verändert: LicenseInfo#getProductLicenseExpirationDate, LicenseInfo#getProductSubscriptionExpirationDate, LicenseInfo#getServerLicenseExpirationDate, LicenseInfo#getServerSubscriptionExpirationDate.

E-Mail-Templates vom Cryptshare Server abrufen

Die Methode Client#requestMailTemplate(String, Map<Set<String>, Set<TemplateReplacement>, <language>, <mailFormat>>, Locale, String) wird nun mit einem neuen letzten Parameter vom Typ EmailFormat aufgerufen. Beispiel:

Alt

Map<List<String>, String> mailTemplates = client.requestMailTemplate("recipient", replacements, new Locale("ja"), "html");
 
Neu

Map<List<String>, String> mailTemplates = client.requestMailTemplate("recipient", replacements, new Locale("ja"), EmailFormat.HTML);

Zusätzlich wurde das Enum TemplatePlaceholder entfernt. Die Zuordnung der alten Platzhalter zu den neu zu verwendenden Strings erhalten Sie im Artikel E-Mail-Benachrichtigungen.

Alt

Set<TemplateReplacement> replacementSet = new HashSet<TemplateReplacement>();
replacementSet.add(new TemplateReplacement(TemplatePlaceholder.NAME, "John Adams"));
replacementSet.add(new TemplateReplacement(TemplatePlaceholder.EMAIL, "john.adams@server.com"));
 
Neu

Set<TemplateReplacement> replacementSet = new HashSet<>();
replacementSet.add(new TemplateReplacement("name", "John Adams"));
replacementSet.add(new TemplateReplacement("email", "john.adams@server.com"));

Überprüfen eines Passworts

Mithilfe der Methode `Client#checkPassword` war es bisher möglich, die Qualität eines Passworts vom Server überprüfen zu lassen. Als Rückgabewert wurde ein `PasswordPolicy`-Objekt zurückgegeben. Dieses wurde für diese Methode durch ein `PasswordCheckResult`-Objekt ersetzt. Nicht von dieser Methode benötigte Felder, die zuvor immer `null` waren, wurden entfernt.

Manuelles Setzen von Transferfehlern

Wurde beispielsweise der Versand von E-Mails nicht vom Cryptshare Server, sondern von der eigenen Anwendung übernommen, so konnte dem Server signalisiert werden, dass der Versand der E-Mails fehlgeschlagen ist. Mithilfe der Methode Client#updateTransferError(String trackingId, TransferError error) kann für eine spezifische Tracking-ID eines Transfers ein TransferError definiert werden. Dieser TransferError wurde entsprechend verändert, sodass nicht mehr setzbare Werte nicht mehr per Setter gesetzt werden können. Ebenso muss für das Setzen derjenigen fehlgeschlagenen Empfänger zunächst die Empfängerliste des TransferErrors abgerufen werden.

Alt

TransferError error = new TransferError();
List<String> recipients = new ArrayList<>();
recipients.add("recipient@cryptshare.com");
error.setFailedRecipients(recipients);
 
Neu

TransferError error = new TransferError();
List<String> recipients = new ArrayList<>();
recipients.add("recipient@cryptshare.com");
error.getFailedRecipients().addAll(recipients);

Entfernte Methoden

  • Transfer#setSessionID
  • Transfer#setTransferSize
  • Client#manageTransfer
  • Client#handleAsynchFileTransferCompleted