FileBasedOpenPgpKeyStore.java

  1. /**
  2.  *
  3.  * Copyright 2018 Paul Schaub.
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.ox.store.filebased;

  18. import java.io.File;
  19. import java.io.FileInputStream;
  20. import java.io.IOException;
  21. import java.io.OutputStream;
  22. import java.util.Date;
  23. import java.util.Map;

  24. import org.jivesoftware.smack.util.CloseableUtil;
  25. import org.jivesoftware.smack.util.FileUtils;
  26. import org.jivesoftware.smack.util.Objects;

  27. import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpKeyStore;
  28. import org.jivesoftware.smackx.ox.store.definition.OpenPgpKeyStore;

  29. import org.bouncycastle.openpgp.PGPException;
  30. import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
  31. import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
  32. import org.jxmpp.jid.BareJid;
  33. import org.pgpainless.PGPainless;
  34. import org.pgpainless.key.OpenPgpV4Fingerprint;

  35. /**
  36.  * This class is an implementation of the {@link OpenPgpKeyStore}, which stores keys in a file structure.
  37.  * The keys are stored in the following directory structure:
  38.  *
  39.  * <pre>
  40.  * {@code
  41.  * <basePath>/
  42.  *     <userjid@server.tld>/
  43.  *         pubring.pkr      // public keys of the user/contact
  44.  *         secring.pkr      // secret keys of the user
  45.  *         fetchDates.list  // date of the last time we fetched the users keys
  46.  * }
  47.  * </pre>
  48.  */
  49. public class FileBasedOpenPgpKeyStore extends AbstractOpenPgpKeyStore {

  50.     private static final String PUB_RING = "pubring.pkr";
  51.     private static final String SEC_RING = "secring.skr";
  52.     private static final String FETCH_DATES = "fetchDates.list";

  53.     private final File basePath;

  54.     public FileBasedOpenPgpKeyStore(File basePath) {
  55.         this.basePath = Objects.requireNonNull(basePath);
  56.     }

  57.     @Override
  58.     public void writePublicKeysOf(BareJid owner, PGPPublicKeyRingCollection publicKeys) throws IOException {
  59.         File file = getPublicKeyRingPath(owner);

  60.         if (publicKeys == null) {
  61.             FileUtils.maybeDeleteFileOrThrow(file);
  62.             return;
  63.         }

  64.         OutputStream outputStream = null;
  65.         try {
  66.             outputStream = FileUtils.prepareFileOutputStream(file);
  67.             publicKeys.encode(outputStream);
  68.         } finally {
  69.             CloseableUtil.maybeClose(outputStream, LOGGER);
  70.         }
  71.     }

  72.     @Override
  73.     public void writeSecretKeysOf(BareJid owner, PGPSecretKeyRingCollection secretKeys) throws IOException {
  74.         File file = getSecretKeyRingPath(owner);

  75.         if (secretKeys == null) {
  76.             FileUtils.maybeDeleteFileOrThrow(file);
  77.             return;
  78.         }

  79.         OutputStream outputStream = null;
  80.         try {
  81.             outputStream = FileUtils.prepareFileOutputStream(file);
  82.             secretKeys.encode(outputStream);
  83.         } finally {
  84.             CloseableUtil.maybeClose(outputStream, LOGGER);
  85.         }
  86.     }

  87.     @Override
  88.     public PGPPublicKeyRingCollection readPublicKeysOf(BareJid owner)
  89.             throws IOException, PGPException {
  90.         File file = getPublicKeyRingPath(owner);
  91.         if (!file.exists()) {
  92.             return null;
  93.         }
  94.         FileInputStream inputStream = FileUtils.prepareFileInputStream(file);

  95.         PGPPublicKeyRingCollection collection = PGPainless.readKeyRing().publicKeyRingCollection(inputStream);
  96.         inputStream.close();
  97.         return collection;
  98.     }

  99.     @Override
  100.     public PGPSecretKeyRingCollection readSecretKeysOf(BareJid owner) throws IOException, PGPException {
  101.         File file = getSecretKeyRingPath(owner);
  102.         if (!file.exists()) {
  103.             return null;
  104.         }
  105.         FileInputStream inputStream = FileUtils.prepareFileInputStream(file);

  106.         PGPSecretKeyRingCollection collection = PGPainless.readKeyRing().secretKeyRingCollection(inputStream);
  107.         inputStream.close();
  108.         return collection;
  109.     }

  110.     @Override
  111.     protected Map<OpenPgpV4Fingerprint, Date> readKeyFetchDates(BareJid owner) throws IOException {
  112.         return FileBasedOpenPgpMetadataStore.readFingerprintsAndDates(getFetchDatesPath(owner));
  113.     }

  114.     @Override
  115.     protected void writeKeyFetchDates(BareJid owner, Map<OpenPgpV4Fingerprint, Date> dates) throws IOException {
  116.         FileBasedOpenPgpMetadataStore.writeFingerprintsAndDates(dates, getFetchDatesPath(owner));
  117.     }

  118.     private File getPublicKeyRingPath(BareJid jid) {
  119.         return new File(FileBasedOpenPgpStore.getContactsPath(basePath, jid), PUB_RING);
  120.     }

  121.     private File getSecretKeyRingPath(BareJid jid) {
  122.         return new File(FileBasedOpenPgpStore.getContactsPath(basePath, jid), SEC_RING);
  123.     }

  124.     private File getFetchDatesPath(BareJid jid) {
  125.         return new File(FileBasedOpenPgpStore.getContactsPath(basePath, jid), FETCH_DATES);
  126.     }
  127. }