001/** 002 * 003 * Copyright 2018 Paul Schaub. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.ox.store.filebased; 018 019import java.io.File; 020import java.io.FileInputStream; 021import java.io.IOException; 022import java.io.OutputStream; 023import java.util.Date; 024import java.util.Map; 025 026import org.jivesoftware.smack.util.CloseableUtil; 027import org.jivesoftware.smack.util.FileUtils; 028import org.jivesoftware.smack.util.Objects; 029import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpKeyStore; 030import org.jivesoftware.smackx.ox.store.definition.OpenPgpKeyStore; 031 032import org.bouncycastle.openpgp.PGPException; 033import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; 034import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; 035import org.jxmpp.jid.BareJid; 036import org.pgpainless.PGPainless; 037import org.pgpainless.key.OpenPgpV4Fingerprint; 038 039/** 040 * This class is an implementation of the {@link OpenPgpKeyStore}, which stores keys in a file structure. 041 * The keys are stored in the following directory structure: 042 * 043 * <pre> 044 * {@code 045 * <basePath>/ 046 * <userjid@server.tld>/ 047 * pubring.pkr // public keys of the user/contact 048 * secring.pkr // secret keys of the user 049 * fetchDates.list // date of the last time we fetched the users keys 050 * } 051 * </pre> 052 */ 053public class FileBasedOpenPgpKeyStore extends AbstractOpenPgpKeyStore { 054 055 private static final String PUB_RING = "pubring.pkr"; 056 private static final String SEC_RING = "secring.skr"; 057 private static final String FETCH_DATES = "fetchDates.list"; 058 059 private final File basePath; 060 061 public FileBasedOpenPgpKeyStore(File basePath) { 062 this.basePath = Objects.requireNonNull(basePath); 063 } 064 065 @Override 066 public void writePublicKeysOf(BareJid owner, PGPPublicKeyRingCollection publicKeys) throws IOException { 067 File file = getPublicKeyRingPath(owner); 068 069 if (publicKeys == null) { 070 FileUtils.maybeDeleteFileOrThrow(file); 071 return; 072 } 073 074 OutputStream outputStream = null; 075 try { 076 outputStream = FileUtils.prepareFileOutputStream(file); 077 publicKeys.encode(outputStream); 078 } finally { 079 CloseableUtil.maybeClose(outputStream, LOGGER); 080 } 081 } 082 083 @Override 084 public void writeSecretKeysOf(BareJid owner, PGPSecretKeyRingCollection secretKeys) throws IOException { 085 File file = getSecretKeyRingPath(owner); 086 087 if (secretKeys == null) { 088 FileUtils.maybeDeleteFileOrThrow(file); 089 return; 090 } 091 092 OutputStream outputStream = null; 093 try { 094 outputStream = FileUtils.prepareFileOutputStream(file); 095 secretKeys.encode(outputStream); 096 } finally { 097 CloseableUtil.maybeClose(outputStream, LOGGER); 098 } 099 } 100 101 @Override 102 public PGPPublicKeyRingCollection readPublicKeysOf(BareJid owner) 103 throws IOException, PGPException { 104 File file = getPublicKeyRingPath(owner); 105 if (!file.exists()) { 106 return null; 107 } 108 FileInputStream inputStream = FileUtils.prepareFileInputStream(file); 109 110 PGPPublicKeyRingCollection collection = PGPainless.readKeyRing().publicKeyRingCollection(inputStream); 111 inputStream.close(); 112 return collection; 113 } 114 115 @Override 116 public PGPSecretKeyRingCollection readSecretKeysOf(BareJid owner) throws IOException, PGPException { 117 File file = getSecretKeyRingPath(owner); 118 if (!file.exists()) { 119 return null; 120 } 121 FileInputStream inputStream = FileUtils.prepareFileInputStream(file); 122 123 PGPSecretKeyRingCollection collection = PGPainless.readKeyRing().secretKeyRingCollection(inputStream); 124 inputStream.close(); 125 return collection; 126 } 127 128 @Override 129 protected Map<OpenPgpV4Fingerprint, Date> readKeyFetchDates(BareJid owner) throws IOException { 130 return FileBasedOpenPgpMetadataStore.readFingerprintsAndDates(getFetchDatesPath(owner)); 131 } 132 133 @Override 134 protected void writeKeyFetchDates(BareJid owner, Map<OpenPgpV4Fingerprint, Date> dates) throws IOException { 135 FileBasedOpenPgpMetadataStore.writeFingerprintsAndDates(dates, getFetchDatesPath(owner)); 136 } 137 138 private File getPublicKeyRingPath(BareJid jid) { 139 return new File(FileBasedOpenPgpStore.getContactsPath(basePath, jid), PUB_RING); 140 } 141 142 private File getSecretKeyRingPath(BareJid jid) { 143 return new File(FileBasedOpenPgpStore.getContactsPath(basePath, jid), SEC_RING); 144 } 145 146 private File getFetchDatesPath(BareJid jid) { 147 return new File(FileBasedOpenPgpStore.getContactsPath(basePath, jid), FETCH_DATES); 148 } 149}