001/* 002 * Copyright 2018 Paul Schaub. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.pgpainless.util; 017 018import javax.annotation.Nonnull; 019import java.io.ByteArrayInputStream; 020import java.io.ByteArrayOutputStream; 021import java.io.IOException; 022import java.io.InputStream; 023import java.util.Arrays; 024import java.util.HashSet; 025import java.util.Iterator; 026import java.util.Set; 027import java.util.logging.Level; 028import java.util.logging.Logger; 029 030import org.bouncycastle.openpgp.PGPException; 031import org.bouncycastle.openpgp.PGPKeyRing; 032import org.bouncycastle.openpgp.PGPPublicKey; 033import org.bouncycastle.openpgp.PGPPublicKeyRing; 034import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; 035import org.bouncycastle.openpgp.PGPSecretKey; 036import org.bouncycastle.openpgp.PGPSecretKeyRing; 037import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; 038import org.bouncycastle.openpgp.PGPSignature; 039import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; 040import org.bouncycastle.openpgp.PGPUtil; 041import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; 042import org.bouncycastle.util.io.Streams; 043import org.pgpainless.algorithm.KeyFlag; 044import org.pgpainless.key.selection.key.PublicKeySelectionStrategy; 045import org.pgpainless.key.selection.key.impl.And; 046import org.pgpainless.key.selection.key.impl.NoRevocation; 047import org.pgpainless.key.selection.key.impl.SignedByMasterKey; 048 049public class BCUtil { 050 051 private static final Logger LOGGER = Logger.getLogger(BCUtil.class.getName()); 052 053 /* 054 PGPXxxKeyRing -> PGPXxxKeyRingCollection 055 */ 056 public static PGPPublicKeyRingCollection keyRingsToKeyRingCollection(@Nonnull PGPPublicKeyRing... rings) 057 throws IOException, PGPException { 058 return new PGPPublicKeyRingCollection(Arrays.asList(rings)); 059 } 060 061 public static PGPSecretKeyRingCollection keyRingsToKeyRingCollection(@Nonnull PGPSecretKeyRing... rings) 062 throws IOException, PGPException { 063 return new PGPSecretKeyRingCollection(Arrays.asList(rings)); 064 } 065 066 public static PGPPublicKeyRing publicKeyRingFromSecretKeyRing(@Nonnull PGPSecretKeyRing secretKeys) 067 throws PGPException, IOException { 068 PGPSecretKeyRing fixedSecretKeys = KeyRingSubKeyFix.repairSubkeyPackets(secretKeys, null, null); 069 070 ByteArrayOutputStream buffer = new ByteArrayOutputStream(512); 071 for (PGPSecretKey secretKey : fixedSecretKeys) { 072 PGPPublicKey publicKey = secretKey.getPublicKey(); 073 if (publicKey != null) { 074 publicKey.encode(buffer, false); 075 } 076 } 077 078 return new PGPPublicKeyRing(buffer.toByteArray(), new BcKeyFingerprintCalculator()); 079 } 080 081 /* 082 PGPXxxKeyRingCollection -> PGPXxxKeyRing 083 */ 084 085 public static PGPSecretKeyRing getKeyRingFromCollection(@Nonnull PGPSecretKeyRingCollection collection, 086 @Nonnull Long id) 087 throws PGPException { 088 PGPSecretKeyRing uncleanedRing = collection.getSecretKeyRing(id); 089 090 // Determine ids of signed keys 091 Set<Long> signedKeyIds = new HashSet<>(); 092 signedKeyIds.add(id); // Add the signing key itself 093 Iterator<PGPPublicKey> signedPubKeys = uncleanedRing.getKeysWithSignaturesBy(id); 094 while (signedPubKeys.hasNext()) { 095 signedKeyIds.add(signedPubKeys.next().getKeyID()); 096 } 097 098 PGPSecretKeyRing cleanedRing = uncleanedRing; 099 Iterator<PGPSecretKey> secretKeys = uncleanedRing.getSecretKeys(); 100 while (secretKeys.hasNext()) { 101 PGPSecretKey secretKey = secretKeys.next(); 102 if (!signedKeyIds.contains(secretKey.getKeyID())) { 103 cleanedRing = PGPSecretKeyRing.removeSecretKey(cleanedRing, secretKey); 104 } 105 } 106 return cleanedRing; 107 } 108 109 public static PGPPublicKeyRing getKeyRingFromCollection(@Nonnull PGPPublicKeyRingCollection collection, 110 @Nonnull Long id) 111 throws PGPException { 112 PGPPublicKey key = collection.getPublicKey(id); 113 return removeUnassociatedKeysFromKeyRing(collection.getPublicKeyRing(id), key); 114 } 115 116 public static InputStream getPgpDecoderInputStream(@Nonnull byte[] bytes) 117 throws IOException { 118 return getPgpDecoderInputStream(new ByteArrayInputStream(bytes)); 119 } 120 121 public static InputStream getPgpDecoderInputStream(@Nonnull InputStream inputStream) 122 throws IOException { 123 return PGPUtil.getDecoderStream(inputStream); 124 } 125 126 public static byte[] getDecodedBytes(@Nonnull byte[] bytes) 127 throws IOException { 128 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 129 Streams.pipeAll(getPgpDecoderInputStream(bytes), buffer); 130 return buffer.toByteArray(); 131 } 132 133 public static byte[] getDecodedBytes(@Nonnull InputStream inputStream) 134 throws IOException { 135 ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 136 Streams.pipeAll(inputStream, buffer); 137 return getDecodedBytes(buffer.toByteArray()); 138 } 139 140 /** 141 * Remove all keys from the key ring, are either not having a subkey signature from the master key 142 * (identified by {@code masterKeyId}), or are revoked ("normal" key revocation, as well as subkey revocation). 143 * 144 * @param ring key ring 145 * @param masterKey master key 146 * @return "cleaned" key ring 147 */ 148 public static PGPPublicKeyRing removeUnassociatedKeysFromKeyRing(@Nonnull PGPPublicKeyRing ring, 149 @Nonnull PGPPublicKey masterKey) { 150 if (!masterKey.isMasterKey()) { 151 throw new IllegalArgumentException("Given key is not a master key."); 152 } 153 // Only select keys which are signed by the master key and not revoked. 154 PublicKeySelectionStrategy<PGPPublicKey> selector = new And.PubKeySelectionStrategy<>( 155 new SignedByMasterKey.PubkeySelectionStrategy(), 156 new NoRevocation.PubKeySelectionStrategy<>()); 157 158 PGPPublicKeyRing cleaned = ring; 159 160 Iterator<PGPPublicKey> publicKeys = ring.getPublicKeys(); 161 while (publicKeys.hasNext()) { 162 PGPPublicKey publicKey = publicKeys.next(); 163 if (!selector.accept(masterKey, publicKey)) { 164 cleaned = PGPPublicKeyRing.removePublicKey(cleaned, publicKey); 165 } 166 } 167 168 return cleaned; 169 } 170 171 /** 172 * Remove all keys from the key ring, are either not having a subkey signature from the master key 173 * (identified by {@code masterKeyId}), or are revoked ("normal" key revocation, as well as subkey revocation). 174 * 175 * @param ring key ring 176 * @param masterKey master key 177 * @return "cleaned" key ring 178 */ 179 public static PGPSecretKeyRing removeUnassociatedKeysFromKeyRing(@Nonnull PGPSecretKeyRing ring, 180 @Nonnull PGPPublicKey masterKey) { 181 if (!masterKey.isMasterKey()) { 182 throw new IllegalArgumentException("Given key is not a master key."); 183 } 184 // Only select keys which are signed by the master key and not revoked. 185 PublicKeySelectionStrategy<PGPPublicKey> selector = new And.PubKeySelectionStrategy<>( 186 new SignedByMasterKey.PubkeySelectionStrategy(), 187 new NoRevocation.PubKeySelectionStrategy<>()); 188 189 PGPSecretKeyRing cleaned = ring; 190 191 Iterator<PGPSecretKey> secretKeys = ring.getSecretKeys(); 192 while (secretKeys.hasNext()) { 193 PGPSecretKey secretKey = secretKeys.next(); 194 if (!selector.accept(masterKey, secretKey.getPublicKey())) { 195 cleaned = PGPSecretKeyRing.removeSecretKey(cleaned, secretKey); 196 } 197 } 198 199 return cleaned; 200 } 201 202 /** 203 * Return the {@link PGPPublicKey} which is the master key of the key ring. 204 * 205 * @param ring key ring 206 * @return master key 207 */ 208 public static PGPPublicKey getMasterKeyFrom(@Nonnull PGPPublicKeyRing ring) { 209 Iterator<PGPPublicKey> it = ring.getPublicKeys(); 210 while (it.hasNext()) { 211 PGPPublicKey k = it.next(); 212 if (k.isMasterKey()) { 213 // There can only be one master key, so we can immediately return 214 return k; 215 } 216 } 217 return null; 218 } 219 220 public static PGPPublicKey getMasterKeyFrom(@Nonnull PGPKeyRing ring) { 221 Iterator it = ring.getPublicKeys(); 222 while (it.hasNext()) { 223 PGPPublicKey k = (PGPPublicKey) it.next(); 224 if (k.isMasterKey()) { 225 // There can only be one master key, so we can immediately return 226 return k; 227 } 228 } 229 return null; 230 } 231 232 public static Set<Long> signingKeyIds(@Nonnull PGPSecretKeyRing ring) { 233 Set<Long> ids = new HashSet<>(); 234 Iterator<PGPPublicKey> it = ring.getPublicKeys(); 235 while (it.hasNext()) { 236 PGPPublicKey k = it.next(); 237 238 boolean signingKey = false; 239 240 Iterator sit = k.getSignatures(); 241 while (sit.hasNext()) { 242 Object n = sit.next(); 243 if (!(n instanceof PGPSignature)) { 244 continue; 245 } 246 247 PGPSignature s = (PGPSignature) n; 248 if (!s.hasSubpackets()) { 249 continue; 250 } 251 252 try { 253 s.verifyCertification(ring.getPublicKey(s.getKeyID())); 254 } catch (PGPException e) { 255 LOGGER.log(Level.WARNING, "Could not verify signature on " + Long.toHexString(k.getKeyID()) + " made by " + Long.toHexString(s.getKeyID())); 256 continue; 257 } 258 259 PGPSignatureSubpacketVector hashed = s.getHashedSubPackets(); 260 if (KeyFlag.fromInteger(hashed.getKeyFlags()).contains(KeyFlag.SIGN_DATA)) { 261 signingKey = true; 262 break; 263 } 264 } 265 266 if (signingKey) { 267 ids.add(k.getKeyID()); 268 } 269 } 270 return ids; 271 } 272 273 public static boolean keyRingContainsKeyWithId(@Nonnull PGPPublicKeyRing ring, 274 long keyId) { 275 return ring.getPublicKey(keyId) != null; 276 } 277 278 public static boolean keyRingContainsKeyWithId(@Nonnull PGPSecretKeyRing ring, 279 long keyId) { 280 return ring.getSecretKey(keyId) != null; 281 } 282}