mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-23 20:42:06 +01:00
WiP finished, no more compiler errors - untested
This commit is contained in:
parent
49a51bfa2d
commit
422baa6d2e
21 changed files with 647 additions and 396 deletions
|
@ -1,9 +1,28 @@
|
|||
/**
|
||||
*
|
||||
* 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 de.vanitasvitae.crypto.pgpainless.key.SecretKeyRingProtector;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
||||
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
||||
|
@ -16,6 +35,28 @@ public abstract class AbstractPainlessOpenPgpStore implements PainlessOpenPgpSto
|
|||
|
||||
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 getPrimaryOpenPgpKeyPairFingerprint() {
|
||||
return primaryKeyFingerprint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrimaryOpenPgpKeyPairFingerprint(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 {
|
||||
|
@ -33,7 +74,7 @@ public abstract class AbstractPainlessOpenPgpStore implements PainlessOpenPgpSto
|
|||
}
|
||||
|
||||
@Override
|
||||
public PGPSecretKeyRingCollection getSecretKeyRing(BareJid owner) throws IOException, PGPException {
|
||||
public PGPSecretKeyRingCollection getSecretKeyRings(BareJid owner) throws IOException, PGPException {
|
||||
PGPSecretKeyRingCollection keyRing = secretKeyRings.get(owner);
|
||||
if (keyRing != null) {
|
||||
return keyRing;
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
package org.jivesoftware.smackx.ox.bouncycastle;
|
||||
|
||||
public interface FileBasedOpenPgpStore {
|
||||
}
|
|
@ -1,74 +1,440 @@
|
|||
/**
|
||||
*
|
||||
* 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.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.OpenPgpStore;
|
||||
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 de.vanitasvitae.crypto.pgpainless.key.SecretKeyRingProtector;
|
||||
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;
|
||||
|
||||
public class FileBasedPainlessOpenPgpStore implements OpenPgpStore, FileBasedOpenPgpStore {
|
||||
public class FileBasedPainlessOpenPgpStore extends AbstractPainlessOpenPgpStore {
|
||||
|
||||
@Override
|
||||
public OpenPgpV4Fingerprint getPrimaryOpenPgpKeyPairFingerprint() {
|
||||
return null;
|
||||
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 void setPrimaryOpenPgpKeyPairFingerprint(OpenPgpV4Fingerprint fingerprint) {
|
||||
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.INFO, "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 Set<OpenPgpV4Fingerprint> getAvailableKeyPairFingerprints() {
|
||||
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.INFO, "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) {
|
||||
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 {
|
||||
return null;
|
||||
Map<OpenPgpV4Fingerprint, Date> availableFingerprints = new HashMap<>();
|
||||
try {
|
||||
PGPPublicKeyRingCollection publicKeys = getPublicKeyRings(contact);
|
||||
Set<OpenPgpV4Fingerprint> fingerprints = new HashSet<>();
|
||||
for (PGPPublicKeyRing ring : publicKeys) {
|
||||
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) {
|
||||
LOGGER.log(Level.WARNING, "Could not read public keys of contact " + contact.toString(), e);
|
||||
}
|
||||
return availableFingerprints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<OpenPgpV4Fingerprint, Date> getAnnouncedKeysFingerprints(BareJid contact) {
|
||||
return null;
|
||||
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 Date getPubkeysLastRevision(BareJid owner, OpenPgpV4Fingerprint fingerprint) {
|
||||
return null;
|
||||
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, OpenPgpV4Fingerprint fingerprint, Date revision) {
|
||||
|
||||
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() {
|
||||
return null;
|
||||
MultiMap<BareJid, OpenPgpV4Fingerprint> trustedFingerprints = new MultiMap<>();
|
||||
try {
|
||||
File contactsDir = getContactsPath(false);
|
||||
if (!contactsDir.exists()) {
|
||||
LOGGER.log(Level.INFO, "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[] getPublicKeyBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
|
||||
public byte[] getPublicKeyRingBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
|
||||
throws MissingOpenPgpPublicKeyException {
|
||||
return new byte[0];
|
||||
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[] getSecretKeyBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
|
||||
public byte[] getSecretKeyRingBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
|
||||
throws MissingOpenPgpKeyPairException {
|
||||
return new byte[0];
|
||||
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,3 +1,19 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ox.bouncycastle;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
@ -86,9 +102,9 @@ public class PainlessOpenPgpProvider implements OpenPgpProvider {
|
|||
InputStream fromPlain = element.toInputStream();
|
||||
PGPSecretKeyRing signingKeyRing;
|
||||
try {
|
||||
signingKeyRing = store.getSecretKeyRing(owner).getSecretKeyRing(signingKeyFingerprint.getKeyId());
|
||||
signingKeyRing = store.getSecretKeyRings(owner).getSecretKeyRing(signingKeyFingerprint.getKeyId());
|
||||
} catch (PGPException e) {
|
||||
throw new MissingOpenPgpKeyPairException(owner, e);
|
||||
throw new MissingOpenPgpKeyPairException(owner, signingKeyFingerprint, e);
|
||||
}
|
||||
|
||||
ByteArrayOutputStream toSigned = new ByteArrayOutputStream();
|
||||
|
@ -146,7 +162,7 @@ public class PainlessOpenPgpProvider implements OpenPgpProvider {
|
|||
DecryptionStream decryptionStream;
|
||||
try {
|
||||
decryptionStream = PGPainless.createDecryptor().onInputStream(fromEncrypted)
|
||||
.decryptWith(store.getSecretKeyRing(owner), store.getSecretKeyProtector())
|
||||
.decryptWith(store.getSecretKeyRings(owner), store.getSecretKeyProtector())
|
||||
.verifyWith(trustedKeyIds, senderKeys)
|
||||
.handleMissingPublicKeysWith(new MissingPublicKeyCallback() {
|
||||
@Override
|
||||
|
@ -218,9 +234,9 @@ public class PainlessOpenPgpProvider implements OpenPgpProvider {
|
|||
throws IOException, MissingOpenPgpKeyPairException {
|
||||
PGPSecretKeyRing signingKeyRing;
|
||||
try {
|
||||
signingKeyRing = store.getSecretKeyRing(owner).getSecretKeyRing(signingKey.getKeyId());
|
||||
signingKeyRing = store.getSecretKeyRings(owner).getSecretKeyRing(signingKey.getKeyId());
|
||||
} catch (PGPException e) {
|
||||
throw new MissingOpenPgpKeyPairException(owner, e);
|
||||
throw new MissingOpenPgpKeyPairException(owner, signingKey, e);
|
||||
}
|
||||
return signingKeyRing;
|
||||
}
|
||||
|
|
|
@ -30,11 +30,19 @@ public interface PainlessOpenPgpStore extends OpenPgpStore {
|
|||
|
||||
PGPPublicKeyRingCollection getPublicKeyRings(BareJid owner) throws IOException, PGPException;
|
||||
|
||||
PGPSecretKeyRingCollection getSecretKeyRing(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,3 +1,19 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ox.bouncycastle.selection_strategy;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
|
|
@ -1,165 +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 static org.jivesoftware.smackx.ox.TestKeys.JULIET_UID;
|
||||
import static org.jivesoftware.smackx.ox.TestKeys.ROMEO_UID;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||
import org.jivesoftware.smackx.ox.TestKeys;
|
||||
|
||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.BouncyGPG;
|
||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.KeySelectionStrategy;
|
||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.KeyringConfigCallbacks;
|
||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.XmppKeySelectionStrategy;
|
||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.InMemoryKeyring;
|
||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.KeyringConfig;
|
||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.KeyringConfigs;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPKeyRingGenerator;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||
import org.bouncycastle.util.io.Streams;
|
||||
import org.junit.Ignore;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
|
||||
public class BasicEncryptionTest extends SmackTestSuite {
|
||||
|
||||
private static final Charset UTF8 = Charset.forName("UTF-8");
|
||||
private final KeyringConfig keyringJuliet;
|
||||
private final KeyringConfig keyringRomeo;
|
||||
|
||||
public BasicEncryptionTest() throws IOException, PGPException {
|
||||
super();
|
||||
|
||||
// Prepare Juliet's keyring
|
||||
keyringJuliet = KeyringConfigs.forGpgExportedKeys(KeyringConfigCallbacks.withUnprotectedKeys());
|
||||
((InMemoryKeyring) keyringJuliet).addSecretKey(TestKeys.JULIET_PRIV.getBytes(UTF8));
|
||||
((InMemoryKeyring) keyringJuliet).addPublicKey(TestKeys.JULIET_PUB.getBytes(UTF8));
|
||||
((InMemoryKeyring) keyringJuliet).addPublicKey(TestKeys.ROMEO_PUB.getBytes(UTF8));
|
||||
|
||||
// Prepare Romeos keyring
|
||||
keyringRomeo = KeyringConfigs.forGpgExportedKeys(KeyringConfigCallbacks.withUnprotectedKeys());
|
||||
((InMemoryKeyring) keyringRomeo).addSecretKey(TestKeys.ROMEO_PRIV.getBytes(UTF8));
|
||||
((InMemoryKeyring) keyringRomeo).addPublicKey(TestKeys.ROMEO_PUB.getBytes(UTF8));
|
||||
((InMemoryKeyring) keyringRomeo).addPublicKey(TestKeys.JULIET_PUB.getBytes(UTF8));
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public void encryptionTest()
|
||||
throws IOException, PGPException, NoSuchAlgorithmException, SignatureException, NoSuchProviderException {
|
||||
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
KeySelectionStrategy selectionStrategy = new XmppKeySelectionStrategy(new Date());
|
||||
|
||||
byte[] message = "How long do you want these messages to remain secret?".getBytes(UTF8);
|
||||
|
||||
// Encrypt
|
||||
OutputStream out = BouncyGPG.encryptToStream()
|
||||
.withConfig(keyringJuliet)
|
||||
.withKeySelectionStrategy(selectionStrategy)
|
||||
.withOxAlgorithms()
|
||||
.toRecipients(ROMEO_UID, JULIET_UID)
|
||||
.andSignWith(JULIET_UID)
|
||||
.binaryOutput()
|
||||
.andWriteTo(result);
|
||||
|
||||
out.write(message);
|
||||
out.close();
|
||||
|
||||
byte[] encrypted = result.toByteArray();
|
||||
|
||||
// Decrypt
|
||||
ByteArrayInputStream encIn = new ByteArrayInputStream(encrypted);
|
||||
InputStream in = BouncyGPG.decryptAndVerifyStream()
|
||||
.withConfig(keyringRomeo)
|
||||
.withKeySelectionStrategy(selectionStrategy)
|
||||
.andRequireSignatureFromAllKeys(JULIET_UID)
|
||||
.fromEncryptedInputStream(encIn);
|
||||
|
||||
ByteArrayOutputStream decrypted = new ByteArrayOutputStream();
|
||||
Streams.pipeAll(in, decrypted);
|
||||
|
||||
byte[] message2 = decrypted.toByteArray();
|
||||
|
||||
assertTrue(Arrays.equals(message, message2));
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public void encryptionWithFreshKeysTest()
|
||||
throws IOException, PGPException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException {
|
||||
final String alice = "alice@wonderland.lit";
|
||||
final String bob = "bob@builder.tv";
|
||||
PGPKeyRingGenerator g1 = BCOpenPgpProvider.generateKey(JidCreate.bareFrom(alice));
|
||||
PGPKeyRingGenerator g2 = BCOpenPgpProvider.generateKey(JidCreate.bareFrom(bob));
|
||||
PGPSecretKey s1 = g1.generateSecretKeyRing().getSecretKey();
|
||||
PGPSecretKey s2 = g2.generateSecretKeyRing().getSecretKey();
|
||||
PGPPublicKey p1 = g1.generatePublicKeyRing().getPublicKey();
|
||||
PGPPublicKey p2 = g2.generatePublicKeyRing().getPublicKey();
|
||||
|
||||
KeyringConfig c1 = KeyringConfigs.forGpgExportedKeys(KeyringConfigCallbacks.withUnprotectedKeys());
|
||||
KeyringConfig c2 = KeyringConfigs.forGpgExportedKeys(KeyringConfigCallbacks.withUnprotectedKeys());
|
||||
((InMemoryKeyring) c1).addSecretKey(s1.getEncoded());
|
||||
((InMemoryKeyring) c1).addPublicKey(p1.getEncoded());
|
||||
((InMemoryKeyring) c1).addPublicKey(p2.getEncoded());
|
||||
((InMemoryKeyring) c2).addSecretKey(s2.getEncoded());
|
||||
((InMemoryKeyring) c2).addPublicKey(p2.getEncoded());
|
||||
((InMemoryKeyring) c2).addPublicKey(p1.getEncoded());
|
||||
|
||||
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
|
||||
byte[] m1 = "I want them to remain secret for as long as men are capable of evil.".getBytes(UTF8);
|
||||
OutputStream encrypt = BouncyGPG.encryptToStream()
|
||||
.withConfig(c1)
|
||||
.withKeySelectionStrategy(new XmppKeySelectionStrategy(new Date()))
|
||||
.withOxAlgorithms()
|
||||
.toRecipients("xmpp:" + alice, "xmpp:" + bob)
|
||||
.andSignWith("xmpp:" + alice)
|
||||
.binaryOutput()
|
||||
.andWriteTo(result);
|
||||
|
||||
encrypt.write(m1);
|
||||
encrypt.close();
|
||||
|
||||
byte[] e1 = result.toByteArray();
|
||||
result.reset();
|
||||
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(e1);
|
||||
InputStream decrypt = BouncyGPG.decryptAndVerifyStream()
|
||||
.withConfig(c2)
|
||||
.withKeySelectionStrategy(new XmppKeySelectionStrategy(new Date()))
|
||||
.andValidateSomeoneSigned()
|
||||
.fromEncryptedInputStream(in);
|
||||
|
||||
Streams.pipeAll(decrypt, result);
|
||||
|
||||
byte[] m2 = result.toByteArray();
|
||||
assertTrue(Arrays.equals(m1, m2));
|
||||
}
|
||||
}
|
|
@ -1,79 +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 static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
|
||||
|
||||
import java.security.Security;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||
import org.jivesoftware.smackx.ox.chat.OpenPgpMessage;
|
||||
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.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.junit.Ignore;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
import org.jxmpp.jid.Jid;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
|
||||
public class BouncyCastleOpenPgpProviderTest extends SmackTestSuite {
|
||||
|
||||
@Ignore
|
||||
public void encryptAndSign_decryptAndVerifyElementTest() throws Exception {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
|
||||
// Create providers for alice and the cat
|
||||
BareJid alice = JidCreate.bareFrom("alice@wonderland.lit");
|
||||
BareJid cheshire = JidCreate.bareFrom("cheshire@wonderland.lit");
|
||||
BCOpenPgpProvider aliceProvider = new BCOpenPgpProvider(alice);
|
||||
BCOpenPgpProvider cheshireProvider = new BCOpenPgpProvider(cheshire);
|
||||
|
||||
aliceProvider.createOpenPgpKeyPair();
|
||||
cheshireProvider.createOpenPgpKeyPair();
|
||||
|
||||
// dry exchange keys
|
||||
|
||||
PubkeyElement aliceKeys = aliceProvider.createPubkeyElement(aliceProvider.getPrimaryOpenPgpKeyPairFingerprint());
|
||||
PubkeyElement cheshireKeys = cheshireProvider.createPubkeyElement(cheshireProvider.getPrimaryOpenPgpKeyPairFingerprint());
|
||||
aliceProvider.storePublicKey(cheshire, cheshireProvider.getPrimaryOpenPgpKeyPairFingerprint(), cheshireKeys);
|
||||
cheshireProvider.storePublicKey(alice, aliceProvider.getPrimaryOpenPgpKeyPairFingerprint(), aliceKeys);
|
||||
|
||||
// Create signed and encrypted message from alice to the cheshire cat
|
||||
SigncryptElement signcryptElement = new SigncryptElement(
|
||||
Collections.<Jid>singleton(cheshire),
|
||||
Collections.<ExtensionElement>singletonList(
|
||||
new Message.Body("en", "How do you know I’m mad?")));
|
||||
OpenPgpElement encrypted = aliceProvider.signAndEncrypt(
|
||||
signcryptElement,
|
||||
aliceProvider.getPrimaryOpenPgpKeyPairFingerprint(),
|
||||
Collections.singleton(cheshireProvider.getPrimaryOpenPgpKeyPairFingerprint()));
|
||||
|
||||
// Decrypt the message as the cheshire cat
|
||||
OpenPgpMessage decrypted = cheshireProvider.decryptAndVerify(encrypted, Collections.singleton(aliceProvider.getPrimaryOpenPgpKeyPairFingerprint()));
|
||||
OpenPgpContentElement content = decrypted.getOpenPgpContentElement();
|
||||
|
||||
assertTrue(content instanceof SigncryptElement);
|
||||
assertXMLEqual(signcryptElement.toXML(null).toString(), content.toXML(null).toString());
|
||||
}
|
||||
}
|
|
@ -1,49 +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 org.jivesoftware.smack.util.StringUtils.UTF8;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
||||
import org.jivesoftware.smackx.ox.TestKeys;
|
||||
import org.jivesoftware.smackx.ox.util.Util;
|
||||
|
||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.KeyringConfigCallbacks;
|
||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.InMemoryKeyring;
|
||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.KeyringConfigs;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.junit.Test;
|
||||
|
||||
public class BouncyCastle_OpenPgpV4FingerprintTest extends SmackTestSuite {
|
||||
|
||||
@Test
|
||||
public void keyIdFromFingerprintTest() throws IOException, PGPException {
|
||||
// Parse the key
|
||||
InMemoryKeyring keyringJuliet = KeyringConfigs.forGpgExportedKeys(
|
||||
KeyringConfigCallbacks.withUnprotectedKeys());
|
||||
keyringJuliet.addPublicKey(TestKeys.JULIET_PUB.getBytes(UTF8));
|
||||
PGPPublicKey publicKey = keyringJuliet.getPublicKeyRings().iterator().next().getPublicKey();
|
||||
|
||||
OpenPgpV4Fingerprint fp = BCOpenPgpProvider.getFingerprint(publicKey);
|
||||
assertEquals(publicKey.getKeyID(), Util.keyIdFromFingerprint(fp));
|
||||
}
|
||||
}
|
|
@ -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.bouncycastle;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PublicKeyType;
|
||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||
import org.junit.Ignore;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
|
||||
public class KeyGenerationTest {
|
||||
|
||||
@Ignore
|
||||
public void createSecretKey() throws Exception {
|
||||
PGPSecretKey secretKey = BCOpenPgpProvider
|
||||
.generateKey(JidCreate.bareFrom("alice@wonderland.lit"))
|
||||
.generateSecretKeyRing()
|
||||
.getSecretKey();
|
||||
assertEquals(PublicKeyType.RSA_GENERAL.getId(), secretKey.getPublicKey().getAlgorithm());
|
||||
assertTrue(secretKey.getPublicKey().isEncryptionKey());
|
||||
assertEquals(2048, secretKey.getPublicKey().getBitStrength());
|
||||
assertEquals("xmpp:alice@wonderland.lit", secretKey.getPublicKey().getUserIDs().next());
|
||||
}
|
||||
}
|
|
@ -232,14 +232,14 @@ public final class OpenPgpManager extends Manager {
|
|||
SecretKeyBackupSelectionCallback selectKeyCallback)
|
||||
throws InterruptedException, PubSubException.NotALeafNodeException,
|
||||
XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException,
|
||||
SmackException.NotLoggedInException {
|
||||
SmackException.NotLoggedInException, SmackOpenPgpException, IOException {
|
||||
throwIfNoProviderSet();
|
||||
throwIfNotAuthenticated();
|
||||
|
||||
BareJid ownJid = connection().getUser().asBareJid();
|
||||
|
||||
String backupCode = generateBackupPassword();
|
||||
Set<OpenPgpV4Fingerprint> availableKeyPairs = provider.getStore().getAvailableKeyPairFingerprints();
|
||||
Set<OpenPgpV4Fingerprint> availableKeyPairs = provider.getStore().getAvailableKeyPairFingerprints(ownJid);
|
||||
Set<OpenPgpV4Fingerprint> selectedKeyPairs = selectKeyCallback.selectKeysToBackup(availableKeyPairs);
|
||||
|
||||
SecretkeyElement secretKey = createSecretkeyElement(ownJid, selectedKeyPairs, backupCode);
|
||||
|
@ -419,7 +419,7 @@ public final class OpenPgpManager extends Manager {
|
|||
OpenPgpV4Fingerprint fingerprint,
|
||||
Date date)
|
||||
throws MissingOpenPgpPublicKeyException {
|
||||
byte[] keyBytes = provider.getStore().getPublicKeyBytes(owner, fingerprint);
|
||||
byte[] keyBytes = provider.getStore().getPublicKeyRingBytes(owner, fingerprint);
|
||||
return createPubkeyElement(keyBytes, date);
|
||||
}
|
||||
|
||||
|
@ -429,11 +429,11 @@ public final class OpenPgpManager extends Manager {
|
|||
|
||||
private SecretkeyElement createSecretkeyElement(BareJid owner,
|
||||
Set<OpenPgpV4Fingerprint> fingerprints,
|
||||
String backupCode) {
|
||||
String backupCode) throws SmackOpenPgpException, IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
for (OpenPgpV4Fingerprint fingerprint : fingerprints) {
|
||||
try {
|
||||
byte[] bytes = provider.getStore().getSecretKeyBytes(owner, fingerprint);
|
||||
byte[] bytes = provider.getStore().getSecretKeyRingBytes(owner, fingerprint);
|
||||
buffer.write(bytes);
|
||||
} catch (MissingOpenPgpKeyPairException | IOException e) {
|
||||
LOGGER.log(Level.WARNING, "Cannot backup secret key " + Long.toHexString(fingerprint.getKeyId()) + ".", e);
|
||||
|
@ -443,7 +443,8 @@ public final class OpenPgpManager extends Manager {
|
|||
return createSecretkeyElement(buffer.toByteArray(), backupCode);
|
||||
}
|
||||
|
||||
private SecretkeyElement createSecretkeyElement(byte[] keys, String backupCode) {
|
||||
private SecretkeyElement createSecretkeyElement(byte[] keys, String backupCode)
|
||||
throws SmackOpenPgpException, IOException {
|
||||
byte[] encrypted = provider.symmetricallyEncryptWithPassword(keys, backupCode);
|
||||
return new SecretkeyElement(Base64.encode(encrypted));
|
||||
}
|
||||
|
|
|
@ -49,11 +49,12 @@ public interface OpenPgpStore {
|
|||
|
||||
/**
|
||||
* Return a {@link Set} containing the {@link OpenPgpV4Fingerprint}s of the master keys of all available
|
||||
* OpenPGP key pairs.
|
||||
* OpenPGP key pairs of {@code owner}.
|
||||
*
|
||||
* @param owner owner.
|
||||
* @return set of fingerprints of available OpenPGP key pairs master keys.
|
||||
*/
|
||||
Set<OpenPgpV4Fingerprint> getAvailableKeyPairFingerprints();
|
||||
Set<OpenPgpV4Fingerprint> getAvailableKeyPairFingerprints(BareJid owner) throws SmackOpenPgpException;
|
||||
|
||||
/**
|
||||
* Return a {@link Map} containing the {@link OpenPgpV4Fingerprint}s of all OpenPGP public keys of a
|
||||
|
@ -86,7 +87,7 @@ public interface OpenPgpStore {
|
|||
Map<OpenPgpV4Fingerprint, Date> getAnnouncedKeysFingerprints(BareJid contact);
|
||||
|
||||
/**
|
||||
* Store a {@Map} of a contacts fingerprints and publication dates in persistent storage.
|
||||
* 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}.
|
||||
|
@ -94,22 +95,22 @@ public interface OpenPgpStore {
|
|||
void setAnnouncedKeysFingerprints(BareJid contact, Map<OpenPgpV4Fingerprint, Date> fingerprints);
|
||||
|
||||
/**
|
||||
* Return the {@link Date} of the last revision which was fetched from PubSub.
|
||||
* 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 key
|
||||
* @param fingerprint fingerprint of the key.
|
||||
* @return {@link Date} or {@code null} if no record found.
|
||||
* @param owner owner of the keys
|
||||
* @return {@link Map} of keys last revision dates.
|
||||
*/
|
||||
Date getPubkeysLastRevision(BareJid owner, OpenPgpV4Fingerprint fingerprint);
|
||||
Map<OpenPgpV4Fingerprint, Date> getPubkeysLastRevisions(BareJid owner);
|
||||
|
||||
/**
|
||||
* Set the {@link Date} of the last revision which was fetched from PubSub.
|
||||
* Set the last revision dates of all keys of a contact.
|
||||
*
|
||||
* @param owner owner of the key
|
||||
* @param fingerprint fingerprint of the key
|
||||
* @param revision {@link Date} of the revision
|
||||
* @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, OpenPgpV4Fingerprint fingerprint, Date revision);
|
||||
void setPubkeysLastRevision(BareJid owner, Map<OpenPgpV4Fingerprint, Date> revisionDates);
|
||||
|
||||
/**
|
||||
* Return a {@link MultiMap} which contains contacts and their trusted keys {@link OpenPgpV4Fingerprint}s.
|
||||
|
@ -125,7 +126,7 @@ public interface OpenPgpStore {
|
|||
* @param fingerprint fingerprint of the key
|
||||
* @return byte representation of the public key.
|
||||
*/
|
||||
byte[] getPublicKeyBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
|
||||
byte[] getPublicKeyRingBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
|
||||
throws MissingOpenPgpPublicKeyException;
|
||||
|
||||
/**
|
||||
|
@ -135,7 +136,7 @@ public interface OpenPgpStore {
|
|||
* @param fingerprint fingerprint of the key
|
||||
* @return byte representation of the secret key.
|
||||
*/
|
||||
byte[] getSecretKeyBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
|
||||
byte[] getSecretKeyRingBytes(BareJid owner, OpenPgpV4Fingerprint fingerprint)
|
||||
throws MissingOpenPgpKeyPairException;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ox.callback;
|
||||
|
||||
public interface SmackMissingOpenPgpPublicKeyCallback {
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* Chat related classes for XEP-0373: OpenPGP for XMPP.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ox.chat;
|
|
@ -16,6 +16,8 @@
|
|||
*/
|
||||
package org.jivesoftware.smackx.ox.exception;
|
||||
|
||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
||||
|
||||
import org.jxmpp.jid.BareJid;
|
||||
|
||||
/**
|
||||
|
@ -26,15 +28,23 @@ public class MissingOpenPgpKeyPairException extends Exception {
|
|||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final BareJid owner;
|
||||
private final OpenPgpV4Fingerprint fingerprint;
|
||||
|
||||
/**
|
||||
* Create a new {@link MissingOpenPgpKeyPairException}.
|
||||
*
|
||||
* @param owner owner of the missing key pair.
|
||||
* @param e
|
||||
* @param fingerprint fingerprint of the missing key.
|
||||
*/
|
||||
public MissingOpenPgpKeyPairException(BareJid owner, Throwable e) {
|
||||
super("Missing OpenPGP key pair for user " + owner);
|
||||
public MissingOpenPgpKeyPairException(BareJid owner, OpenPgpV4Fingerprint fingerprint) {
|
||||
super("Missing OpenPGP key pair " + fingerprint.toString() + " for user " + owner);
|
||||
this.owner = owner;
|
||||
this.fingerprint = fingerprint;
|
||||
}
|
||||
|
||||
public MissingOpenPgpKeyPairException(BareJid owner, OpenPgpV4Fingerprint fingerprint, Throwable e) {
|
||||
super("Missing OpenPGP key pair " + fingerprint.toString() + " for user " + owner, e);
|
||||
this.fingerprint = fingerprint;
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
|
@ -46,4 +56,10 @@ public class MissingOpenPgpKeyPairException extends Exception {
|
|||
public BareJid getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public OpenPgpV4Fingerprint getFingerprint() {
|
||||
return fingerprint;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ public class MissingOpenPgpPublicKeyException extends Exception {
|
|||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private BareJid user;
|
||||
private OpenPgpV4Fingerprint fingerprint;
|
||||
private final BareJid owner;
|
||||
private final OpenPgpV4Fingerprint fingerprint;
|
||||
|
||||
/**
|
||||
* Create a new {@link MissingOpenPgpPublicKeyException}.
|
||||
|
@ -37,22 +37,25 @@ public class MissingOpenPgpPublicKeyException extends Exception {
|
|||
* @param fingerprint {@link OpenPgpV4Fingerprint} of the missing key.
|
||||
*/
|
||||
public MissingOpenPgpPublicKeyException(BareJid owner, OpenPgpV4Fingerprint fingerprint) {
|
||||
super("Missing public key " + fingerprint.toString() + " for user " + owner + ".");
|
||||
this.user = owner;
|
||||
super("Missing public key " + fingerprint.toString() + " for owner " + owner + ".");
|
||||
this.owner = owner;
|
||||
this.fingerprint = fingerprint;
|
||||
}
|
||||
|
||||
public MissingOpenPgpPublicKeyException(Throwable e) {
|
||||
|
||||
public MissingOpenPgpPublicKeyException(BareJid owner, OpenPgpV4Fingerprint fingerprint, Throwable e) {
|
||||
super("Missing public key " + fingerprint.toString() + " for owner " + owner + ".", e);
|
||||
this.owner = owner;
|
||||
this.fingerprint = fingerprint;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the {@link BareJid} of the owner of the missing key.
|
||||
*
|
||||
* @return owner of missing key.
|
||||
*/
|
||||
public BareJid getUser() {
|
||||
return user;
|
||||
public BareJid getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ox.exception;
|
||||
|
||||
import org.jxmpp.jid.BareJid;
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ox.exception;
|
||||
|
||||
public class NoBackupFoundException {
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ox.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ox.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/**
|
||||
* Utility classes for XEP-0373: OpenPGP for XMPP.
|
||||
*/
|
||||
package org.jivesoftware.smackx.ox.util;
|
Loading…
Reference in a new issue