mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-23 20:42:06 +01:00
Get rid of smack-openpgp-bouncycastle
This commit is contained in:
parent
15eee8e65f
commit
06dbb37a0e
54 changed files with 218 additions and 2622 deletions
|
@ -79,6 +79,7 @@ allprojects {
|
||||||
':smack-experimental',
|
':smack-experimental',
|
||||||
':smack-omemo',
|
':smack-omemo',
|
||||||
':smack-omemo-signal',
|
':smack-omemo-signal',
|
||||||
|
':smack-openpgp',
|
||||||
].collect{ project(it) }
|
].collect{ project(it) }
|
||||||
androidBootClasspathProjects = [
|
androidBootClasspathProjects = [
|
||||||
':smack-android',
|
':smack-android',
|
||||||
|
|
|
@ -27,6 +27,5 @@ include 'smack-core',
|
||||||
'smack-omemo-signal',
|
'smack-omemo-signal',
|
||||||
'smack-omemo-signal-integration-test',
|
'smack-omemo-signal-integration-test',
|
||||||
'smack-repl',
|
'smack-repl',
|
||||||
'smack-openpgp',
|
'smack-openpgp'
|
||||||
'smack-openpgp-bouncycastle'
|
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ dependencies {
|
||||||
compile project(':smack-experimental')
|
compile project(':smack-experimental')
|
||||||
compile project(':smack-omemo')
|
compile project(':smack-omemo')
|
||||||
compile project(':smack-openpgp')
|
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,13 +0,0 @@
|
||||||
description = """\
|
|
||||||
Smack API for OpenPGP using Bouncycastle."""
|
|
||||||
|
|
||||||
// Note that the test dependencies (junit, …) are inferred from the
|
|
||||||
// sourceSet.test of the core subproject
|
|
||||||
dependencies {
|
|
||||||
compile project(':smack-core')
|
|
||||||
compile project(':smack-openpgp')
|
|
||||||
compile 'org.pgpainless:pgpainless:0.1-SNAPSHOT'
|
|
||||||
testCompile project(path: ":smack-core", configuration: "testRuntime")
|
|
||||||
testCompile project(path: ":smack-core", configuration: "archives")
|
|
||||||
testCompile project(path: ":smack-openpgp", configuration: "testRuntime")
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
|
||||||
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector;
|
|
||||||
|
|
||||||
public abstract class AbstractPainlessOpenPgpStore implements PainlessOpenPgpStore {
|
|
||||||
|
|
||||||
private final BcKeyFingerprintCalculator fingerprintCalculator = new BcKeyFingerprintCalculator();
|
|
||||||
|
|
||||||
private final Map<BareJid, PGPPublicKeyRingCollection> publicKeyRings = new HashMap<>();
|
|
||||||
private final Map<BareJid, PGPSecretKeyRingCollection> secretKeyRings = new HashMap<>();
|
|
||||||
private OpenPgpV4Fingerprint primaryKeyFingerprint = null;
|
|
||||||
private final SecretKeyRingProtector secretKeyRingProtector;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OpenPgpV4Fingerprint getSigningKeyPairFingerprint() {
|
|
||||||
return primaryKeyFingerprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSigningKeyPairFingerprint(OpenPgpV4Fingerprint fingerprint) {
|
|
||||||
this.primaryKeyFingerprint = fingerprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SecretKeyRingProtector getSecretKeyProtector() {
|
|
||||||
return secretKeyRingProtector;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AbstractPainlessOpenPgpStore(SecretKeyRingProtector secretKeyRingProtector) {
|
|
||||||
this.secretKeyRingProtector = secretKeyRingProtector;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PGPPublicKeyRingCollection getPublicKeyRings(BareJid owner) throws IOException, PGPException {
|
|
||||||
PGPPublicKeyRingCollection keyRing = publicKeyRings.get(owner);
|
|
||||||
if (keyRing != null) {
|
|
||||||
return keyRing;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] bytes = loadPublicKeyRingBytes(owner);
|
|
||||||
if (bytes == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
keyRing = new PGPPublicKeyRingCollection(bytes, fingerprintCalculator);
|
|
||||||
|
|
||||||
publicKeyRings.put(owner, keyRing);
|
|
||||||
|
|
||||||
return keyRing;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PGPSecretKeyRingCollection getSecretKeyRings(BareJid owner) throws IOException, PGPException {
|
|
||||||
PGPSecretKeyRingCollection keyRing = secretKeyRings.get(owner);
|
|
||||||
if (keyRing != null) {
|
|
||||||
return keyRing;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] bytes = loadSecretKeyRingBytes(owner);
|
|
||||||
if (bytes == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
keyRing = new PGPSecretKeyRingCollection(bytes, fingerprintCalculator);
|
|
||||||
|
|
||||||
secretKeyRings.put(owner, keyRing);
|
|
||||||
|
|
||||||
return keyRing;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void storePublicKeyRing(BareJid owner, PGPPublicKeyRingCollection publicKeys) throws IOException {
|
|
||||||
publicKeyRings.put(owner, publicKeys);
|
|
||||||
storePublicKeyRingBytes(owner, publicKeys.getEncoded());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void storeSecretKeyRing(BareJid owner, PGPSecretKeyRingCollection secretKeys) throws IOException {
|
|
||||||
secretKeyRings.put(owner, secretKeys);
|
|
||||||
storeSecretKeyRingBytes(owner, secretKeys.getEncoded());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,440 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileFilter;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.MultiMap;
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
|
||||||
import org.bouncycastle.util.io.Streams;
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
|
||||||
import org.jxmpp.util.XmppDateTime;
|
|
||||||
import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector;
|
|
||||||
|
|
||||||
public class FileBasedPainlessOpenPgpStore extends AbstractPainlessOpenPgpStore {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(FileBasedPainlessOpenPgpStore.class.getName());
|
|
||||||
|
|
||||||
private final File basePath;
|
|
||||||
|
|
||||||
public FileBasedPainlessOpenPgpStore(File base, SecretKeyRingProtector secretKeyRingProtector) {
|
|
||||||
super(secretKeyRingProtector);
|
|
||||||
this.basePath = base;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] loadPublicKeyRingBytes(BareJid owner) {
|
|
||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
|
||||||
FileInputStream inputStream = null;
|
|
||||||
byte[] bytes = null;
|
|
||||||
try {
|
|
||||||
inputStream = new FileInputStream(getContactsPubringFile(owner, false));
|
|
||||||
Streams.pipeAll(inputStream, buffer);
|
|
||||||
inputStream.close();
|
|
||||||
bytes = buffer.toByteArray();
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
LOGGER.log(Level.FINE, "Pubring of user " + owner.toString() + " does not exist.");
|
|
||||||
return null;
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (inputStream != null) {
|
|
||||||
try {
|
|
||||||
inputStream.close();
|
|
||||||
} catch (IOException ee) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not close InputStream:", ee);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] loadSecretKeyRingBytes(BareJid owner) {
|
|
||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
|
||||||
FileInputStream inputStream = null;
|
|
||||||
byte[] bytes = null;
|
|
||||||
try {
|
|
||||||
inputStream = new FileInputStream(getContactsSecringFile(owner, false));
|
|
||||||
Streams.pipeAll(inputStream, buffer);
|
|
||||||
inputStream.close();
|
|
||||||
bytes = buffer.toByteArray();
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
LOGGER.log(Level.FINE, "Secring of user " + owner.toString() + " does not exist.");
|
|
||||||
return null;
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (inputStream != null) {
|
|
||||||
try {
|
|
||||||
inputStream.close();
|
|
||||||
} catch (IOException ee) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not close InputStream:", ee);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void storePublicKeyRingBytes(BareJid owner, byte[] bytes) {
|
|
||||||
FileOutputStream outputStream = null;
|
|
||||||
try {
|
|
||||||
outputStream = new FileOutputStream(getContactsPubringFile(owner, true));
|
|
||||||
outputStream.write(bytes);
|
|
||||||
outputStream.close();
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
throw new AssertionError("File does not exist, even though it MUST exist at this point.", e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (outputStream != null) {
|
|
||||||
try {
|
|
||||||
outputStream.close();
|
|
||||||
} catch (IOException ee) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not close OutputStream:", ee);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void storeSecretKeyRingBytes(BareJid owner, byte[] bytes) {
|
|
||||||
FileOutputStream outputStream = null;
|
|
||||||
try {
|
|
||||||
outputStream = new FileOutputStream(getContactsSecringFile(owner, true));
|
|
||||||
outputStream.write(bytes);
|
|
||||||
outputStream.close();
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
throw new AssertionError("File does not exist, even though it MUST exist at this point.", e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (outputStream != null) {
|
|
||||||
try {
|
|
||||||
outputStream.close();
|
|
||||||
} catch (IOException ee) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not close OutputStream:", ee);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<OpenPgpV4Fingerprint> getAvailableKeyPairFingerprints(BareJid owner) throws SmackOpenPgpException {
|
|
||||||
Set<OpenPgpV4Fingerprint> fingerprints = new HashSet<>();
|
|
||||||
try {
|
|
||||||
PGPSecretKeyRingCollection secretKeys = getSecretKeyRings(owner);
|
|
||||||
for (PGPSecretKeyRing s : secretKeys != null ? secretKeys : Collections.<PGPSecretKeyRing>emptySet()) {
|
|
||||||
fingerprints.add(PainlessOpenPgpProvider.getFingerprint(s.getPublicKey()));
|
|
||||||
}
|
|
||||||
} catch (IOException | PGPException e) {
|
|
||||||
throw new SmackOpenPgpException("Could not get available key pair fingerprints.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fingerprints;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<OpenPgpV4Fingerprint, Date> getAvailableKeysFingerprints(BareJid contact) throws SmackOpenPgpException {
|
|
||||||
Map<OpenPgpV4Fingerprint, Date> availableFingerprints = new HashMap<>();
|
|
||||||
try {
|
|
||||||
PGPPublicKeyRingCollection publicKeys = getPublicKeyRings(contact);
|
|
||||||
Set<OpenPgpV4Fingerprint> fingerprints = new HashSet<>();
|
|
||||||
for (PGPPublicKeyRing ring : publicKeys != null ? publicKeys : Collections.<PGPPublicKeyRing>emptySet()) {
|
|
||||||
OpenPgpV4Fingerprint fingerprint = PainlessOpenPgpProvider.getFingerprint(ring.getPublicKey());
|
|
||||||
fingerprints.add(fingerprint);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<OpenPgpV4Fingerprint, Date> announced = getAnnouncedKeysFingerprints(contact);
|
|
||||||
for (OpenPgpV4Fingerprint fingerprint : fingerprints) {
|
|
||||||
if (announced.containsKey(fingerprint)) {
|
|
||||||
availableFingerprints.put(fingerprint, announced.get(fingerprint));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (PGPException | IOException e) {
|
|
||||||
throw new SmackOpenPgpException("Could not read public keys of contact " + contact.toString(), e);
|
|
||||||
}
|
|
||||||
return availableFingerprints;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<OpenPgpV4Fingerprint, Date> getAnnouncedKeysFingerprints(BareJid contact) {
|
|
||||||
try {
|
|
||||||
File file = getContactsPubkeyAnnouncementFile(contact, false);
|
|
||||||
return loadFingerprintsAndDates(file);
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not read announced fingerprints of contact " + contact, e);
|
|
||||||
}
|
|
||||||
return new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAnnouncedKeysFingerprints(BareJid contact, Map<OpenPgpV4Fingerprint, Date> fingerprints) {
|
|
||||||
try {
|
|
||||||
File file = getContactsPubkeyAnnouncementFile(contact, true);
|
|
||||||
writeFingerprintsAndDates(fingerprints, file);
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not write announced fingerprints of " + contact.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<OpenPgpV4Fingerprint, Date> getPubkeysLastRevisions(BareJid owner) {
|
|
||||||
try {
|
|
||||||
return loadFingerprintsAndDates(getContactsPubkeyRevisionInfoFile(owner, false));
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not read revision dates of pubkeys of " + owner.toString(), e);
|
|
||||||
return new HashMap<>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<OpenPgpV4Fingerprint, Date> loadFingerprintsAndDates(File file) throws IOException {
|
|
||||||
Map<OpenPgpV4Fingerprint, Date> revisionDates = new HashMap<>();
|
|
||||||
BufferedReader reader = null;
|
|
||||||
try {
|
|
||||||
reader = Files.newBufferedReader(file.toPath(), Charset.forName("UTF-8"));
|
|
||||||
int lineNr = 0;
|
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
lineNr++;
|
|
||||||
line = line.trim();
|
|
||||||
if (line.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] split = line.split(" ");
|
|
||||||
if (split.length != 2) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(split[0]);
|
|
||||||
Date date = XmppDateTime.parseXEP0082Date(split[1]);
|
|
||||||
revisionDates.put(fingerprint, date);
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Encountered illegal line in file " +
|
|
||||||
file.getAbsolutePath() + ": " + lineNr, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reader.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not read fingerprints and dates from file " + file.getAbsolutePath(), e);
|
|
||||||
if (reader != null) {
|
|
||||||
reader.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return revisionDates;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void writeFingerprintsAndDates(Map<OpenPgpV4Fingerprint, Date> fingerprints, File file) throws IOException {
|
|
||||||
BufferedWriter writer = null;
|
|
||||||
try {
|
|
||||||
writer = Files.newBufferedWriter(file.toPath(), Charset.forName("UTF-8"));
|
|
||||||
for (OpenPgpV4Fingerprint fingerprint : fingerprints.keySet()) {
|
|
||||||
Date date = fingerprints.get(fingerprint);
|
|
||||||
String line = fingerprint.toString() + " " + (date != null ? XmppDateTime.formatXEP0082Date(date) : XmppDateTime.formatXEP0082Date(new Date()));
|
|
||||||
writer.write(line);
|
|
||||||
}
|
|
||||||
writer.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not write fingerprints and dates to file " + file.getAbsolutePath());
|
|
||||||
if (writer != null) {
|
|
||||||
writer.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setPubkeysLastRevision(BareJid owner, Map<OpenPgpV4Fingerprint, Date> revisionDates) {
|
|
||||||
try {
|
|
||||||
File file = getContactsPubkeyRevisionInfoFile(owner, true);
|
|
||||||
writeFingerprintsAndDates(revisionDates, file);
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not write last pubkey revisions of " + owner, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MultiMap<BareJid, OpenPgpV4Fingerprint> getAllContactsTrustedFingerprints() {
|
|
||||||
MultiMap<BareJid, OpenPgpV4Fingerprint> trustedFingerprints = new MultiMap<>();
|
|
||||||
try {
|
|
||||||
File contactsDir = getContactsPath(false);
|
|
||||||
if (!contactsDir.exists()) {
|
|
||||||
LOGGER.log(Level.FINE, "Contacts directory does not exists yet.");
|
|
||||||
return trustedFingerprints;
|
|
||||||
}
|
|
||||||
|
|
||||||
File[] subDirectories = contactsDir.listFiles(directoryFilter);
|
|
||||||
if (subDirectories == null) {
|
|
||||||
return trustedFingerprints;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (File contact : subDirectories) {
|
|
||||||
BareJid jid = JidCreate.bareFrom(contact.getName());
|
|
||||||
try {
|
|
||||||
PGPPublicKeyRingCollection publicKeyRings = getPublicKeyRings(jid);
|
|
||||||
for (PGPPublicKeyRing ring : publicKeyRings) {
|
|
||||||
OpenPgpV4Fingerprint fingerprint = PainlessOpenPgpProvider.getFingerprint(ring.getPublicKey());
|
|
||||||
trustedFingerprints.put(jid, fingerprint);
|
|
||||||
}
|
|
||||||
} catch (PGPException e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not read public key ring of " + jid, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not read contacts directory", e);
|
|
||||||
}
|
|
||||||
return trustedFingerprints;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getPublicKeyRingBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
|
|
||||||
throws MissingOpenPgpPublicKeyException {
|
|
||||||
try {
|
|
||||||
PGPPublicKeyRingCollection publicKeyRings = getPublicKeyRings(owner);
|
|
||||||
PGPPublicKeyRing ring = publicKeyRings.getPublicKeyRing(fingerprint.getKeyId());
|
|
||||||
if (ring != null) {
|
|
||||||
return ring.getEncoded(true);
|
|
||||||
} else {
|
|
||||||
throw new MissingOpenPgpPublicKeyException(owner, fingerprint);
|
|
||||||
}
|
|
||||||
} catch (IOException | PGPException e) {
|
|
||||||
throw new MissingOpenPgpPublicKeyException(owner, fingerprint, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getSecretKeyRingBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
|
|
||||||
throws MissingOpenPgpKeyPairException {
|
|
||||||
try {
|
|
||||||
PGPSecretKeyRingCollection secretKeyRings = getSecretKeyRings(owner);
|
|
||||||
PGPSecretKeyRing ring = secretKeyRings.getSecretKeyRing(fingerprint.getKeyId());
|
|
||||||
if (ring != null) {
|
|
||||||
return ring.getEncoded();
|
|
||||||
} else {
|
|
||||||
throw new MissingOpenPgpKeyPairException(owner, fingerprint);
|
|
||||||
}
|
|
||||||
} catch (IOException | PGPException e) {
|
|
||||||
throw new MissingOpenPgpKeyPairException(owner, fingerprint, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
####################################################################################################################
|
|
||||||
File System Hierarchy
|
|
||||||
####################################################################################################################
|
|
||||||
*/
|
|
||||||
|
|
||||||
private File getStorePath(boolean create)
|
|
||||||
throws IOException {
|
|
||||||
if (create && !basePath.exists()) {
|
|
||||||
createDirectoryOrThrow(basePath);
|
|
||||||
}
|
|
||||||
return basePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getContactsPath(boolean create) throws IOException {
|
|
||||||
File path = new File(getStorePath(create), "contacts");
|
|
||||||
if (create && !path.exists()) {
|
|
||||||
createDirectoryOrThrow(path);
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getContactsPath(BareJid owner, boolean create) throws IOException {
|
|
||||||
File path = new File(getContactsPath(create), owner.toString());
|
|
||||||
if (create && !path.exists()) {
|
|
||||||
createDirectoryOrThrow(path);
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getContactsPubringFile(BareJid owner, boolean create) throws IOException {
|
|
||||||
File file = new File(getContactsPath(owner, create), "pubring.pkr");
|
|
||||||
if (create && !file.exists()) {
|
|
||||||
createFileOrThrow(file);
|
|
||||||
}
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getContactsSecringFile(BareJid owner, boolean create) throws IOException {
|
|
||||||
File file = new File(getContactsPath(owner, create), "secring.skr");
|
|
||||||
if (create && !file.exists()) {
|
|
||||||
createFileOrThrow(file);
|
|
||||||
}
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getContactsPubkeyRevisionInfoFile(BareJid owner, boolean create) throws IOException {
|
|
||||||
File file = new File(getContactsPath(owner, create), "revisionDates.lst");
|
|
||||||
if (create && !file.exists()) {
|
|
||||||
createFileOrThrow(file);
|
|
||||||
}
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getContactsPubkeyAnnouncementFile(BareJid owner, boolean create) throws IOException {
|
|
||||||
File file = new File(getContactsPath(owner, create), "announcedKeys.lst");
|
|
||||||
if (create && !file.exists()) {
|
|
||||||
createFileOrThrow(file);
|
|
||||||
}
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void createDirectoryOrThrow(File dir) throws IOException {
|
|
||||||
if (!dir.mkdirs()) {
|
|
||||||
throw new IOException("Could not create directory \"" + dir.getAbsolutePath() + "\"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void createFileOrThrow(File file) throws IOException {
|
|
||||||
if (!file.createNewFile()) {
|
|
||||||
throw new IOException("Could not create file \"" + file.getAbsolutePath() + "\"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final FileFilter directoryFilter = new FileFilter() {
|
|
||||||
@Override
|
|
||||||
public boolean accept(File file) {
|
|
||||||
return file != null && file.isDirectory();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,452 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.MultiMap;
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpProvider;
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
import org.jivesoftware.smackx.ox.callback.backup.SmackMissingOpenPgpPublicKeyCallback;
|
|
||||||
import org.jivesoftware.smackx.ox.element.CryptElement;
|
|
||||||
import org.jivesoftware.smackx.ox.element.SignElement;
|
|
||||||
import org.jivesoftware.smackx.ox.element.SigncryptElement;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
|
||||||
import org.jivesoftware.smackx.ox.selection_strategy.BareJidUserId;
|
|
||||||
import org.jivesoftware.smackx.ox.util.DecryptedBytesAndMetadata;
|
|
||||||
import org.jivesoftware.smackx.ox.util.KeyBytesAndFingerprint;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
|
||||||
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
|
|
||||||
import org.bouncycastle.util.encoders.Hex;
|
|
||||||
import org.bouncycastle.util.io.Streams;
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
import org.pgpainless.pgpainless.PGPainless;
|
|
||||||
import org.pgpainless.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
|
||||||
import org.pgpainless.pgpainless.decryption_verification.DecryptionStream;
|
|
||||||
import org.pgpainless.pgpainless.decryption_verification.PainlessResult;
|
|
||||||
import org.pgpainless.pgpainless.key.generation.type.length.RsaLength;
|
|
||||||
import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector;
|
|
||||||
import org.pgpainless.pgpainless.util.BCUtil;
|
|
||||||
|
|
||||||
public class PainlessOpenPgpProvider implements OpenPgpProvider {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(PainlessOpenPgpProvider.class.getName());
|
|
||||||
|
|
||||||
private final PainlessOpenPgpStore store;
|
|
||||||
private final BareJid owner;
|
|
||||||
|
|
||||||
public PainlessOpenPgpProvider(BareJid owner, PainlessOpenPgpStore store) {
|
|
||||||
this.owner = owner;
|
|
||||||
this.store = store;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] signAndEncrypt(SigncryptElement element,
|
|
||||||
OpenPgpV4Fingerprint signingKey,
|
|
||||||
MultiMap<BareJid, OpenPgpV4Fingerprint> encryptionKeys)
|
|
||||||
throws MissingOpenPgpKeyPairException, MissingOpenPgpPublicKeyException, SmackOpenPgpException,
|
|
||||||
IOException {
|
|
||||||
|
|
||||||
PGPSecretKeyRing secretKeyRing;
|
|
||||||
SecretKeyRingProtector protector = getStore().getSecretKeyProtector();
|
|
||||||
|
|
||||||
try {
|
|
||||||
secretKeyRing = getStore()
|
|
||||||
.getSecretKeyRings(owner)
|
|
||||||
.getSecretKeyRing(
|
|
||||||
signingKey.getKeyId());
|
|
||||||
} catch (PGPException e) {
|
|
||||||
LOGGER.log(Level.INFO, "Could not get secret key with id " + Long.toHexString(signingKey.getKeyId()), e);
|
|
||||||
throw new MissingOpenPgpKeyPairException(owner, signingKey, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
MultiMap<BareJid, PGPPublicKeyRing> publicKeyRingMultiMap = new MultiMap<>();
|
|
||||||
for (BareJid jid : encryptionKeys.keySet()) {
|
|
||||||
try {
|
|
||||||
PGPPublicKeyRingCollection publicKeyRings = getStore().getPublicKeyRings(jid);
|
|
||||||
for (OpenPgpV4Fingerprint f : encryptionKeys.getAll(jid)) {
|
|
||||||
PGPPublicKeyRing ring = publicKeyRings.getPublicKeyRing(f.getKeyId());
|
|
||||||
if (ring != null) {
|
|
||||||
publicKeyRingMultiMap.put(jid, ring);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (PGPException e) {
|
|
||||||
LOGGER.log(Level.INFO, "Could get public keys of " + jid.toString());
|
|
||||||
throw new MissingOpenPgpPublicKeyException(owner, encryptionKeys.getFirst(jid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return signAndEncryptImpl(element, secretKeyRing, protector, publicKeyRingMultiMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] signAndEncryptImpl(SigncryptElement element,
|
|
||||||
PGPSecretKeyRing signingKey,
|
|
||||||
SecretKeyRingProtector secretKeyRingProtector,
|
|
||||||
MultiMap<BareJid, PGPPublicKeyRing> encryptionKeys)
|
|
||||||
throws SmackOpenPgpException, IOException {
|
|
||||||
InputStream fromPlain = element.toInputStream();
|
|
||||||
ByteArrayOutputStream encryptedBytes = new ByteArrayOutputStream();
|
|
||||||
|
|
||||||
OutputStream toEncrypted;
|
|
||||||
try {
|
|
||||||
toEncrypted = PGPainless.createEncryptor()
|
|
||||||
.onOutputStream(encryptedBytes)
|
|
||||||
.toRecipients(new ArrayList<>(encryptionKeys.values()).toArray(new PGPPublicKeyRing[] {}))
|
|
||||||
.usingSecureAlgorithms()
|
|
||||||
.signWith(secretKeyRingProtector, signingKey)
|
|
||||||
.noArmor();
|
|
||||||
} catch (PGPException | IOException e) {
|
|
||||||
throw new SmackOpenPgpException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Streams.pipeAll(fromPlain, toEncrypted);
|
|
||||||
toEncrypted.flush();
|
|
||||||
toEncrypted.close();
|
|
||||||
|
|
||||||
encryptedBytes.close();
|
|
||||||
|
|
||||||
return encryptedBytes.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] sign(SignElement element, OpenPgpV4Fingerprint signingKeyFingerprint)
|
|
||||||
throws MissingOpenPgpKeyPairException, IOException, SmackOpenPgpException {
|
|
||||||
PGPSecretKeyRing signingKeyRing;
|
|
||||||
try {
|
|
||||||
signingKeyRing = store.getSecretKeyRings(owner).getSecretKeyRing(signingKeyFingerprint.getKeyId());
|
|
||||||
} catch (PGPException e) {
|
|
||||||
throw new MissingOpenPgpKeyPairException(owner, signingKeyFingerprint, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return signImpl(element, signingKeyRing, store.getSecretKeyProtector());
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] signImpl(SignElement element, PGPSecretKeyRing signingKey, SecretKeyRingProtector secretKeyRingProtector)
|
|
||||||
throws IOException, SmackOpenPgpException {
|
|
||||||
ByteArrayOutputStream toSigned = new ByteArrayOutputStream();
|
|
||||||
OutputStream signer;
|
|
||||||
try {
|
|
||||||
signer = PGPainless.createEncryptor().onOutputStream(toSigned)
|
|
||||||
.doNotEncrypt()
|
|
||||||
.signWith(secretKeyRingProtector, signingKey)
|
|
||||||
.noArmor();
|
|
||||||
} catch (PGPException e) {
|
|
||||||
throw new SmackOpenPgpException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStream fromPlain = element.toInputStream();
|
|
||||||
Streams.pipeAll(fromPlain, signer);
|
|
||||||
|
|
||||||
fromPlain.close();
|
|
||||||
signer.close();
|
|
||||||
|
|
||||||
return toSigned.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] encrypt(CryptElement element, MultiMap<BareJid, OpenPgpV4Fingerprint> encryptionKeyFingerprints)
|
|
||||||
throws IOException, SmackOpenPgpException {
|
|
||||||
PGPPublicKeyRing[] allRecipientsKeys = getEncryptionKeys(encryptionKeyFingerprints);
|
|
||||||
return encryptImpl(element, allRecipientsKeys);
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] encryptImpl(CryptElement element, PGPPublicKeyRing[] encryptionKeys)
|
|
||||||
throws IOException, SmackOpenPgpException {
|
|
||||||
InputStream fromPlain = element.toInputStream();
|
|
||||||
ByteArrayOutputStream encrypted = new ByteArrayOutputStream();
|
|
||||||
OutputStream encryptor;
|
|
||||||
try {
|
|
||||||
encryptor = PGPainless.createEncryptor()
|
|
||||||
.onOutputStream(encrypted)
|
|
||||||
.toRecipients(encryptionKeys)
|
|
||||||
.usingSecureAlgorithms()
|
|
||||||
.doNotSign()
|
|
||||||
.noArmor();
|
|
||||||
} catch (PGPException e) {
|
|
||||||
throw new SmackOpenPgpException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Streams.pipeAll(fromPlain, encryptor);
|
|
||||||
fromPlain.close();
|
|
||||||
encryptor.close();
|
|
||||||
|
|
||||||
return encrypted.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DecryptedBytesAndMetadata decrypt(byte[] bytes, BareJid sender, final SmackMissingOpenPgpPublicKeyCallback missingPublicKeyCallback)
|
|
||||||
throws MissingOpenPgpKeyPairException, SmackOpenPgpException {
|
|
||||||
|
|
||||||
PGPSecretKeyRingCollection secretKeyRings;
|
|
||||||
try {
|
|
||||||
secretKeyRings = getStore().getSecretKeyRings(owner);
|
|
||||||
} catch (PGPException | IOException e) {
|
|
||||||
LOGGER.log(Level.INFO, "Could not get secret keys of user " + owner);
|
|
||||||
throw new MissingOpenPgpKeyPairException(owner, getStore().getSigningKeyPairFingerprint());
|
|
||||||
}
|
|
||||||
|
|
||||||
SecretKeyRingProtector protector = getStore().getSecretKeyProtector();
|
|
||||||
|
|
||||||
List<OpenPgpV4Fingerprint> trustedFingerprints = getStore().getAllContactsTrustedFingerprints().getAll(sender);
|
|
||||||
Set<Long> trustedKeyIds = new HashSet<>();
|
|
||||||
for (OpenPgpV4Fingerprint fingerprint : trustedFingerprints) {
|
|
||||||
trustedKeyIds.add(fingerprint.getKeyId());
|
|
||||||
}
|
|
||||||
|
|
||||||
PGPPublicKeyRingCollection publicKeyRings;
|
|
||||||
try {
|
|
||||||
publicKeyRings = getStore().getPublicKeyRings(sender);
|
|
||||||
} catch (PGPException | IOException e) {
|
|
||||||
LOGGER.log(Level.INFO, "Could not get public keys of sender " + sender.toString(), e);
|
|
||||||
if (missingPublicKeyCallback != null) {
|
|
||||||
// TODO: Handle missing key
|
|
||||||
}
|
|
||||||
throw new SmackOpenPgpException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterator<PGPPublicKeyRing> iterator = publicKeyRings.getKeyRings();
|
|
||||||
Set<PGPPublicKeyRing> trustedKeys = new HashSet<>();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
PGPPublicKeyRing ring = iterator.next();
|
|
||||||
if (trustedKeyIds.contains(ring.getPublicKey().getKeyID())) {
|
|
||||||
trustedKeys.add(ring);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return decryptImpl(bytes, secretKeyRings, protector, trustedKeys);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new SmackOpenPgpException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DecryptedBytesAndMetadata decryptImpl(byte[] bytes, PGPSecretKeyRingCollection decryptionKeys,
|
|
||||||
SecretKeyRingProtector protector,
|
|
||||||
Set<PGPPublicKeyRing> verificationKeys)
|
|
||||||
throws SmackOpenPgpException, IOException {
|
|
||||||
|
|
||||||
InputStream encryptedBytes = new ByteArrayInputStream(bytes);
|
|
||||||
ByteArrayOutputStream toPlain = new ByteArrayOutputStream();
|
|
||||||
DecryptionStream fromEncrypted;
|
|
||||||
try {
|
|
||||||
fromEncrypted = PGPainless.createDecryptor()
|
|
||||||
.onInputStream(encryptedBytes)
|
|
||||||
.decryptWith(protector, decryptionKeys)
|
|
||||||
.verifyWith(verificationKeys)
|
|
||||||
.ignoreMissingPublicKeys()
|
|
||||||
.build();
|
|
||||||
} catch (IOException | PGPException e) {
|
|
||||||
throw new SmackOpenPgpException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Streams.pipeAll(fromEncrypted, toPlain);
|
|
||||||
|
|
||||||
fromEncrypted.close();
|
|
||||||
toPlain.flush();
|
|
||||||
toPlain.close();
|
|
||||||
|
|
||||||
PainlessResult result = fromEncrypted.getResult();
|
|
||||||
return new DecryptedBytesAndMetadata(toPlain.toByteArray(),
|
|
||||||
result.getVerifiedSignatureKeyIds(),
|
|
||||||
result.getDecryptionKeyId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] symmetricallyEncryptWithPassword(byte[] bytes, String password)
|
|
||||||
throws SmackOpenPgpException, IOException {
|
|
||||||
try {
|
|
||||||
return PGPainless.encryptWithPassword(bytes, password.toCharArray(), SymmetricKeyAlgorithm.AES_256);
|
|
||||||
} catch (PGPException e) {
|
|
||||||
throw new SmackOpenPgpException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] symmetricallyDecryptWithPassword(byte[] bytes, String password)
|
|
||||||
throws SmackOpenPgpException, IOException {
|
|
||||||
try {
|
|
||||||
return PGPainless.decryptWithPassword(bytes, password.toCharArray());
|
|
||||||
} catch (PGPException e) {
|
|
||||||
throw new SmackOpenPgpException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PainlessOpenPgpStore getStore() {
|
|
||||||
return store;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PGPPublicKeyRing[] getEncryptionKeys(MultiMap<BareJid, OpenPgpV4Fingerprint> encryptionKeys)
|
|
||||||
throws IOException, SmackOpenPgpException {
|
|
||||||
Set<PGPPublicKeyRing> allRecipientsKeys = new HashSet<>();
|
|
||||||
|
|
||||||
for (BareJid recipient : encryptionKeys.keySet()) {
|
|
||||||
PGPPublicKeyRingCollection recipientsKeyRings;
|
|
||||||
try {
|
|
||||||
recipientsKeyRings = store.getPublicKeyRings(recipient);
|
|
||||||
for (OpenPgpV4Fingerprint fingerprint : encryptionKeys.getAll(recipient)) {
|
|
||||||
PGPPublicKeyRing ring = recipientsKeyRings.getPublicKeyRing(fingerprint.getKeyId());
|
|
||||||
if (ring != null) allRecipientsKeys.add(ring);
|
|
||||||
}
|
|
||||||
} catch (PGPException e) {
|
|
||||||
throw new SmackOpenPgpException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PGPPublicKeyRing[] allEncryptionKeys = new PGPPublicKeyRing[allRecipientsKeys.size()];
|
|
||||||
Iterator<PGPPublicKeyRing> iterator = allRecipientsKeys.iterator();
|
|
||||||
for (int i = 0; i < allEncryptionKeys.length; i++) {
|
|
||||||
allEncryptionKeys[i] = iterator.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
return allEncryptionKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
private PGPSecretKeyRing getSigningKey(OpenPgpV4Fingerprint signingKey)
|
|
||||||
throws IOException, MissingOpenPgpKeyPairException {
|
|
||||||
PGPSecretKeyRing signingKeyRing;
|
|
||||||
try {
|
|
||||||
signingKeyRing = store.getSecretKeyRings(owner).getSecretKeyRing(signingKey.getKeyId());
|
|
||||||
} catch (PGPException e) {
|
|
||||||
throw new MissingOpenPgpKeyPairException(owner, signingKey, e);
|
|
||||||
}
|
|
||||||
return signingKeyRing;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KeyBytesAndFingerprint generateOpenPgpKeyPair(BareJid owner)
|
|
||||||
throws SmackOpenPgpException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
|
|
||||||
NoSuchProviderException, IOException {
|
|
||||||
PGPSecretKeyRing secretKey;
|
|
||||||
try {
|
|
||||||
secretKey = PGPainless.generateKeyRing().simpleRsaKeyRing("xmpp:" + owner.toString(), RsaLength._4096);
|
|
||||||
} catch (PGPException e) {
|
|
||||||
throw new SmackOpenPgpException("Could not generate OpenPGP Key Pair.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new KeyBytesAndFingerprint(secretKey.getEncoded(), getFingerprint(secretKey.getPublicKey()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OpenPgpV4Fingerprint importPublicKey(BareJid owner, byte[] bytes)
|
|
||||||
throws MissingUserIdOnKeyException, IOException, SmackOpenPgpException {
|
|
||||||
PGPPublicKeyRing publicKeys = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator());
|
|
||||||
// LOGGER.log(Level.INFO, "Importing key " + Long.toHexString(publicKeys.getPublicKey().getKeyID()));
|
|
||||||
return importPublicKey(owner, publicKeys);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OpenPgpV4Fingerprint importPublicKey(BareJid owner, PGPPublicKeyRing ring)
|
|
||||||
throws SmackOpenPgpException, IOException, MissingUserIdOnKeyException {
|
|
||||||
if (!new BareJidUserId.PubRingSelectionStrategy().accept(owner, ring)) {
|
|
||||||
throw new MissingUserIdOnKeyException(owner, ring.getPublicKey().getKeyID());
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
PGPPublicKeyRingCollection publicKeyRings = getStore().getPublicKeyRings(owner);
|
|
||||||
if (publicKeyRings == null) {
|
|
||||||
publicKeyRings = new PGPPublicKeyRingCollection(Collections.singleton(ring));
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
publicKeyRings = PGPPublicKeyRingCollection.addPublicKeyRing(publicKeyRings, ring);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
LOGGER.log(Level.INFO, "Skip key " + Long.toHexString(ring.getPublicKey().getKeyID()) +
|
|
||||||
" as it is already in the public key ring.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getStore().storePublicKeyRing(owner, publicKeyRings);
|
|
||||||
} catch (PGPException e) {
|
|
||||||
throw new SmackOpenPgpException(e);
|
|
||||||
}
|
|
||||||
return getFingerprint(ring.getPublicKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OpenPgpV4Fingerprint importSecretKey(BareJid owner, byte[] bytes)
|
|
||||||
throws MissingUserIdOnKeyException, SmackOpenPgpException, IOException {
|
|
||||||
PGPSecretKeyRing importSecretKeys;
|
|
||||||
try {
|
|
||||||
importSecretKeys = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator());
|
|
||||||
} catch (PGPException | IOException e) {
|
|
||||||
throw new SmackOpenPgpException("Could not deserialize PGP secret key of " + owner.toString(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!new BareJidUserId.SecRingSelectionStrategy().accept(owner, importSecretKeys)) {
|
|
||||||
throw new MissingUserIdOnKeyException(owner, importSecretKeys.getPublicKey().getKeyID());
|
|
||||||
}
|
|
||||||
|
|
||||||
PGPSecretKeyRingCollection secretKeyRings;
|
|
||||||
try {
|
|
||||||
secretKeyRings = getStore().getSecretKeyRings(owner);
|
|
||||||
} catch (PGPException | IOException e) {
|
|
||||||
throw new SmackOpenPgpException("Could not load secret key ring of " + owner.toString(), e);
|
|
||||||
}
|
|
||||||
if (secretKeyRings == null) {
|
|
||||||
try {
|
|
||||||
secretKeyRings = new PGPSecretKeyRingCollection(Collections.singleton(importSecretKeys));
|
|
||||||
} catch (IOException | PGPException e) {
|
|
||||||
throw new SmackOpenPgpException("Could not create SecretKeyRingCollection from SecretKeyRing.", e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
secretKeyRings = PGPSecretKeyRingCollection.addSecretKeyRing(secretKeyRings, importSecretKeys);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
LOGGER.log(Level.INFO, "Skip key " + Long.toHexString(importSecretKeys.getPublicKey().getKeyID()) +
|
|
||||||
" as it is already part of the key ring.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getStore().storeSecretKeyRing(owner, secretKeyRings);
|
|
||||||
|
|
||||||
PGPPublicKeyRing publicKeys = BCUtil.publicKeyRingFromSecretKeyRing(importSecretKeys);
|
|
||||||
importPublicKey(owner, publicKeys);
|
|
||||||
|
|
||||||
return getFingerprint(publicKeys.getPublicKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public OpenPgpV4Fingerprint importSecretKey(byte[] bytes)
|
|
||||||
throws MissingUserIdOnKeyException, SmackOpenPgpException, IOException {
|
|
||||||
return importSecretKey(owner, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static OpenPgpV4Fingerprint getFingerprint(PGPPublicKey publicKey) {
|
|
||||||
byte[] hex = Hex.encode(publicKey.getFingerprint());
|
|
||||||
return new OpenPgpV4Fingerprint(hex);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpStore;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector;
|
|
||||||
|
|
||||||
public interface PainlessOpenPgpStore extends OpenPgpStore {
|
|
||||||
|
|
||||||
PGPPublicKeyRingCollection getPublicKeyRings(BareJid owner) throws IOException, PGPException;
|
|
||||||
|
|
||||||
PGPSecretKeyRingCollection getSecretKeyRings(BareJid owner) throws IOException, PGPException;
|
|
||||||
|
|
||||||
void storePublicKeyRing(BareJid owner, PGPPublicKeyRingCollection publicKeys) throws IOException;
|
|
||||||
|
|
||||||
void storeSecretKeyRing(BareJid owner, PGPSecretKeyRingCollection secretKeys) throws IOException;
|
|
||||||
|
|
||||||
byte[] loadPublicKeyRingBytes(BareJid owner);
|
|
||||||
|
|
||||||
byte[] loadSecretKeyRingBytes(BareJid owner);
|
|
||||||
|
|
||||||
void storePublicKeyRingBytes(BareJid owner, byte[] bytes);
|
|
||||||
|
|
||||||
void storeSecretKeyRingBytes(BareJid owner, byte[] bytes);
|
|
||||||
|
|
||||||
SecretKeyRingProtector getSecretKeyProtector();
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
|
||||||
import static junit.framework.TestCase.assertTrue;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.FileUtils;
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
|
||||||
import org.jivesoftware.smackx.ox.util.KeyBytesAndFingerprint;
|
|
||||||
import org.jivesoftware.smackx.ox.util.SecretKeyBackupHelper;
|
|
||||||
|
|
||||||
import org.junit.AfterClass;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
import org.jxmpp.jid.JidTestUtil;
|
|
||||||
import org.pgpainless.pgpainless.key.protection.UnprotectedKeysProtector;
|
|
||||||
|
|
||||||
public class BackupRestoreTest extends OxTestSuite {
|
|
||||||
|
|
||||||
private static final File backupPath = FileUtils.getTempDir("ox-backup");
|
|
||||||
private static final File restorePath = FileUtils.getTempDir("ox-restore");
|
|
||||||
private static final BareJid owner = JidTestUtil.BARE_JID_1;
|
|
||||||
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
@AfterClass
|
|
||||||
public static void deletePaths() {
|
|
||||||
FileUtils.deleteDirectory(backupPath);
|
|
||||||
FileUtils.deleteDirectory(restorePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test()
|
|
||||||
throws NoSuchAlgorithmException, IOException, InvalidAlgorithmParameterException, NoSuchProviderException,
|
|
||||||
SmackOpenPgpException, MissingUserIdOnKeyException, InvalidBackupCodeException,
|
|
||||||
MissingOpenPgpKeyPairException, MissingOpenPgpPublicKeyException {
|
|
||||||
FileBasedPainlessOpenPgpStore backupStore = new FileBasedPainlessOpenPgpStore(backupPath, new UnprotectedKeysProtector());
|
|
||||||
FileBasedPainlessOpenPgpStore restoreStore = new FileBasedPainlessOpenPgpStore(restorePath, new UnprotectedKeysProtector());
|
|
||||||
|
|
||||||
PainlessOpenPgpProvider backupProvider = new PainlessOpenPgpProvider(owner, backupStore);
|
|
||||||
PainlessOpenPgpProvider restoreProvider = new PainlessOpenPgpProvider(owner, restoreStore);
|
|
||||||
|
|
||||||
KeyBytesAndFingerprint key = backupProvider.generateOpenPgpKeyPair(owner);
|
|
||||||
backupProvider.importSecretKey(key.getBytes());
|
|
||||||
|
|
||||||
final String backupCode = SecretKeyBackupHelper.generateBackupPassword();
|
|
||||||
SecretkeyElement backup = SecretKeyBackupHelper.createSecretkeyElement(backupProvider, owner, Collections.singleton(key.getFingerprint()), backupCode);
|
|
||||||
|
|
||||||
OpenPgpV4Fingerprint fingerprint = SecretKeyBackupHelper.restoreSecretKeyBackup(restoreProvider, backup, backupCode);
|
|
||||||
|
|
||||||
assertEquals(key.getFingerprint(), fingerprint);
|
|
||||||
|
|
||||||
assertTrue(Arrays.equals(backupStore.getSecretKeyRingBytes(owner, fingerprint), restoreStore.getSecretKeyRingBytes(owner, fingerprint)));
|
|
||||||
assertTrue(Arrays.equals(backupStore.getPublicKeyRingBytes(owner, fingerprint), restoreStore.getPublicKeyRingBytes(owner, fingerprint)));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,138 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
|
||||||
import static junit.framework.TestCase.assertNotNull;
|
|
||||||
import static junit.framework.TestCase.assertTrue;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.DummyConnection;
|
|
||||||
import org.jivesoftware.smack.SmackException;
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
|
||||||
import org.jivesoftware.smack.packet.Message;
|
|
||||||
import org.jivesoftware.smack.util.FileUtils;
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpContact;
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
import org.jivesoftware.smackx.ox.TestKeys;
|
|
||||||
import org.jivesoftware.smackx.ox.element.OpenPgpContentElement;
|
|
||||||
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
|
||||||
import org.jivesoftware.smackx.ox.element.PubkeyElement;
|
|
||||||
import org.jivesoftware.smackx.ox.element.SigncryptElement;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
|
||||||
|
|
||||||
import org.bouncycastle.util.encoders.Base64;
|
|
||||||
import org.junit.AfterClass;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
import org.pgpainless.pgpainless.key.protection.UnprotectedKeysProtector;
|
|
||||||
import org.pgpainless.pgpainless.util.BCUtil;
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
|
||||||
|
|
||||||
|
|
||||||
public class DryOxEncryptionTest extends OxTestSuite {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(DryOxEncryptionTest.class.getName());
|
|
||||||
private static final Charset UTF8 = Charset.forName("UTF-8");
|
|
||||||
|
|
||||||
private static final File julietPath = FileUtils.getTempDir("ox-juliet");
|
|
||||||
private static final File romeoPath = FileUtils.getTempDir("ox-romeo");
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
@AfterClass
|
|
||||||
public static void deletePath() {
|
|
||||||
LOGGER.log(Level.INFO, "Delete paths " + julietPath.getAbsolutePath() + " " + romeoPath.getAbsolutePath());
|
|
||||||
FileUtils.deleteDirectory(julietPath);
|
|
||||||
FileUtils.deleteDirectory(romeoPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Test
|
|
||||||
public void dryEncryptionTest()
|
|
||||||
throws IOException, SmackOpenPgpException, MissingUserIdOnKeyException, MissingOpenPgpPublicKeyException,
|
|
||||||
MissingOpenPgpKeyPairException, XmlPullParserException, SmackException.NotLoggedInException {
|
|
||||||
BareJid julietJid = TestKeys.JULIET_JID;
|
|
||||||
BareJid romeoJid = TestKeys.ROMEO_JID;
|
|
||||||
|
|
||||||
XMPPConnection julietCon = new DummyConnection();
|
|
||||||
XMPPConnection romeoCon = new DummyConnection();
|
|
||||||
|
|
||||||
FileBasedPainlessOpenPgpStore julietStore = new FileBasedPainlessOpenPgpStore(julietPath, new UnprotectedKeysProtector());
|
|
||||||
FileBasedPainlessOpenPgpStore romeoStore = new FileBasedPainlessOpenPgpStore(romeoPath, new UnprotectedKeysProtector());
|
|
||||||
|
|
||||||
PainlessOpenPgpProvider julietProvider = new PainlessOpenPgpProvider(julietJid, julietStore);
|
|
||||||
PainlessOpenPgpProvider romeoProvider = new PainlessOpenPgpProvider(romeoJid, romeoStore);
|
|
||||||
|
|
||||||
OpenPgpV4Fingerprint julietFinger = julietProvider.importSecretKey(julietJid,
|
|
||||||
BCUtil.getDecodedBytes(TestKeys.JULIET_PRIV.getBytes(UTF8)));
|
|
||||||
OpenPgpV4Fingerprint romeoFinger = romeoProvider.importSecretKey(romeoJid,
|
|
||||||
BCUtil.getDecodedBytes(TestKeys.ROMEO_PRIV.getBytes(UTF8)));
|
|
||||||
|
|
||||||
julietStore.setSigningKeyPairFingerprint(julietFinger);
|
|
||||||
romeoStore.setSigningKeyPairFingerprint(romeoFinger);
|
|
||||||
|
|
||||||
byte[] julietPubBytes = julietStore.getPublicKeyRingBytes(julietJid, julietFinger);
|
|
||||||
byte[] romeoPubBytes = romeoStore.getPublicKeyRingBytes(romeoJid, romeoFinger);
|
|
||||||
|
|
||||||
assertNotNull(julietPubBytes);
|
|
||||||
assertNotNull(romeoPubBytes);
|
|
||||||
assertTrue(julietPubBytes.length != 0);
|
|
||||||
assertTrue(romeoPubBytes.length != 0);
|
|
||||||
|
|
||||||
PubkeyElement julietPub = new PubkeyElement(new PubkeyElement.PubkeyDataElement(
|
|
||||||
Base64.encode(julietStore.getPublicKeyRingBytes(julietJid, julietFinger))),
|
|
||||||
new Date());
|
|
||||||
PubkeyElement romeoPub = new PubkeyElement(new PubkeyElement.PubkeyDataElement(
|
|
||||||
Base64.encode(romeoStore.getPublicKeyRingBytes(romeoJid, romeoFinger))),
|
|
||||||
new Date());
|
|
||||||
|
|
||||||
julietProvider.importPublicKey(romeoJid, Base64.decode(romeoPub.getDataElement().getB64Data()));
|
|
||||||
romeoProvider.importPublicKey(julietJid, Base64.decode(julietPub.getDataElement().getB64Data()));
|
|
||||||
|
|
||||||
julietStore.setAnnouncedKeysFingerprints(romeoJid, Collections.singletonMap(romeoFinger, new Date()));
|
|
||||||
romeoStore.setAnnouncedKeysFingerprints(julietJid, Collections.singletonMap(julietFinger, new Date()));
|
|
||||||
|
|
||||||
OpenPgpContact julietForRomeo = new OpenPgpContact(romeoProvider, julietJid, romeoCon);
|
|
||||||
OpenPgpContact romeoForJuliet = new OpenPgpContact(julietProvider, romeoJid, julietCon);
|
|
||||||
|
|
||||||
String bodyText = "Finden wir eine Kompromisslösung – machen wir es so, wie ich es sage.";
|
|
||||||
List<ExtensionElement> payload = Collections.<ExtensionElement>singletonList(new Message.Body("de",
|
|
||||||
bodyText));
|
|
||||||
|
|
||||||
OpenPgpElement encrypted = romeoForJuliet.encryptAndSign(payload);
|
|
||||||
|
|
||||||
LOGGER.log(Level.INFO, encrypted.toXML(null).toString());
|
|
||||||
|
|
||||||
OpenPgpContentElement decrypted = julietForRomeo.receive(encrypted);
|
|
||||||
assertTrue(decrypted instanceof SigncryptElement);
|
|
||||||
|
|
||||||
assertEquals(1, decrypted.getExtensions().size());
|
|
||||||
Message.Body body = (Message.Body) decrypted.getExtensions().get(0);
|
|
||||||
assertEquals(bodyText, body.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,103 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
|
||||||
import static junit.framework.TestCase.assertTrue;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.FileUtils;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
import org.jxmpp.jid.JidTestUtil;
|
|
||||||
import org.pgpainless.pgpainless.PGPainless;
|
|
||||||
import org.pgpainless.pgpainless.key.generation.type.length.RsaLength;
|
|
||||||
import org.pgpainless.pgpainless.key.protection.UnprotectedKeysProtector;
|
|
||||||
import org.pgpainless.pgpainless.util.BCUtil;
|
|
||||||
|
|
||||||
public class FileBasedPainlessOpenPgpStoreTest extends OxTestSuite {
|
|
||||||
|
|
||||||
private final File basePath;
|
|
||||||
private final BareJid alice;
|
|
||||||
private final BareJid bob;
|
|
||||||
|
|
||||||
private FileBasedPainlessOpenPgpStore store;
|
|
||||||
|
|
||||||
public FileBasedPainlessOpenPgpStoreTest() {
|
|
||||||
super();
|
|
||||||
this.basePath = FileUtils.getTempDir("ox-filebased-store-test");
|
|
||||||
this.alice = JidTestUtil.BARE_JID_1;
|
|
||||||
this.bob = JidTestUtil.BARE_JID_2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
@Before
|
|
||||||
public void deleteStore() {
|
|
||||||
FileUtils.deleteDirectory(basePath);
|
|
||||||
this.store = new FileBasedPainlessOpenPgpStore(basePath, new UnprotectedKeysProtector());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void storeSecretKeyRingsTest()
|
|
||||||
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
|
|
||||||
IOException {
|
|
||||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing().simpleRsaKeyRing("xmpp:" + alice.toString(), RsaLength._3072);
|
|
||||||
PGPSecretKeyRingCollection saving = new PGPSecretKeyRingCollection(Collections.singleton(secretKey));
|
|
||||||
store.storeSecretKeyRing(alice, saving);
|
|
||||||
|
|
||||||
FileBasedPainlessOpenPgpStore store2 = new FileBasedPainlessOpenPgpStore(basePath, new UnprotectedKeysProtector());
|
|
||||||
PGPSecretKeyRingCollection restored = store2.getSecretKeyRings(alice);
|
|
||||||
|
|
||||||
assertTrue(Arrays.equals(saving.getEncoded(), restored.getEncoded()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void storePublicKeyRingTest()
|
|
||||||
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
|
|
||||||
IOException {
|
|
||||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().simpleRsaKeyRing("xmpp:" + alice.toString(), RsaLength._3072);
|
|
||||||
|
|
||||||
PGPPublicKeyRing publicKeys = BCUtil.publicKeyRingFromSecretKeyRing(secretKeys);
|
|
||||||
for (PGPSecretKey k : secretKeys) {
|
|
||||||
assertEquals(publicKeys.getPublicKey(k.getKeyID()), k.getPublicKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
PGPPublicKeyRingCollection saving = new PGPPublicKeyRingCollection(Collections.singleton(publicKeys));
|
|
||||||
store.storePublicKeyRing(alice, saving);
|
|
||||||
|
|
||||||
FileBasedPainlessOpenPgpStore store2 = new FileBasedPainlessOpenPgpStore(basePath, new UnprotectedKeysProtector());
|
|
||||||
|
|
||||||
PGPPublicKeyRingCollection restored = store2.getPublicKeyRings(alice);
|
|
||||||
assertTrue(Arrays.equals(saving.getEncoded(), restored.getEncoded()));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
import java.security.Security;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
|
||||||
|
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
|
||||||
import org.junit.BeforeClass;
|
|
||||||
|
|
||||||
public abstract class OxTestSuite extends SmackTestSuite {
|
|
||||||
|
|
||||||
@BeforeClass
|
|
||||||
public static void registerProvider() {
|
|
||||||
Security.removeProvider("BC");
|
|
||||||
Security.addProvider(new BouncyCastleProvider());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertTrue;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPDataValidationException;
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.pgpainless.pgpainless.PGPainless;
|
|
||||||
import org.pgpainless.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
|
||||||
|
|
||||||
public class SymmetricEncryptionTest extends OxTestSuite {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void successfulEncryptionDecryption() throws IOException, PGPException {
|
|
||||||
byte[] data = new byte[5000];
|
|
||||||
new Random().nextBytes(data);
|
|
||||||
|
|
||||||
byte[] encrypted = PGPainless.encryptWithPassword(data, "Password".toCharArray(), SymmetricKeyAlgorithm.AES_256);
|
|
||||||
byte[] decrypted = PGPainless.decryptWithPassword(encrypted, "Password".toCharArray());
|
|
||||||
|
|
||||||
assertTrue(Arrays.equals(data, decrypted));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = PGPDataValidationException.class)
|
|
||||||
public void failingEncryptionDecryption() throws IOException, PGPException {
|
|
||||||
byte[] data = new byte[5000];
|
|
||||||
new Random().nextBytes(data);
|
|
||||||
|
|
||||||
byte[] encrypted = PGPainless.encryptWithPassword(data, "Password".toCharArray(), SymmetricKeyAlgorithm.AES_256);
|
|
||||||
byte[] decrypted = PGPainless.decryptWithPassword(encrypted, "Swordfish".toCharArray());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.FileUtils;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
import org.jxmpp.jid.JidTestUtil;
|
|
||||||
import org.pgpainless.pgpainless.PGPainless;
|
|
||||||
import org.pgpainless.pgpainless.key.protection.UnprotectedKeysProtector;
|
|
||||||
|
|
||||||
public class UserIdTest extends OxTestSuite {
|
|
||||||
|
|
||||||
private final File path;
|
|
||||||
|
|
||||||
public UserIdTest() {
|
|
||||||
this.path = FileUtils.getTempDir("ox-user-id-test");
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
@Before
|
|
||||||
public void deleteDir() {
|
|
||||||
FileUtils.deleteDirectory(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void requireUserIdOnImportTest()
|
|
||||||
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
|
|
||||||
IOException, SmackOpenPgpException, MissingUserIdOnKeyException {
|
|
||||||
BareJid owner = JidTestUtil.BARE_JID_1;
|
|
||||||
FileBasedPainlessOpenPgpStore store = new FileBasedPainlessOpenPgpStore(path, new UnprotectedKeysProtector());
|
|
||||||
PainlessOpenPgpProvider provider = new PainlessOpenPgpProvider(owner, store);
|
|
||||||
PGPSecretKeyRing ownerKey = PGPainless.generateKeyRing().simpleEcKeyRing("xmpp:" + owner.toString());
|
|
||||||
provider.importSecretKey(owner, ownerKey.getEncoded());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = MissingUserIdOnKeyException.class)
|
|
||||||
public void throwOnMissingUserIdTest()
|
|
||||||
throws PGPException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException,
|
|
||||||
IOException, SmackOpenPgpException, MissingUserIdOnKeyException {
|
|
||||||
BareJid owner = JidTestUtil.BARE_JID_1;
|
|
||||||
BareJid stranger = JidTestUtil.BARE_JID_2;
|
|
||||||
FileBasedPainlessOpenPgpStore store = new FileBasedPainlessOpenPgpStore(path, new UnprotectedKeysProtector());
|
|
||||||
PainlessOpenPgpProvider provider = new PainlessOpenPgpProvider(owner, store);
|
|
||||||
PGPSecretKeyRing strangerKey = PGPainless.generateKeyRing().simpleEcKeyRing("xmpp:" + stranger.toString());
|
|
||||||
provider.importSecretKey(owner, strangerKey.getEncoded());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -172,7 +172,7 @@ public final class OXInstantMessagingManager extends Manager implements Signcryp
|
||||||
StoreHint.set(message);
|
StoreHint.set(message);
|
||||||
message.setBody("This message is encrypted using XEP-0374: OpenPGP for XMPP: Instant Messaging.");
|
message.setBody("This message is encrypted using XEP-0374: OpenPGP for XMPP: Instant Messaging.");
|
||||||
|
|
||||||
contact.addSignedEncryptedPayloadTo(message, payload);
|
//contact.addSignedEncryptedPayloadTo(message, payload);
|
||||||
|
|
||||||
ChatManager.getInstanceFor(connection()).chatWith(contact.getJid().asEntityBareJidIfPossible()).send(message);
|
ChatManager.getInstanceFor(connection()).chatWith(contact.getJid().asEntityBareJidIfPossible()).send(message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,381 +1,51 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
|
||||||
import org.jivesoftware.smack.XMPPException;
|
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
|
||||||
import org.jivesoftware.smack.packet.Message;
|
|
||||||
import org.jivesoftware.smack.util.MultiMap;
|
|
||||||
import org.jivesoftware.smack.util.stringencoder.Base64;
|
|
||||||
import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement;
|
|
||||||
import org.jivesoftware.smackx.hints.element.StoreHint;
|
|
||||||
import org.jivesoftware.smackx.ox.element.OpenPgpContentElement;
|
|
||||||
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
|
||||||
import org.jivesoftware.smackx.ox.element.PubkeyElement;
|
|
||||||
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
|
||||||
import org.jivesoftware.smackx.ox.element.SigncryptElement;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
|
||||||
import org.jivesoftware.smackx.ox.util.DecryptedBytesAndMetadata;
|
|
||||||
import org.jivesoftware.smackx.ox.util.PubSubDelegate;
|
|
||||||
import org.jivesoftware.smackx.pubsub.LeafNode;
|
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
import org.jxmpp.jid.Jid;
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
|
||||||
|
|
||||||
public class OpenPgpContact {
|
public class OpenPgpContact {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(OpenPgpContact.class.getName());
|
private final Logger LOGGER;
|
||||||
|
|
||||||
private final BareJid jid;
|
protected final BareJid jid;
|
||||||
protected final OpenPgpProvider cryptoProvider;
|
protected final OpenPgpStore store;
|
||||||
private final XMPPConnection connection;
|
|
||||||
|
|
||||||
private Map<OpenPgpV4Fingerprint, Date> announcedKeys = null;
|
public OpenPgpContact(BareJid jid, OpenPgpStore store) {
|
||||||
private Map<OpenPgpV4Fingerprint, Date> availableKeys = null;
|
|
||||||
private final Map<OpenPgpV4Fingerprint, Throwable> unfetchableKeys = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a OpenPgpContact.
|
|
||||||
*
|
|
||||||
* @param cryptoProvider {@link OpenPgpProvider}
|
|
||||||
* @param jid {@link BareJid} of the contact
|
|
||||||
* @param connection our authenticated {@link XMPPConnection}
|
|
||||||
*/
|
|
||||||
public OpenPgpContact(OpenPgpProvider cryptoProvider,
|
|
||||||
BareJid jid,
|
|
||||||
XMPPConnection connection) {
|
|
||||||
this.jid = jid;
|
this.jid = jid;
|
||||||
this.cryptoProvider = cryptoProvider;
|
this.store = store;
|
||||||
this.connection = connection;
|
LOGGER = Logger.getLogger(OpenPgpContact.class.getName() + ":" + jid.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the {@link BareJid} of the contact.
|
|
||||||
*
|
|
||||||
* @return jid
|
|
||||||
*/
|
|
||||||
public BareJid getJid() {
|
public BareJid getJid() {
|
||||||
return jid;
|
return jid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public PGPPublicKeyRingCollection getAnyPublicKeys() throws IOException, PGPException {
|
||||||
* Return a {@link Map} of the announced keys of the contact and their last update dates.
|
return store.getPublicKeysOf(jid);
|
||||||
*
|
|
||||||
* @return announced keys
|
|
||||||
*/
|
|
||||||
public Map<OpenPgpV4Fingerprint, Date> getAnnouncedKeys() {
|
|
||||||
if (announcedKeys == null) {
|
|
||||||
announcedKeys = cryptoProvider.getStore().getAnnouncedKeysFingerprints(getJid());
|
|
||||||
}
|
|
||||||
return announcedKeys;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public PGPPublicKeyRingCollection getAnnouncedPublicKeys() throws IOException, PGPException {
|
||||||
* Return a {@link Map} of all locally available keys of the contact.
|
PGPPublicKeyRingCollection anyKeys = getAnyPublicKeys();
|
||||||
*
|
Set<OpenPgpV4Fingerprint> announced = store.getAnnouncedFingerprintsOf(jid).keySet();
|
||||||
* Note: This list might contain keys which are no longer associated to the contact.
|
|
||||||
* For encryption please use {@link #getActiveKeys()} instead.
|
|
||||||
*
|
|
||||||
* @return available keys
|
|
||||||
* @throws SmackOpenPgpException if we cannot read the locally available keys for some reason
|
|
||||||
*/
|
|
||||||
public Map<OpenPgpV4Fingerprint, Date> getAvailableKeys() throws SmackOpenPgpException {
|
|
||||||
if (availableKeys == null) {
|
|
||||||
availableKeys = cryptoProvider.getStore().getAvailableKeysFingerprints(getJid());
|
|
||||||
}
|
|
||||||
return availableKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
PGPPublicKeyRingCollection announcedKeysCollection = anyKeys;
|
||||||
* Return a {@link Map} of all the keys which cannot be fetched and the reason why this is.
|
for (PGPPublicKeyRing ring : anyKeys) {
|
||||||
*
|
|
||||||
* @return unfetched keys
|
|
||||||
*/
|
|
||||||
public Map<OpenPgpV4Fingerprint, Throwable> getUnfetchableKeys() {
|
|
||||||
return unfetchableKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(ring.getPublicKey());
|
||||||
* Return a {@link Set} of all active keys of this contact.
|
|
||||||
* Active keys are keys which are announced, not revoked or otherwise invalidated and locally available.
|
|
||||||
*
|
|
||||||
* @return active keys.
|
|
||||||
* @throws SmackOpenPgpException if we cannot access the keys of the contact.
|
|
||||||
*/
|
|
||||||
public Set<OpenPgpV4Fingerprint> getActiveKeys() throws SmackOpenPgpException {
|
|
||||||
Set<OpenPgpV4Fingerprint> fingerprints = getAvailableKeys().keySet();
|
|
||||||
fingerprints.retainAll(getAnnouncedKeys().keySet());
|
|
||||||
return fingerprints;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if (!announced.contains(fingerprint)) {
|
||||||
* Fetch the metadata node to get a {@link PublicKeysListElement} and update any missing or outdated keys.
|
announcedKeysCollection = PGPPublicKeyRingCollection.removePublicKeyRing(announcedKeysCollection, ring);
|
||||||
*
|
|
||||||
* @throws InterruptedException if the thread is interrupted
|
|
||||||
* @throws XMPPException.XMPPErrorException in case of an XMPP protocol error
|
|
||||||
* @throws SmackOpenPgpException in case of an OpenPGP exception
|
|
||||||
* @throws PubSubException.NotAPubSubNodeException in case the metadata node is not a PubSub node
|
|
||||||
* @throws PubSubException.NotALeafNodeException in case the metadata node is not a {@link LeafNode}
|
|
||||||
* @throws SmackException.NotConnectedException in case we are not connected
|
|
||||||
* @throws SmackException.NoResponseException in case the server doesn't respond
|
|
||||||
*/
|
|
||||||
public void updateKeys()
|
|
||||||
throws InterruptedException, XMPPException.XMPPErrorException, SmackOpenPgpException,
|
|
||||||
PubSubException.NotAPubSubNodeException, PubSubException.NotALeafNodeException,
|
|
||||||
SmackException.NotConnectedException, SmackException.NoResponseException {
|
|
||||||
updateKeys(PubSubDelegate.fetchPubkeysList(connection, getJid()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update any missing or outdated keys based on the given {@link PublicKeysListElement}.
|
|
||||||
*
|
|
||||||
* @param metadata {@link PublicKeysListElement}
|
|
||||||
* @throws SmackOpenPgpException in case the available keys cannot be fetched
|
|
||||||
*/
|
|
||||||
public void updateKeys(PublicKeysListElement metadata)
|
|
||||||
throws SmackOpenPgpException {
|
|
||||||
storePublishedDevices(metadata);
|
|
||||||
this.availableKeys = getAvailableKeys();
|
|
||||||
|
|
||||||
for (OpenPgpV4Fingerprint fingerprint : announcedKeys.keySet()) {
|
|
||||||
Date announcedDate = announcedKeys.get(fingerprint);
|
|
||||||
Date availableDate = availableKeys.get(fingerprint);
|
|
||||||
|
|
||||||
if (availableDate == null || availableDate.before(announcedDate)) {
|
|
||||||
try {
|
|
||||||
updateKey(fingerprint);
|
|
||||||
unfetchableKeys.remove(fingerprint);
|
|
||||||
} catch (IOException | XMPPException.XMPPErrorException | SmackException | InterruptedException |
|
|
||||||
SmackOpenPgpException | MissingUserIdOnKeyException | NullPointerException e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not update key " + fingerprint + " of " + getJid());
|
|
||||||
unfetchableKeys.put(fingerprint, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return announcedKeysCollection;
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the key identified by the {@code fingerprint}.
|
|
||||||
*
|
|
||||||
* @param fingerprint fingerprint of the key
|
|
||||||
* @throws InterruptedException if the thread is interrupted
|
|
||||||
* @throws XMPPException.XMPPErrorException in case of an XMPP protocol error
|
|
||||||
* @throws SmackException in case of an exception in Smack
|
|
||||||
* @throws IOException IO is dangerous
|
|
||||||
* @throws MissingUserIdOnKeyException if the key is missing a user id with the contacts jid
|
|
||||||
* @throws SmackOpenPgpException in case of an OpenPGP exception
|
|
||||||
*/
|
|
||||||
public void updateKey(OpenPgpV4Fingerprint fingerprint)
|
|
||||||
throws InterruptedException, XMPPException.XMPPErrorException, SmackException, IOException,
|
|
||||||
MissingUserIdOnKeyException, SmackOpenPgpException {
|
|
||||||
PubkeyElement pubkeyElement = PubSubDelegate.fetchPubkey(connection, getJid(), fingerprint);
|
|
||||||
if (pubkeyElement == null) {
|
|
||||||
throw new NullPointerException("Fetched pubkeyElement for key " + fingerprint + " of " + getJid() + " is null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] base64 = pubkeyElement.getDataElement().getB64Data();
|
|
||||||
OpenPgpV4Fingerprint imported = importPublicKey(Base64.decode(base64));
|
|
||||||
|
|
||||||
if (!fingerprint.equals(imported)) {
|
|
||||||
// Not sure, if this can/should happen. Lets be safe and throw, even if its too late at this point.
|
|
||||||
throw new AssertionError("Fingerprint of imported key differs from expected fingerprint. " +
|
|
||||||
"Expected: " + fingerprint + " Imported: " + imported);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Import a public key.
|
|
||||||
*
|
|
||||||
* @param data OpenPgp keys byte representation.
|
|
||||||
* @return the fingerprint of the imported key.
|
|
||||||
* @throws SmackOpenPgpException in case of an OpenPGP error
|
|
||||||
* @throws MissingUserIdOnKeyException if the key is missing a user id with the contacts jid
|
|
||||||
* @throws IOException IO is dangerous
|
|
||||||
*/
|
|
||||||
private OpenPgpV4Fingerprint importPublicKey(byte[] data)
|
|
||||||
throws SmackOpenPgpException, MissingUserIdOnKeyException, IOException {
|
|
||||||
OpenPgpV4Fingerprint fingerprint = cryptoProvider.importPublicKey(getJid(), data);
|
|
||||||
availableKeys.put(fingerprint, new Date());
|
|
||||||
return fingerprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process an incoming {@link PublicKeysListElement} and store the contained {@link OpenPgpV4Fingerprint}s and
|
|
||||||
* their publication dates in local storage.
|
|
||||||
*
|
|
||||||
* @param element publicKeysListElement
|
|
||||||
* @return {@link Map} with the contents of the element
|
|
||||||
*/
|
|
||||||
public Map<OpenPgpV4Fingerprint, Date> storePublishedDevices(PublicKeysListElement element) {
|
|
||||||
Map<OpenPgpV4Fingerprint, Date> announcedKeys = new HashMap<>();
|
|
||||||
|
|
||||||
for (OpenPgpV4Fingerprint f : element.getMetadata().keySet()) {
|
|
||||||
PublicKeysListElement.PubkeyMetadataElement meta = element.getMetadata().get(f);
|
|
||||||
announcedKeys.put(meta.getV4Fingerprint(), meta.getDate());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!announcedKeys.equals(this.announcedKeys)) {
|
|
||||||
cryptoProvider.getStore().setAnnouncedKeysFingerprints(getJid(), announcedKeys);
|
|
||||||
this.announcedKeys = announcedKeys;
|
|
||||||
}
|
|
||||||
return announcedKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all keys to which a message sent to the contact would be encrypted to.
|
|
||||||
* Those are all active keys of the contact as well as our own active keys.
|
|
||||||
*
|
|
||||||
* @return encryption keys
|
|
||||||
* @throws SmackOpenPgpException if we cannot read the contacts keys or our own keys.
|
|
||||||
* @throws SmackException.NotLoggedInException if we are not logged in.
|
|
||||||
*/
|
|
||||||
private MultiMap<BareJid, OpenPgpV4Fingerprint> getEncryptionKeys()
|
|
||||||
throws SmackOpenPgpException, SmackException.NotLoggedInException {
|
|
||||||
OpenPgpSelf self = getSelf();
|
|
||||||
|
|
||||||
Set<OpenPgpV4Fingerprint> contactsKeys = getActiveKeys();
|
|
||||||
Set<OpenPgpV4Fingerprint> ourKeys = self.getActiveKeys();
|
|
||||||
|
|
||||||
MultiMap<BareJid, OpenPgpV4Fingerprint> recipientsKeys = new MultiMap<>();
|
|
||||||
for (OpenPgpV4Fingerprint fingerprint : contactsKeys) {
|
|
||||||
recipientsKeys.put(getJid(), fingerprint);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (OpenPgpV4Fingerprint fingerprint : ourKeys) {
|
|
||||||
recipientsKeys.put(self.getJid(), fingerprint);
|
|
||||||
}
|
|
||||||
|
|
||||||
return recipientsKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get our OpenPgpSelf.
|
|
||||||
* The {@link OpenPgpSelf} contains all our fingerprints.
|
|
||||||
*
|
|
||||||
* @return openPgpSelf
|
|
||||||
* @throws SmackException.NotLoggedInException
|
|
||||||
*/
|
|
||||||
private OpenPgpSelf getSelf() throws SmackException.NotLoggedInException {
|
|
||||||
return OpenPgpManager.getInstanceFor(connection).getOpenPgpSelf();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt a message to a contact and ourselves and sign it with our signing key.
|
|
||||||
* The payload will be wrapped in a {@link SigncryptElement} before encryption.
|
|
||||||
*
|
|
||||||
* @param payload the payload we want to encrypt.
|
|
||||||
* @return {@link OpenPgpElement} containing the encrypted, signed {@link SigncryptElement}.
|
|
||||||
* @throws IOException IO is dangerous
|
|
||||||
* @throws SmackOpenPgpException OpenPgp is brittle
|
|
||||||
* @throws MissingOpenPgpKeyPairException if we cannot read our signing key pair
|
|
||||||
* @throws SmackException.NotLoggedInException if we are not logged in.
|
|
||||||
*/
|
|
||||||
public OpenPgpElement encryptAndSign(List<ExtensionElement> payload)
|
|
||||||
throws IOException, SmackOpenPgpException, MissingOpenPgpKeyPairException,
|
|
||||||
SmackException.NotLoggedInException {
|
|
||||||
|
|
||||||
OpenPgpSelf self = OpenPgpManager.getInstanceFor(connection).getOpenPgpSelf();
|
|
||||||
|
|
||||||
SigncryptElement preparedPayload = new SigncryptElement(
|
|
||||||
Collections.<Jid>singleton(getJid()),
|
|
||||||
payload);
|
|
||||||
|
|
||||||
byte[] encryptedBytes;
|
|
||||||
|
|
||||||
// Encrypt the payload
|
|
||||||
try {
|
|
||||||
encryptedBytes = cryptoProvider.signAndEncrypt(
|
|
||||||
preparedPayload,
|
|
||||||
self.getSigningKey(),
|
|
||||||
getEncryptionKeys());
|
|
||||||
} catch (MissingOpenPgpPublicKeyException e) {
|
|
||||||
throw new AssertionError("Missing OpenPGP public key, even though this should not happen here.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new OpenPgpElement(Base64.encodeToString(encryptedBytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a signed and encrypted {@link SigncryptElement} containing the {@code payload} and append it as a
|
|
||||||
* {@link OpenPgpElement} to the {@code message}.
|
|
||||||
*
|
|
||||||
* @param message {@link Message} which will transport the payload.
|
|
||||||
* @param payload payload that will be encrypted and signed.
|
|
||||||
* @throws IOException IO is dangerous.
|
|
||||||
* @throws SmackOpenPgpException OpenPGP is brittle.
|
|
||||||
* @throws MissingOpenPgpKeyPairException if we cannot access our signing key.
|
|
||||||
* @throws SmackException.NotLoggedInException if we are not logged in.
|
|
||||||
*/
|
|
||||||
public void addSignedEncryptedPayloadTo(Message message, List<ExtensionElement> payload)
|
|
||||||
throws IOException, SmackOpenPgpException, MissingOpenPgpKeyPairException,
|
|
||||||
SmackException.NotLoggedInException {
|
|
||||||
|
|
||||||
// Add encrypted payload to message
|
|
||||||
OpenPgpElement encryptedPayload = encryptAndSign(payload);
|
|
||||||
message.addExtension(encryptedPayload);
|
|
||||||
|
|
||||||
// Add additional information to the message
|
|
||||||
if (!ExplicitMessageEncryptionElement.hasProtocol(message,
|
|
||||||
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.openpgpV0)) {
|
|
||||||
message.addExtension(new ExplicitMessageEncryptionElement(
|
|
||||||
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.openpgpV0));
|
|
||||||
}
|
|
||||||
StoreHint.set(message);
|
|
||||||
message.setBody("This message is encrypted using XEP-0374: OpenPGP for XMPP: Instant Messaging.");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process an incoming {@link OpenPgpElement} and return the decrypted and verified {@link OpenPgpContentElement}.
|
|
||||||
*
|
|
||||||
* @param element possibly encrypted, possibly signed {@link OpenPgpElement}.
|
|
||||||
* @return decrypted {@link OpenPgpContentElement}
|
|
||||||
* @throws XmlPullParserException if the decrypted message does not represent valid XML.
|
|
||||||
* @throws MissingOpenPgpKeyPairException if we are missing the public key counterpart of the key that signed the message.
|
|
||||||
* @throws SmackOpenPgpException if the message cannot be decrypted and or verified.
|
|
||||||
* @throws IOException IO is dangerous
|
|
||||||
*/
|
|
||||||
public OpenPgpContentElement receive(OpenPgpElement element)
|
|
||||||
throws XmlPullParserException, MissingOpenPgpKeyPairException, SmackOpenPgpException, IOException {
|
|
||||||
byte[] decoded = Base64.decode(element.getEncryptedBase64MessageContent());
|
|
||||||
|
|
||||||
DecryptedBytesAndMetadata decryptedBytes = cryptoProvider.decrypt(decoded, getJid(), null);
|
|
||||||
|
|
||||||
OpenPgpMessage openPgpMessage = new OpenPgpMessage(decryptedBytes.getBytes(),
|
|
||||||
new OpenPgpMessage.Metadata(decryptedBytes.getDecryptionKey(),
|
|
||||||
decryptedBytes.getVerifiedSignatures()));
|
|
||||||
|
|
||||||
return openPgpMessage.getOpenPgpContentElement();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ package org.jivesoftware.smackx.ox;
|
||||||
|
|
||||||
import static org.jivesoftware.smackx.ox.util.PubSubDelegate.PEP_NODE_PUBLIC_KEYS;
|
import static org.jivesoftware.smackx.ox.util.PubSubDelegate.PEP_NODE_PUBLIC_KEYS;
|
||||||
import static org.jivesoftware.smackx.ox.util.PubSubDelegate.PEP_NODE_PUBLIC_KEYS_NOTIFY;
|
import static org.jivesoftware.smackx.ox.util.PubSubDelegate.PEP_NODE_PUBLIC_KEYS_NOTIFY;
|
||||||
import static org.jivesoftware.smackx.ox.util.PubSubDelegate.publishPublicKey;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -49,16 +48,14 @@ import org.jivesoftware.smackx.ox.callback.backup.AskForBackupCodeCallback;
|
||||||
import org.jivesoftware.smackx.ox.callback.backup.DisplayBackupCodeCallback;
|
import org.jivesoftware.smackx.ox.callback.backup.DisplayBackupCodeCallback;
|
||||||
import org.jivesoftware.smackx.ox.callback.backup.SecretKeyBackupSelectionCallback;
|
import org.jivesoftware.smackx.ox.callback.backup.SecretKeyBackupSelectionCallback;
|
||||||
import org.jivesoftware.smackx.ox.callback.backup.SecretKeyRestoreSelectionCallback;
|
import org.jivesoftware.smackx.ox.callback.backup.SecretKeyRestoreSelectionCallback;
|
||||||
|
import org.jivesoftware.smackx.ox.crypto.OpenPgpProvider;
|
||||||
import org.jivesoftware.smackx.ox.element.CryptElement;
|
import org.jivesoftware.smackx.ox.element.CryptElement;
|
||||||
import org.jivesoftware.smackx.ox.element.OpenPgpContentElement;
|
|
||||||
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
|
||||||
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.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.InvalidBackupCodeException;
|
import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException;
|
||||||
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.MissingUserIdOnKeyException;
|
import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException;
|
||||||
import org.jivesoftware.smackx.ox.exception.NoBackupFoundException;
|
import org.jivesoftware.smackx.ox.exception.NoBackupFoundException;
|
||||||
|
@ -66,11 +63,10 @@ import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
||||||
import org.jivesoftware.smackx.ox.listener.internal.CryptElementReceivedListener;
|
import org.jivesoftware.smackx.ox.listener.internal.CryptElementReceivedListener;
|
||||||
import org.jivesoftware.smackx.ox.listener.internal.SignElementReceivedListener;
|
import org.jivesoftware.smackx.ox.listener.internal.SignElementReceivedListener;
|
||||||
import org.jivesoftware.smackx.ox.listener.internal.SigncryptElementReceivedListener;
|
import org.jivesoftware.smackx.ox.listener.internal.SigncryptElementReceivedListener;
|
||||||
import org.jivesoftware.smackx.ox.util.KeyBytesAndFingerprint;
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore;
|
||||||
import org.jivesoftware.smackx.ox.util.PubSubDelegate;
|
import org.jivesoftware.smackx.ox.util.PubSubDelegate;
|
||||||
import org.jivesoftware.smackx.ox.util.SecretKeyBackupHelper;
|
import org.jivesoftware.smackx.ox.util.SecretKeyBackupHelper;
|
||||||
import org.jivesoftware.smackx.pep.PEPListener;
|
import org.jivesoftware.smackx.pep.PEPListener;
|
||||||
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.LeafNode;
|
||||||
|
@ -78,9 +74,11 @@ import org.jivesoftware.smackx.pubsub.PayloadItem;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubFeature;
|
import org.jivesoftware.smackx.pubsub.PubSubFeature;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
public final class OpenPgpManager extends Manager {
|
public final class OpenPgpManager extends Manager {
|
||||||
|
|
||||||
|
@ -96,6 +94,8 @@ public final class OpenPgpManager extends Manager {
|
||||||
*/
|
*/
|
||||||
private OpenPgpProvider provider;
|
private OpenPgpProvider provider;
|
||||||
|
|
||||||
|
private OpenPgpStore store;
|
||||||
|
|
||||||
private final Map<BareJid, OpenPgpContact> openPgpCapableContacts = new HashMap<>();
|
private final Map<BareJid, OpenPgpContact> openPgpCapableContacts = new HashMap<>();
|
||||||
|
|
||||||
private final Set<SigncryptElementReceivedListener> signcryptElementReceivedListeners = new HashSet<>();
|
private final Set<SigncryptElementReceivedListener> signcryptElementReceivedListeners = new HashSet<>();
|
||||||
|
@ -129,6 +129,11 @@ public final class OpenPgpManager extends Manager {
|
||||||
return manager;
|
return manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BareJid getJidOrThrow() throws SmackException.NotLoggedInException {
|
||||||
|
throwIfNotAuthenticated();
|
||||||
|
return connection().getUser().asEntityBareJidOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the {@link OpenPgpProvider} which will be used to process incoming OpenPGP elements,
|
* Set the {@link OpenPgpProvider} which will be used to process incoming OpenPGP elements,
|
||||||
* as well as to execute cryptographic operations.
|
* as well as to execute cryptographic operations.
|
||||||
|
@ -146,10 +151,8 @@ public final class OpenPgpManager extends Manager {
|
||||||
* @throws SmackException.NotLoggedInException if we are not logged in
|
* @throws SmackException.NotLoggedInException if we are not logged in
|
||||||
*/
|
*/
|
||||||
public OpenPgpSelf getOpenPgpSelf() throws SmackException.NotLoggedInException {
|
public OpenPgpSelf getOpenPgpSelf() throws SmackException.NotLoggedInException {
|
||||||
throwIfNotAuthenticated();
|
|
||||||
|
|
||||||
if (self == null) {
|
if (self == null) {
|
||||||
self = new OpenPgpSelf(provider, connection().getUser().asBareJid(), connection());
|
self = new OpenPgpSelf(getJidOrThrow(), store);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
@ -182,25 +185,25 @@ public final class OpenPgpManager extends Manager {
|
||||||
|
|
||||||
BareJid ourJid = connection().getUser().asBareJid();
|
BareJid ourJid = connection().getUser().asBareJid();
|
||||||
|
|
||||||
OpenPgpV4Fingerprint primaryFingerprint = getOurFingerprint();
|
// OpenPgpV4Fingerprint primaryFingerprint = getOurFingerprint();
|
||||||
|
|
||||||
if (primaryFingerprint == null) {
|
// if (primaryFingerprint == null) {
|
||||||
primaryFingerprint = generateAndImportKeyPair(ourJid);
|
// primaryFingerprint = generateAndImportKeyPair(ourJid);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Create <pubkey/> element
|
// Create <pubkey/> element
|
||||||
PubkeyElement pubkeyElement;
|
PubkeyElement pubkeyElement;
|
||||||
try {
|
// try {
|
||||||
pubkeyElement = createPubkeyElement(ourJid, primaryFingerprint, new Date());
|
// pubkeyElement = createPubkeyElement(ourJid, primaryFingerprint, new Date());
|
||||||
} catch (MissingOpenPgpPublicKeyException e) {
|
// } catch (MissingOpenPgpPublicKeyException e) {
|
||||||
throw new AssertionError("Cannot publish our public key, since it is missing (MUST NOT happen!)");
|
// throw new AssertionError("Cannot publish our public key, since it is missing (MUST NOT happen!)");
|
||||||
}
|
// }
|
||||||
|
|
||||||
// publish it
|
// publish it
|
||||||
publishPublicKey(connection(), pubkeyElement, primaryFingerprint);
|
// publishPublicKey(connection(), pubkeyElement, primaryFingerprint);
|
||||||
|
|
||||||
// Subscribe to public key changes
|
// Subscribe to public key changes
|
||||||
PEPManager.getInstanceFor(connection()).addPEPListener(metadataListener);
|
// PEPManager.getInstanceFor(connection()).addPEPListener(metadataListener);
|
||||||
ServiceDiscoveryManager.getInstanceFor(connection())
|
ServiceDiscoveryManager.getInstanceFor(connection())
|
||||||
.addFeature(PEP_NODE_PUBLIC_KEYS_NOTIFY);
|
.addFeature(PEP_NODE_PUBLIC_KEYS_NOTIFY);
|
||||||
}
|
}
|
||||||
|
@ -211,25 +214,23 @@ public final class OpenPgpManager extends Manager {
|
||||||
* @param ourJid our {@link BareJid}.
|
* @param ourJid our {@link BareJid}.
|
||||||
* @return {@link OpenPgpV4Fingerprint} of the generated key.
|
* @return {@link OpenPgpV4Fingerprint} of the generated key.
|
||||||
* @throws NoSuchAlgorithmException if the JVM doesn't support one of the used algorithms.
|
* @throws NoSuchAlgorithmException if the JVM doesn't support one of the used algorithms.
|
||||||
* @throws IOException IO is dangerous.
|
|
||||||
* @throws InvalidAlgorithmParameterException if the used algorithm parameters are invalid.
|
* @throws InvalidAlgorithmParameterException if the used algorithm parameters are invalid.
|
||||||
* @throws NoSuchProviderException if we are missing a cryptographic provider.
|
* @throws NoSuchProviderException if we are missing a cryptographic provider.
|
||||||
* @throws SmackOpenPgpException in case of an OpenPGP error.
|
* @throws SmackOpenPgpException in case of an OpenPGP error.
|
||||||
*/
|
*/
|
||||||
public OpenPgpV4Fingerprint generateAndImportKeyPair(BareJid ourJid)
|
public OpenPgpV4Fingerprint generateAndImportKeyPair(BareJid ourJid)
|
||||||
throws NoSuchAlgorithmException, IOException, InvalidAlgorithmParameterException, NoSuchProviderException,
|
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException,
|
||||||
SmackOpenPgpException {
|
SmackOpenPgpException, PGPException {
|
||||||
KeyBytesAndFingerprint bytesAndFingerprint = provider.generateOpenPgpKeyPair(ourJid);
|
|
||||||
OpenPgpV4Fingerprint fingerprint = bytesAndFingerprint.getFingerprint();
|
|
||||||
|
|
||||||
// This should never throw, since we set our jid literally one line above this comment.
|
PGPSecretKeyRing keyPair = store.generateKeyRing(ourJid);
|
||||||
try {
|
// try {
|
||||||
provider.importSecretKey(ourJid, bytesAndFingerprint.getBytes());
|
// provider.importSecretKey(ourJid, keyPair);
|
||||||
} catch (MissingUserIdOnKeyException e) {
|
// } catch (MissingUserIdOnKeyException e) {
|
||||||
throw new AssertionError(e);
|
// This should never throw, since we set our jid literally one line above this comment.
|
||||||
}
|
// throw new AssertionError(e);
|
||||||
|
// }
|
||||||
|
|
||||||
return fingerprint;
|
return new OpenPgpV4Fingerprint(keyPair);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -237,9 +238,9 @@ public final class OpenPgpManager extends Manager {
|
||||||
*
|
*
|
||||||
* @return fingerprint.
|
* @return fingerprint.
|
||||||
*/
|
*/
|
||||||
public OpenPgpV4Fingerprint getOurFingerprint() {
|
public OpenPgpV4Fingerprint getOurFingerprint()
|
||||||
throwIfNoProviderSet();
|
throws SmackException.NotLoggedInException, IOException, PGPException {
|
||||||
return provider.getStore().getSigningKeyPairFingerprint();
|
return getOpenPgpSelf().getSigningKeyFingerprint();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -250,15 +251,7 @@ public final class OpenPgpManager extends Manager {
|
||||||
* @return {@link OpenPgpContact}.
|
* @return {@link OpenPgpContact}.
|
||||||
*/
|
*/
|
||||||
public OpenPgpContact getOpenPgpContact(EntityBareJid jid) {
|
public OpenPgpContact getOpenPgpContact(EntityBareJid jid) {
|
||||||
|
return store.getOpenPgpContact(jid);
|
||||||
OpenPgpContact openPgpContact = openPgpCapableContacts.get(jid);
|
|
||||||
|
|
||||||
if (openPgpContact == null) {
|
|
||||||
openPgpContact = new OpenPgpContact(provider, jid, connection());
|
|
||||||
openPgpCapableContacts.put(jid, openPgpContact);
|
|
||||||
}
|
|
||||||
|
|
||||||
return openPgpContact;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -311,13 +304,13 @@ public final class OpenPgpManager extends Manager {
|
||||||
BareJid ownJid = connection().getUser().asBareJid();
|
BareJid ownJid = connection().getUser().asBareJid();
|
||||||
|
|
||||||
String backupCode = SecretKeyBackupHelper.generateBackupPassword();
|
String backupCode = SecretKeyBackupHelper.generateBackupPassword();
|
||||||
Set<OpenPgpV4Fingerprint> availableKeyPairs = provider.getStore().getAvailableKeyPairFingerprints(ownJid);
|
// Set<OpenPgpV4Fingerprint> availableKeyPairs = provider.getStore().getAvailableKeyPairFingerprints(ownJid);
|
||||||
Set<OpenPgpV4Fingerprint> selectedKeyPairs = selectKeyCallback.selectKeysToBackup(availableKeyPairs);
|
// Set<OpenPgpV4Fingerprint> selectedKeyPairs = selectKeyCallback.selectKeysToBackup(availableKeyPairs);
|
||||||
|
|
||||||
SecretkeyElement secretKey = SecretKeyBackupHelper.createSecretkeyElement(provider, ownJid, selectedKeyPairs, backupCode);
|
// SecretkeyElement secretKey = SecretKeyBackupHelper.createSecretkeyElement(provider, ownJid, selectedKeyPairs, backupCode);
|
||||||
|
|
||||||
PubSubDelegate.depositSecretKey(connection(), secretKey);
|
// PubSubDelegate.depositSecretKey(connection(), secretKey);
|
||||||
displayCodeCallback.displayBackupCode(backupCode);
|
// displayCodeCallback.displayBackupCode(backupCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -401,19 +394,21 @@ public final class OpenPgpManager extends Manager {
|
||||||
};
|
};
|
||||||
|
|
||||||
private void processPublicKeysListElement(BareJid contact, PublicKeysListElement listElement) {
|
private void processPublicKeysListElement(BareJid contact, PublicKeysListElement listElement) {
|
||||||
|
/*
|
||||||
OpenPgpContact openPgpContact = getOpenPgpContact(contact.asEntityBareJidIfPossible());
|
OpenPgpContact openPgpContact = getOpenPgpContact(contact.asEntityBareJidIfPossible());
|
||||||
try {
|
try {
|
||||||
openPgpContact.updateKeys(listElement);
|
openPgpContact.updateKeys(listElement);
|
||||||
} catch (SmackOpenPgpException e) {
|
} catch (SmackOpenPgpException e) {
|
||||||
LOGGER.log(Level.WARNING, "Could not read key ring of contact " + contact, e);
|
LOGGER.log(Level.WARNING, "Could not read key ring of contact " + contact, e);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private final IncomingChatMessageListener incomingOpenPgpMessageListener =
|
private final IncomingChatMessageListener incomingOpenPgpMessageListener =
|
||||||
new IncomingChatMessageListener() {
|
new IncomingChatMessageListener() {
|
||||||
@Override
|
@Override
|
||||||
public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
|
public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
|
||||||
|
/*
|
||||||
OpenPgpElement element = message.getExtension(OpenPgpElement.ELEMENT, OpenPgpElement.NAMESPACE);
|
OpenPgpElement element = message.getExtension(OpenPgpElement.ELEMENT, OpenPgpElement.NAMESPACE);
|
||||||
if (element == null) {
|
if (element == null) {
|
||||||
// Message does not contain an OpenPgpElement -> discard
|
// Message does not contain an OpenPgpElement -> discard
|
||||||
|
@ -457,6 +452,7 @@ public final class OpenPgpManager extends Manager {
|
||||||
else {
|
else {
|
||||||
throw new AssertionError("Invalid element received: " + contentElement.getClass().getName());
|
throw new AssertionError("Invalid element received: " + contentElement.getClass().getName());
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -477,7 +473,7 @@ public final class OpenPgpManager extends Manager {
|
||||||
private void processPublicKey(PubkeyElement pubkeyElement, BareJid owner)
|
private void processPublicKey(PubkeyElement pubkeyElement, BareJid owner)
|
||||||
throws MissingUserIdOnKeyException, IOException, SmackOpenPgpException {
|
throws MissingUserIdOnKeyException, IOException, SmackOpenPgpException {
|
||||||
byte[] base64 = pubkeyElement.getDataElement().getB64Data();
|
byte[] base64 = pubkeyElement.getDataElement().getB64Data();
|
||||||
provider.importPublicKey(owner, Base64.decode(base64));
|
// provider.importPublicKey(owner, Base64.decode(base64));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -495,8 +491,9 @@ public final class OpenPgpManager extends Manager {
|
||||||
OpenPgpV4Fingerprint fingerprint,
|
OpenPgpV4Fingerprint fingerprint,
|
||||||
Date date)
|
Date date)
|
||||||
throws MissingOpenPgpPublicKeyException {
|
throws MissingOpenPgpPublicKeyException {
|
||||||
byte[] keyBytes = provider.getStore().getPublicKeyRingBytes(owner, fingerprint);
|
// byte[] keyBytes = provider.getStore().getPublicKeyRingBytes(owner, fingerprint);
|
||||||
return createPubkeyElement(keyBytes, date);
|
// return createPubkeyElement(keyBytes, date);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -29,6 +29,7 @@ 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.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -133,31 +134,31 @@ public class OpenPgpMessage {
|
||||||
|
|
||||||
public static class Metadata {
|
public static class Metadata {
|
||||||
|
|
||||||
private final Long encryptionKeyId;
|
private final OpenPgpV4Fingerprint decryptionFingerprint;
|
||||||
private final Set<Long> validSignatureIds;
|
private final Set<OpenPgpV4Fingerprint> validSignatureFingerprints;
|
||||||
|
|
||||||
public Metadata(Long encryptionKeyId, Set<Long> validSignatureIds) {
|
public Metadata(OpenPgpV4Fingerprint decryptionFingerprint, Set<OpenPgpV4Fingerprint> validSignatureFingerprints) {
|
||||||
this.encryptionKeyId = encryptionKeyId;
|
this.decryptionFingerprint = decryptionFingerprint;
|
||||||
this.validSignatureIds = validSignatureIds;
|
this.validSignatureFingerprints = validSignatureFingerprints;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Long getEncryptionKeyId() {
|
public OpenPgpV4Fingerprint getDecryptionFingerprint() {
|
||||||
return encryptionKeyId;
|
return decryptionFingerprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Long> getValidSignatureIds() {
|
public Set<OpenPgpV4Fingerprint> getValidSignatureFingerprints() {
|
||||||
return new HashSet<>(validSignatureIds);
|
return new HashSet<>(validSignatureFingerprints);
|
||||||
}
|
}
|
||||||
|
|
||||||
public State getState() {
|
public State getState() {
|
||||||
if (validSignatureIds.size() != 0) {
|
if (validSignatureFingerprints.size() != 0) {
|
||||||
if (encryptionKeyId != null) {
|
if (decryptionFingerprint != null) {
|
||||||
return State.signcrypt;
|
return State.signcrypt;
|
||||||
} else {
|
} else {
|
||||||
return State.sign;
|
return State.sign;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (encryptionKeyId != null) {
|
if (decryptionFingerprint != null) {
|
||||||
return State.crypt;
|
return State.crypt;
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("OpenPGP message appears to be neither encrypted, " +
|
throw new IllegalStateException("OpenPGP message appears to be neither encrypted, " +
|
||||||
|
|
|
@ -1,216 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright 2017 Florian Schmaus.
|
|
||||||
*
|
|
||||||
* 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.io.IOException;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.MultiMap;
|
|
||||||
import org.jivesoftware.smackx.ox.callback.backup.SmackMissingOpenPgpPublicKeyCallback;
|
|
||||||
import org.jivesoftware.smackx.ox.element.CryptElement;
|
|
||||||
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.SigncryptElement;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
|
||||||
import org.jivesoftware.smackx.ox.util.DecryptedBytesAndMetadata;
|
|
||||||
import org.jivesoftware.smackx.ox.util.KeyBytesAndFingerprint;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
|
|
||||||
public interface OpenPgpProvider {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign and encrypt a {@link SigncryptElement} element for usage within the context of instant messaging.
|
|
||||||
* The resulting byte array can be decrypted by each recipient, as well as all devices of the user.
|
|
||||||
* The message contains a signature made by our key.
|
|
||||||
*
|
|
||||||
* @see <a href="https://xmpp.org/extensions/xep-0373.html#signcrypt">XEP-0373 §3</a>
|
|
||||||
* @see <a href="https://xmpp.org/extensions/xep-0374.html#openpgp-secured-im">XEP-0374 §2.1</a>
|
|
||||||
*
|
|
||||||
* @param element {@link SigncryptElement} which contains the content of the message as plaintext.
|
|
||||||
* @param signingKey {@link OpenPgpV4Fingerprint} of the signing key.
|
|
||||||
* @param encryptionKeys {@link MultiMap} containing all {@link OpenPgpV4Fingerprint}s of recipients which will
|
|
||||||
* be able to decrypt the message.
|
|
||||||
* @return encrypted and signed data which contains the encrypted, encoded message.
|
|
||||||
*
|
|
||||||
* @throws MissingOpenPgpKeyPairException if the OpenPGP key pair with the given {@link OpenPgpV4Fingerprint}
|
|
||||||
* is not available.
|
|
||||||
* @throws MissingOpenPgpPublicKeyException if any of the OpenPGP public keys whose {@link OpenPgpV4Fingerprint}
|
|
||||||
* is listed in {@code encryptionKeys} is not available.
|
|
||||||
* @throws SmackOpenPgpException in case of an OpenPGP error
|
|
||||||
* @throws IOException IO is dangerous
|
|
||||||
*/
|
|
||||||
byte[] signAndEncrypt(SigncryptElement element,
|
|
||||||
OpenPgpV4Fingerprint signingKey,
|
|
||||||
MultiMap<BareJid, OpenPgpV4Fingerprint> encryptionKeys)
|
|
||||||
throws MissingOpenPgpKeyPairException, MissingOpenPgpPublicKeyException, SmackOpenPgpException, IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign a {@link SignElement} with the users signing key.
|
|
||||||
* The resulting byte array contains the signed byte representation of the {@link SignElement}.
|
|
||||||
*
|
|
||||||
* @see <a href="https://xmpp.org/extensions/xep-0373.html#exchange">XEP-0373 §3.1</a>
|
|
||||||
* @see <a href="https://xmpp.org/extensions/xep-0374.html#openpgp-secured-im">XEP-0374 §2.1</a>
|
|
||||||
*
|
|
||||||
* @param element {@link SignElement} which will be signed.
|
|
||||||
* @param singingKeyFingerprint {@link OpenPgpV4Fingerprint} of the key that is used for signing.
|
|
||||||
* @return byte array which contains the signed {@link SignElement}.
|
|
||||||
*
|
|
||||||
* @throws MissingOpenPgpKeyPairException if we don't have the key pair for the
|
|
||||||
* {@link OpenPgpV4Fingerprint} available.
|
|
||||||
* @throws IOException IO is dangerous
|
|
||||||
* @throws SmackOpenPgpException in case of an OpenPGP error
|
|
||||||
*/
|
|
||||||
byte[] sign(SignElement element, OpenPgpV4Fingerprint singingKeyFingerprint)
|
|
||||||
throws MissingOpenPgpKeyPairException, IOException, SmackOpenPgpException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt a {@link CryptElement} for all keys which fingerprints are contained in
|
|
||||||
* {@code encryptionKeyFingerprints}.
|
|
||||||
* The resulting byte array contains the encrypted {@link CryptElement}
|
|
||||||
* which can be decrypted by all recipients, as well as by ourselves.
|
|
||||||
* <br>
|
|
||||||
* Note: DO NOT use this method in the context of instant messaging, as XEP-0374 forbids that.
|
|
||||||
*
|
|
||||||
* @see <a href="https://xmpp.org/extensions/xep-0374.html#openpgp-secured-im">XEP-0374 §2.1</a>
|
|
||||||
*
|
|
||||||
* @param element plaintext {@link CryptElement} which will be encrypted.
|
|
||||||
* @param encryptionKeyFingerprints {@link MultiMap} of recipients and {@link OpenPgpV4Fingerprint}s of the
|
|
||||||
* keys which are used for encryption.
|
|
||||||
* @return byte array which contains the encrypted {@link CryptElement}.
|
|
||||||
* @throws MissingOpenPgpPublicKeyException if any of the OpenPGP public keys whose
|
|
||||||
* {@link OpenPgpV4Fingerprint} is listed in {@code encryptionKeys}
|
|
||||||
* is not available.
|
|
||||||
* @throws IOException IO is dangerous
|
|
||||||
* @throws SmackOpenPgpException in case of an OpenPGP error
|
|
||||||
*/
|
|
||||||
byte[] encrypt(CryptElement element, MultiMap<BareJid, OpenPgpV4Fingerprint> encryptionKeyFingerprints)
|
|
||||||
throws MissingOpenPgpPublicKeyException, IOException, SmackOpenPgpException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process an incoming {@link OpenPgpElement}.
|
|
||||||
* If its content is encrypted ({@link CryptElement} or {@link SigncryptElement}), the content will be decrypted.
|
|
||||||
* If its content is signed ({@link SignElement} or {@link SigncryptElement}), signatures are verified using
|
|
||||||
* the announced public keys of the sender.
|
|
||||||
* The resulting byte array will contain the decrypted {@link OpenPgpContentElement}.
|
|
||||||
*
|
|
||||||
* @see <a href="https://xmpp.org/extensions/xep-0373.html#exchange">XEP-0373 §3.1</a>
|
|
||||||
*
|
|
||||||
* @param bytes byte array which contains the encrypted {@link OpenPgpContentElement}.
|
|
||||||
* @param sender sender of the message
|
|
||||||
* @param missingPublicKeyCallback callback to handle missing public keys
|
|
||||||
* @return byte array which contains the decrypted {@link OpenPgpContentElement}, as well as metadata.
|
|
||||||
*
|
|
||||||
* @throws MissingOpenPgpKeyPairException if we don't have an OpenPGP key pair available that to decrypt
|
|
||||||
* the message.
|
|
||||||
* @throws SmackOpenPgpException in case of an OpenPGP error
|
|
||||||
*/
|
|
||||||
DecryptedBytesAndMetadata decrypt(byte[] bytes, BareJid sender, SmackMissingOpenPgpPublicKeyCallback missingPublicKeyCallback)
|
|
||||||
throws MissingOpenPgpKeyPairException, SmackOpenPgpException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt some data symmetrically using a password.
|
|
||||||
* @param bytes data
|
|
||||||
* @param password password
|
|
||||||
* @return encrypted data
|
|
||||||
* @throws SmackOpenPgpException in case of an OpenPGP error
|
|
||||||
* @throws IOException IO is dangerous
|
|
||||||
*/
|
|
||||||
byte[] symmetricallyEncryptWithPassword(byte[] bytes, String password) throws SmackOpenPgpException, IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt a symmetrically encrypted array of data using the provided password.
|
|
||||||
*
|
|
||||||
* @param bytes symmetrically encrypted data
|
|
||||||
* @param password password for decryption
|
|
||||||
* @return decrypted data
|
|
||||||
* @throws SmackOpenPgpException if the password is incorrect
|
|
||||||
* @throws IOException io is dangerous
|
|
||||||
*/
|
|
||||||
byte[] symmetricallyDecryptWithPassword(byte[] bytes, String password) throws SmackOpenPgpException, IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a fresh OpenPGP key pair.
|
|
||||||
*
|
|
||||||
* @param owner JID of the keys owner.
|
|
||||||
* @return byte array representation + {@link OpenPgpV4Fingerprint} of the generated key pair.
|
|
||||||
* @throws SmackOpenPgpException in case of an OpenPGP error
|
|
||||||
* @throws InvalidAlgorithmParameterException if invalid algorithm parameters are used for crypto
|
|
||||||
* @throws NoSuchAlgorithmException if the JVM is lacking support for a used algorithm
|
|
||||||
* @throws NoSuchProviderException if the JVM is missing a security provider
|
|
||||||
* @throws IOException IO is dangerous
|
|
||||||
*/
|
|
||||||
KeyBytesAndFingerprint generateOpenPgpKeyPair(BareJid owner)
|
|
||||||
throws SmackOpenPgpException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
|
|
||||||
NoSuchProviderException, IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Import a public key. The bytes are expected to be decoded from base64.
|
|
||||||
*
|
|
||||||
* @param owner owner of the public key
|
|
||||||
* @param bytes byte representation of the publick key
|
|
||||||
*
|
|
||||||
* @return fingerprint of the imported public key
|
|
||||||
*
|
|
||||||
* @throws MissingUserIdOnKeyException if the key is missing a user id with {@code owner}.
|
|
||||||
* @throws IOException IO is dangerous
|
|
||||||
* @throws SmackOpenPgpException if an OpenPGP error occurs
|
|
||||||
*/
|
|
||||||
OpenPgpV4Fingerprint importPublicKey(BareJid owner, byte[] bytes)
|
|
||||||
throws MissingUserIdOnKeyException, IOException, SmackOpenPgpException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Import a secret key. The bytes are expected to be decoded from base64.
|
|
||||||
*
|
|
||||||
* @param owner owner of the secret key
|
|
||||||
* @param bytes byte representation of the secret key
|
|
||||||
*
|
|
||||||
* @return fingerprint of the imported secret key
|
|
||||||
*
|
|
||||||
* @throws MissingUserIdOnKeyException if the key is missing a user-id of {@code owner}
|
|
||||||
* @throws SmackOpenPgpException in case of an OpenPGP error
|
|
||||||
* @throws IOException IO is dangerous
|
|
||||||
*/
|
|
||||||
OpenPgpV4Fingerprint importSecretKey(BareJid owner, byte[] bytes)
|
|
||||||
throws MissingUserIdOnKeyException, SmackOpenPgpException, IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Import a secret key that belong to ourselves.
|
|
||||||
*
|
|
||||||
* @param bytes byte representation of the secret key.
|
|
||||||
*
|
|
||||||
* @return fingerprint of the imported secret key.
|
|
||||||
*
|
|
||||||
* @throws MissingUserIdOnKeyException if the secret key is missing a user-id with our jid
|
|
||||||
* @throws SmackOpenPgpException in case of an OpenPGP error
|
|
||||||
* @throws IOException IO is dangerous
|
|
||||||
*/
|
|
||||||
OpenPgpV4Fingerprint importSecretKey(byte[] bytes)
|
|
||||||
throws MissingUserIdOnKeyException, SmackOpenPgpException, IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the underlying {@link OpenPgpStore}.
|
|
||||||
* @return store
|
|
||||||
*/
|
|
||||||
OpenPgpStore getStore();
|
|
||||||
}
|
|
|
@ -1,32 +1,52 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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 org.jivesoftware.smack.XMPPConnection;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
public class OpenPgpSelf extends OpenPgpContact {
|
public class OpenPgpSelf extends OpenPgpContact {
|
||||||
|
|
||||||
public OpenPgpSelf(OpenPgpProvider cryptoProvider, BareJid jid, XMPPConnection connection) {
|
public OpenPgpSelf(BareJid jid, OpenPgpStore store) {
|
||||||
super(cryptoProvider, jid, connection);
|
super(jid, store);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenPgpV4Fingerprint getSigningKey() {
|
public boolean hasSecretKeyAvailable() throws IOException, PGPException {
|
||||||
return cryptoProvider.getStore().getSigningKeyPairFingerprint();
|
return getSecretKeys() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PGPSecretKeyRingCollection getSecretKeys() throws IOException, PGPException {
|
||||||
|
return store.getSecretKeysOf(jid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PGPSecretKeyRing getSigningKeyRing() throws IOException, PGPException {
|
||||||
|
PGPSecretKeyRingCollection secretKeyRings = getSecretKeys();
|
||||||
|
if (secretKeyRings == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
PGPSecretKeyRing signingKeyRing = null;
|
||||||
|
for (PGPSecretKeyRing ring : secretKeyRings) {
|
||||||
|
if (signingKeyRing == null) {
|
||||||
|
signingKeyRing = ring;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ring.getPublicKey().getCreationTime().after(signingKeyRing.getPublicKey().getCreationTime())) {
|
||||||
|
signingKeyRing = ring;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return signingKeyRing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenPgpV4Fingerprint getSigningKeyFingerprint() throws IOException, PGPException {
|
||||||
|
PGPSecretKeyRing signingKeyRing = getSigningKeyRing();
|
||||||
|
return signingKeyRing != null ? new OpenPgpV4Fingerprint(signingKeyRing.getPublicKey()) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,147 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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.Date;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.MultiMap;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException;
|
|
||||||
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
|
|
||||||
public interface OpenPgpStore {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the {@link OpenPgpV4Fingerprint} of the primary OpenPGP key pair.
|
|
||||||
* If multiple key pairs are available, only the primary key pair is used for signing.
|
|
||||||
* <br>
|
|
||||||
* Note: This method returns {@code null} if no key pair is available.
|
|
||||||
*
|
|
||||||
* @return fingerprint of the primary OpenPGP key pair.
|
|
||||||
*/
|
|
||||||
OpenPgpV4Fingerprint getSigningKeyPairFingerprint();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the {@link OpenPgpV4Fingerprint} of the primary OpenPGP key pair.
|
|
||||||
* If multiple key pairs are available, only the primary key pair is used for signing.
|
|
||||||
*
|
|
||||||
* @param fingerprint {@link OpenPgpV4Fingerprint} of the new primary key pair.
|
|
||||||
*/
|
|
||||||
void setSigningKeyPairFingerprint(OpenPgpV4Fingerprint fingerprint);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a {@link Set} containing the {@link OpenPgpV4Fingerprint}s of the master keys of all available
|
|
||||||
* OpenPGP key pairs of {@code owner}.
|
|
||||||
*
|
|
||||||
* @param owner owner.
|
|
||||||
* @return set of fingerprints of available OpenPGP key pairs master keys.
|
|
||||||
*
|
|
||||||
* @throws SmackOpenPgpException in case of an OpenPGP error
|
|
||||||
*/
|
|
||||||
Set<OpenPgpV4Fingerprint> getAvailableKeyPairFingerprints(BareJid owner) throws SmackOpenPgpException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a {@link Map} containing the {@link OpenPgpV4Fingerprint}s of all OpenPGP public keys of a
|
|
||||||
* contact, which we have locally available, as well as the date, those keys had been published on.
|
|
||||||
* <br>
|
|
||||||
* Note: This returns a {@link Map} that might be different from the result of (BareJid)}.
|
|
||||||
* Messages should be encrypted to the intersection of both key sets.
|
|
||||||
*
|
|
||||||
* @param contact contact.
|
|
||||||
* @return list of contacts locally available public keys.
|
|
||||||
*
|
|
||||||
* @throws SmackOpenPgpException if something goes wrong
|
|
||||||
*/
|
|
||||||
Map<OpenPgpV4Fingerprint, Date> getAvailableKeysFingerprints(BareJid contact)
|
|
||||||
throws SmackOpenPgpException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a {@link Map} containing the {@link OpenPgpV4Fingerprint}s of all currently announced OpenPGP
|
|
||||||
* public keys of a contact along with the dates of their latest revision.
|
|
||||||
* <br>
|
|
||||||
* Note: Those are the keys announced in the latest received metadata update.
|
|
||||||
* This returns a {@link Map} which might contain different {@link OpenPgpV4Fingerprint}s than the result of
|
|
||||||
* {@link #getAvailableKeysFingerprints(BareJid)} (BareJid)}.
|
|
||||||
* Messages should be encrypted to the intersection of both key sets.
|
|
||||||
*
|
|
||||||
* @param contact contact.
|
|
||||||
* @return map of contacts last announced public keys and their update dates.
|
|
||||||
*/
|
|
||||||
Map<OpenPgpV4Fingerprint, Date> getAnnouncedKeysFingerprints(BareJid contact);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a {@link Map} of a contacts fingerprints and publication dates in persistent storage.
|
|
||||||
*
|
|
||||||
* @param contact {@link BareJid} of the owner of the announced public keys.
|
|
||||||
* @param fingerprints {@link Map} which contains a list of the keys of {@code owner}.
|
|
||||||
*/
|
|
||||||
void setAnnouncedKeysFingerprints(BareJid contact, Map<OpenPgpV4Fingerprint, Date> fingerprints);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the a {@link Map} of {@link OpenPgpV4Fingerprint}s and the {@link Date}s of when they were last
|
|
||||||
* fetched from PubSub.
|
|
||||||
*
|
|
||||||
* @param owner owner of the keys
|
|
||||||
* @return {@link Map} of keys last revision dates.
|
|
||||||
*/
|
|
||||||
Map<OpenPgpV4Fingerprint, Date> getPubkeysLastRevisions(BareJid owner);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the last revision dates of all keys of a contact.
|
|
||||||
*
|
|
||||||
* @param owner owner of the keys
|
|
||||||
* @param revisionDates {@link Map} of {@link OpenPgpV4Fingerprint}s and the {@link Date}s of when they
|
|
||||||
* were last fetched from PubSub.
|
|
||||||
*/
|
|
||||||
void setPubkeysLastRevision(BareJid owner, Map<OpenPgpV4Fingerprint, Date> revisionDates);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a {@link MultiMap} which contains contacts and their trusted keys {@link OpenPgpV4Fingerprint}s.
|
|
||||||
*
|
|
||||||
* @return trusted fingerprints.
|
|
||||||
*/
|
|
||||||
MultiMap<BareJid, OpenPgpV4Fingerprint> getAllContactsTrustedFingerprints();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the byte array representation of {@code owner}s public key ring with fingerprint {@code fingerprint}.
|
|
||||||
*
|
|
||||||
* @param owner owner of the key
|
|
||||||
* @param fingerprint fingerprint of the key
|
|
||||||
* @return byte representation of the public key.
|
|
||||||
*
|
|
||||||
* @throws MissingOpenPgpPublicKeyException if the key does not exist.
|
|
||||||
*/
|
|
||||||
byte[] getPublicKeyRingBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
|
|
||||||
throws MissingOpenPgpPublicKeyException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the byte array representation of {@code owner}s secret key ring with fingerprint {@code fingerprint}.
|
|
||||||
*
|
|
||||||
* @param owner owner of the key
|
|
||||||
* @param fingerprint fingerprint of the key
|
|
||||||
* @return byte representation of the secret key.
|
|
||||||
*
|
|
||||||
* @throws MissingOpenPgpKeyPairException if the secret key doesn't exist.
|
|
||||||
*/
|
|
||||||
byte[] getSecretKeyRingBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
|
|
||||||
throws MissingOpenPgpKeyPairException;
|
|
||||||
|
|
||||||
}
|
|
|
@ -16,7 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox.callback;
|
package org.jivesoftware.smackx.ox.callback;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
public interface SecretKeyPassphraseCallback {
|
public interface SecretKeyPassphraseCallback {
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ package org.jivesoftware.smackx.ox.callback.backup;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback to allow the user to decide, which locally available secret keys they want to include in a backup.
|
* Callback to allow the user to decide, which locally available secret keys they want to include in a backup.
|
||||||
|
|
|
@ -18,7 +18,7 @@ package org.jivesoftware.smackx.ox.callback.backup;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback to let the user decide which key from a backup they want to restore.
|
* Callback to let the user decide which key from a backup they want to restore.
|
||||||
|
|
|
@ -14,12 +14,14 @@
|
||||||
* 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.v2;
|
package org.jivesoftware.smackx.ox.crypto;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.ox.OpenPgpContact;
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpMessage;
|
import org.jivesoftware.smackx.ox.OpenPgpMessage;
|
||||||
|
import org.jivesoftware.smackx.ox.OpenPgpSelf;
|
||||||
import org.jivesoftware.smackx.ox.element.CryptElement;
|
import org.jivesoftware.smackx.ox.element.CryptElement;
|
||||||
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SignElement;
|
import org.jivesoftware.smackx.ox.element.SignElement;
|
||||||
|
@ -29,11 +31,15 @@ import org.bouncycastle.openpgp.PGPException;
|
||||||
|
|
||||||
public interface OpenPgpProvider {
|
public interface OpenPgpProvider {
|
||||||
|
|
||||||
OpenPgpElement signAndEncrypt(SigncryptElement element, OpenPgpSelf self, Collection<OpenPgpContact> recipients) throws IOException, PGPException;
|
OpenPgpElement signAndEncrypt(SigncryptElement element, OpenPgpSelf self, Collection<OpenPgpContact> recipients)
|
||||||
|
throws IOException, PGPException;
|
||||||
|
|
||||||
OpenPgpElement sign(SignElement element, OpenPgpSelf self) throws IOException, PGPException;
|
OpenPgpElement sign(SignElement element, OpenPgpSelf self)
|
||||||
|
throws IOException, PGPException;
|
||||||
|
|
||||||
OpenPgpElement encrypt(CryptElement element, OpenPgpSelf self, Collection<OpenPgpContact> recipients) throws IOException, PGPException;
|
OpenPgpElement encrypt(CryptElement element, OpenPgpSelf self, Collection<OpenPgpContact> recipients)
|
||||||
|
throws IOException, PGPException;
|
||||||
|
|
||||||
OpenPgpMessage decryptAndOrVerify(OpenPgpElement element, OpenPgpSelf self, OpenPgpContact sender) throws IOException, PGPException;
|
OpenPgpMessage decryptAndOrVerify(OpenPgpElement element, OpenPgpSelf self, OpenPgpContact sender)
|
||||||
|
throws IOException, PGPException;
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package org.jivesoftware.smackx.ox.v2;
|
package org.jivesoftware.smackx.ox.crypto;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -7,7 +7,9 @@ import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.stringencoder.Base64;
|
import org.jivesoftware.smack.util.stringencoder.Base64;
|
||||||
|
import org.jivesoftware.smackx.ox.OpenPgpContact;
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpMessage;
|
import org.jivesoftware.smackx.ox.OpenPgpMessage;
|
||||||
|
import org.jivesoftware.smackx.ox.OpenPgpSelf;
|
||||||
import org.jivesoftware.smackx.ox.element.CryptElement;
|
import org.jivesoftware.smackx.ox.element.CryptElement;
|
||||||
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
import org.jivesoftware.smackx.ox.element.OpenPgpElement;
|
||||||
import org.jivesoftware.smackx.ox.element.SignElement;
|
import org.jivesoftware.smackx.ox.element.SignElement;
|
||||||
|
@ -122,6 +124,6 @@ public class PainlessOpenPgpProvider implements OpenPgpProvider {
|
||||||
PainlessResult info = cipherStream.getResult();
|
PainlessResult info = cipherStream.getResult();
|
||||||
|
|
||||||
return new OpenPgpMessage(plainText.toByteArray(), new OpenPgpMessage.Metadata(
|
return new OpenPgpMessage(plainText.toByteArray(), new OpenPgpMessage.Metadata(
|
||||||
info.getDecryptionKeyId(), info.getVerifiedSignatureKeyIds()));
|
info.getDecryptionFingerprint(), info.getVerifiedSignaturesFingerprints()));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,7 +25,8 @@ import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
import org.jivesoftware.smack.packet.NamedElement;
|
import org.jivesoftware.smack.packet.NamedElement;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
public final class PublicKeysListElement implements ExtensionElement {
|
public final class PublicKeysListElement implements ExtensionElement {
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox.exception;
|
package org.jivesoftware.smackx.ox.exception;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception that gets thrown whenever an operation is missing an OpenPGP key pair.
|
* Exception that gets thrown whenever an operation is missing an OpenPGP key pair.
|
||||||
|
|
|
@ -16,9 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox.exception;
|
package org.jivesoftware.smackx.ox.exception;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception that gets thrown when an operation is missing an OpenPGP public key.
|
* Exception that gets thrown when an operation is missing an OpenPGP public key.
|
||||||
|
|
|
@ -22,10 +22,10 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
||||||
|
|
||||||
import org.jxmpp.util.XmppDateTime;
|
import org.jxmpp.util.XmppDateTime;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
public final class PublicKeysListElementProvider extends ExtensionElementProvider<PublicKeysListElement> {
|
public final class PublicKeysListElementProvider extends ExtensionElementProvider<PublicKeysListElement> {
|
||||||
|
|
|
@ -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.v2.store.abstr;
|
package org.jivesoftware.smackx.ox.store.abstr;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
@ -23,8 +23,7 @@ import java.security.NoSuchProviderException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpKeyStore;
|
||||||
import org.jivesoftware.smackx.ox.v2.store.definition.OpenPgpKeyStore;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
@ -33,6 +32,7 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
import org.pgpainless.pgpainless.PGPainless;
|
import org.pgpainless.pgpainless.PGPainless;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.pgpainless.pgpainless.key.generation.type.length.RsaLength;
|
import org.pgpainless.pgpainless.key.generation.type.length.RsaLength;
|
||||||
|
|
||||||
public abstract class AbstractOpenPgpKeyStore implements OpenPgpKeyStore {
|
public abstract class AbstractOpenPgpKeyStore implements OpenPgpKeyStore {
|
|
@ -14,17 +14,17 @@
|
||||||
* 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.v2.store.abstr;
|
package org.jivesoftware.smackx.ox.store.abstr;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpMetadataStore;
|
||||||
import org.jivesoftware.smackx.ox.v2.store.definition.OpenPgpMetadataStore;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
public abstract class AbstractOpenPgpMetadataStore implements OpenPgpMetadataStore {
|
public abstract class AbstractOpenPgpMetadataStore implements OpenPgpMetadataStore {
|
||||||
|
|
|
@ -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.v2.store.abstr;
|
package org.jivesoftware.smackx.ox.store.abstr;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
@ -27,12 +27,11 @@ import java.util.Observable;
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpContact;
|
import org.jivesoftware.smackx.ox.OpenPgpContact;
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
import org.jivesoftware.smackx.ox.callback.SecretKeyPassphraseCallback;
|
import org.jivesoftware.smackx.ox.callback.SecretKeyPassphraseCallback;
|
||||||
import org.jivesoftware.smackx.ox.v2.store.definition.OpenPgpKeyStore;
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpKeyStore;
|
||||||
import org.jivesoftware.smackx.ox.v2.store.definition.OpenPgpMetadataStore;
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpMetadataStore;
|
||||||
import org.jivesoftware.smackx.ox.v2.store.definition.OpenPgpStore;
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpStore;
|
||||||
import org.jivesoftware.smackx.ox.v2.store.definition.OpenPgpTrustStore;
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
@ -40,6 +39,7 @@ import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
|
|
||||||
public abstract class AbstractOpenPgpStore extends Observable implements OpenPgpStore {
|
public abstract class AbstractOpenPgpStore extends Observable implements OpenPgpStore {
|
||||||
|
@ -76,6 +76,11 @@ public abstract class AbstractOpenPgpStore extends Observable implements OpenPgp
|
||||||
this.unlocker = protector;
|
this.unlocker = protector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecretKeyRingProtector getKeyRingProtector() {
|
||||||
|
return unlocker;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSecretKeyPassphraseCallback(SecretKeyPassphraseCallback callback) {
|
public void setSecretKeyPassphraseCallback(SecretKeyPassphraseCallback callback) {
|
||||||
this.secretKeyPassphraseCallback = callback;
|
this.secretKeyPassphraseCallback = callback;
|
|
@ -1,13 +1,13 @@
|
||||||
package org.jivesoftware.smackx.ox.v2.store.abstr;
|
package org.jivesoftware.smackx.ox.store.abstr;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
|
||||||
import org.jivesoftware.smackx.ox.v2.store.definition.OpenPgpTrustStore;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
public abstract class AbstractOpenPgpTrustStore implements OpenPgpTrustStore {
|
public abstract class AbstractOpenPgpTrustStore implements OpenPgpTrustStore {
|
||||||
|
|
|
@ -15,6 +15,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Providers for XEP-0373: OpenPGP for XMPP using Bouncycastle.
|
* Abstract OpenPGP store implementations.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox.bouncycastle;
|
package org.jivesoftware.smackx.ox.store.abstr;
|
|
@ -14,21 +14,20 @@
|
||||||
* 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.v2.store.definition;
|
package org.jivesoftware.smackx.ox.store.definition;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
import java.security.NoSuchProviderException;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
public interface OpenPgpKeyStore {
|
public interface OpenPgpKeyStore {
|
||||||
|
|
|
@ -14,15 +14,14 @@
|
||||||
* 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.v2.store.definition;
|
package org.jivesoftware.smackx.ox.store.definition;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
public interface OpenPgpMetadataStore {
|
public interface OpenPgpMetadataStore {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package org.jivesoftware.smackx.ox.v2.store.definition;
|
package org.jivesoftware.smackx.ox.store.definition;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpContact;
|
import org.jivesoftware.smackx.ox.OpenPgpContact;
|
||||||
import org.jivesoftware.smackx.ox.callback.SecretKeyPassphraseCallback;
|
import org.jivesoftware.smackx.ox.callback.SecretKeyPassphraseCallback;
|
|
@ -14,13 +14,12 @@
|
||||||
* 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.v2.store.definition;
|
package org.jivesoftware.smackx.ox.store.definition;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
public interface OpenPgpTrustStore {
|
public interface OpenPgpTrustStore {
|
||||||
|
|
|
@ -15,6 +15,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Second iteration.
|
* OpenPgp store class definitions.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox.v2.store.filebased;
|
package org.jivesoftware.smackx.ox.store.definition;
|
|
@ -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.v2.store.filebased;
|
package org.jivesoftware.smackx.ox.store.filebased;
|
||||||
|
|
||||||
import static org.jivesoftware.smackx.ox.util.FileUtils.prepareFileInputStream;
|
import static org.jivesoftware.smackx.ox.util.FileUtils.prepareFileInputStream;
|
||||||
import static org.jivesoftware.smackx.ox.util.FileUtils.prepareFileOutputStream;
|
import static org.jivesoftware.smackx.ox.util.FileUtils.prepareFileOutputStream;
|
||||||
|
@ -26,7 +26,7 @@ import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smackx.ox.v2.store.abstr.AbstractOpenPgpKeyStore;
|
import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpKeyStore;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
|
@ -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.v2.store.filebased;
|
package org.jivesoftware.smackx.ox.store.filebased;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
|
@ -28,12 +28,13 @@ import java.util.Map;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpMetadataStore;
|
||||||
import org.jivesoftware.smackx.ox.util.Util;
|
import org.jivesoftware.smackx.ox.util.Util;
|
||||||
import org.jivesoftware.smackx.ox.v2.store.abstr.AbstractOpenPgpMetadataStore;
|
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
import org.jxmpp.util.XmppDateTime;
|
import org.jxmpp.util.XmppDateTime;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
public class FileBasedOpenPgpMetadataStore extends AbstractOpenPgpMetadataStore {
|
public class FileBasedOpenPgpMetadataStore extends AbstractOpenPgpMetadataStore {
|
||||||
|
|
||||||
|
@ -81,7 +82,7 @@ public class FileBasedOpenPgpMetadataStore extends AbstractOpenPgpMetadataStore
|
||||||
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(split[0]);
|
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(split[0]);
|
||||||
Date date = XmppDateTime.parseXEP0082Date(split[1]);
|
Date date = XmppDateTime.parseXEP0082Date(split[1]);
|
||||||
fingerprintDateMap.put(fingerprint, date);
|
fingerprintDateMap.put(fingerprint, date);
|
||||||
} catch (ParseException e) {
|
} catch (PGPException | ParseException e) {
|
||||||
LOGGER.log(Level.WARNING, "Error parsing fingerprint/date touple in line " + lineNr +
|
LOGGER.log(Level.WARNING, "Error parsing fingerprint/date touple in line " + lineNr +
|
||||||
" of file " + source.getAbsolutePath(), e);
|
" of file " + source.getAbsolutePath(), e);
|
||||||
}
|
}
|
|
@ -14,12 +14,12 @@
|
||||||
* 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.v2.store.filebased;
|
package org.jivesoftware.smackx.ox.store.filebased;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smackx.ox.v2.store.abstr.AbstractOpenPgpStore;
|
import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpStore;
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
|
|
@ -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.v2.store.filebased;
|
package org.jivesoftware.smackx.ox.store.filebased;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
|
@ -25,11 +25,11 @@ import java.nio.file.Files;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpTrustStore;
|
||||||
import org.jivesoftware.smackx.ox.util.Util;
|
import org.jivesoftware.smackx.ox.util.Util;
|
||||||
import org.jivesoftware.smackx.ox.v2.store.abstr.AbstractOpenPgpTrustStore;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
public class FileBasedOpenPgpTrustStore extends AbstractOpenPgpTrustStore {
|
public class FileBasedOpenPgpTrustStore extends AbstractOpenPgpTrustStore {
|
||||||
|
|
|
@ -15,6 +15,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Second iteration.
|
* File based store implementations.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox.v2;
|
package org.jivesoftware.smackx.ox.store.filebased;
|
|
@ -15,6 +15,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Second iteration.
|
* OpenPGP store implementations.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox.v2.store;
|
package org.jivesoftware.smackx.ox.store;
|
|
@ -1,40 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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.util;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
|
|
||||||
public class KeyBytesAndFingerprint {
|
|
||||||
|
|
||||||
private final byte[] bytes;
|
|
||||||
private final OpenPgpV4Fingerprint fingerprint;
|
|
||||||
|
|
||||||
public KeyBytesAndFingerprint(byte[] bytes, OpenPgpV4Fingerprint fingerprint) {
|
|
||||||
this.bytes = bytes;
|
|
||||||
this.fingerprint = fingerprint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getBytes() {
|
|
||||||
return Arrays.copyOf(bytes, bytes.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OpenPgpV4Fingerprint getFingerprint() {
|
|
||||||
return fingerprint;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -31,7 +31,6 @@ import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.packet.StanzaError;
|
import org.jivesoftware.smack.packet.StanzaError;
|
||||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
import org.jivesoftware.smackx.ox.OpenPgpManager;
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
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;
|
||||||
|
@ -46,6 +45,7 @@ import org.jivesoftware.smackx.pubsub.PubSubManager;
|
||||||
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
import org.jivesoftware.smackx.xdata.packet.DataForm;
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
public class PubSubDelegate {
|
public class PubSubDelegate {
|
||||||
|
|
||||||
|
|
|
@ -16,21 +16,18 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox.util;
|
package org.jivesoftware.smackx.ox.util;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.stringencoder.Base64;
|
import org.jivesoftware.smackx.ox.crypto.OpenPgpProvider;
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpProvider;
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
import org.jivesoftware.smackx.ox.element.SecretkeyElement;
|
||||||
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.MissingUserIdOnKeyException;
|
import org.jivesoftware.smackx.ox.exception.MissingUserIdOnKeyException;
|
||||||
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
import org.jivesoftware.smackx.ox.exception.SmackOpenPgpException;
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
public class SecretKeyBackupHelper {
|
public class SecretKeyBackupHelper {
|
||||||
|
|
||||||
|
@ -66,6 +63,7 @@ public class SecretKeyBackupHelper {
|
||||||
BareJid owner,
|
BareJid owner,
|
||||||
Set<OpenPgpV4Fingerprint> fingerprints,
|
Set<OpenPgpV4Fingerprint> fingerprints,
|
||||||
String backupCode) throws SmackOpenPgpException, IOException {
|
String backupCode) throws SmackOpenPgpException, IOException {
|
||||||
|
/*
|
||||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
for (OpenPgpV4Fingerprint fingerprint : fingerprints) {
|
for (OpenPgpV4Fingerprint fingerprint : fingerprints) {
|
||||||
try {
|
try {
|
||||||
|
@ -76,18 +74,22 @@ public class SecretKeyBackupHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return createSecretkeyElement(provider, buffer.toByteArray(), backupCode);
|
return createSecretkeyElement(provider, buffer.toByteArray(), backupCode);
|
||||||
|
*/
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SecretkeyElement createSecretkeyElement(OpenPgpProvider provider,
|
public static SecretkeyElement createSecretkeyElement(OpenPgpProvider provider,
|
||||||
byte[] keys,
|
byte[] keys,
|
||||||
String backupCode)
|
String backupCode)
|
||||||
throws SmackOpenPgpException, IOException {
|
throws SmackOpenPgpException, IOException {
|
||||||
byte[] encrypted = provider.symmetricallyEncryptWithPassword(keys, backupCode);
|
// byte[] encrypted = provider.symmetricallyEncryptWithPassword(keys, backupCode);
|
||||||
return new SecretkeyElement(Base64.encode(encrypted));
|
// return new SecretkeyElement(Base64.encode(encrypted));
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OpenPgpV4Fingerprint restoreSecretKeyBackup(OpenPgpProvider provider, SecretkeyElement backup, String backupCode)
|
public static OpenPgpV4Fingerprint restoreSecretKeyBackup(OpenPgpProvider provider, SecretkeyElement backup, String backupCode)
|
||||||
throws InvalidBackupCodeException, IOException, MissingUserIdOnKeyException, SmackOpenPgpException {
|
throws InvalidBackupCodeException, IOException, MissingUserIdOnKeyException, SmackOpenPgpException {
|
||||||
|
/*
|
||||||
byte[] encrypted = Base64.decode(backup.getB64Data());
|
byte[] encrypted = Base64.decode(backup.getB64Data());
|
||||||
|
|
||||||
byte[] decrypted;
|
byte[] decrypted;
|
||||||
|
@ -98,5 +100,7 @@ public class SecretKeyBackupHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider.importSecretKey(decrypted);
|
return provider.importSecretKey(decrypted);
|
||||||
|
*/
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
package org.jivesoftware.smackx.ox.v2;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.v2.store.definition.OpenPgpStore;
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
|
|
||||||
public class OpenPgpContact {
|
|
||||||
|
|
||||||
private final Logger LOGGER;
|
|
||||||
|
|
||||||
protected final BareJid jid;
|
|
||||||
protected final OpenPgpStore store;
|
|
||||||
|
|
||||||
public OpenPgpContact(BareJid jid, OpenPgpStore store) {
|
|
||||||
this.jid = jid;
|
|
||||||
this.store = store;
|
|
||||||
LOGGER = Logger.getLogger(OpenPgpContact.class.getName() + ":" + jid.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public PGPPublicKeyRingCollection getAnyPublicKeys() throws IOException, PGPException {
|
|
||||||
return store.getPublicKeysOf(jid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PGPPublicKeyRingCollection getAnnouncedPublicKeys() throws IOException, PGPException {
|
|
||||||
PGPPublicKeyRingCollection anyKeys = getAnyPublicKeys();
|
|
||||||
Set<OpenPgpV4Fingerprint> announced = store.getAnnouncedFingerprintsOf(jid).keySet();
|
|
||||||
|
|
||||||
PGPPublicKeyRingCollection announcedKeysCollection = anyKeys;
|
|
||||||
for (PGPPublicKeyRing ring : anyKeys) {
|
|
||||||
|
|
||||||
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(ring.getPublicKey());
|
|
||||||
|
|
||||||
if (!announced.contains(fingerprint)) {
|
|
||||||
announcedKeysCollection = PGPPublicKeyRingCollection.removePublicKeyRing(announcedKeysCollection, ring);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return announcedKeysCollection;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package org.jivesoftware.smackx.ox.v2;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.v2.store.definition.OpenPgpStore;
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
|
|
||||||
public class OpenPgpSelf extends OpenPgpContact {
|
|
||||||
|
|
||||||
public OpenPgpSelf(BareJid jid, OpenPgpStore store) {
|
|
||||||
super(jid, store);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasSecretKeyAvailable() throws IOException, PGPException {
|
|
||||||
return getSecretKeys() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PGPSecretKeyRingCollection getSecretKeys() throws IOException, PGPException {
|
|
||||||
return store.getSecretKeysOf(jid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PGPSecretKeyRing getSigningKeyRing() throws IOException, PGPException {
|
|
||||||
PGPSecretKeyRingCollection secretKeyRings = getSecretKeys();
|
|
||||||
if (secretKeyRings == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
PGPSecretKeyRing signingKeyRing = null;
|
|
||||||
for (PGPSecretKeyRing ring : secretKeyRings) {
|
|
||||||
if (signingKeyRing == null) {
|
|
||||||
signingKeyRing = ring;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ring.getPublicKey().getCreationTime().after(signingKeyRing.getPublicKey().getCreationTime())) {
|
|
||||||
signingKeyRing = ring;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return signingKeyRing;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OpenPgpV4Fingerprint getSigningKeyFingerprint() throws IOException, PGPException {
|
|
||||||
PGPSecretKeyRing signingKeyRing = getSigningKeyRing();
|
|
||||||
return signingKeyRing != null ? new OpenPgpV4Fingerprint(signingKeyRing.getPublicKey()) : null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 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 static junit.framework.TestCase.assertEquals;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
public class OpenPgpV4FingerprintTest extends SmackTestSuite {
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void fpTooShort() {
|
|
||||||
String fp = "484f57414c495645"; // Asking Mark
|
|
||||||
new OpenPgpV4Fingerprint(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
|
||||||
public void invalidHexTest() {
|
|
||||||
String fp = "UNFORTUNATELYTHISISNOVALIDHEXADECIMALDOH";
|
|
||||||
new OpenPgpV4Fingerprint(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void validFingerprintTest() {
|
|
||||||
String fp = "4A4F48414E4E53454E2049532041204E45524421";
|
|
||||||
OpenPgpV4Fingerprint finger = new OpenPgpV4Fingerprint(fp);
|
|
||||||
assertEquals(fp, finger.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void convertsToUpperCaseTest() {
|
|
||||||
String fp = "444f4e5420552048415645204120484f4242593f";
|
|
||||||
OpenPgpV4Fingerprint finger = new OpenPgpV4Fingerprint(fp);
|
|
||||||
assertEquals("444F4E5420552048415645204120484F4242593F", finger.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void equalsOtherFingerprintTest() {
|
|
||||||
OpenPgpV4Fingerprint finger = new OpenPgpV4Fingerprint("5448452043414b452049532041204c4945212121");
|
|
||||||
assertEquals(finger, new OpenPgpV4Fingerprint("5448452043414B452049532041204C4945212121"));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -21,12 +21,14 @@ import static junit.framework.TestCase.assertEquals;
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
import org.jivesoftware.smackx.ox.util.PubSubDelegate;
|
import org.jivesoftware.smackx.ox.util.PubSubDelegate;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
public class PubSubDelegateTest extends SmackTestSuite {
|
public class PubSubDelegateTest extends SmackTestSuite {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void pubkeyNodeNameTest() {
|
public void pubkeyNodeNameTest() throws PGPException {
|
||||||
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint("486f7065207520646f6e2068617665204f43640a");
|
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint("486f7065207520646f6e2068617665204f43640a");
|
||||||
assertEquals("urn:xmpp:openpgp:0:public-keys:486F7065207520646F6E2068617665204F43640A",
|
assertEquals("urn:xmpp:openpgp:0:public-keys:486F7065207520646F6E2068617665204F43640A",
|
||||||
PubSubDelegate.PEP_NODE_PUBLIC_KEY(fingerprint));
|
PubSubDelegate.PEP_NODE_PUBLIC_KEY(fingerprint));
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package org.jivesoftware.smackx.ox;
|
package org.jivesoftware.smackx.ox;
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
import static junit.framework.TestCase.assertEquals;
|
||||||
|
|
||||||
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
|
import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -27,8 +26,10 @@ import org.jivesoftware.smack.test.util.TestUtils;
|
||||||
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
||||||
import org.jivesoftware.smackx.ox.provider.PublicKeysListElementProvider;
|
import org.jivesoftware.smackx.ox.provider.PublicKeysListElementProvider;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.jxmpp.util.XmppDateTime;
|
import org.jxmpp.util.XmppDateTime;
|
||||||
|
import org.pgpainless.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
|
||||||
public class PublicKeysListElementTest extends SmackTestSuite {
|
public class PublicKeysListElementTest extends SmackTestSuite {
|
||||||
|
@ -70,7 +71,7 @@ public class PublicKeysListElementTest extends SmackTestSuite {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void listBuilderRefusesDuplicatesTest() {
|
public void listBuilderRefusesDuplicatesTest() throws PGPException {
|
||||||
PublicKeysListElement.Builder builder = PublicKeysListElement.builder();
|
PublicKeysListElement.Builder builder = PublicKeysListElement.builder();
|
||||||
String fp40 = "49545320414c4c2041424f555420444120484558";
|
String fp40 = "49545320414c4c2041424f555420444120484558";
|
||||||
Date oneDate = new Date(12337883234L);
|
Date oneDate = new Date(12337883234L);
|
||||||
|
|
Loading…
Reference in a new issue