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; 029 030import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpKeyStore; 031import org.jivesoftware.smackx.ox.store.definition.OpenPgpKeyStore; 032 033import org.bouncycastle.openpgp.PGPException; 034import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; 035import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; 036import org.jxmpp.jid.BareJid; 037import org.pgpainless.PGPainless; 038import org.pgpainless.key.OpenPgpV4Fingerprint; 039 040/** 041 * This class is an implementation of the {@link OpenPgpKeyStore}, which stores keys in a file structure. 042 * The keys are stored in the following directory structure: 043 * 044 * <pre> 045 * {@code 046 * <basePath>/ 047 * <userjid@server.tld>/ 048 * pubring.pkr // public keys of the user/contact 049 * secring.pkr // secret keys of the user 050 * fetchDates.list // date of the last time we fetched the users keys 051 * } 052 * </pre> 053 */ 054public class FileBasedOpenPgpKeyStore extends AbstractOpenPgpKeyStore { 055 056 private static final String PUB_RING = "pubring.pkr"; 057 private static final String SEC_RING = "secring.skr"; 058 private static final String FETCH_DATES = "fetchDates.list"; 059 060 private final File basePath; 061 062 public FileBasedOpenPgpKeyStore(File basePath) { 063 this.basePath = Objects.requireNonNull(basePath); 064 } 065 066 @Override 067 public void writePublicKeysOf(BareJid owner, PGPPublicKeyRingCollection publicKeys) throws IOException { 068 File file = getPublicKeyRingPath(owner); 069 070 if (publicKeys == null) { 071 FileUtils.maybeDeleteFileOrThrow(file); 072 return; 073 } 074 075 OutputStream outputStream = null; 076 try { 077 outputStream = FileUtils.prepareFileOutputStream(file); 078 publicKeys.encode(outputStream); 079 } finally { 080 CloseableUtil.maybeClose(outputStream, LOGGER); 081 } 082 } 083 084 @Override 085 public void writeSecretKeysOf(BareJid owner, PGPSecretKeyRingCollection secretKeys) throws IOException { 086 File file = getSecretKeyRingPath(owner); 087 088 if (secretKeys == null) { 089 FileUtils.maybeDeleteFileOrThrow(file); 090 return; 091 } 092 093 OutputStream outputStream = null; 094 try { 095 outputStream = FileUtils.prepareFileOutputStream(file); 096 secretKeys.encode(outputStream); 097 } finally { 098 CloseableUtil.maybeClose(outputStream, LOGGER); 099 } 100 } 101 102 @Override 103 public PGPPublicKeyRingCollection readPublicKeysOf(BareJid owner) 104 throws IOException, PGPException { 105 File file = getPublicKeyRingPath(owner); 106 if (!file.exists()) { 107 return null; 108 } 109 FileInputStream inputStream = FileUtils.prepareFileInputStream(file); 110 111 PGPPublicKeyRingCollection collection = PGPainless.readKeyRing().publicKeyRingCollection(inputStream); 112 inputStream.close(); 113 return collection; 114 } 115 116 @Override 117 public PGPSecretKeyRingCollection readSecretKeysOf(BareJid owner) throws IOException, PGPException { 118 File file = getSecretKeyRingPath(owner); 119 if (!file.exists()) { 120 return null; 121 } 122 FileInputStream inputStream = FileUtils.prepareFileInputStream(file); 123 124 PGPSecretKeyRingCollection collection = PGPainless.readKeyRing().secretKeyRingCollection(inputStream); 125 inputStream.close(); 126 return collection; 127 } 128 129 @Override 130 protected Map<OpenPgpV4Fingerprint, Date> readKeyFetchDates(BareJid owner) throws IOException { 131 return FileBasedOpenPgpMetadataStore.readFingerprintsAndDates(getFetchDatesPath(owner)); 132 } 133 134 @Override 135 protected void writeKeyFetchDates(BareJid owner, Map<OpenPgpV4Fingerprint, Date> dates) throws IOException { 136 FileBasedOpenPgpMetadataStore.writeFingerprintsAndDates(dates, getFetchDatesPath(owner)); 137 } 138 139 private File getPublicKeyRingPath(BareJid jid) { 140 return new File(FileBasedOpenPgpStore.getContactsPath(basePath, jid), PUB_RING); 141 } 142 143 private File getSecretKeyRingPath(BareJid jid) { 144 return new File(FileBasedOpenPgpStore.getContactsPath(basePath, jid), SEC_RING); 145 } 146 147 private File getFetchDatesPath(BareJid jid) { 148 return new File(FileBasedOpenPgpStore.getContactsPath(basePath, jid), FETCH_DATES); 149 } 150}