001// SPDX-FileCopyrightText: 2018 Paul Schaub <vanitasvitae@fsfe.org> 002// 003// SPDX-License-Identifier: Apache-2.0 004 005package org.pgpainless.key.parsing; 006 007import java.io.ByteArrayInputStream; 008import java.io.IOException; 009import java.io.InputStream; 010import java.nio.charset.Charset; 011import java.util.ArrayList; 012import java.util.Iterator; 013import java.util.List; 014import javax.annotation.Nonnull; 015 016import org.bouncycastle.openpgp.PGPException; 017import org.bouncycastle.openpgp.PGPMarker; 018import org.bouncycastle.openpgp.PGPObjectFactory; 019import org.bouncycastle.openpgp.PGPPublicKeyRing; 020import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; 021import org.bouncycastle.openpgp.PGPSecretKeyRing; 022import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; 023import org.bouncycastle.util.io.Streams; 024import org.pgpainless.implementation.ImplementationFactory; 025import org.pgpainless.key.collection.PGPKeyRingCollection; 026import org.pgpainless.util.ArmorUtils; 027 028public class KeyRingReader { 029 030 public static final int MAX_ITERATIONS = 10000; 031 032 @SuppressWarnings("CharsetObjectCanBeUsed") 033 public static final Charset UTF8 = Charset.forName("UTF-8"); 034 035 public PGPPublicKeyRing publicKeyRing(@Nonnull InputStream inputStream) throws IOException { 036 return readPublicKeyRing(inputStream); 037 } 038 039 public PGPPublicKeyRing publicKeyRing(@Nonnull byte[] bytes) throws IOException { 040 return publicKeyRing(new ByteArrayInputStream(bytes)); 041 } 042 043 public PGPPublicKeyRing publicKeyRing(@Nonnull String asciiArmored) throws IOException { 044 return publicKeyRing(asciiArmored.getBytes(UTF8)); 045 } 046 047 public PGPPublicKeyRingCollection publicKeyRingCollection(@Nonnull InputStream inputStream) 048 throws IOException, PGPException { 049 return readPublicKeyRingCollection(inputStream); 050 } 051 052 public PGPPublicKeyRingCollection publicKeyRingCollection(@Nonnull byte[] bytes) throws IOException, PGPException { 053 return publicKeyRingCollection(new ByteArrayInputStream(bytes)); 054 } 055 056 public PGPPublicKeyRingCollection publicKeyRingCollection(@Nonnull String asciiArmored) throws IOException, PGPException { 057 return publicKeyRingCollection(asciiArmored.getBytes(UTF8)); 058 } 059 060 public PGPSecretKeyRing secretKeyRing(@Nonnull InputStream inputStream) throws IOException { 061 return readSecretKeyRing(inputStream); 062 } 063 064 public PGPSecretKeyRing secretKeyRing(@Nonnull byte[] bytes) throws IOException { 065 return secretKeyRing(new ByteArrayInputStream(bytes)); 066 } 067 068 public PGPSecretKeyRing secretKeyRing(@Nonnull String asciiArmored) throws IOException { 069 return secretKeyRing(asciiArmored.getBytes(UTF8)); 070 } 071 072 public PGPSecretKeyRingCollection secretKeyRingCollection(@Nonnull InputStream inputStream) 073 throws IOException, PGPException { 074 return readSecretKeyRingCollection(inputStream); 075 } 076 077 public PGPSecretKeyRingCollection secretKeyRingCollection(@Nonnull byte[] bytes) throws IOException, PGPException { 078 return secretKeyRingCollection(new ByteArrayInputStream(bytes)); 079 } 080 081 public PGPSecretKeyRingCollection secretKeyRingCollection(@Nonnull String asciiArmored) throws IOException, PGPException { 082 return secretKeyRingCollection(asciiArmored.getBytes(UTF8)); 083 } 084 085 public PGPKeyRingCollection keyRingCollection(@Nonnull InputStream inputStream, boolean isSilent) 086 throws IOException, PGPException { 087 return readKeyRingCollection(inputStream, isSilent); 088 } 089 090 public PGPKeyRingCollection keyRingCollection(@Nonnull byte[] bytes, boolean isSilent) throws IOException, PGPException { 091 return keyRingCollection(new ByteArrayInputStream(bytes), isSilent); 092 } 093 094 public PGPKeyRingCollection keyRingCollection(@Nonnull String asciiArmored, boolean isSilent) throws IOException, PGPException { 095 return keyRingCollection(asciiArmored.getBytes(UTF8), isSilent); 096 } 097 098 public static PGPPublicKeyRing readPublicKeyRing(@Nonnull InputStream inputStream) throws IOException { 099 return readPublicKeyRing(inputStream, MAX_ITERATIONS); 100 } 101 102 /** 103 * Read a public key ring from the provided {@link InputStream}. 104 * If more than maxIterations PGP packets are encountered before a {@link PGPPublicKeyRing} is read, 105 * an {@link IOException} is thrown. 106 * 107 * @param inputStream input stream 108 * @param maxIterations max iterations before abort 109 * @return public key ring 110 */ 111 public static PGPPublicKeyRing readPublicKeyRing(@Nonnull InputStream inputStream, int maxIterations) throws IOException { 112 PGPObjectFactory objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory( 113 ArmorUtils.getDecoderStream(inputStream)); 114 int i = 0; 115 Object next; 116 do { 117 next = objectFactory.nextObject(); 118 if (next == null) { 119 return null; 120 } 121 if (next instanceof PGPMarker) { 122 continue; 123 } 124 if (next instanceof PGPPublicKeyRing) { 125 return (PGPPublicKeyRing) next; 126 } 127 } while (++i < maxIterations); 128 129 throw new IOException("Loop exceeded max iteration count."); 130 } 131 132 public static PGPPublicKeyRingCollection readPublicKeyRingCollection(@Nonnull InputStream inputStream) 133 throws IOException, PGPException { 134 return readPublicKeyRingCollection(inputStream, MAX_ITERATIONS); 135 } 136 137 /** 138 * Read a public key ring collection from the provided {@link InputStream}. 139 * If more than maxIterations PGP packets are encountered before the stream is exhausted, 140 * an {@link IOException} is thrown. 141 * 142 * @param inputStream input stream 143 * @param maxIterations max iterations before abort 144 * @return public key ring collection 145 */ 146 public static PGPPublicKeyRingCollection readPublicKeyRingCollection(@Nonnull InputStream inputStream, int maxIterations) 147 throws IOException, PGPException { 148 PGPObjectFactory objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory( 149 ArmorUtils.getDecoderStream(inputStream)); 150 151 List<PGPPublicKeyRing> rings = new ArrayList<>(); 152 int i = 0; 153 Object next; 154 do { 155 next = objectFactory.nextObject(); 156 if (next == null) { 157 return new PGPPublicKeyRingCollection(rings); 158 } 159 if (next instanceof PGPMarker) { 160 continue; 161 } 162 if (next instanceof PGPPublicKeyRing) { 163 rings.add((PGPPublicKeyRing) next); 164 } 165 if (next instanceof PGPPublicKeyRingCollection) { 166 PGPPublicKeyRingCollection collection = (PGPPublicKeyRingCollection) next; 167 Iterator<PGPPublicKeyRing> iterator = collection.getKeyRings(); 168 while (iterator.hasNext()) { 169 rings.add(iterator.next()); 170 } 171 } 172 } while (++i < maxIterations); 173 174 throw new IOException("Loop exceeded max iteration count."); 175 } 176 177 public static PGPSecretKeyRing readSecretKeyRing(@Nonnull InputStream inputStream) throws IOException { 178 return readSecretKeyRing(inputStream, MAX_ITERATIONS); 179 } 180 181 /** 182 * Read a secret key ring from the provided {@link InputStream}. 183 * If more than maxIterations PGP packets are encountered before a {@link PGPSecretKeyRing} is read, 184 * an {@link IOException} is thrown. 185 * 186 * @param inputStream input stream 187 * @param maxIterations max iterations before abort 188 * @return public key ring 189 */ 190 public static PGPSecretKeyRing readSecretKeyRing(@Nonnull InputStream inputStream, int maxIterations) throws IOException { 191 InputStream decoderStream = ArmorUtils.getDecoderStream(inputStream); 192 PGPObjectFactory objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(decoderStream); 193 int i = 0; 194 Object next; 195 do { 196 next = objectFactory.nextObject(); 197 if (next == null) { 198 return null; 199 } 200 if (next instanceof PGPMarker) { 201 continue; 202 } 203 if (next instanceof PGPSecretKeyRing) { 204 Streams.drain(decoderStream); 205 return (PGPSecretKeyRing) next; 206 } 207 } while (++i < maxIterations); 208 209 throw new IOException("Loop exceeded max iteration count."); 210 } 211 212 public static PGPSecretKeyRingCollection readSecretKeyRingCollection(@Nonnull InputStream inputStream) 213 throws IOException, PGPException { 214 return readSecretKeyRingCollection(inputStream, MAX_ITERATIONS); 215 } 216 217 /** 218 * Read a secret key ring collection from the provided {@link InputStream}. 219 * If more than maxIterations PGP packets are encountered before the stream is exhausted, 220 * an {@link IOException} is thrown. 221 * 222 * @param inputStream input stream 223 * @param maxIterations max iterations before abort 224 * @return secret key ring collection 225 */ 226 public static PGPSecretKeyRingCollection readSecretKeyRingCollection(@Nonnull InputStream inputStream, 227 int maxIterations) 228 throws IOException, PGPException { 229 PGPObjectFactory objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory( 230 ArmorUtils.getDecoderStream(inputStream)); 231 232 List<PGPSecretKeyRing> rings = new ArrayList<>(); 233 int i = 0; 234 Object next; 235 do { 236 next = objectFactory.nextObject(); 237 if (next == null) { 238 return new PGPSecretKeyRingCollection(rings); 239 } 240 if (next instanceof PGPMarker) { 241 continue; 242 } 243 if (next instanceof PGPSecretKeyRing) { 244 rings.add((PGPSecretKeyRing) next); 245 } 246 if (next instanceof PGPSecretKeyRingCollection) { 247 PGPSecretKeyRingCollection collection = (PGPSecretKeyRingCollection) next; 248 Iterator<PGPSecretKeyRing> iterator = collection.getKeyRings(); 249 while (iterator.hasNext()) { 250 rings.add(iterator.next()); 251 } 252 } 253 } while (++i < maxIterations); 254 255 throw new IOException("Loop exceeded max iteration count."); 256 } 257 258 public static PGPKeyRingCollection readKeyRingCollection(@Nonnull InputStream inputStream, boolean isSilent) 259 throws IOException, PGPException { 260 return new PGPKeyRingCollection(inputStream, isSilent); 261 } 262}