mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-27 14:32:06 +01:00
Another overhaul
This commit is contained in:
parent
f522cea748
commit
e8f09fc842
24 changed files with 644 additions and 98 deletions
|
@ -12,6 +12,8 @@ dependencies {
|
||||||
compile project(':smack-extensions')
|
compile project(':smack-extensions')
|
||||||
compile project(':smack-experimental')
|
compile project(':smack-experimental')
|
||||||
compile project(':smack-omemo')
|
compile project(':smack-omemo')
|
||||||
|
compile project(':smack-openpgp')
|
||||||
|
compile project(':smack-openpgp-bouncycastle')
|
||||||
compile project(':smack-debug')
|
compile project(':smack-debug')
|
||||||
compile project(path: ":smack-omemo", configuration: "testRuntime")
|
compile project(path: ":smack-omemo", configuration: "testRuntime")
|
||||||
compile 'org.reflections:reflections:0.9.9-RC1'
|
compile 'org.reflections:reflections:0.9.9-RC1'
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
package org.jivesoftware.smackx.openpgp;
|
|
||||||
|
|
||||||
public class OpenPgpIntegrationTest {
|
|
||||||
}
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2018 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package org.jivesoftware.smackx.ox.bouncycastle;
|
package org.jivesoftware.smackx.ox.bouncycastle;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
@ -24,10 +40,10 @@ import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SignElement;
|
import org.jivesoftware.smackx.ox.element.SignElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SigncryptElement;
|
import org.jivesoftware.smackx.ox.element.SigncryptElement;
|
||||||
import org.jivesoftware.smackx.ox.exception.CorruptedOpenPgpKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
||||||
|
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
||||||
|
|
||||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.BouncyGPG;
|
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.BouncyGPG;
|
||||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PublicKeySize;
|
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PublicKeySize;
|
||||||
|
@ -38,19 +54,16 @@ import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
import org.bouncycastle.util.encoders.Hex;
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
import org.bouncycastle.util.io.Streams;
|
import org.bouncycastle.util.io.Streams;
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
|
|
||||||
|
|
||||||
public class BCOpenPgpProvider implements OpenPgpProvider {
|
public class BCOpenPgpProvider implements OpenPgpProvider {
|
||||||
|
|
||||||
private final BareJid user;
|
private final BareJid user;
|
||||||
private OpenPgpV4Fingerprint primaryKeyPair;
|
|
||||||
|
|
||||||
private BCOpenPgpStore store;
|
private BCOpenPgpStore store;
|
||||||
|
|
||||||
|
|
||||||
public BCOpenPgpProvider(BareJid user) {
|
public BCOpenPgpProvider(BareJid user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.primaryKeyPair = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStore(BCOpenPgpStore store) {
|
public void setStore(BCOpenPgpStore store) {
|
||||||
|
@ -59,7 +72,7 @@ public class BCOpenPgpProvider implements OpenPgpProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OpenPgpV4Fingerprint primaryOpenPgpKeyPairFingerprint() {
|
public OpenPgpV4Fingerprint primaryOpenPgpKeyPairFingerprint() {
|
||||||
return primaryKeyPair;
|
return store.primaryOpenPgpKeyPairFingerprint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -141,35 +154,36 @@ public class BCOpenPgpProvider implements OpenPgpProvider {
|
||||||
@Override
|
@Override
|
||||||
public OpenPgpElement sign(SignElement element, OpenPgpV4Fingerprint singingKeyFingerprint)
|
public OpenPgpElement sign(SignElement element, OpenPgpV4Fingerprint singingKeyFingerprint)
|
||||||
throws MissingOpenPgpKeyPairException {
|
throws MissingOpenPgpKeyPairException {
|
||||||
throw new NotImplementedException();
|
throw new AssertionError("Feature not implemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OpenPgpMessage verify(OpenPgpElement element, Set<OpenPgpV4Fingerprint> singingKeyFingerprints)
|
public OpenPgpMessage verify(OpenPgpElement element, Set<OpenPgpV4Fingerprint> singingKeyFingerprints)
|
||||||
throws MissingOpenPgpPublicKeyException {
|
throws MissingOpenPgpPublicKeyException {
|
||||||
throw new NotImplementedException();
|
throw new AssertionError("Feature not implemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OpenPgpElement encrypt(CryptElement element, Set<OpenPgpV4Fingerprint> encryptionKeyFingerprints)
|
public OpenPgpElement encrypt(CryptElement element, Set<OpenPgpV4Fingerprint> encryptionKeyFingerprints)
|
||||||
throws MissingOpenPgpPublicKeyException {
|
throws MissingOpenPgpPublicKeyException {
|
||||||
throw new NotImplementedException();
|
throw new AssertionError("Feature not implemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OpenPgpMessage decrypt(OpenPgpElement element) throws MissingOpenPgpKeyPairException {
|
public OpenPgpMessage decrypt(OpenPgpElement element)
|
||||||
throw new NotImplementedException();
|
throws MissingOpenPgpKeyPairException {
|
||||||
|
throw new AssertionError("Feature not implemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PubkeyElement createPubkeyElement(OpenPgpV4Fingerprint fingerprint)
|
public PubkeyElement createPubkeyElement(OpenPgpV4Fingerprint fingerprint)
|
||||||
throws MissingOpenPgpPublicKeyException, CorruptedOpenPgpKeyException {
|
throws MissingOpenPgpPublicKeyException, SmackOpenPgpException {
|
||||||
return store.createPubkeyElement(fingerprint);
|
return store.createPubkeyElement(fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element)
|
public void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element)
|
||||||
throws CorruptedOpenPgpKeyException {
|
throws SmackOpenPgpException {
|
||||||
store.storePublicKey(owner, fingerprint, element);
|
store.storePublicKey(owner, fingerprint, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,25 +194,25 @@ public class BCOpenPgpProvider implements OpenPgpProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OpenPgpV4Fingerprint createOpenPgpKeyPair()
|
public OpenPgpV4Fingerprint createOpenPgpKeyPair()
|
||||||
throws NoSuchAlgorithmException, NoSuchProviderException, CorruptedOpenPgpKeyException {
|
throws NoSuchAlgorithmException, NoSuchProviderException, SmackOpenPgpException {
|
||||||
return store.createOpenPgpKeyPair();
|
return store.createOpenPgpKeyPair();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretkeyElement createSecretkeyElement(Set<OpenPgpV4Fingerprint> fingerprints, String password)
|
public SecretkeyElement createSecretkeyElement(Set<OpenPgpV4Fingerprint> fingerprints, String password)
|
||||||
throws MissingOpenPgpKeyPairException, CorruptedOpenPgpKeyException {
|
throws MissingOpenPgpKeyPairException, SmackOpenPgpException {
|
||||||
return store.createSecretkeyElement(fingerprints, password);
|
return store.createSecretkeyElement(fingerprints, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<OpenPgpV4Fingerprint> availableOpenPgpPublicKeysFingerprints(BareJid contact)
|
public Set<OpenPgpV4Fingerprint> availableOpenPgpPublicKeysFingerprints(BareJid contact)
|
||||||
throws CorruptedOpenPgpKeyException {
|
throws SmackOpenPgpException {
|
||||||
return store.availableOpenPgpPublicKeysFingerprints(contact);
|
return store.availableOpenPgpPublicKeysFingerprints(contact);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, String password, SecretKeyRestoreSelectionCallback callback)
|
public void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, String password, SecretKeyRestoreSelectionCallback callback)
|
||||||
throws CorruptedOpenPgpKeyException, InvalidBackupCodeException {
|
throws SmackOpenPgpException, InvalidBackupCodeException {
|
||||||
store.restoreSecretKeyBackup(secretkeyElement, password, callback);
|
store.restoreSecretKeyBackup(secretkeyElement, password, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2018 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package org.jivesoftware.smackx.ox.bouncycastle;
|
package org.jivesoftware.smackx.ox.bouncycastle;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpStore;
|
import org.jivesoftware.smackx.ox.OpenPgpStore;
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2018 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package org.jivesoftware.smackx.ox.bouncycastle;
|
package org.jivesoftware.smackx.ox.bouncycastle;
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
|
@ -31,10 +47,10 @@ import org.jivesoftware.smackx.ox.callback.SecretKeyRestoreSelectionCallback;
|
||||||
import org.jivesoftware.smackx.ox.element.PubkeyElement;
|
import org.jivesoftware.smackx.ox.element.PubkeyElement;
|
||||||
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
||||||
import org.jivesoftware.smackx.ox.exception.CorruptedOpenPgpKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
||||||
|
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
||||||
|
|
||||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PGPHashAlgorithms;
|
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PGPHashAlgorithms;
|
||||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PGPSymmetricEncryptionAlgorithms;
|
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PGPSymmetricEncryptionAlgorithms;
|
||||||
|
@ -156,7 +172,7 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<OpenPgpV4Fingerprint> availableOpenPgpPublicKeysFingerprints(BareJid contact)
|
public Set<OpenPgpV4Fingerprint> availableOpenPgpPublicKeysFingerprints(BareJid contact)
|
||||||
throws CorruptedOpenPgpKeyException {
|
throws SmackOpenPgpException {
|
||||||
Set<OpenPgpV4Fingerprint> availableKeys = new HashSet<>();
|
Set<OpenPgpV4Fingerprint> availableKeys = new HashSet<>();
|
||||||
try {
|
try {
|
||||||
Iterator<PGPPublicKeyRing> ringIterator = keyringConfig.getPublicKeyRings().getKeyRings("xmpp:" + contact.toString());
|
Iterator<PGPPublicKeyRing> ringIterator = keyringConfig.getPublicKeyRings().getKeyRings("xmpp:" + contact.toString());
|
||||||
|
@ -171,7 +187,7 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (PGPException | IOException e) {
|
} catch (PGPException | IOException e) {
|
||||||
throw new CorruptedOpenPgpKeyException(e);
|
throw new SmackOpenPgpException(e);
|
||||||
}
|
}
|
||||||
return availableKeys;
|
return availableKeys;
|
||||||
}
|
}
|
||||||
|
@ -208,7 +224,7 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PubkeyElement createPubkeyElement(OpenPgpV4Fingerprint fingerprint)
|
public PubkeyElement createPubkeyElement(OpenPgpV4Fingerprint fingerprint)
|
||||||
throws MissingOpenPgpPublicKeyException, CorruptedOpenPgpKeyException {
|
throws MissingOpenPgpPublicKeyException, SmackOpenPgpException {
|
||||||
try {
|
try {
|
||||||
PGPPublicKey publicKey = keyringConfig.getPublicKeyRings().getPublicKey(Util.keyIdFromFingerprint(fingerprint));
|
PGPPublicKey publicKey = keyringConfig.getPublicKeyRings().getPublicKey(Util.keyIdFromFingerprint(fingerprint));
|
||||||
if (publicKey == null) {
|
if (publicKey == null) {
|
||||||
|
@ -217,19 +233,19 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
byte[] base64 = Base64.encode(publicKey.getEncoded());
|
byte[] base64 = Base64.encode(publicKey.getEncoded());
|
||||||
return new PubkeyElement(new PubkeyElement.PubkeyDataElement(base64), new Date());
|
return new PubkeyElement(new PubkeyElement.PubkeyDataElement(base64), new Date());
|
||||||
} catch (PGPException | IOException e) {
|
} catch (PGPException | IOException e) {
|
||||||
throw new CorruptedOpenPgpKeyException(e);
|
throw new SmackOpenPgpException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element)
|
public void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element)
|
||||||
throws CorruptedOpenPgpKeyException {
|
throws SmackOpenPgpException {
|
||||||
byte[] base64decoded = Base64.decode(element.getDataElement().getB64Data());
|
byte[] base64decoded = Base64.decode(element.getDataElement().getB64Data());
|
||||||
try {
|
try {
|
||||||
keyringConfig.addPublicKey(base64decoded);
|
keyringConfig.addPublicKey(base64decoded);
|
||||||
writePublicKeysToFile(keyringConfig, publicKeyringPath());
|
writePublicKeysToFile(keyringConfig, publicKeyringPath());
|
||||||
} catch (PGPException | IOException e) {
|
} catch (PGPException | IOException e) {
|
||||||
throw new CorruptedOpenPgpKeyException(e);
|
throw new SmackOpenPgpException(e);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
LOGGER.log(Level.WARNING, "Public Key with ID " + fingerprint.toString() + " of " +
|
LOGGER.log(Level.WARNING, "Public Key with ID " + fingerprint.toString() + " of " +
|
||||||
owner + " is already in memory. Skip.");
|
owner + " is already in memory. Skip.");
|
||||||
|
@ -238,7 +254,7 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretkeyElement createSecretkeyElement(Set<OpenPgpV4Fingerprint> fingerprints, String password)
|
public SecretkeyElement createSecretkeyElement(Set<OpenPgpV4Fingerprint> fingerprints, String password)
|
||||||
throws MissingOpenPgpKeyPairException, CorruptedOpenPgpKeyException {
|
throws MissingOpenPgpKeyPairException, SmackOpenPgpException {
|
||||||
|
|
||||||
PGPDigestCalculator calculator;
|
PGPDigestCalculator calculator;
|
||||||
try {
|
try {
|
||||||
|
@ -281,13 +297,13 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
return new SecretkeyElement(Base64.encode(buffer.toByteArray()));
|
return new SecretkeyElement(Base64.encode(buffer.toByteArray()));
|
||||||
|
|
||||||
} catch (PGPException | IOException e) {
|
} catch (PGPException | IOException e) {
|
||||||
throw new CorruptedOpenPgpKeyException(e);
|
throw new SmackOpenPgpException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, String password, SecretKeyRestoreSelectionCallback callback)
|
public void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, String password, SecretKeyRestoreSelectionCallback callback)
|
||||||
throws CorruptedOpenPgpKeyException, InvalidBackupCodeException {
|
throws SmackOpenPgpException, InvalidBackupCodeException {
|
||||||
byte[] base64Decoded = Base64.decode(secretkeyElement.getB64Data());
|
byte[] base64Decoded = Base64.decode(secretkeyElement.getB64Data());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -332,13 +348,13 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
writePrivateKeysToFile(keyringConfig, secretKeyringPath());
|
writePrivateKeysToFile(keyringConfig, secretKeyringPath());
|
||||||
}
|
}
|
||||||
} catch (PGPException | IOException e) {
|
} catch (PGPException | IOException e) {
|
||||||
throw new CorruptedOpenPgpKeyException(e);
|
throw new SmackOpenPgpException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OpenPgpV4Fingerprint createOpenPgpKeyPair()
|
public OpenPgpV4Fingerprint createOpenPgpKeyPair()
|
||||||
throws NoSuchAlgorithmException, NoSuchProviderException, CorruptedOpenPgpKeyException {
|
throws NoSuchAlgorithmException, NoSuchProviderException, SmackOpenPgpException {
|
||||||
try {
|
try {
|
||||||
PGPSecretKeyRing ourKey = BCOpenPgpProvider.generateKey(user).generateSecretKeyRing();
|
PGPSecretKeyRing ourKey = BCOpenPgpProvider.generateKey(user).generateSecretKeyRing();
|
||||||
keyringConfig.addSecretKey(ourKey.getSecretKey().getEncoded());
|
keyringConfig.addSecretKey(ourKey.getSecretKey().getEncoded());
|
||||||
|
@ -346,7 +362,7 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
primaryKeyFingerprint = BCOpenPgpProvider.getFingerprint(ourKey.getPublicKey());
|
primaryKeyFingerprint = BCOpenPgpProvider.getFingerprint(ourKey.getPublicKey());
|
||||||
return primaryKeyFingerprint;
|
return primaryKeyFingerprint;
|
||||||
} catch (PGPException | IOException e) {
|
} catch (PGPException | IOException e) {
|
||||||
throw new CorruptedOpenPgpKeyException(e);
|
throw new SmackOpenPgpException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,7 +375,7 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
private File contactsPath() {
|
private File contactsPath() {
|
||||||
return new File(basePath, "users");
|
return new File(basePath, user.toString() + "/users");
|
||||||
}
|
}
|
||||||
|
|
||||||
private File contactsPath(BareJid contact) {
|
private File contactsPath(BareJid contact) {
|
||||||
|
|
|
@ -6,6 +6,7 @@ Smack API for XEP-0373: OpenPGP for XMPP."""
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':smack-core')
|
compile project(':smack-core')
|
||||||
compile project(':smack-extensions')
|
compile project(':smack-extensions')
|
||||||
|
compile project(':smack-experimental')
|
||||||
testCompile project(path: ":smack-core", configuration: "testRuntime")
|
testCompile project(path: ":smack-core", configuration: "testRuntime")
|
||||||
testCompile project(path: ":smack-core", configuration: "archives")
|
testCompile project(path: ":smack-core", configuration: "archives")
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,24 +16,42 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox;
|
package org.jivesoftware.smackx.ox;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.Manager;
|
import org.jivesoftware.smack.Manager;
|
||||||
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
|
import org.jivesoftware.smack.chat2.Chat;
|
||||||
|
import org.jivesoftware.smack.chat2.ChatManager;
|
||||||
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
|
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
|
||||||
public class OXInstantMessagingManager extends Manager {
|
/**
|
||||||
|
* Entry point of Smacks API for XEP-0374: OpenPGP for XMPP: Instant Messaging.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0374.html">
|
||||||
|
* XEP-0374: OpenPGP for XMPP: Instant Messaging</a>
|
||||||
|
*/
|
||||||
|
public final class OXInstantMessagingManager extends Manager {
|
||||||
|
|
||||||
|
public static final String NAMESPACE_0 = "urn:xmpp:openpgp:im:0";
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(OXInstantMessagingManager.class.getName());
|
||||||
|
|
||||||
private static final Map<XMPPConnection, OXInstantMessagingManager> INSTANCES = new WeakHashMap<>();
|
private static final Map<XMPPConnection, OXInstantMessagingManager> INSTANCES = new WeakHashMap<>();
|
||||||
private final OpenPgpManager openPgpManager;
|
private final OpenPgpManager openPgpManager;
|
||||||
|
private final ChatManager chatManager;
|
||||||
|
|
||||||
private OXInstantMessagingManager(XMPPConnection connection) {
|
private OXInstantMessagingManager(XMPPConnection connection) {
|
||||||
super(connection);
|
super(connection);
|
||||||
this.openPgpManager = OpenPgpManager.getInstanceFor(connection);
|
this.openPgpManager = OpenPgpManager.getInstanceFor(connection);
|
||||||
|
this.chatManager = ChatManager.getInstanceFor(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OXInstantMessagingManager getInstanceFor(XMPPConnection connection) {
|
public static OXInstantMessagingManager getInstanceFor(XMPPConnection connection) {
|
||||||
|
@ -45,7 +63,51 @@ public class OXInstantMessagingManager extends Manager {
|
||||||
return manager;
|
return manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void send(List<ExtensionElement> messageContent, BareJid recipient) {
|
/**
|
||||||
|
* Determine, whether a contact announces support for XEP-0374: OpenPGP for XMPP: Instant Messaging.
|
||||||
|
*
|
||||||
|
* @param jid {@link BareJid} of the contact in question.
|
||||||
|
* @return true if contact announces support, otherwise false.
|
||||||
|
* @throws XMPPException.XMPPErrorException
|
||||||
|
* @throws SmackException.NotConnectedException
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @throws SmackException.NoResponseException
|
||||||
|
*/
|
||||||
|
public boolean contactSupportsOxInstantMessaging(BareJid jid)
|
||||||
|
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
||||||
|
SmackException.NoResponseException {
|
||||||
|
return ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(jid, NAMESPACE_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start an encrypted chat with {@code jid}.
|
||||||
|
* The chat is encrypted with OpenPGP for XMPP: Instant Messaging (XEP-0374).
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0374.html">XEP-0374: OpenPGP for XMPP: Instant Messaging</a>
|
||||||
|
* @param jid {@link BareJid} of the contact.
|
||||||
|
* @return {@link OpenPgpEncryptedChat} with the contact.
|
||||||
|
* @throws SmackOpenPgpException if something happens while gathering fingerprints.
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @throws XMPPException.XMPPErrorException
|
||||||
|
* @throws SmackException.NotConnectedException
|
||||||
|
* @throws SmackException.NoResponseException
|
||||||
|
* @throws SmackException.FeatureNotSupportedException if the contact does not announce support for XEP-0374.
|
||||||
|
*/
|
||||||
|
public OpenPgpEncryptedChat chatWith(EntityBareJid jid)
|
||||||
|
throws SmackOpenPgpException, InterruptedException, XMPPException.XMPPErrorException,
|
||||||
|
SmackException.NotConnectedException, SmackException.NoResponseException,
|
||||||
|
SmackException.FeatureNotSupportedException {
|
||||||
|
if (!contactSupportsOxInstantMessaging(jid)) {
|
||||||
|
throw new SmackException.FeatureNotSupportedException(NAMESPACE_0, jid);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenPgpFingerprints theirKeys = openPgpManager.determineContactsKeys(jid);
|
||||||
|
OpenPgpFingerprints ourKeys = openPgpManager.determineContactsKeys(connection().getUser().asBareJid());
|
||||||
|
Chat chat = chatManager.chatWith(jid);
|
||||||
|
return new OpenPgpEncryptedChat(openPgpManager.getOpenPgpProvider(), chat, ourKeys, theirKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addOpenPgpEncryptedMessageListener() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2018 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.ox;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.chat2.Chat;
|
||||||
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement;
|
||||||
|
import org.jivesoftware.smackx.hints.element.StoreHint;
|
||||||
|
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
||||||
|
import org.jivesoftware.smackx.ox.element.SigncryptElement;
|
||||||
|
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
||||||
|
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
public class OpenPgpEncryptedChat {
|
||||||
|
|
||||||
|
private final Chat chat;
|
||||||
|
private final OpenPgpFingerprints contactsFingerprints;
|
||||||
|
private final OpenPgpFingerprints ourFingerprints;
|
||||||
|
private final OpenPgpProvider cryptoProvider;
|
||||||
|
private final OpenPgpV4Fingerprint singingKey;
|
||||||
|
|
||||||
|
public OpenPgpEncryptedChat(OpenPgpProvider cryptoProvider,
|
||||||
|
Chat chat,
|
||||||
|
OpenPgpFingerprints ourFingerprints,
|
||||||
|
OpenPgpFingerprints contactsFingerprints) {
|
||||||
|
this.cryptoProvider = cryptoProvider;
|
||||||
|
this.chat = chat;
|
||||||
|
this.singingKey = cryptoProvider.primaryOpenPgpKeyPairFingerprint();
|
||||||
|
this.ourFingerprints = ourFingerprints;
|
||||||
|
this.contactsFingerprints = contactsFingerprints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(Message message, List<ExtensionElement> payload)
|
||||||
|
throws MissingOpenPgpKeyPairException {
|
||||||
|
Set<OpenPgpV4Fingerprint> encryptionFingerprints = new HashSet<>(contactsFingerprints.getActiveKeys());
|
||||||
|
encryptionFingerprints.addAll(ourFingerprints.getActiveKeys());
|
||||||
|
|
||||||
|
SigncryptElement preparedPayload = new SigncryptElement(
|
||||||
|
Collections.<Jid>singleton(chat.getXmppAddressOfChatPartner()),
|
||||||
|
payload);
|
||||||
|
OpenPgpElement encryptedPayload;
|
||||||
|
|
||||||
|
// Encrypt the payload
|
||||||
|
try {
|
||||||
|
encryptedPayload = cryptoProvider.signAndEncrypt(
|
||||||
|
preparedPayload,
|
||||||
|
singingKey,
|
||||||
|
encryptionFingerprints);
|
||||||
|
} catch (MissingOpenPgpPublicKeyException e) {
|
||||||
|
throw new AssertionError("Missing OpenPGP public key, even though this should not happen here.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add encrypted payload to message
|
||||||
|
message.addExtension(encryptedPayload);
|
||||||
|
|
||||||
|
// Add additional information to the message
|
||||||
|
message.addExtension(new ExplicitMessageEncryptionElement(
|
||||||
|
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.openpgpV0));
|
||||||
|
StoreHint.set(message);
|
||||||
|
message.setBody("This message is encrypted using XEP-0374: OpenPGP for XMPP: Instant Messaging.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void send(Message message, CharSequence body)
|
||||||
|
throws MissingOpenPgpKeyPairException {
|
||||||
|
List<ExtensionElement> payload = new ArrayList<>();
|
||||||
|
payload.add(new Message.Body(null, body.toString()));
|
||||||
|
send(message, payload);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2018 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.ox;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.BareJid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata about a contacts OpenPGP fingerprints.
|
||||||
|
*/
|
||||||
|
public class OpenPgpFingerprints {
|
||||||
|
|
||||||
|
private final Set<OpenPgpV4Fingerprint> announcedKeys;
|
||||||
|
private final Set<OpenPgpV4Fingerprint> availableKeys;
|
||||||
|
private final Map<OpenPgpV4Fingerprint, Throwable> unfetchableKeys;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param announcedKeys keys the contact currently announces via their metadata node.
|
||||||
|
* @param availableKeys keys which contain the contacts {@link BareJid} as user ID.
|
||||||
|
* @param unfetchableKeys keys that are announced, but cannot be fetched fro PubSub.
|
||||||
|
*/
|
||||||
|
public OpenPgpFingerprints(Set<OpenPgpV4Fingerprint> announcedKeys,
|
||||||
|
Set<OpenPgpV4Fingerprint> availableKeys,
|
||||||
|
Map<OpenPgpV4Fingerprint, Throwable> unfetchableKeys) {
|
||||||
|
this.announcedKeys = Collections.unmodifiableSet(announcedKeys);
|
||||||
|
this.availableKeys = Collections.unmodifiableSet(availableKeys);
|
||||||
|
this.unfetchableKeys = Collections.unmodifiableMap(unfetchableKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a {@link Set} of {@link OpenPgpV4Fingerprint}s, which the contact in question announced via their
|
||||||
|
* metadata node.
|
||||||
|
*
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0373.html#announcing-pubkey-list">
|
||||||
|
* XEP-0373 §4.2 about the Public Key Metadata Node</a>
|
||||||
|
*
|
||||||
|
* @return announced keys.
|
||||||
|
*/
|
||||||
|
public Set<OpenPgpV4Fingerprint> getAnnouncedKeys() {
|
||||||
|
return announcedKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a {@link Set} of {@link OpenPgpV4Fingerprint}s, which are available in the local key ring, which contain
|
||||||
|
* the users {@link BareJid} as user-id (eg. "xmpp:juliet@capulet.lit").
|
||||||
|
* <br>
|
||||||
|
* Note: As everybody can publish a key with an arbitrary user-id, the keys contained in the result set MAY be keys
|
||||||
|
* of an potential attacker. NEVER use these keys for encryption without collating them with the users announced
|
||||||
|
* keys.
|
||||||
|
*
|
||||||
|
* @return locally available keys with users user-id.
|
||||||
|
*/
|
||||||
|
public Set<OpenPgpV4Fingerprint> getAvailableKeys() {
|
||||||
|
return availableKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a {@link Map}, which maps {@link OpenPgpV4Fingerprint}s which the user announced, but that cannot be fetched
|
||||||
|
* via PubSub to {@link Throwable}s which were thrown when we tried to fetch them.
|
||||||
|
*
|
||||||
|
* @return unfetchable keys.
|
||||||
|
*/
|
||||||
|
public Map<OpenPgpV4Fingerprint, Throwable> getUnfetchableKeys() {
|
||||||
|
return unfetchableKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a {@link Set} of {@link OpenPgpV4Fingerprint}s, which are both announced, as well as available.
|
||||||
|
* It is recommended to use those keys for encryption.
|
||||||
|
*
|
||||||
|
* @return active keys.
|
||||||
|
*/
|
||||||
|
public Set<OpenPgpV4Fingerprint> getActiveKeys() {
|
||||||
|
Set<OpenPgpV4Fingerprint> active = new HashSet<>();
|
||||||
|
for (OpenPgpV4Fingerprint fingerprint : getAvailableKeys()) {
|
||||||
|
if (getAnnouncedKeys().contains(fingerprint)) {
|
||||||
|
active.add(fingerprint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableSet(active);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,9 +18,15 @@ package org.jivesoftware.smackx.ox;
|
||||||
|
|
||||||
import static org.jivesoftware.smackx.ox.PubSubDelegate.PEP_NODE_PUBLIC_KEYS;
|
import static org.jivesoftware.smackx.ox.PubSubDelegate.PEP_NODE_PUBLIC_KEYS;
|
||||||
import static org.jivesoftware.smackx.ox.PubSubDelegate.PEP_NODE_PUBLIC_KEYS_NOTIFY;
|
import static org.jivesoftware.smackx.ox.PubSubDelegate.PEP_NODE_PUBLIC_KEYS_NOTIFY;
|
||||||
|
import static org.jivesoftware.smackx.ox.PubSubDelegate.fetchPubkey;
|
||||||
|
import static org.jivesoftware.smackx.ox.PubSubDelegate.publishPublicKey;
|
||||||
|
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
@ -34,20 +40,25 @@ import org.jivesoftware.smack.util.Async;
|
||||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
import org.jivesoftware.smackx.ox.callback.AskForBackupCodeCallback;
|
import org.jivesoftware.smackx.ox.callback.AskForBackupCodeCallback;
|
||||||
import org.jivesoftware.smackx.ox.callback.DisplayBackupCodeCallback;
|
import org.jivesoftware.smackx.ox.callback.DisplayBackupCodeCallback;
|
||||||
|
import org.jivesoftware.smackx.ox.callback.SecretKeyBackupSelectionCallback;
|
||||||
import org.jivesoftware.smackx.ox.callback.SecretKeyRestoreSelectionCallback;
|
import org.jivesoftware.smackx.ox.callback.SecretKeyRestoreSelectionCallback;
|
||||||
|
import org.jivesoftware.smackx.ox.element.PubkeyElement;
|
||||||
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
||||||
import org.jivesoftware.smackx.ox.exception.CorruptedOpenPgpKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
||||||
|
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
||||||
|
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
||||||
import org.jivesoftware.smackx.pep.PEPListener;
|
import org.jivesoftware.smackx.pep.PEPListener;
|
||||||
import org.jivesoftware.smackx.pep.PEPManager;
|
import org.jivesoftware.smackx.pep.PEPManager;
|
||||||
import org.jivesoftware.smackx.pubsub.EventElement;
|
import org.jivesoftware.smackx.pubsub.EventElement;
|
||||||
import org.jivesoftware.smackx.pubsub.ItemsExtension;
|
import org.jivesoftware.smackx.pubsub.ItemsExtension;
|
||||||
|
import org.jivesoftware.smackx.pubsub.LeafNode;
|
||||||
import org.jivesoftware.smackx.pubsub.PayloadItem;
|
import org.jivesoftware.smackx.pubsub.PayloadItem;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubManager;
|
import org.jivesoftware.smackx.pubsub.PubSubManager;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.BareJid;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
|
||||||
public final class OpenPgpManager extends Manager {
|
public final class OpenPgpManager extends Manager {
|
||||||
|
@ -71,11 +82,6 @@ public final class OpenPgpManager extends Manager {
|
||||||
*/
|
*/
|
||||||
private OpenPgpManager(XMPPConnection connection) {
|
private OpenPgpManager(XMPPConnection connection) {
|
||||||
super(connection);
|
super(connection);
|
||||||
|
|
||||||
// Subscribe to public key changes
|
|
||||||
PEPManager.getInstanceFor(connection()).addPEPListener(metadataListener);
|
|
||||||
ServiceDiscoveryManager.getInstanceFor(connection())
|
|
||||||
.addFeature(PEP_NODE_PUBLIC_KEYS_NOTIFY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,13 +109,62 @@ public final class OpenPgpManager extends Manager {
|
||||||
this.provider = provider;
|
this.provider = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the registered {@link OpenPgpProvider}.
|
||||||
|
*
|
||||||
|
* @return provider.
|
||||||
|
*/
|
||||||
|
OpenPgpProvider getOpenPgpProvider() {
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a fresh OpenPGP key pair, given we don't have one already.
|
||||||
|
* Publish the public key to the Public Key Node and update the Public Key Metadata Node with our keys fingerprint.
|
||||||
|
* Lastly register a {@link PEPListener} which listens for updates to Public Key Metadata Nodes.
|
||||||
|
*
|
||||||
|
* @throws NoSuchAlgorithmException if we are missing an algorithm to generate a fresh key pair.
|
||||||
|
* @throws NoSuchProviderException if we are missing a suitable {@link java.security.Provider}.
|
||||||
|
* @throws SmackOpenPgpException if something bad happens during key generation/loading.
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @throws PubSubException.NotALeafNodeException
|
||||||
|
* @throws XMPPException.XMPPErrorException
|
||||||
|
* @throws SmackException.NotConnectedException
|
||||||
|
* @throws SmackException.NoResponseException
|
||||||
|
*/
|
||||||
|
public void announceSupportAndPublish() throws NoSuchAlgorithmException, NoSuchProviderException, SmackOpenPgpException,
|
||||||
|
InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
|
||||||
|
SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
|
throwIfNoProviderSet();
|
||||||
|
|
||||||
|
OpenPgpV4Fingerprint primaryFingerprint = provider.primaryOpenPgpKeyPairFingerprint();
|
||||||
|
if (primaryFingerprint == null) {
|
||||||
|
primaryFingerprint = provider.createOpenPgpKeyPair();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create <pubkey/> element
|
||||||
|
PubkeyElement pubkeyElement;
|
||||||
|
try {
|
||||||
|
pubkeyElement = provider.createPubkeyElement(primaryFingerprint);
|
||||||
|
} catch (MissingOpenPgpPublicKeyException e) {
|
||||||
|
throw new AssertionError("Cannot publish our public key, since it is missing (MUST NOT happen!)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// publish it
|
||||||
|
publishPublicKey(connection(), pubkeyElement, primaryFingerprint);
|
||||||
|
|
||||||
|
// Subscribe to public key changes
|
||||||
|
PEPManager.getInstanceFor(connection()).addPEPListener(metadataListener);
|
||||||
|
ServiceDiscoveryManager.getInstanceFor(connection())
|
||||||
|
.addFeature(PEP_NODE_PUBLIC_KEYS_NOTIFY);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the upper-case hex encoded OpenPGP v4 fingerprint of our key pair.
|
* Return the upper-case hex encoded OpenPGP v4 fingerprint of our key pair.
|
||||||
*
|
*
|
||||||
* @return fingerprint.
|
* @return fingerprint.
|
||||||
* @throws CorruptedOpenPgpKeyException if for some reason we cannot determine our fingerprint.
|
|
||||||
*/
|
*/
|
||||||
public OpenPgpV4Fingerprint getOurFingerprint() throws CorruptedOpenPgpKeyException {
|
public OpenPgpV4Fingerprint getOurFingerprint() {
|
||||||
throwIfNoProviderSet();
|
throwIfNoProviderSet();
|
||||||
return provider.primaryOpenPgpKeyPairFingerprint();
|
return provider.primaryOpenPgpKeyPairFingerprint();
|
||||||
}
|
}
|
||||||
|
@ -140,35 +195,60 @@ public final class OpenPgpManager extends Manager {
|
||||||
*
|
*
|
||||||
* @see <a href="https://xmpp.org/extensions/xep-0373.html#synchro-pep">XEP-0373 §5</a>
|
* @see <a href="https://xmpp.org/extensions/xep-0373.html#synchro-pep">XEP-0373 §5</a>
|
||||||
*
|
*
|
||||||
* @param callback callback, which will receive the backup password used to encrypt the secret key.
|
* @param displayCodeCallback callback, which will receive the backup password used to encrypt the secret key.
|
||||||
* @throws CorruptedOpenPgpKeyException if the secret key is corrupted or can for some reason not be serialized.
|
* @param selectKeyCallback callback, which will receive the users choice of which keys will be backed up.
|
||||||
|
* @throws SmackOpenPgpException if the secret key is corrupted or can for some reason not be serialized.
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
* @throws PubSubException.NotALeafNodeException
|
* @throws PubSubException.NotALeafNodeException
|
||||||
* @throws XMPPException.XMPPErrorException
|
* @throws XMPPException.XMPPErrorException
|
||||||
* @throws SmackException.NotConnectedException
|
* @throws SmackException.NotConnectedException
|
||||||
* @throws SmackException.NoResponseException
|
* @throws SmackException.NoResponseException
|
||||||
*/
|
*/
|
||||||
public void backupSecretKeyToServer(DisplayBackupCodeCallback callback)
|
public void backupSecretKeyToServer(DisplayBackupCodeCallback displayCodeCallback,
|
||||||
throws CorruptedOpenPgpKeyException, InterruptedException, PubSubException.NotALeafNodeException,
|
SecretKeyBackupSelectionCallback selectKeyCallback)
|
||||||
|
throws SmackOpenPgpException, InterruptedException, PubSubException.NotALeafNodeException,
|
||||||
XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException,
|
XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException,
|
||||||
MissingOpenPgpKeyPairException {
|
MissingOpenPgpKeyPairException {
|
||||||
throwIfNoProviderSet();
|
throwIfNoProviderSet();
|
||||||
String backupCode = generateBackupPassword();
|
String backupCode = generateBackupPassword();
|
||||||
SecretkeyElement secretKey = provider.createSecretkeyElement(null, backupCode); // TODO
|
Set<OpenPgpV4Fingerprint> availableKeyPairs = provider.availableOpenPgpKeyPairFingerprints();
|
||||||
|
SecretkeyElement secretKey = provider.createSecretkeyElement(
|
||||||
|
selectKeyCallback.selectKeysToBackup(availableKeyPairs), backupCode);
|
||||||
PubSubDelegate.depositSecretKey(connection(), secretKey);
|
PubSubDelegate.depositSecretKey(connection(), secretKey);
|
||||||
callback.displayBackupCode(backupCode);
|
displayCodeCallback.displayBackupCode(backupCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the private {@link LeafNode} containing our secret key backup.
|
||||||
|
*
|
||||||
|
* @throws XMPPException.XMPPErrorException
|
||||||
|
* @throws SmackException.NotConnectedException
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @throws SmackException.NoResponseException
|
||||||
|
*/
|
||||||
public void deleteSecretKeyServerBackup()
|
public void deleteSecretKeyServerBackup()
|
||||||
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
||||||
SmackException.NoResponseException {
|
SmackException.NoResponseException {
|
||||||
PubSubDelegate.deleteSecretKeyNode(connection());
|
PubSubDelegate.deleteSecretKeyNode(connection());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch a secret key backup from the server and try to restore a selected secret key from it.
|
||||||
|
*
|
||||||
|
* @param codeCallback callback for prompting the user to provide the secret backup code.
|
||||||
|
* @param selectionCallback callback allowing the user to select a secret key which will be restored.
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @throws PubSubException.NotALeafNodeException
|
||||||
|
* @throws XMPPException.XMPPErrorException
|
||||||
|
* @throws SmackException.NotConnectedException
|
||||||
|
* @throws SmackException.NoResponseException
|
||||||
|
* @throws SmackOpenPgpException if something goes wrong while restoring the secret key.
|
||||||
|
* @throws InvalidBackupCodeException if the user-provided backup code is invalid.
|
||||||
|
*/
|
||||||
public void restoreSecretKeyServerBackup(AskForBackupCodeCallback codeCallback,
|
public void restoreSecretKeyServerBackup(AskForBackupCodeCallback codeCallback,
|
||||||
SecretKeyRestoreSelectionCallback selectionCallback)
|
SecretKeyRestoreSelectionCallback selectionCallback)
|
||||||
throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
|
throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
|
||||||
SmackException.NotConnectedException, SmackException.NoResponseException, CorruptedOpenPgpKeyException,
|
SmackException.NotConnectedException, SmackException.NoResponseException, SmackOpenPgpException,
|
||||||
InvalidBackupCodeException {
|
InvalidBackupCodeException {
|
||||||
throwIfNoProviderSet();
|
throwIfNoProviderSet();
|
||||||
SecretkeyElement backup = PubSubDelegate.fetchSecretKey(connection());
|
SecretkeyElement backup = PubSubDelegate.fetchSecretKey(connection());
|
||||||
|
@ -176,6 +256,38 @@ public final class OpenPgpManager extends Manager {
|
||||||
// TODO: catch InvalidBackupCodeException in order to prevent re-fetching the backup on next try.
|
// TODO: catch InvalidBackupCodeException in order to prevent re-fetching the backup on next try.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine which keys belong to a user and fetch any missing keys.
|
||||||
|
*
|
||||||
|
* @param jid {@link BareJid} of the user in question.
|
||||||
|
* @return {@link OpenPgpFingerprints} object containing the announced, available and unfetchable keys of the user.
|
||||||
|
* @throws SmackOpenPgpException
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @throws XMPPException.XMPPErrorException
|
||||||
|
* @throws SmackException.NotConnectedException
|
||||||
|
* @throws SmackException.NoResponseException
|
||||||
|
*/
|
||||||
|
public OpenPgpFingerprints determineContactsKeys(BareJid jid)
|
||||||
|
throws SmackOpenPgpException, InterruptedException, XMPPException.XMPPErrorException,
|
||||||
|
SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
|
Set<OpenPgpV4Fingerprint> announced = provider.announcedOpenPgpKeyFingerprints(jid);
|
||||||
|
Set<OpenPgpV4Fingerprint> available = provider.availableOpenPgpPublicKeysFingerprints(jid);
|
||||||
|
Map<OpenPgpV4Fingerprint, Throwable> unfetched = new HashMap<>();
|
||||||
|
for (OpenPgpV4Fingerprint f : announced) {
|
||||||
|
if (!available.contains(f)) {
|
||||||
|
try {
|
||||||
|
PubkeyElement pubkeyElement = PubSubDelegate.fetchPubkey(connection(), jid, f);
|
||||||
|
provider.storePublicKey(jid, f, pubkeyElement);
|
||||||
|
available.add(f);
|
||||||
|
} catch (PubSubException.NotAPubSubNodeException | PubSubException.NotALeafNodeException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not fetch public key " + f.toString() + " of user " + jid.toString(), e);
|
||||||
|
unfetched.put(f, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new OpenPgpFingerprints(announced, available, unfetched);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link PEPListener} that listens for changes to the OX public keys metadata node.
|
* {@link PEPListener} that listens for changes to the OX public keys metadata node.
|
||||||
*
|
*
|
||||||
|
@ -185,18 +297,31 @@ public final class OpenPgpManager extends Manager {
|
||||||
@Override
|
@Override
|
||||||
public void eventReceived(final EntityBareJid from, final EventElement event, Message message) {
|
public void eventReceived(final EntityBareJid from, final EventElement event, Message message) {
|
||||||
if (PEP_NODE_PUBLIC_KEYS.equals(event.getEvent().getNode())) {
|
if (PEP_NODE_PUBLIC_KEYS.equals(event.getEvent().getNode())) {
|
||||||
LOGGER.log(Level.INFO, "Received OpenPGP metadata update from " + from);
|
final BareJid contact = from.asBareJid();
|
||||||
|
LOGGER.log(Level.INFO, "Received OpenPGP metadata update from " + contact);
|
||||||
Async.go(new Runnable() {
|
Async.go(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
ItemsExtension items = (ItemsExtension) event.getExtensions().get(0);
|
ItemsExtension items = (ItemsExtension) event.getExtensions().get(0);
|
||||||
PayloadItem<?> payload = (PayloadItem) items.getItems().get(0);
|
PayloadItem<?> payload = (PayloadItem) items.getItems().get(0);
|
||||||
PublicKeysListElement listElement = (PublicKeysListElement) payload.getPayload();
|
PublicKeysListElement listElement = (PublicKeysListElement) payload.getPayload();
|
||||||
|
provider.storePublicKeysList(connection(), listElement, contact);
|
||||||
|
|
||||||
|
Set<OpenPgpV4Fingerprint> missingKeys = listElement.getMetadata().keySet();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
provider.storePublicKeysList(connection(), listElement, from.asBareJid());
|
provider.storePublicKeysList(connection(), listElement, contact);
|
||||||
|
missingKeys.removeAll(provider.availableOpenPgpPublicKeysFingerprints(contact));
|
||||||
|
for (OpenPgpV4Fingerprint missing : missingKeys) {
|
||||||
|
try {
|
||||||
|
PubkeyElement pubkeyElement = fetchPubkey(connection(), contact, missing);
|
||||||
|
provider.storePublicKey(contact, missing, pubkeyElement);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.log(Level.WARNING, "Error processing OpenPGP metadata update from " + from + ".", e);
|
LOGGER.log(Level.WARNING, "Error fetching missing OpenPGP key " + missing.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Error processing OpenPGP metadata update from " + contact + ".", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, "ProcessOXMetadata");
|
}, "ProcessOXMetadata");
|
||||||
|
|
|
@ -18,18 +18,32 @@ package org.jivesoftware.smackx.ox;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.ox.element.CryptElement;
|
||||||
import org.jivesoftware.smackx.ox.element.OpenPgpContentElement;
|
import org.jivesoftware.smackx.ox.element.OpenPgpContentElement;
|
||||||
|
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SignElement;
|
import org.jivesoftware.smackx.ox.element.SignElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SigncryptElement;
|
import org.jivesoftware.smackx.ox.element.SigncryptElement;
|
||||||
import org.jivesoftware.smackx.ox.provider.OpenPgpContentElementProvider;
|
import org.jivesoftware.smackx.ox.provider.OpenPgpContentElementProvider;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class embodies a decrypted {@link OpenPgpElement}.
|
||||||
|
*/
|
||||||
public class OpenPgpMessage {
|
public class OpenPgpMessage {
|
||||||
|
|
||||||
public enum State {
|
public enum State {
|
||||||
|
/**
|
||||||
|
* Represents a {@link SigncryptElement}.
|
||||||
|
*/
|
||||||
signcrypt,
|
signcrypt,
|
||||||
|
/**
|
||||||
|
* Represents a {@link SignElement}.
|
||||||
|
*/
|
||||||
sign,
|
sign,
|
||||||
|
/**
|
||||||
|
* Represents a {@link CryptElement}.
|
||||||
|
*/
|
||||||
crypt,
|
crypt,
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
@ -39,11 +53,26 @@ public class OpenPgpMessage {
|
||||||
|
|
||||||
private OpenPgpContentElement openPgpContentElement;
|
private OpenPgpContentElement openPgpContentElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param state state of the content element.
|
||||||
|
* @param content XML representation of the decrypted {@link OpenPgpContentElement}.
|
||||||
|
*/
|
||||||
public OpenPgpMessage(State state, String content) {
|
public OpenPgpMessage(State state, String content) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.element = content;
|
this.element = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the decrypted {@link OpenPgpContentElement} of this message.
|
||||||
|
* To determine, whether the element is a {@link SignElement}, {@link CryptElement} or {@link SigncryptElement},
|
||||||
|
* please consult {@link #getState()}.
|
||||||
|
*
|
||||||
|
* @return {@link OpenPgpContentElement}
|
||||||
|
* @throws XmlPullParserException if the parser encounters an error.
|
||||||
|
* @throws IOException if the parser encounters an error.
|
||||||
|
*/
|
||||||
public OpenPgpContentElement getOpenPgpContentElement() throws XmlPullParserException, IOException {
|
public OpenPgpContentElement getOpenPgpContentElement() throws XmlPullParserException, IOException {
|
||||||
ensureOpenPgpContentElementSet();
|
ensureOpenPgpContentElementSet();
|
||||||
|
|
||||||
|
@ -59,15 +88,27 @@ public class OpenPgpMessage {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine the state of the content element.
|
||||||
if (openPgpContentElement instanceof SigncryptElement) {
|
if (openPgpContentElement instanceof SigncryptElement) {
|
||||||
state = State.signcrypt;
|
state = State.signcrypt;
|
||||||
} else if (openPgpContentElement instanceof SignElement) {
|
} else if (openPgpContentElement instanceof SignElement) {
|
||||||
state = State.sign;
|
state = State.sign;
|
||||||
} else {
|
} else if (openPgpContentElement instanceof CryptElement) {
|
||||||
state = State.crypt;
|
state = State.crypt;
|
||||||
|
} else {
|
||||||
|
throw new AssertionError("OpenPgpContentElement is neither a SignElement, " +
|
||||||
|
"CryptElement nor a SignCryptElement.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the state of the message. This value determines, whether the message was a {@link SignElement},
|
||||||
|
* {@link CryptElement} or {@link SigncryptElement}.
|
||||||
|
*
|
||||||
|
* @return state of the content element.
|
||||||
|
* @throws IOException if the parser encounters an error.
|
||||||
|
* @throws XmlPullParserException if the parser encounters and error.
|
||||||
|
*/
|
||||||
public State getState() throws IOException, XmlPullParserException {
|
public State getState() throws IOException, XmlPullParserException {
|
||||||
ensureOpenPgpContentElementSet();
|
ensureOpenPgpContentElementSet();
|
||||||
return state;
|
return state;
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2018 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package org.jivesoftware.smackx.ox;
|
package org.jivesoftware.smackx.ox;
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
@ -10,10 +26,10 @@ import org.jivesoftware.smackx.ox.element.PubkeyElement;
|
||||||
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SigncryptElement;
|
import org.jivesoftware.smackx.ox.element.SigncryptElement;
|
||||||
import org.jivesoftware.smackx.ox.exception.CorruptedOpenPgpKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
||||||
|
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
|
||||||
|
@ -60,9 +76,10 @@ public interface OpenPgpStore {
|
||||||
*
|
*
|
||||||
* @param contact contact.
|
* @param contact contact.
|
||||||
* @return list of contacts locally available public keys.
|
* @return list of contacts locally available public keys.
|
||||||
|
* @throws SmackOpenPgpException if something goes wrong
|
||||||
*/
|
*/
|
||||||
Set<OpenPgpV4Fingerprint> availableOpenPgpPublicKeysFingerprints(BareJid contact)
|
Set<OpenPgpV4Fingerprint> availableOpenPgpPublicKeysFingerprints(BareJid contact)
|
||||||
throws CorruptedOpenPgpKeyException;
|
throws SmackOpenPgpException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store incoming update to the OpenPGP metadata node in persistent storage.
|
* Store incoming update to the OpenPGP metadata node in persistent storage.
|
||||||
|
@ -81,20 +98,21 @@ public interface OpenPgpStore {
|
||||||
* @throws NoSuchAlgorithmException if a Hash algorithm is not available
|
* @throws NoSuchAlgorithmException if a Hash algorithm is not available
|
||||||
* @throws NoSuchProviderException id no suitable cryptographic provider (for example BouncyCastleProvider)
|
* @throws NoSuchProviderException id no suitable cryptographic provider (for example BouncyCastleProvider)
|
||||||
* is registered.
|
* is registered.
|
||||||
* @throws CorruptedOpenPgpKeyException if the generated key cannot be added to the keyring for some reason.
|
* @throws SmackOpenPgpException if the generated key cannot be added to the keyring for some reason.
|
||||||
*/
|
*/
|
||||||
OpenPgpV4Fingerprint createOpenPgpKeyPair()
|
OpenPgpV4Fingerprint createOpenPgpKeyPair()
|
||||||
throws NoSuchAlgorithmException, NoSuchProviderException, CorruptedOpenPgpKeyException;
|
throws NoSuchAlgorithmException, NoSuchProviderException, SmackOpenPgpException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link PubkeyElement} which contains our exported OpenPGP public key.
|
* Create a {@link PubkeyElement} which contains our exported OpenPGP public key.
|
||||||
* The element can for example be published.
|
* The element can for example be published.
|
||||||
*
|
*
|
||||||
* @return {@link PubkeyElement} containing our public key.
|
* @return {@link PubkeyElement} containing our public key.
|
||||||
* @throws CorruptedOpenPgpKeyException if our public key can for some reason not be serialized.
|
* @throws MissingOpenPgpPublicKeyException if we have no OpenPGP key pair.
|
||||||
|
* @throws SmackOpenPgpException if something goes wrong.
|
||||||
*/
|
*/
|
||||||
PubkeyElement createPubkeyElement(OpenPgpV4Fingerprint fingerprint)
|
PubkeyElement createPubkeyElement(OpenPgpV4Fingerprint fingerprint)
|
||||||
throws MissingOpenPgpPublicKeyException, CorruptedOpenPgpKeyException;
|
throws SmackOpenPgpException, MissingOpenPgpPublicKeyException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process an incoming {@link PubkeyElement} of a contact or ourselves.
|
* Process an incoming {@link PubkeyElement} of a contact or ourselves.
|
||||||
|
@ -103,23 +121,23 @@ public interface OpenPgpStore {
|
||||||
* @param owner owner of the OpenPGP public key contained in the {@link PubkeyElement}.
|
* @param owner owner of the OpenPGP public key contained in the {@link PubkeyElement}.
|
||||||
* @param fingerprint {@link OpenPgpV4Fingerprint} of the key.
|
* @param fingerprint {@link OpenPgpV4Fingerprint} of the key.
|
||||||
* @param element {@link PubkeyElement} which presumably contains the public key of the {@code owner}.
|
* @param element {@link PubkeyElement} which presumably contains the public key of the {@code owner}.
|
||||||
* @throws CorruptedOpenPgpKeyException if the key found in the {@link PubkeyElement}
|
* @throws SmackOpenPgpException if the key found in the {@link PubkeyElement}
|
||||||
* can not be deserialized or imported.
|
* can not be deserialized or imported.
|
||||||
*/
|
*/
|
||||||
void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element)
|
void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element)
|
||||||
throws CorruptedOpenPgpKeyException;
|
throws SmackOpenPgpException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an encrypted backup of our secret keys.
|
* Create an encrypted backup of our secret keys.
|
||||||
*
|
*
|
||||||
* @param fingerprints {@link Set} of IDs of the keys that will be included in the backup.
|
* @param fingerprints {@link Set} of IDs of the keys that will be included in the backup.
|
||||||
* @param password password that is used to symmetrically encrypt the backup.
|
* @param password password that is used to symmetrically encrypt the backup.
|
||||||
* @return {@link SigncryptElement}.
|
* @return {@link SigncryptElement} containing the selected encrypted secret keys.
|
||||||
* @throws MissingOpenPgpKeyPairException if we don't have an OpenPGP key available.
|
* @throws MissingOpenPgpKeyPairException if we don't have an OpenPGP key available.
|
||||||
* @throws CorruptedOpenPgpKeyException if for some reason the key pair cannot be serialized.
|
* @throws SmackOpenPgpException if for some reason the key pair cannot be serialized.
|
||||||
*/
|
*/
|
||||||
SecretkeyElement createSecretkeyElement(Set<OpenPgpV4Fingerprint> fingerprints, String password)
|
SecretkeyElement createSecretkeyElement(Set<OpenPgpV4Fingerprint> fingerprints, String password)
|
||||||
throws MissingOpenPgpKeyPairException, CorruptedOpenPgpKeyException;
|
throws MissingOpenPgpKeyPairException, SmackOpenPgpException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrypt a secret key backup and restore the key from it.
|
* Decrypt a secret key backup and restore the key from it.
|
||||||
|
@ -127,9 +145,10 @@ public interface OpenPgpStore {
|
||||||
* @param secretkeyElement {@link SecretkeyElement} containing the backup.
|
* @param secretkeyElement {@link SecretkeyElement} containing the backup.
|
||||||
* @param password password to decrypt the backup.
|
* @param password password to decrypt the backup.
|
||||||
* @param callback {@link SecretKeyRestoreSelectionCallback} to let the user decide which key to restore.
|
* @param callback {@link SecretKeyRestoreSelectionCallback} to let the user decide which key to restore.
|
||||||
* @throws CorruptedOpenPgpKeyException if the selected key is corrupted and cannot be restored.
|
* @throws SmackOpenPgpException if the selected key is corrupted and cannot be restored or our key ring
|
||||||
|
* is corrupted.
|
||||||
* @throws InvalidBackupCodeException if the user provided backup code is invalid.
|
* @throws InvalidBackupCodeException if the user provided backup code is invalid.
|
||||||
*/
|
*/
|
||||||
void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, String password, SecretKeyRestoreSelectionCallback callback)
|
void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, String password, SecretKeyRestoreSelectionCallback callback)
|
||||||
throws CorruptedOpenPgpKeyException, InvalidBackupCodeException;
|
throws SmackOpenPgpException, InvalidBackupCodeException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,6 @@ import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
import org.jivesoftware.smackx.ox.element.PubkeyElement;
|
import org.jivesoftware.smackx.ox.element.PubkeyElement;
|
||||||
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
||||||
import org.jivesoftware.smackx.ox.exception.CorruptedOpenPgpKeyException;
|
|
||||||
import org.jivesoftware.smackx.pubsub.AccessModel;
|
import org.jivesoftware.smackx.pubsub.AccessModel;
|
||||||
import org.jivesoftware.smackx.pubsub.ConfigureForm;
|
import org.jivesoftware.smackx.pubsub.ConfigureForm;
|
||||||
import org.jivesoftware.smackx.pubsub.Item;
|
import org.jivesoftware.smackx.pubsub.Item;
|
||||||
|
@ -91,8 +90,6 @@ public class PubSubDelegate {
|
||||||
*
|
*
|
||||||
* @see <a href="https://xmpp.org/extensions/xep-0373.html#annoucning-pubkey">XEP-0373 §4.1</a>
|
* @see <a href="https://xmpp.org/extensions/xep-0373.html#annoucning-pubkey">XEP-0373 §4.1</a>
|
||||||
*
|
*
|
||||||
* @throws CorruptedOpenPgpKeyException if our OpenPGP key is corrupted and for that reason cannot
|
|
||||||
* be serialized.
|
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
* @throws PubSubException.NotALeafNodeException
|
* @throws PubSubException.NotALeafNodeException
|
||||||
* @throws XMPPException.XMPPErrorException
|
* @throws XMPPException.XMPPErrorException
|
||||||
|
@ -100,7 +97,7 @@ public class PubSubDelegate {
|
||||||
* @throws SmackException.NoResponseException
|
* @throws SmackException.NoResponseException
|
||||||
*/
|
*/
|
||||||
public static void publishPublicKey(XMPPConnection connection, PubkeyElement pubkeyElement, OpenPgpV4Fingerprint fingerprint)
|
public static void publishPublicKey(XMPPConnection connection, PubkeyElement pubkeyElement, OpenPgpV4Fingerprint fingerprint)
|
||||||
throws CorruptedOpenPgpKeyException, InterruptedException, PubSubException.NotALeafNodeException,
|
throws InterruptedException, PubSubException.NotALeafNodeException,
|
||||||
XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
|
XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
|
|
||||||
String keyNodeName = PEP_NODE_PUBLIC_KEY(fingerprint);
|
String keyNodeName = PEP_NODE_PUBLIC_KEY(fingerprint);
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2018 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package org.jivesoftware.smackx.ox;
|
package org.jivesoftware.smackx.ox;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2018 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package org.jivesoftware.smackx.ox.callback;
|
package org.jivesoftware.smackx.ox.callback;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
|
@ -16,15 +16,15 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox.exception;
|
package org.jivesoftware.smackx.ox.exception;
|
||||||
|
|
||||||
public class CorruptedOpenPgpKeyException extends Exception {
|
public class SmackOpenPgpException extends Exception {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
public CorruptedOpenPgpKeyException() {
|
public SmackOpenPgpException(Throwable cause) {
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CorruptedOpenPgpKeyException(Exception cause) {
|
|
||||||
super(cause);
|
super(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SmackOpenPgpException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2018 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.jivesoftware.smackx.ox.listener;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
import org.jivesoftware.smackx.ox.OpenPgpEncryptedChat;
|
||||||
|
import org.jivesoftware.smackx.ox.element.SigncryptElement;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
|
||||||
|
public interface OXEncryptedChatMessageListener {
|
||||||
|
|
||||||
|
void newIncomingEncryptedMessage(EntityBareJid from,
|
||||||
|
Message originalMessage,
|
||||||
|
SigncryptElement decryptedPayload,
|
||||||
|
OpenPgpEncryptedChat chat);
|
||||||
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox;
|
package org.jivesoftware.smackx.ox.listener;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.element.CryptElement;
|
import org.jivesoftware.smackx.ox.element.CryptElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SignElement;
|
import org.jivesoftware.smackx.ox.element.SignElement;
|
||||||
|
@ -22,7 +22,7 @@ import org.jivesoftware.smackx.ox.element.SigncryptElement;
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
|
||||||
public interface OpenPgpMessageListener {
|
interface OpenPgpMessageListener {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method gets called whenever we received and successfully decrypted/verified an encrypted, signed message.
|
* This method gets called whenever we received and successfully decrypted/verified an encrypted, signed message.
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Copyright 2015 Florian Schmaus
|
* Copyright 2017 Florian Schmaus.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,8 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Smacks implementation of XEP-0199: XMPP Ping.
|
* Listeners for XEP-0373: OpenPGP for XMPP.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ping;
|
package org.jivesoftware.smackx.ox.listener;
|
|
@ -25,7 +25,7 @@ import org.xmlpull.v1.XmlPullParser;
|
||||||
*/
|
*/
|
||||||
public class CryptElementProvider extends OpenPgpContentElementProvider<CryptElement> {
|
public class CryptElementProvider extends OpenPgpContentElementProvider<CryptElement> {
|
||||||
|
|
||||||
public static final CryptElementProvider TEST_INSTANCE = new CryptElementProvider();
|
public static final CryptElementProvider INSTANCE = new CryptElementProvider();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CryptElement parse(XmlPullParser parser, int initialDepth)
|
public CryptElement parse(XmlPullParser parser, int initialDepth)
|
||||||
|
|
|
@ -45,8 +45,8 @@ import org.xmlpull.v1.XmlPullParser;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract {@link ExtensionElementProvider} implementation for the also abstract
|
* Abstract {@link ExtensionElementProvider} implementation for the also abstract {@link OpenPgpContentElement}.
|
||||||
* {@link OpenPgpContentElement}.
|
*
|
||||||
* @param <O> Specialized subclass of {@link OpenPgpContentElement}.
|
* @param <O> Specialized subclass of {@link OpenPgpContentElement}.
|
||||||
*/
|
*/
|
||||||
public abstract class OpenPgpContentElementProvider<O extends OpenPgpContentElement> extends ExtensionElementProvider<O> {
|
public abstract class OpenPgpContentElementProvider<O extends OpenPgpContentElement> extends ExtensionElementProvider<O> {
|
||||||
|
@ -64,12 +64,13 @@ public abstract class OpenPgpContentElementProvider<O extends OpenPgpContentElem
|
||||||
try {
|
try {
|
||||||
switch (parser.getName()) {
|
switch (parser.getName()) {
|
||||||
case SigncryptElement.ELEMENT:
|
case SigncryptElement.ELEMENT:
|
||||||
return SigncryptElementProvider.TEST_INSTANCE.parse(parser);
|
return SigncryptElementProvider.INSTANCE.parse(parser);
|
||||||
case SignElement.ELEMENT:
|
case SignElement.ELEMENT:
|
||||||
return SignElementProvider.TEST_INSTANCE.parse(parser);
|
return SignElementProvider.INSTANCE.parse(parser);
|
||||||
case CryptElement.ELEMENT:
|
case CryptElement.ELEMENT:
|
||||||
return CryptElementProvider.TEST_INSTANCE.parse(parser);
|
return CryptElementProvider.INSTANCE.parse(parser);
|
||||||
default: return null;
|
default: throw new XmlPullParserException("Expected <crypt/>, <sign/> or <signcrypt/> element, " +
|
||||||
|
"but got neither of them.");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new XmlPullParserException(e.getMessage());
|
throw new XmlPullParserException(e.getMessage());
|
||||||
|
|
|
@ -29,7 +29,7 @@ import org.xmlpull.v1.XmlPullParser;
|
||||||
public class SignElementProvider extends OpenPgpContentElementProvider<SignElement> {
|
public class SignElementProvider extends OpenPgpContentElementProvider<SignElement> {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(SigncryptElementProvider.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(SigncryptElementProvider.class.getName());
|
||||||
public static final SignElementProvider TEST_INSTANCE = new SignElementProvider();
|
public static final SignElementProvider INSTANCE = new SignElementProvider();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SignElement parse(XmlPullParser parser, int initialDepth) throws Exception {
|
public SignElement parse(XmlPullParser parser, int initialDepth) throws Exception {
|
||||||
|
|
|
@ -25,7 +25,7 @@ import org.xmlpull.v1.XmlPullParser;
|
||||||
*/
|
*/
|
||||||
public class SigncryptElementProvider extends OpenPgpContentElementProvider<SigncryptElement> {
|
public class SigncryptElementProvider extends OpenPgpContentElementProvider<SigncryptElement> {
|
||||||
|
|
||||||
public static final SigncryptElementProvider TEST_INSTANCE = new SigncryptElementProvider();
|
public static final SigncryptElementProvider INSTANCE = new SigncryptElementProvider();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SigncryptElement parse(XmlPullParser parser, int initialDepth) throws Exception {
|
public SigncryptElement parse(XmlPullParser parser, int initialDepth) throws Exception {
|
||||||
|
|
|
@ -104,7 +104,7 @@ public class OpenPgpElementTest extends SmackTestSuite {
|
||||||
assertXMLEqual(expected, element.toXML(null).toString());
|
assertXMLEqual(expected, element.toXML(null).toString());
|
||||||
|
|
||||||
XmlPullParser parser = TestUtils.getParser(expected);
|
XmlPullParser parser = TestUtils.getParser(expected);
|
||||||
SignElement parsed = SignElementProvider.TEST_INSTANCE.parse(parser);
|
SignElement parsed = SignElementProvider.INSTANCE.parse(parser);
|
||||||
|
|
||||||
assertEquals(element.getTimestamp(), parsed.getTimestamp());
|
assertEquals(element.getTimestamp(), parsed.getTimestamp());
|
||||||
assertEquals(element.getTo(), parsed.getTo());
|
assertEquals(element.getTo(), parsed.getTo());
|
||||||
|
@ -134,7 +134,7 @@ public class OpenPgpElementTest extends SmackTestSuite {
|
||||||
assertXMLEqual(expected, element.toXML(null).toString());
|
assertXMLEqual(expected, element.toXML(null).toString());
|
||||||
|
|
||||||
XmlPullParser parser = TestUtils.getParser(expected);
|
XmlPullParser parser = TestUtils.getParser(expected);
|
||||||
CryptElement parsed = CryptElementProvider.TEST_INSTANCE.parse(parser);
|
CryptElement parsed = CryptElementProvider.INSTANCE.parse(parser);
|
||||||
|
|
||||||
assertEquals(element.getTimestamp(), parsed.getTimestamp());
|
assertEquals(element.getTimestamp(), parsed.getTimestamp());
|
||||||
assertEquals(element.getTo(), parsed.getTo());
|
assertEquals(element.getTo(), parsed.getTo());
|
||||||
|
@ -164,7 +164,7 @@ public class OpenPgpElementTest extends SmackTestSuite {
|
||||||
assertXMLEqual(expected, element.toXML(null).toString());
|
assertXMLEqual(expected, element.toXML(null).toString());
|
||||||
|
|
||||||
XmlPullParser parser = TestUtils.getParser(expected);
|
XmlPullParser parser = TestUtils.getParser(expected);
|
||||||
SigncryptElement parsed = SigncryptElementProvider.TEST_INSTANCE.parse(parser);
|
SigncryptElement parsed = SigncryptElementProvider.INSTANCE.parse(parser);
|
||||||
|
|
||||||
assertEquals(element.getTimestamp(), parsed.getTimestamp());
|
assertEquals(element.getTimestamp(), parsed.getTimestamp());
|
||||||
assertEquals(element.getTo(), parsed.getTo());
|
assertEquals(element.getTo(), parsed.getTo());
|
||||||
|
|
Loading…
Reference in a new issue