2018-06-14 14:40:35 +02:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-06-13 18:39:09 +02:00
|
|
|
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;
|
2018-06-19 15:06:25 +02:00
|
|
|
import java.util.ArrayList;
|
2018-06-14 16:02:53 +02:00
|
|
|
import java.util.Collections;
|
2018-06-13 18:39:09 +02:00
|
|
|
import java.util.HashSet;
|
2018-06-14 16:02:53 +02:00
|
|
|
import java.util.Iterator;
|
2018-06-19 15:06:25 +02:00
|
|
|
import java.util.List;
|
2018-06-13 18:39:09 +02:00
|
|
|
import java.util.Set;
|
2018-06-19 15:06:25 +02:00
|
|
|
import java.util.logging.Level;
|
|
|
|
import java.util.logging.Logger;
|
2018-06-13 18:39:09 +02:00
|
|
|
|
|
|
|
import org.jivesoftware.smack.util.MultiMap;
|
|
|
|
import org.jivesoftware.smackx.ox.OpenPgpProvider;
|
|
|
|
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
2018-07-07 17:58:32 +02:00
|
|
|
import org.jivesoftware.smackx.ox.callback.backup.SmackMissingOpenPgpPublicKeyCallback;
|
2018-06-13 18:39:09 +02:00
|
|
|
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;
|
2018-07-07 17:58:32 +02:00
|
|
|
import org.jivesoftware.smackx.ox.selection_strategy.BareJidUserId;
|
2018-06-13 18:39:09 +02:00
|
|
|
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;
|
2018-06-14 16:02:53 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
|
|
|
|
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
|
2018-06-13 18:39:09 +02:00
|
|
|
import org.bouncycastle.util.encoders.Hex;
|
|
|
|
import org.bouncycastle.util.io.Streams;
|
|
|
|
import org.jxmpp.jid.BareJid;
|
2018-06-30 16:03:21 +02:00
|
|
|
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;
|
2018-07-06 14:15:40 +02:00
|
|
|
import org.pgpainless.pgpainless.key.protection.SecretKeyRingProtector;
|
2018-06-30 16:03:21 +02:00
|
|
|
import org.pgpainless.pgpainless.util.BCUtil;
|
2018-06-13 18:39:09 +02:00
|
|
|
|
|
|
|
public class PainlessOpenPgpProvider implements OpenPgpProvider {
|
|
|
|
|
2018-06-19 15:06:25 +02:00
|
|
|
private static final Logger LOGGER = Logger.getLogger(PainlessOpenPgpProvider.class.getName());
|
|
|
|
|
2018-06-13 18:39:09 +02:00
|
|
|
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 {
|
|
|
|
|
2018-06-19 15:06:25 +02:00
|
|
|
PGPSecretKeyRing secretKeyRing;
|
|
|
|
SecretKeyRingProtector protector = getStore().getSecretKeyProtector();
|
|
|
|
|
|
|
|
try {
|
2018-06-21 15:20:04 +02:00
|
|
|
secretKeyRing = getStore()
|
|
|
|
.getSecretKeyRings(owner)
|
|
|
|
.getSecretKeyRing(
|
|
|
|
signingKey.getKeyId());
|
2018-06-19 15:06:25 +02:00
|
|
|
} 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);
|
|
|
|
}
|
2018-06-13 18:39:09 +02:00
|
|
|
|
2018-06-19 15:06:25 +02:00
|
|
|
byte[] signAndEncryptImpl(SigncryptElement element,
|
|
|
|
PGPSecretKeyRing signingKey,
|
|
|
|
SecretKeyRingProtector secretKeyRingProtector,
|
|
|
|
MultiMap<BareJid, PGPPublicKeyRing> encryptionKeys)
|
|
|
|
throws SmackOpenPgpException, IOException {
|
2018-06-13 18:39:09 +02:00
|
|
|
InputStream fromPlain = element.toInputStream();
|
2018-06-19 15:06:25 +02:00
|
|
|
ByteArrayOutputStream encryptedBytes = new ByteArrayOutputStream();
|
|
|
|
|
|
|
|
OutputStream toEncrypted;
|
2018-06-13 18:39:09 +02:00
|
|
|
try {
|
2018-06-19 15:06:25 +02:00
|
|
|
toEncrypted = PGPainless.createEncryptor()
|
|
|
|
.onOutputStream(encryptedBytes)
|
2018-06-20 11:02:30 +02:00
|
|
|
.toRecipients(new ArrayList<>(encryptionKeys.values()).toArray(new PGPPublicKeyRing[] {}))
|
2018-06-13 18:39:09 +02:00
|
|
|
.usingSecureAlgorithms()
|
2018-06-19 15:06:25 +02:00
|
|
|
.signWith(secretKeyRingProtector, signingKey)
|
2018-06-13 18:39:09 +02:00
|
|
|
.noArmor();
|
2018-06-19 15:06:25 +02:00
|
|
|
} catch (PGPException | IOException e) {
|
2018-06-13 18:39:09 +02:00
|
|
|
throw new SmackOpenPgpException(e);
|
|
|
|
}
|
|
|
|
|
2018-06-19 15:06:25 +02:00
|
|
|
Streams.pipeAll(fromPlain, toEncrypted);
|
|
|
|
toEncrypted.flush();
|
|
|
|
toEncrypted.close();
|
2018-06-13 18:39:09 +02:00
|
|
|
|
2018-06-19 15:06:25 +02:00
|
|
|
encryptedBytes.close();
|
|
|
|
|
|
|
|
return encryptedBytes.toByteArray();
|
2018-06-13 18:39:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public byte[] sign(SignElement element, OpenPgpV4Fingerprint signingKeyFingerprint)
|
|
|
|
throws MissingOpenPgpKeyPairException, IOException, SmackOpenPgpException {
|
|
|
|
PGPSecretKeyRing signingKeyRing;
|
|
|
|
try {
|
2018-06-14 14:40:35 +02:00
|
|
|
signingKeyRing = store.getSecretKeyRings(owner).getSecretKeyRing(signingKeyFingerprint.getKeyId());
|
2018-06-13 18:39:09 +02:00
|
|
|
} catch (PGPException e) {
|
2018-06-14 14:40:35 +02:00
|
|
|
throw new MissingOpenPgpKeyPairException(owner, signingKeyFingerprint, e);
|
2018-06-13 18:39:09 +02:00
|
|
|
}
|
|
|
|
|
2018-06-27 16:20:55 +02:00
|
|
|
return signImpl(element, signingKeyRing, store.getSecretKeyProtector());
|
|
|
|
}
|
|
|
|
|
|
|
|
byte[] signImpl(SignElement element, PGPSecretKeyRing signingKey, SecretKeyRingProtector secretKeyRingProtector)
|
|
|
|
throws IOException, SmackOpenPgpException {
|
2018-06-13 18:39:09 +02:00
|
|
|
ByteArrayOutputStream toSigned = new ByteArrayOutputStream();
|
|
|
|
OutputStream signer;
|
|
|
|
try {
|
|
|
|
signer = PGPainless.createEncryptor().onOutputStream(toSigned)
|
|
|
|
.doNotEncrypt()
|
2018-06-27 16:20:55 +02:00
|
|
|
.signWith(secretKeyRingProtector, signingKey)
|
2018-06-13 18:39:09 +02:00
|
|
|
.noArmor();
|
|
|
|
} catch (PGPException e) {
|
|
|
|
throw new SmackOpenPgpException(e);
|
|
|
|
}
|
|
|
|
|
2018-06-27 16:20:55 +02:00
|
|
|
InputStream fromPlain = element.toInputStream();
|
2018-06-13 18:39:09 +02:00
|
|
|
Streams.pipeAll(fromPlain, signer);
|
|
|
|
|
|
|
|
fromPlain.close();
|
|
|
|
signer.close();
|
|
|
|
|
|
|
|
return toSigned.toByteArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public byte[] encrypt(CryptElement element, MultiMap<BareJid, OpenPgpV4Fingerprint> encryptionKeyFingerprints)
|
2018-06-27 16:20:55 +02:00
|
|
|
throws IOException, SmackOpenPgpException {
|
2018-06-14 16:02:53 +02:00
|
|
|
PGPPublicKeyRing[] allRecipientsKeys = getEncryptionKeys(encryptionKeyFingerprints);
|
2018-06-27 16:20:55 +02:00
|
|
|
return encryptImpl(element, allRecipientsKeys);
|
|
|
|
}
|
2018-06-13 18:39:09 +02:00
|
|
|
|
2018-06-27 16:20:55 +02:00
|
|
|
byte[] encryptImpl(CryptElement element, PGPPublicKeyRing[] encryptionKeys)
|
|
|
|
throws IOException, SmackOpenPgpException {
|
2018-06-13 18:39:09 +02:00
|
|
|
InputStream fromPlain = element.toInputStream();
|
|
|
|
ByteArrayOutputStream encrypted = new ByteArrayOutputStream();
|
|
|
|
OutputStream encryptor;
|
|
|
|
try {
|
|
|
|
encryptor = PGPainless.createEncryptor()
|
|
|
|
.onOutputStream(encrypted)
|
2018-06-27 16:20:55 +02:00
|
|
|
.toRecipients(encryptionKeys)
|
2018-06-13 18:39:09 +02:00
|
|
|
.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)
|
2018-06-20 11:02:30 +02:00
|
|
|
throws MissingOpenPgpKeyPairException, SmackOpenPgpException {
|
2018-06-19 15:06:25 +02:00
|
|
|
|
|
|
|
PGPSecretKeyRingCollection secretKeyRings;
|
|
|
|
try {
|
|
|
|
secretKeyRings = getStore().getSecretKeyRings(owner);
|
2018-06-20 11:02:30 +02:00
|
|
|
} catch (PGPException | IOException e) {
|
2018-06-19 15:06:25 +02:00
|
|
|
LOGGER.log(Level.INFO, "Could not get secret keys of user " + owner);
|
2018-07-03 11:29:27 +02:00
|
|
|
throw new MissingOpenPgpKeyPairException(owner, getStore().getSigningKeyPairFingerprint());
|
2018-06-19 15:06:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
SecretKeyRingProtector protector = getStore().getSecretKeyProtector();
|
|
|
|
|
|
|
|
List<OpenPgpV4Fingerprint> trustedFingerprints = getStore().getAllContactsTrustedFingerprints().getAll(sender);
|
2018-06-13 18:39:09 +02:00
|
|
|
Set<Long> trustedKeyIds = new HashSet<>();
|
2018-06-19 15:06:25 +02:00
|
|
|
for (OpenPgpV4Fingerprint fingerprint : trustedFingerprints) {
|
|
|
|
trustedKeyIds.add(fingerprint.getKeyId());
|
|
|
|
}
|
|
|
|
|
|
|
|
PGPPublicKeyRingCollection publicKeyRings;
|
|
|
|
try {
|
|
|
|
publicKeyRings = getStore().getPublicKeyRings(sender);
|
2018-06-20 11:02:30 +02:00
|
|
|
} catch (PGPException | IOException e) {
|
2018-06-19 15:06:25 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-20 11:02:30 +02:00
|
|
|
try {
|
|
|
|
return decryptImpl(bytes, secretKeyRings, protector, trustedKeys);
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new SmackOpenPgpException(e);
|
|
|
|
}
|
2018-06-19 15:06:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
DecryptedBytesAndMetadata decryptImpl(byte[] bytes, PGPSecretKeyRingCollection decryptionKeys,
|
|
|
|
SecretKeyRingProtector protector,
|
|
|
|
Set<PGPPublicKeyRing> verificationKeys)
|
|
|
|
throws SmackOpenPgpException, IOException {
|
|
|
|
|
|
|
|
InputStream encryptedBytes = new ByteArrayInputStream(bytes);
|
2018-06-13 18:39:09 +02:00
|
|
|
ByteArrayOutputStream toPlain = new ByteArrayOutputStream();
|
2018-06-19 15:06:25 +02:00
|
|
|
DecryptionStream fromEncrypted;
|
2018-06-13 18:39:09 +02:00
|
|
|
try {
|
2018-06-19 15:06:25 +02:00
|
|
|
fromEncrypted = PGPainless.createDecryptor()
|
|
|
|
.onInputStream(encryptedBytes)
|
2018-06-27 16:20:55 +02:00
|
|
|
.decryptWith(protector, decryptionKeys)
|
2018-06-19 16:41:02 +02:00
|
|
|
.verifyWith(verificationKeys)
|
2018-06-19 15:06:25 +02:00
|
|
|
.ignoreMissingPublicKeys()
|
2018-06-13 18:39:09 +02:00
|
|
|
.build();
|
2018-06-19 15:06:25 +02:00
|
|
|
} catch (IOException | PGPException e) {
|
2018-06-13 18:39:09 +02:00
|
|
|
throw new SmackOpenPgpException(e);
|
|
|
|
}
|
|
|
|
|
2018-06-19 15:06:25 +02:00
|
|
|
Streams.pipeAll(fromEncrypted, toPlain);
|
|
|
|
|
|
|
|
fromEncrypted.close();
|
|
|
|
toPlain.flush();
|
|
|
|
toPlain.close();
|
2018-06-13 18:39:09 +02:00
|
|
|
|
2018-06-19 15:06:25 +02:00
|
|
|
PainlessResult result = fromEncrypted.getResult();
|
2018-06-13 18:39:09 +02:00
|
|
|
return new DecryptedBytesAndMetadata(toPlain.toByteArray(),
|
|
|
|
result.getVerifiedSignatureKeyIds(),
|
|
|
|
result.getDecryptionKeyId());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public byte[] symmetricallyEncryptWithPassword(byte[] bytes, String password)
|
|
|
|
throws SmackOpenPgpException, IOException {
|
|
|
|
try {
|
2018-06-19 15:06:25 +02:00
|
|
|
return PGPainless.encryptWithPassword(bytes, password.toCharArray(), SymmetricKeyAlgorithm.AES_256);
|
2018-06-13 18:39:09 +02:00
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
2018-06-14 16:02:53 +02:00
|
|
|
private PGPPublicKeyRing[] getEncryptionKeys(MultiMap<BareJid, OpenPgpV4Fingerprint> encryptionKeys)
|
2018-06-13 18:39:09 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-14 16:02:53 +02:00
|
|
|
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;
|
2018-06-13 18:39:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private PGPSecretKeyRing getSigningKey(OpenPgpV4Fingerprint signingKey)
|
|
|
|
throws IOException, MissingOpenPgpKeyPairException {
|
|
|
|
PGPSecretKeyRing signingKeyRing;
|
|
|
|
try {
|
2018-06-14 14:40:35 +02:00
|
|
|
signingKeyRing = store.getSecretKeyRings(owner).getSecretKeyRing(signingKey.getKeyId());
|
2018-06-13 18:39:09 +02:00
|
|
|
} catch (PGPException e) {
|
2018-06-14 14:40:35 +02:00
|
|
|
throw new MissingOpenPgpKeyPairException(owner, signingKey, e);
|
2018-06-13 18:39:09 +02:00
|
|
|
}
|
|
|
|
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
|
2018-06-14 16:02:53 +02:00
|
|
|
public OpenPgpV4Fingerprint importPublicKey(BareJid owner, byte[] bytes)
|
|
|
|
throws MissingUserIdOnKeyException, IOException, SmackOpenPgpException {
|
|
|
|
PGPPublicKeyRing publicKeys = new PGPPublicKeyRing(bytes, new BcKeyFingerprintCalculator());
|
2018-06-25 19:54:45 +02:00
|
|
|
// LOGGER.log(Level.INFO, "Importing key " + Long.toHexString(publicKeys.getPublicKey().getKeyID()));
|
2018-06-14 16:02:53 +02:00
|
|
|
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 {
|
2018-06-21 17:45:22 +02:00
|
|
|
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.");
|
|
|
|
}
|
2018-06-14 16:02:53 +02:00
|
|
|
}
|
|
|
|
getStore().storePublicKeyRing(owner, publicKeyRings);
|
|
|
|
} catch (PGPException e) {
|
|
|
|
throw new SmackOpenPgpException(e);
|
|
|
|
}
|
|
|
|
return getFingerprint(ring.getPublicKey());
|
2018-06-13 18:39:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2018-06-14 16:02:53 +02:00
|
|
|
public OpenPgpV4Fingerprint importSecretKey(BareJid owner, byte[] bytes)
|
|
|
|
throws MissingUserIdOnKeyException, SmackOpenPgpException, IOException {
|
2018-06-21 16:06:42 +02:00
|
|
|
PGPSecretKeyRing importSecretKeys;
|
2018-06-14 16:02:53 +02:00
|
|
|
try {
|
2018-06-21 16:06:42 +02:00
|
|
|
importSecretKeys = new PGPSecretKeyRing(bytes, new BcKeyFingerprintCalculator());
|
2018-06-14 16:02:53 +02:00
|
|
|
} catch (PGPException | IOException e) {
|
|
|
|
throw new SmackOpenPgpException("Could not deserialize PGP secret key of " + owner.toString(), e);
|
|
|
|
}
|
|
|
|
|
2018-06-21 16:06:42 +02:00
|
|
|
if (!new BareJidUserId.SecRingSelectionStrategy().accept(owner, importSecretKeys)) {
|
|
|
|
throw new MissingUserIdOnKeyException(owner, importSecretKeys.getPublicKey().getKeyID());
|
2018-06-14 16:02:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2018-06-21 16:06:42 +02:00
|
|
|
secretKeyRings = new PGPSecretKeyRingCollection(Collections.singleton(importSecretKeys));
|
2018-06-14 16:02:53 +02:00
|
|
|
} catch (IOException | PGPException e) {
|
|
|
|
throw new SmackOpenPgpException("Could not create SecretKeyRingCollection from SecretKeyRing.", e);
|
|
|
|
}
|
|
|
|
} else {
|
2018-06-21 15:57:56 +02:00
|
|
|
try {
|
2018-06-21 16:06:42 +02:00
|
|
|
secretKeyRings = PGPSecretKeyRingCollection.addSecretKeyRing(secretKeyRings, importSecretKeys);
|
2018-06-21 15:57:56 +02:00
|
|
|
} catch (IllegalArgumentException e) {
|
2018-06-21 16:06:42 +02:00
|
|
|
LOGGER.log(Level.INFO, "Skip key " + Long.toHexString(importSecretKeys.getPublicKey().getKeyID()) +
|
2018-06-21 15:57:56 +02:00
|
|
|
" as it is already part of the key ring.");
|
|
|
|
}
|
2018-06-14 16:02:53 +02:00
|
|
|
}
|
|
|
|
getStore().storeSecretKeyRing(owner, secretKeyRings);
|
|
|
|
|
2018-06-21 16:06:42 +02:00
|
|
|
PGPPublicKeyRing publicKeys = BCUtil.publicKeyRingFromSecretKeyRing(importSecretKeys);
|
2018-06-14 16:02:53 +02:00
|
|
|
importPublicKey(owner, publicKeys);
|
|
|
|
|
|
|
|
return getFingerprint(publicKeys.getPublicKey());
|
2018-06-13 18:39:09 +02:00
|
|
|
}
|
|
|
|
|
2018-06-21 17:45:22 +02:00
|
|
|
@Override
|
|
|
|
public OpenPgpV4Fingerprint importSecretKey(byte[] bytes)
|
|
|
|
throws MissingUserIdOnKeyException, SmackOpenPgpException, IOException {
|
|
|
|
return importSecretKey(owner, bytes);
|
|
|
|
}
|
|
|
|
|
2018-06-13 18:39:09 +02:00
|
|
|
public static OpenPgpV4Fingerprint getFingerprint(PGPPublicKey publicKey) {
|
|
|
|
byte[] hex = Hex.encode(publicKey.getFingerprint());
|
|
|
|
return new OpenPgpV4Fingerprint(hex);
|
|
|
|
}
|
|
|
|
}
|