diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/OpenPgpIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/OpenPgpIntegrationTest.java new file mode 100644 index 000000000..217dedbe4 --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/OpenPgpIntegrationTest.java @@ -0,0 +1,4 @@ +package org.jivesoftware.smackx.openpgp; + +public class OpenPgpIntegrationTest { +} diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/package-info.java new file mode 100644 index 000000000..49e29068b --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/openpgp/package-info.java @@ -0,0 +1,21 @@ +/** + * + * Copyright 2015 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. + */ + +/** + * Smacks implementation of XEP-0199: XMPP Ping. + */ +package org.jivesoftware.smackx.ping; diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BCOpenPgpProvider.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BCOpenPgpProvider.java index 7a246c4d9..ba9a09988 100644 --- a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BCOpenPgpProvider.java +++ b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BCOpenPgpProvider.java @@ -1,11 +1,18 @@ 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.nio.charset.Charset; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; +import java.util.Date; import java.util.Set; -import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.util.stringencoder.Base64; import org.jivesoftware.smackx.ox.OpenPgpMessage; import org.jivesoftware.smackx.ox.OpenPgpProvider; import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint; @@ -22,21 +29,34 @@ import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException; import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException; import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException; +import name.neuhalfen.projects.crypto.bouncycastle.openpgp.BouncyGPG; +import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PublicKeySize; +import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.XmppKeySelectionStrategy; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyRingGenerator; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.io.Streams; import org.jxmpp.jid.BareJid; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; public class BCOpenPgpProvider implements OpenPgpProvider { private final BareJid user; private OpenPgpV4Fingerprint primaryKeyPair; + private BCOpenPgpStore store; + public BCOpenPgpProvider(BareJid user) { this.user = user; this.primaryKeyPair = null; } + public void setStore(BCOpenPgpStore store) { + this.store = store; + } + @Override public OpenPgpV4Fingerprint primaryOpenPgpKeyPairFingerprint() { return primaryKeyPair; @@ -44,89 +64,153 @@ public class BCOpenPgpProvider implements OpenPgpProvider { @Override public Set availableOpenPgpKeyPairFingerprints() { - return null; + return store.availableOpenPgpKeyPairFingerprints(); } @Override public Set announcedOpenPgpKeyFingerprints(BareJid contact) { - return null; + return store.announcedOpenPgpKeyFingerprints(contact); } @Override public OpenPgpElement signAndEncrypt(SigncryptElement element, OpenPgpV4Fingerprint signingKey, Set encryptionKeys) throws MissingOpenPgpKeyPairException, MissingOpenPgpPublicKeyException { - return null; + if (encryptionKeys.isEmpty()) { + throw new IllegalArgumentException("Set of recipients must not be empty"); + } + + encryptionKeys.addAll(store.announcedOpenPgpKeyFingerprints(user)); + long[] recipientIds = new long[encryptionKeys.size()]; + int pos = 0; + for (OpenPgpV4Fingerprint f : encryptionKeys) { + recipientIds[pos++] = f.getKeyId(); + } + + InputStream inputStream = element.toInputStream(); + ByteArrayOutputStream encryptedOut = new ByteArrayOutputStream(); + + try { + OutputStream encryptor = BouncyGPG.encryptToStream() + .withConfig(store.getKeyringConfig()) + .withKeySelectionStrategy(new XmppKeySelectionStrategy(new Date())) + .withOxAlgorithms() + .toRecipients(recipientIds) + .andSignWith(signingKey.getKeyId()) + .binaryOutput() + .andWriteTo(encryptedOut); + + Streams.pipeAll(inputStream, encryptor); + encryptor.close(); + + String base64 = Base64.encodeToString(encryptedOut.toByteArray()); + + return new OpenPgpElement(base64); + } catch (Exception e) { + throw new AssertionError(e); + // TODO: Later + } } @Override public OpenPgpMessage decryptAndVerify(OpenPgpElement element, Set sendersKeys) throws MissingOpenPgpKeyPairException, MissingOpenPgpPublicKeyException { - return null; + + ByteArrayInputStream encryptedIn = new ByteArrayInputStream( + element.getEncryptedBase64MessageContent().getBytes(Charset.forName("UTF-8"))); + + try { + InputStream decrypted = BouncyGPG.decryptAndVerifyStream() + .withConfig(store.getKeyringConfig()) + .withKeySelectionStrategy(new XmppKeySelectionStrategy(new Date())) + .andValidateSomeoneSigned() // TODO: Validate using sender keys + .fromEncryptedInputStream(encryptedIn); + + ByteArrayOutputStream decryptedOut = new ByteArrayOutputStream(); + + Streams.pipeAll(decrypted, decryptedOut); + + return new OpenPgpMessage(OpenPgpMessage.State.signcrypt, new String(decryptedOut.toByteArray(), Charset.forName("UTF-8"))); + } catch (IOException e) { + // TODO: Hm... + return null; + } catch (NoSuchProviderException e) { + throw new AssertionError(e); + } } @Override public OpenPgpElement sign(SignElement element, OpenPgpV4Fingerprint singingKeyFingerprint) throws MissingOpenPgpKeyPairException { - return null; + throw new NotImplementedException(); } @Override public OpenPgpMessage verify(OpenPgpElement element, Set singingKeyFingerprints) throws MissingOpenPgpPublicKeyException { - return null; + throw new NotImplementedException(); } @Override public OpenPgpElement encrypt(CryptElement element, Set encryptionKeyFingerprints) throws MissingOpenPgpPublicKeyException { - return null; + throw new NotImplementedException(); } @Override public OpenPgpMessage decrypt(OpenPgpElement element) throws MissingOpenPgpKeyPairException { - return null; + throw new NotImplementedException(); } @Override public PubkeyElement createPubkeyElement(OpenPgpV4Fingerprint fingerprint) throws MissingOpenPgpPublicKeyException, CorruptedOpenPgpKeyException { - return null; + return store.createPubkeyElement(fingerprint); } @Override public void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element) throws CorruptedOpenPgpKeyException { - + store.storePublicKey(owner, fingerprint, element); } @Override - public void storePublicKeysList(XMPPConnection connection, PublicKeysListElement listElement, BareJid owner) - throws CorruptedOpenPgpKeyException, InterruptedException, SmackException.NotConnectedException, - SmackException.NoResponseException { + public void storePublicKeysList(XMPPConnection connection, PublicKeysListElement listElement, BareJid owner) { + store.storePublicKeysList(connection, listElement, owner); + } + @Override + public OpenPgpV4Fingerprint createOpenPgpKeyPair() + throws NoSuchAlgorithmException, NoSuchProviderException, CorruptedOpenPgpKeyException { + return store.createOpenPgpKeyPair(); } @Override public SecretkeyElement createSecretkeyElement(Set fingerprints, String password) throws MissingOpenPgpKeyPairException, CorruptedOpenPgpKeyException { - return null; + return store.createSecretkeyElement(fingerprints, password); } @Override - public Set availableOpenPgpKeysFingerprints(BareJid contact) { - return null; + public Set availableOpenPgpPublicKeysFingerprints(BareJid contact) + throws CorruptedOpenPgpKeyException { + return store.availableOpenPgpPublicKeysFingerprints(contact); } @Override public void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, String password, SecretKeyRestoreSelectionCallback callback) throws CorruptedOpenPgpKeyException, InvalidBackupCodeException { - + store.restoreSecretKeyBackup(secretkeyElement, password, callback); } - @Override - public OpenPgpV4Fingerprint createOpenPgpKeyPair() - throws NoSuchAlgorithmException, NoSuchProviderException { - return null; + static PGPKeyRingGenerator generateKey(BareJid owner) + throws NoSuchAlgorithmException, PGPException, NoSuchProviderException { + PGPKeyRingGenerator generator = BouncyGPG.createKeyPair() + .withRSAKeys() + .ofSize(PublicKeySize.RSA._2048) + .forIdentity("xmpp:" + owner.toString()) + .withoutPassphrase() + .build(); + return generator; } public static OpenPgpV4Fingerprint getFingerprint(PGPPublicKey publicKey) { diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BCOpenPgpStore.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BCOpenPgpStore.java new file mode 100644 index 000000000..32fa64314 --- /dev/null +++ b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BCOpenPgpStore.java @@ -0,0 +1,10 @@ +package org.jivesoftware.smackx.ox.bouncycastle; + +import org.jivesoftware.smackx.ox.OpenPgpStore; + +import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.KeyringConfig; + +public interface BCOpenPgpStore extends OpenPgpStore { + + KeyringConfig getKeyringConfig(); +} diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastleOpenPgpProvider.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastleOpenPgpProvider.java deleted file mode 100644 index d76022a1b..000000000 --- a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastleOpenPgpProvider.java +++ /dev/null @@ -1,414 +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.bouncycastle; - -import java.io.IOException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.logging.Logger; - -import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.util.stringencoder.Base64; -import org.jivesoftware.smackx.ox.OpenPgpMessage; -import org.jivesoftware.smackx.ox.OpenPgpProvider; -import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint; -import org.jivesoftware.smackx.ox.Util; -import org.jivesoftware.smackx.ox.callback.SecretKeyRestoreSelectionCallback; -import org.jivesoftware.smackx.ox.element.CryptElement; -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.SecretkeyElement; -import org.jivesoftware.smackx.ox.element.SignElement; -import org.jivesoftware.smackx.ox.element.SigncryptElement; -import org.jivesoftware.smackx.ox.exception.CorruptedOpenPgpKeyException; -import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException; -import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException; - -import name.neuhalfen.projects.crypto.bouncycastle.openpgp.BouncyGPG; -import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PublicKeySize; -import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.InMemoryKeyring; -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPKeyRingGenerator; -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.util.encoders.Hex; -import org.jxmpp.jid.BareJid; - -public class BouncyCastleOpenPgpProvider implements OpenPgpProvider { - - private static final Logger LOGGER = Logger.getLogger(BouncyCastleOpenPgpProvider.class.getName()); - - private final BareJid ourJid; - private OpenPgpV4Fingerprint primaryKeyPairFingerprint; - private InMemoryKeyring keyring; - - private final Map> contactsFingerprints = new HashMap<>(); - - public BouncyCastleOpenPgpProvider(BareJid ourJid) { - this.ourJid = ourJid; - } - - /** - * {@inheritDoc} - */ - @Override - public PubkeyElement createPubkeyElement(OpenPgpV4Fingerprint keyFingerprint) - throws MissingOpenPgpPublicKeyException, CorruptedOpenPgpKeyException { - // TODO: throw missing key exception - try { - PGPPublicKey pubKey = keyring.getPublicKeyRings().getPublicKey(Util.keyIdFromFingerprint(keyFingerprint)); - PubkeyElement.PubkeyDataElement dataElement = new PubkeyElement.PubkeyDataElement( - Base64.encode(pubKey.getEncoded())); - return new PubkeyElement(dataElement, new Date()); - } catch (PGPException | IOException e) { - throw new CorruptedOpenPgpKeyException(e); - } - } - - /** - * {@inheritDoc} - */ - @Override - public SecretkeyElement createSecretkeyElement(Set fingerprints, String password) throws MissingOpenPgpKeyPairException, CorruptedOpenPgpKeyException { - /* - try { - // Our unencrypted secret key - PGPSecretKey secretKey; - try { - secretKey = ourKeys.getSecretKeyRings().getSecretKey(ourKeyId); - } catch (NullPointerException e) { - throw new MissingOpenPgpKeyPairException(ourJid); - } - - PGPDigestCalculator calculator = new JcaPGPDigestCalculatorProviderBuilder() - .setProvider(BouncyCastleProvider.PROVIDER_NAME) - .build() - .get(HashAlgorithmTags.SHA1); - - PBESecretKeyEncryptor encryptor = new JcePBESecretKeyEncryptorBuilder( - PGPSymmetricEncryptionAlgorithms.AES_256.getAlgorithmId()) - .setProvider(BouncyCastleProvider.PROVIDER_NAME) - .build(password.toCharArray()); - - PGPSecretKey encrypted = new PGPSecretKey( - secretKey.extractPrivateKey(null), - secretKey.getPublicKey(), - calculator, - true, - encryptor); - - byte[] base64 = Base64.encode(encrypted.getEncoded()); - - return new SecretkeyElement(base64); - - } catch (PGPException | IOException e) { - throw new CorruptedOpenPgpKeyException(e); - } - */ - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element) throws CorruptedOpenPgpKeyException { - /* - byte[] decoded = Base64.decode(element.getDataElement().getB64Data()); - - try { - InMemoryKeyring contactsKeyring = theirKeys.get(owner); - if (contactsKeyring == null) { - contactsKeyring = KeyringConfigs.forGpgExportedKeys(KeyringConfigCallbacks.withUnprotectedKeys()); - theirKeys.put(owner, contactsKeyring); - } - - contactsKeyring.addPublicKey(decoded); - } catch (IOException | PGPException e) { - throw new CorruptedOpenPgpKeyException(e); - } - */ - } - - /** - * {@inheritDoc} - */ - @Override - public void storePublicKeysList(XMPPConnection connection, PublicKeysListElement listElement, BareJid owner) { - /* - InMemoryKeyring contactsKeys = theirKeys.get(owner); - for (OpenPgpV4Fingerprint fingerprint : listElement.getMetadata().keySet()) { - byte[] asBytes = fingerprint.toString().getBytes(Charset.forName("UTF-8")); - try { - if (contactsKeys.getPublicKeyRings().getPublicKey(asBytes) == null) { - try { - PubkeyElement pubkey = PubSubDelegate.fetchPubkey(connection, owner, fingerprint); - storePublicKey(pubkey, owner); - } catch (PubSubException.NotAPubSubNodeException | PubSubException.NotALeafNodeException | - XMPPException.XMPPErrorException e) { - LOGGER.log(Level.WARNING, "Could not fetch public key " + fingerprint + " of " + owner + ".", e); - } catch (CorruptedOpenPgpKeyException e) { - LOGGER.log(Level.WARNING, "Key " + fingerprint + " of " + owner + " is corrupted and cannot be imported.", e); - } - } - } catch (PGPException | IOException e) { - throw new CorruptedOpenPgpKeyException(e); - } - } - */ - } - - /** - * {@inheritDoc} - */ - @Override - public void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, String password, SecretKeyRestoreSelectionCallback callback) - throws CorruptedOpenPgpKeyException { - /* - byte[] encoded = Base64.decode(secretkeyElement.getB64Data()); - - try { - PGPDigestCalculatorProvider calculatorProvider = new JcaPGPDigestCalculatorProviderBuilder() - .setProvider(BouncyCastleProvider.PROVIDER_NAME) - .build(); - - InMemoryKeyring keyring = KeyringConfigs.forGpgExportedKeys( - KeyringConfigCallbacks.withPassword(password)); - keyring.addSecretKey(encoded); - for (PGPSecretKeyRing r : keyring.getSecretKeyRings()) { - PGPSecretKey s = r.getSecretKey(); - PGPPrivateKey privateKey = s.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder(calculatorProvider).build(password.toCharArray())); - PGPPublicKey publicKey = s.getPublicKey(); - PGPSecretKey secretKey = new PGPSecretKey( - privateKey, - publicKey, - calculatorProvider.get(PGPHashAlgorithms.SHA1.getAlgorithmId()), - true, - null); - - InMemoryKeyring newKeyring = KeyringConfigs.forGpgExportedKeys( - KeyringConfigCallbacks.withUnprotectedKeys()); - - newKeyring.addPublicKey(secretKey.getPublicKey().getEncoded()); - newKeyring.addSecretKey(secretKey.getEncoded()); - - ourKeys = newKeyring; - ourKeyId = secretKey.getKeyID(); - - InMemoryKeyring theirKeyRing = KeyringConfigs.forGpgExportedKeys( - KeyringConfigCallbacks.withUnprotectedKeys()); - theirKeyRing.addPublicKey(secretKey.getPublicKey().getEncoded()); - - theirKeys.put(ourJid, theirKeyRing); - } - } catch (PGPException | IOException e) { - throw new CorruptedOpenPgpKeyException(e); - } - */ - } - - /** - * {@inheritDoc} - */ - @Override - public OpenPgpV4Fingerprint primaryOpenPgpKeyPairFingerprint() { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Set availableOpenPgpKeyPairFingerprints() { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public Set announcedOpenPgpKeyFingerprints(BareJid contact) { - return null; - } - - @Override - public Set availableOpenPgpKeysFingerprints(BareJid contact) { - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public OpenPgpElement signAndEncrypt(SigncryptElement element, - OpenPgpV4Fingerprint signingKey, - Set encryptionKeys) - throws MissingOpenPgpKeyPairException, MissingOpenPgpPublicKeyException { - /* - if (encryptionKeys.isEmpty()) { - throw new IllegalArgumentException("Set of recipients must not be empty"); - } - - InMemoryKeyring encryptionConfig = KeyringConfigs.forGpgExportedKeys(KeyringConfigCallbacks.withUnprotectedKeys()); - - // Add all recipients public keys to encryption config - for (BareJid recipient : recipients) { - KeyringConfig c = theirKeys.get(recipient); - for (PGPPublicKeyRing p : c.getPublicKeyRings()) { - encryptionConfig.addPublicKey(p.getPublicKey().getEncoded()); - } - } - - // Add our public and secret keys to encryption config - for (PGPPublicKeyRing p : ourKeys.getPublicKeyRings()) { - encryptionConfig.addPublicKey(p.getPublicKey().getEncoded()); - } - for (PGPSecretKeyRing s : ourKeys.getSecretKeyRings()) { - encryptionConfig.addSecretKey(s.getSecretKey().getEncoded()); - } - - String[] recipientUIDs = new String[recipients.size() + 1]; - int pos = 0; - for (BareJid b : recipients) { - recipientUIDs[pos++] = "xmpp:" + b.toString(); - } - recipientUIDs[pos] = "xmpp:" + ourJid.toString(); - - InputStream inputStream = element.toInputStream(); - ByteArrayOutputStream encryptedOut = new ByteArrayOutputStream(); - - OutputStream encryptor = BouncyGPG.encryptToStream() - .withConfig(encryptionConfig) - .withKeySelectionStrategy(new XmppKeySelectionStrategy(new Date())) - .withOxAlgorithms() - .toRecipients(recipientUIDs) - .andSignWith(ourKeyId) - .binaryOutput() - .andWriteTo(encryptedOut); - - Streams.pipeAll(inputStream, encryptor); - encryptor.close(); - - String base64 = Base64.encodeToString(encryptedOut.toByteArray()); - - return new OpenPgpElement(base64); - */ - return null; - } - - /** - * {@inheritDoc} - */ - @Override - public OpenPgpMessage decryptAndVerify(OpenPgpElement element, Set possibleSigningKeys) - throws MissingOpenPgpPublicKeyException, MissingOpenPgpKeyPairException { - /* - InMemoryKeyring decryptionConfig = KeyringConfigs.forGpgExportedKeys(KeyringConfigCallbacks.withUnprotectedKeys()); - - // Add our secret keys to decryption config - for (PGPSecretKeyRing s : ourKeys.getSecretKeyRings()) { - decryptionConfig.addSecretKey(s.getSecretKey().getEncoded()); - } - - // Add their public keys to decryption config - for (PGPPublicKeyRing p : theirKeys.get(sender).getPublicKeyRings()) { - decryptionConfig.addPublicKey(p.getPublicKey().getEncoded()); - } - - byte[] b64decoded = Base64.decode(element.getEncryptedBase64MessageContent()); - - ByteArrayInputStream encryptedIn = new ByteArrayInputStream(b64decoded); - - InputStream decrypted = BouncyGPG.decryptAndVerifyStream() - .withConfig(decryptionConfig) - .withKeySelectionStrategy(new XmppKeySelectionStrategy(new Date())) - .andValidateSomeoneSigned() - .fromEncryptedInputStream(encryptedIn); - - ByteArrayOutputStream decryptedOut = new ByteArrayOutputStream(); - - Streams.pipeAll(decrypted, decryptedOut); - - return new OpenPgpMessage(OpenPgpMessage.State.signcrypt, new String(decryptedOut.toByteArray(), Charset.forName("UTF-8"))); - */ - return null; - } - - @Override - public OpenPgpElement sign(SignElement element, OpenPgpV4Fingerprint singingKeyFingerprint) - throws MissingOpenPgpKeyPairException { - return null; - } - - @Override - public OpenPgpMessage verify(OpenPgpElement element, Set singingKeyFingerprints) - throws MissingOpenPgpPublicKeyException { - return null; - } - - @Override - public OpenPgpElement encrypt(CryptElement element, Set encryptionKeyFingerprints) - throws MissingOpenPgpPublicKeyException { - return null; - } - - @Override - public OpenPgpMessage decrypt(OpenPgpElement element) throws MissingOpenPgpKeyPairException { - return null; - } - - public static OpenPgpV4Fingerprint getFingerprint(PGPPublicKey publicKey) { - byte[] hex = Hex.encode(publicKey.getFingerprint()); - return new OpenPgpV4Fingerprint(hex); - } - - /** - * {@inheritDoc} - */ - @Override - public OpenPgpV4Fingerprint createOpenPgpKeyPair() - throws NoSuchAlgorithmException, NoSuchProviderException { - /* - try { - PGPSecretKeyRing ourKey = generateKey(ourJid).generateSecretKeyRing(); - primaryKeyPairFingerprint = getFingerprint(ourKey.getPublicKey()); - ourKeys = KeyringConfigs.forGpgExportedKeys(KeyringConfigCallbacks.withUnprotectedKeys()); - ourKeys.addSecretKey(ourKey.getSecretKey().getEncoded()); - ourKeys.addPublicKey(ourKey.getPublicKey().getEncoded()); - } catch (PGPException | IOException e) { - throw new CorruptedOpenPgpKeyException(e); - } - */ - return null; - } - - public static PGPKeyRingGenerator generateKey(BareJid owner) - throws NoSuchAlgorithmException, PGPException, NoSuchProviderException { - PGPKeyRingGenerator generator = BouncyGPG.createKeyPair() - .withRSAKeys() - .ofSize(PublicKeySize.RSA._2048) - .forIdentity("xmpp:" + owner.toString()) - .withoutPassphrase() - .build(); - return generator; - } - -} diff --git a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/FileBasedBcOpenPgpStore.java b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/FileBasedBcOpenPgpStore.java index 1d7996715..eb8a1a3d1 100644 --- a/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/FileBasedBcOpenPgpStore.java +++ b/smack-openpgp-bouncycastle/src/main/java/org/jivesoftware/smackx/ox/bouncycastle/FileBasedBcOpenPgpStore.java @@ -1,20 +1,30 @@ package org.jivesoftware.smackx.ox.bouncycastle; +import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.util.stringencoder.Base64; -import org.jivesoftware.smackx.ox.OpenPgpStore; import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint; import org.jivesoftware.smackx.ox.Util; import org.jivesoftware.smackx.ox.callback.SecretKeyRestoreSelectionCallback; @@ -26,18 +36,30 @@ import org.jivesoftware.smackx.ox.exception.InvalidBackupCodeException; import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException; import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException; +import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PGPHashAlgorithms; +import name.neuhalfen.projects.crypto.bouncycastle.openpgp.algorithms.PGPSymmetricEncryptionAlgorithms; import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.callbacks.KeyringConfigCallback; +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.KeyringConfig; import name.neuhalfen.projects.crypto.bouncycastle.openpgp.keys.keyrings.KeyringConfigs; +import org.bouncycastle.bcpg.HashAlgorithmTags; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPrivateKey; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.bouncycastle.openpgp.operator.PGPDigestCalculator; +import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; import org.jxmpp.jid.BareJid; -public class FileBasedBcOpenPgpStore implements OpenPgpStore { +public class FileBasedBcOpenPgpStore implements BCOpenPgpStore { private static final Logger LOGGER = Logger.getLogger(FileBasedBcOpenPgpStore.class.getName()); @@ -66,68 +88,14 @@ public class FileBasedBcOpenPgpStore implements OpenPgpStore { configCallback = passwordCallback; keyringConfig = KeyringConfigs.forGpgExportedKeys(configCallback); - KeyringConfig load = KeyringConfigs.withKeyRingsFromFiles(pub, sec, passwordCallback); - for (PGPPublicKeyRing pubRing : load.getPublicKeyRings()) { - for (PGPPublicKey pubKey : pubRing) { - keyringConfig.addPublicKey(pubKey.getEncoded()); - } - } - - PGPPublicKey lastAdded = null; - - for (PGPSecretKeyRing secRing : load.getSecretKeyRings()) { - for (PGPSecretKey secKey : secRing) { - keyringConfig.addSecretKey(secKey.getEncoded()); - // Remember last added secret keys public key -> this will be the primary key - if (secKey.getPublicKey() != null) { - lastAdded = secKey.getPublicKey(); - } - } - } + addPublicKeysFromFile(keyringConfig, pub, configCallback); + PGPPublicKey lastAdded = addSecretKeysFromFile(keyringConfig, sec, configCallback); if (lastAdded != null) { primaryKeyFingerprint = BCOpenPgpProvider.getFingerprint(lastAdded); } } - private static void addPublicKeysFromFile(InMemoryKeyring keyring, - File pubring, - KeyringConfigCallback passwordCallback) - throws IOException, PGPException { - KeyringConfig source = KeyringConfigs.withKeyRingsFromFiles(pubring, null, passwordCallback); - for (PGPPublicKeyRing pubRing : source.getPublicKeyRings()) { - for (PGPPublicKey pubKey : pubRing) { - try { - keyring.addPublicKey(pubKey.getEncoded()); - } catch (IllegalArgumentException e) { - LOGGER.log(Level.INFO, "public key " + Long.toHexString(pubKey.getKeyID()) + - " already exists in keyring. Skip."); - } - - } - } - } - - private static PGPPublicKey addSecretKeysFromFile(InMemoryKeyring keyring, - File secring, - KeyringConfigCallback passwordCallback) - throws IOException, PGPException { - KeyringConfig source = KeyringConfigs.withKeyRingsFromFiles(null, secring, passwordCallback); - PGPPublicKey lastAdded = null; - - for (PGPSecretKeyRing secRing : source.getSecretKeyRings()) { - for (PGPSecretKey secKey : secRing) { - keyring.addSecretKey(secKey.getEncoded()); - // Remember last added secret keys public key -> this will be the primary key - if (secKey.getPublicKey() != null) { - lastAdded = secKey.getPublicKey(); - } - } - } - - return lastAdded; - } - @Override public OpenPgpV4Fingerprint primaryOpenPgpKeyPairFingerprint() { return primaryKeyFingerprint; @@ -153,13 +121,13 @@ public class FileBasedBcOpenPgpStore implements OpenPgpStore { Set announcedKeys = new HashSet<>(); File listPath = contactsList(contact); if (listPath.exists() && listPath.isFile()) { - FileReader fileReader = null; + BufferedReader reader = null; try { - fileReader = new FileReader(listPath); - BufferedReader bufferedReader = new BufferedReader(fileReader); - String line; + reader = new BufferedReader(new InputStreamReader( + new FileInputStream(listPath), "UTF8")); - while ((line = bufferedReader.readLine()) != null) { + String line; + while ((line = reader.readLine()) != null) { line = line.trim(); if (line.isEmpty()) { continue; @@ -172,11 +140,11 @@ public class FileBasedBcOpenPgpStore implements OpenPgpStore { LOGGER.log(Level.INFO, "Skip malformed fingerprint " + line + " of " + contact.toString()); } } - bufferedReader.close(); + reader.close(); } catch (IOException e) { - if (fileReader != null) { + if (reader != null) { try { - fileReader.close(); + reader.close(); } catch (IOException e1) { // Ignore } @@ -187,9 +155,25 @@ public class FileBasedBcOpenPgpStore implements OpenPgpStore { } @Override - public Set availableOpenPgpKeysFingerprints(BareJid contact) { + public Set availableOpenPgpPublicKeysFingerprints(BareJid contact) + throws CorruptedOpenPgpKeyException { Set availableKeys = new HashSet<>(); - return null; // TODO + try { + Iterator ringIterator = keyringConfig.getPublicKeyRings().getKeyRings("xmpp:" + contact.toString()); + while (ringIterator.hasNext()) { + PGPPublicKeyRing ring = ringIterator.next(); + Iterator keyIterator = ring.getPublicKeys(); + while (keyIterator.hasNext()) { + PGPPublicKey key = keyIterator.next(); + if (key.isEncryptionKey()) { + availableKeys.add(BCOpenPgpProvider.getFingerprint(key)); + } + } + } + } catch (PGPException | IOException e) { + throw new CorruptedOpenPgpKeyException(e); + } + return availableKeys; } @Override @@ -199,17 +183,17 @@ public class FileBasedBcOpenPgpStore implements OpenPgpStore { if (!listPath.exists()) { listPath.getParentFile().mkdirs(); listPath.createNewFile(); - FileWriter writer = null; + BufferedWriter writer = null; try { - writer = new FileWriter(listPath); - BufferedWriter bufferedWriter = new BufferedWriter(writer); + writer = new BufferedWriter(new OutputStreamWriter( + new FileOutputStream(listPath), "UTF8")); for (OpenPgpV4Fingerprint fingerprint : listElement.getMetadata().keySet()) { - bufferedWriter.write(fingerprint.toString()); - bufferedWriter.newLine(); + writer.write(fingerprint.toString()); + writer.newLine(); } - bufferedWriter.close(); + writer.close(); } catch (IOException e) { if (writer != null) { @@ -243,6 +227,7 @@ public class FileBasedBcOpenPgpStore implements OpenPgpStore { byte[] base64decoded = Base64.decode(element.getDataElement().getB64Data()); try { keyringConfig.addPublicKey(base64decoded); + writePublicKeysToFile(keyringConfig, publicKeyringPath()); } catch (PGPException | IOException e) { throw new CorruptedOpenPgpKeyException(e); } catch (IllegalArgumentException e) { @@ -254,13 +239,115 @@ public class FileBasedBcOpenPgpStore implements OpenPgpStore { @Override public SecretkeyElement createSecretkeyElement(Set fingerprints, String password) throws MissingOpenPgpKeyPairException, CorruptedOpenPgpKeyException { - return null; + + PGPDigestCalculator calculator; + try { + calculator = new JcaPGPDigestCalculatorProviderBuilder() + .setProvider(BouncyCastleProvider.PROVIDER_NAME) + .build() + .get(HashAlgorithmTags.SHA1); + } catch (PGPException e) { + throw new AssertionError(e); + } + + PBESecretKeyEncryptor encryptor = new JcePBESecretKeyEncryptorBuilder( + PGPSymmetricEncryptionAlgorithms.AES_256.getAlgorithmId()) + .setProvider(BouncyCastleProvider.PROVIDER_NAME) + .build(password.toCharArray()); + + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + try { + for (OpenPgpV4Fingerprint fingerprint : fingerprints) { + // Our unencrypted secret key + PGPSecretKey secretKey = keyringConfig.getSecretKeyRings() + .getSecretKey(Util.keyIdFromFingerprint(fingerprint)); + + if (secretKey == null) { + // TODO: Close streams + throw new MissingOpenPgpKeyPairException(user); + } + + PGPSecretKey encrypted = new PGPSecretKey( + secretKey.extractPrivateKey(null), + secretKey.getPublicKey(), + calculator, + true, + encryptor); + + buffer.write(encrypted.getEncoded()); + } + + return new SecretkeyElement(Base64.encode(buffer.toByteArray())); + + } catch (PGPException | IOException e) { + throw new CorruptedOpenPgpKeyException(e); + } } @Override public void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, String password, SecretKeyRestoreSelectionCallback callback) throws CorruptedOpenPgpKeyException, InvalidBackupCodeException { + byte[] base64Decoded = Base64.decode(secretkeyElement.getB64Data()); + try { + PGPDigestCalculatorProvider calculatorProvider = new JcaPGPDigestCalculatorProviderBuilder() + .setProvider(BouncyCastleProvider.PROVIDER_NAME) + .build(); + + ByteArrayInputStream inputStream = new ByteArrayInputStream(base64Decoded); + KeyringConfig keyring = KeyringConfigs.withKeyRingsFromStreams(null, inputStream, + KeyringConfigCallbacks.withPassword(password)); + + Map availableKeys = new HashMap<>(); + OpenPgpV4Fingerprint selectedKey; + + for (PGPSecretKeyRing r : keyring.getSecretKeyRings()) { + PGPSecretKey s = r.getSecretKey(); + PGPPrivateKey privateKey = s.extractPrivateKey(new JcePBESecretKeyDecryptorBuilder(calculatorProvider).build(password.toCharArray())); + PGPPublicKey publicKey = s.getPublicKey(); + PGPSecretKey secretKey = new PGPSecretKey( + privateKey, + publicKey, + calculatorProvider.get(PGPHashAlgorithms.SHA1.getAlgorithmId()), + true, + null); + availableKeys.put(BCOpenPgpProvider.getFingerprint(publicKey), secretKey); + } + + selectedKey = callback.selectSecretKeyToRestore(availableKeys.keySet()); + if (selectedKey != null) { + try { + keyringConfig.addSecretKey(availableKeys.get(selectedKey).getEncoded()); + } catch (IllegalArgumentException e) { + LOGGER.log(Level.INFO, "Users secret key " + selectedKey.toString() + " is already in keyring. Skip."); + } + + try { + keyringConfig.addPublicKey(availableKeys.get(selectedKey).getPublicKey().getEncoded()); + } catch (IllegalArgumentException e) { + LOGGER.log(Level.INFO, "Users public key " + selectedKey.toString() + " is already in keyring. Skip."); + } + primaryKeyFingerprint = selectedKey; + writePrivateKeysToFile(keyringConfig, secretKeyringPath()); + } + } catch (PGPException | IOException e) { + throw new CorruptedOpenPgpKeyException(e); + } + } + + @Override + public OpenPgpV4Fingerprint createOpenPgpKeyPair() + throws NoSuchAlgorithmException, NoSuchProviderException, CorruptedOpenPgpKeyException { + try { + PGPSecretKeyRing ourKey = BCOpenPgpProvider.generateKey(user).generateSecretKeyRing(); + keyringConfig.addSecretKey(ourKey.getSecretKey().getEncoded()); + keyringConfig.addPublicKey(ourKey.getPublicKey().getEncoded()); + primaryKeyFingerprint = BCOpenPgpProvider.getFingerprint(ourKey.getPublicKey()); + return primaryKeyFingerprint; + } catch (PGPException | IOException e) { + throw new CorruptedOpenPgpKeyException(e); + } } private File secretKeyringPath() { @@ -268,11 +355,7 @@ public class FileBasedBcOpenPgpStore implements OpenPgpStore { } private File publicKeyringPath() { - return publicKeyringPath(user); - } - - private File publicKeyringPath(BareJid contact) { - return new File(contactsPath(contact), "pubring.pkr"); + return new File(contactsPath(user), "pubring.pkr"); } private File contactsPath() { @@ -286,4 +369,87 @@ public class FileBasedBcOpenPgpStore implements OpenPgpStore { private File contactsList(BareJid contact) { return new File(contactsPath(contact), "metadata.list"); } + + private static void addPublicKeysFromFile(InMemoryKeyring keyring, + File pubring, + KeyringConfigCallback passwordCallback) + throws IOException, PGPException { + if (!pubring.exists()) { + return; + } + + InputStream inputStream = new FileInputStream(pubring); + KeyringConfig source = KeyringConfigs.withKeyRingsFromStreams(inputStream, null, passwordCallback); + for (PGPPublicKeyRing pubRing : source.getPublicKeyRings()) { + for (PGPPublicKey pubKey : pubRing) { + try { + keyring.addPublicKey(pubKey.getEncoded()); + } catch (IllegalArgumentException e) { + LOGGER.log(Level.INFO, "public key " + Long.toHexString(pubKey.getKeyID()) + + " already exists in keyring. Skip."); + } + + } + } + } + + private static PGPPublicKey addSecretKeysFromFile(InMemoryKeyring keyring, + File secring, + KeyringConfigCallback passwordCallback) + throws IOException, PGPException { + if (!secring.exists()) { + return null; + } + + InputStream inputStream = new FileInputStream(secring); + KeyringConfig source = KeyringConfigs.withKeyRingsFromStreams(null, inputStream, passwordCallback); + PGPPublicKey lastAdded = null; + + for (PGPSecretKeyRing secRing : source.getSecretKeyRings()) { + for (PGPSecretKey secKey : secRing) { + keyring.addSecretKey(secKey.getEncoded()); + // Remember last added secret keys public key -> this will be the primary key + if (secKey.getPublicKey() != null) { + lastAdded = secKey.getPublicKey(); + } + } + } + + return lastAdded; + } + + private static void writePublicKeysToFile(KeyringConfig keyring, File pubring) + throws IOException, PGPException { + writeBytesToFile(keyring.getPublicKeyRings().getEncoded(), pubring); + } + + private static void writePrivateKeysToFile(KeyringConfig keyring, File secring) + throws IOException, PGPException { + writeBytesToFile(keyring.getSecretKeyRings().getEncoded(), secring); + } + + private static void writeBytesToFile(byte[] bytes, File file) throws IOException { + if (!file.exists()) { + file.getParentFile().mkdirs(); + file.createNewFile(); + } + + FileOutputStream outputStream = null; + try { + outputStream = new FileOutputStream(file); + BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); + + bufferedStream.write(bytes); + outputStream.close(); + } catch (IOException e) { + if (outputStream != null) { + outputStream.close(); + } + } + } + + @Override + public KeyringConfig getKeyringConfig() { + return keyringConfig; + } } diff --git a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BasicEncryptionTest.java b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BasicEncryptionTest.java index 787a1d5f5..98c0aef32 100644 --- a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BasicEncryptionTest.java +++ b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BasicEncryptionTest.java @@ -116,8 +116,8 @@ public class BasicEncryptionTest extends SmackTestSuite { throws IOException, PGPException, NoSuchAlgorithmException, NoSuchProviderException, SignatureException { final String alice = "alice@wonderland.lit"; final String bob = "bob@builder.tv"; - PGPKeyRingGenerator g1 = BouncyCastleOpenPgpProvider.generateKey(JidCreate.bareFrom(alice)); - PGPKeyRingGenerator g2 = BouncyCastleOpenPgpProvider.generateKey(JidCreate.bareFrom(bob)); + 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(); diff --git a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastleOpenPgpProviderTest.java b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastleOpenPgpProviderTest.java index d03f5d135..91da7a414 100644 --- a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastleOpenPgpProviderTest.java +++ b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastleOpenPgpProviderTest.java @@ -16,13 +16,25 @@ */ package org.jivesoftware.smackx.ox.bouncycastle; -import java.security.Security; +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.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 { @@ -34,18 +46,18 @@ public class BouncyCastleOpenPgpProviderTest extends SmackTestSuite { // Create providers for alice and the cat BareJid alice = JidCreate.bareFrom("alice@wonderland.lit"); BareJid cheshire = JidCreate.bareFrom("cheshire@wonderland.lit"); - BouncyCastleOpenPgpProvider aliceProvider = new BouncyCastleOpenPgpProvider(alice); - BouncyCastleOpenPgpProvider cheshireProvider = new BouncyCastleOpenPgpProvider(cheshire); + BCOpenPgpProvider aliceProvider = new BCOpenPgpProvider(alice); + BCOpenPgpProvider cheshireProvider = new BCOpenPgpProvider(cheshire); - aliceProvider.createAndUseKey(); - cheshireProvider.createAndUseKey(); + aliceProvider.createOpenPgpKeyPair(); + cheshireProvider.createOpenPgpKeyPair(); // dry exchange keys - /* - PubkeyElement aliceKeys = aliceProvider.createPubkeyElement(); - PubkeyElement cheshireKeys = cheshireProvider.createPubkeyElement(); - aliceProvider.storePublicKey(cheshireKeys, cheshire); - cheshireProvider.storePublicKey(aliceKeys, alice); + + PubkeyElement aliceKeys = aliceProvider.createPubkeyElement(aliceProvider.primaryOpenPgpKeyPairFingerprint()); + PubkeyElement cheshireKeys = cheshireProvider.createPubkeyElement(cheshireProvider.primaryOpenPgpKeyPairFingerprint()); + aliceProvider.storePublicKey(cheshire, cheshireProvider.primaryOpenPgpKeyPairFingerprint(), cheshireKeys); + cheshireProvider.storePublicKey(alice, aliceProvider.primaryOpenPgpKeyPairFingerprint(), aliceKeys); // Create signed and encrypted message from alice to the cheshire cat SigncryptElement signcryptElement = new SigncryptElement( @@ -54,14 +66,14 @@ public class BouncyCastleOpenPgpProviderTest extends SmackTestSuite { new Message.Body("en", "How do you know I’m mad?"))); OpenPgpElement encrypted = aliceProvider.signAndEncrypt( signcryptElement, - Collections.singleton(cheshire)); + aliceProvider.primaryOpenPgpKeyPairFingerprint(), + Collections.singleton(cheshireProvider.primaryOpenPgpKeyPairFingerprint())); // Decrypt the message as the cheshire cat - OpenPgpMessage decrypted = cheshireProvider.decryptAndVerify(encrypted, alice); + OpenPgpMessage decrypted = cheshireProvider.decryptAndVerify(encrypted, Collections.singleton(aliceProvider.primaryOpenPgpKeyPairFingerprint())); OpenPgpContentElement content = decrypted.getOpenPgpContentElement(); assertTrue(content instanceof SigncryptElement); assertXMLEqual(signcryptElement.toXML(null).toString(), content.toXML(null).toString()); - */ } } diff --git a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastle_OpenPgpV4FingerprintTest.java b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastle_OpenPgpV4FingerprintTest.java index 480ec13e5..03ac5c659 100644 --- a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastle_OpenPgpV4FingerprintTest.java +++ b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/BouncyCastle_OpenPgpV4FingerprintTest.java @@ -43,7 +43,7 @@ public class BouncyCastle_OpenPgpV4FingerprintTest extends SmackTestSuite { keyringJuliet.addPublicKey(TestKeys.JULIET_PUB.getBytes(UTF8)); PGPPublicKey publicKey = keyringJuliet.getPublicKeyRings().iterator().next().getPublicKey(); - OpenPgpV4Fingerprint fp = BouncyCastleOpenPgpProvider.getFingerprint(publicKey); + OpenPgpV4Fingerprint fp = BCOpenPgpProvider.getFingerprint(publicKey); assertEquals(publicKey.getKeyID(), Util.keyIdFromFingerprint(fp)); } } diff --git a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/KeyGenerationTest.java b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/KeyGenerationTest.java index ba16b5401..db6a9c528 100644 --- a/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/KeyGenerationTest.java +++ b/smack-openpgp-bouncycastle/src/test/java/org/jivesoftware/smackx/ox/bouncycastle/KeyGenerationTest.java @@ -28,7 +28,7 @@ public class KeyGenerationTest { @Ignore public void createSecretKey() throws Exception { - PGPSecretKey secretKey = BouncyCastleOpenPgpProvider + PGPSecretKey secretKey = BCOpenPgpProvider .generateKey(JidCreate.bareFrom("alice@wonderland.lit")) .generateSecretKeyRing() .getSecretKey(); diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpProvider.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpProvider.java index 6af0cfafd..f3c03d384 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpProvider.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpProvider.java @@ -16,8 +16,6 @@ */ package org.jivesoftware.smackx.ox; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; import java.util.Set; import org.jivesoftware.smackx.ox.element.CryptElement; @@ -27,8 +25,6 @@ import org.jivesoftware.smackx.ox.element.SigncryptElement; import org.jivesoftware.smackx.ox.exception.MissingOpenPgpKeyPairException; import org.jivesoftware.smackx.ox.exception.MissingOpenPgpPublicKeyException; -import org.jxmpp.jid.BareJid; - public interface OpenPgpProvider extends OpenPgpStore { /** @@ -138,16 +134,4 @@ public interface OpenPgpProvider extends OpenPgpStore { */ OpenPgpMessage decrypt(OpenPgpElement element) throws MissingOpenPgpKeyPairException; - - /** - * Create a fresh OpenPGP key pair with the {@link BareJid} of the user prefixed by "xmpp:" as user-id - * (example: {@code "xmpp:juliet@capulet.lit"}). - * Store the key pair in persistent storage and return the public keys {@link OpenPgpV4Fingerprint}. - * - * @throws NoSuchAlgorithmException if a Hash algorithm is not available - * @throws NoSuchProviderException id no suitable cryptographic provider (for example BouncyCastleProvider) - * is registered. - */ - OpenPgpV4Fingerprint createOpenPgpKeyPair() - throws NoSuchAlgorithmException, NoSuchProviderException; } diff --git a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpStore.java b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpStore.java index 5e2947cd1..e0f86ca4a 100644 --- a/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpStore.java +++ b/smack-openpgp/src/main/java/org/jivesoftware/smackx/ox/OpenPgpStore.java @@ -1,8 +1,9 @@ package org.jivesoftware.smackx.ox; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.util.Set; -import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smackx.ox.callback.SecretKeyRestoreSelectionCallback; import org.jivesoftware.smackx.ox.element.PubkeyElement; @@ -41,7 +42,7 @@ public interface OpenPgpStore { *
* Note: Those are the keys announced in the latest received metadata update. * This returns a {@link Set} which might be different from the result of - * {@link #availableOpenPgpKeysFingerprints(BareJid)}. + * {@link #availableOpenPgpPublicKeysFingerprints(BareJid)}. * Messages should be encrypted to the intersection of both sets. * * @param contact contact. @@ -54,13 +55,14 @@ public interface OpenPgpStore { * contact, which we have locally available. *
* Note: This returns a {@link Set} that might be different from the result of - * {@link #availableOpenPgpKeysFingerprints(BareJid)}. + * {@link #availableOpenPgpPublicKeysFingerprints(BareJid)}. * Messages should be encrypted to the intersection of both sets. * * @param contact contact. * @return list of contacts locally available public keys. */ - Set availableOpenPgpKeysFingerprints(BareJid contact); + Set availableOpenPgpPublicKeysFingerprints(BareJid contact) + throws CorruptedOpenPgpKeyException; /** * Store incoming update to the OpenPGP metadata node in persistent storage. @@ -68,13 +70,22 @@ public interface OpenPgpStore { * @param connection authenticated {@link XMPPConnection} of the user. * @param listElement {@link PublicKeysListElement} which contains a list of the keys of {@code owner}. * @param owner {@link BareJid} of the owner of the announced public keys. - * @throws CorruptedOpenPgpKeyException - * @throws InterruptedException - * @throws SmackException.NotConnectedException - * @throws SmackException.NoResponseException */ void storePublicKeysList(XMPPConnection connection, PublicKeysListElement listElement, BareJid owner); + /** + * Create a fresh OpenPGP key pair with the {@link BareJid} of the user prefixed by "xmpp:" as user-id + * (example: {@code "xmpp:juliet@capulet.lit"}). + * Store the key pair in persistent storage and return the public keys {@link OpenPgpV4Fingerprint}. + * + * @throws NoSuchAlgorithmException if a Hash algorithm is not available + * @throws NoSuchProviderException id no suitable cryptographic provider (for example BouncyCastleProvider) + * is registered. + * @throws CorruptedOpenPgpKeyException if the generated key cannot be added to the keyring for some reason. + */ + OpenPgpV4Fingerprint createOpenPgpKeyPair() + throws NoSuchAlgorithmException, NoSuchProviderException, CorruptedOpenPgpKeyException; + /** * Create a {@link PubkeyElement} which contains our exported OpenPGP public key. * The element can for example be published.