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