CSJCurrent de:Migration von Java API v3 auf v5.0
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.
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