001package co.codewizards.cloudstore.updater; 002 003import static co.codewizards.cloudstore.core.io.StreamUtil.*; 004import static java.util.Objects.*; 005 006import java.io.BufferedInputStream; 007import java.io.InputStream; 008 009import org.bouncycastle.openpgp.PGPObjectFactory; 010import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; 011import org.bouncycastle.openpgp.PGPSignature; 012import org.bouncycastle.openpgp.PGPSignatureList; 013import org.bouncycastle.openpgp.PGPUtil; 014import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; 015import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; 016 017import co.codewizards.cloudstore.core.oio.File; 018 019public class PGPVerifier { 020 private PGPPublicKeyRingCollection publicKeyRingWithTrustedKeys; 021 022 /** 023 * Verify the specified {@code file}. 024 * @param file the file to be verified. Must not be <code>null</code>. There must be a second file 025 * with the same name and the additional suffix ".sig" next to this file (in the same directory). 026 * This secondary file is a so-called detached signature. 027 * @throws PGPVerifyException if the given {@code file} could not be verified successfully. Either 028 * there is no detached-signature-file, or its signature is broken or its signature does not match 029 * any of the {@linkplain #getPublicKeyRingWithTrustedKeys() trusted keys}. 030 */ 031 public void verify(final File file, final File signatureFile) throws PGPVerifyException { 032 requireNonNull(file, "file"); 033 requireNonNull(signatureFile, "signatureFile"); 034 035 final PGPSignatureList sl = readSignatureFile(signatureFile); 036 final PGPPublicKeyRingCollection publicKeyRing = getPublicKeyRingWithTrustedKeys(); 037 038 for (int index = 0; index < sl.size(); ++index) { 039 try { 040 final PGPSignature signature = sl.get(index); 041 signature.init(new BcPGPContentVerifierBuilderProvider(), publicKeyRing.getPublicKey(signature.getKeyID())); 042 043 final InputStream contentIn = castStream(file.createInputStream()); 044 try { 045 final byte[] buf = new byte[16 * 1024]; 046 int len; 047 while (0 <= (len = contentIn.read(buf))) { 048 if (len > 0) 049 signature.update(buf, 0, len); 050 } 051 } finally { 052 contentIn.close(); 053 } 054 055 if (signature.verify()) 056 return; 057 058 } catch (final Exception e) { 059 throw new PGPVerifyException(file.getAbsolutePath() + ": " + e, e); 060 } 061 } 062 throw new PGPVerifyException(file.getAbsolutePath()); 063 } 064 065 private PGPPublicKeyRingCollection getPublicKeyRingWithTrustedKeys() { 066 try { 067 PGPPublicKeyRingCollection ring = publicKeyRingWithTrustedKeys; 068 if (ring == null) { 069 // Currently only one single trusted key ;-) 070 final InputStream publicKeyIn = new BufferedInputStream(PGPVerifier.class.getResourceAsStream("/0x4AB0FBC1.asc")); 071 try { 072 ring = new PGPPublicKeyRingCollection( 073 PGPUtil.getDecoderStream(publicKeyIn), new BcKeyFingerprintCalculator()); 074 } finally { 075 publicKeyIn.close(); 076 } 077 publicKeyRingWithTrustedKeys = ring; 078 } 079 return ring; 080 } catch (final RuntimeException x) { 081 throw x; 082 } catch (final Exception x) { 083 throw new RuntimeException(x); 084 } 085 } 086 087 private PGPSignatureList readSignatureFile(final File signatureFile) throws PGPVerifyException { 088 requireNonNull(signatureFile, "signatureFile"); 089 if (!signatureFile.isFile() || !signatureFile.canRead()) 090 throw new PGPVerifyException("The signature-file does not exist or is not readable: " + signatureFile.getAbsolutePath()); 091 092 try { 093 final InputStream in = new BufferedInputStream(castStream(signatureFile.createInputStream())); 094 try { 095 final PGPObjectFactory objectFactory = new PGPObjectFactory( 096 PGPUtil.getDecoderStream(in), new BcKeyFingerprintCalculator()); 097 final PGPSignatureList sl = (PGPSignatureList) objectFactory.nextObject(); 098 return sl; 099 } finally { 100 in.close(); 101 } 102 } catch (final Exception e) { 103 throw new PGPVerifyException(signatureFile.getAbsolutePath() + ": " + e, e); 104 } 105 } 106}