package co.codewizards.cloudstore.updater;
import static co.codewizards.cloudstore.core.io.StreamUtil.*;
import java.io.BufferedInputStream;
import java.io.InputStream;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider;
import co.codewizards.cloudstore.core.oio.File;
import co.codewizards.cloudstore.core.util.AssertUtil;
public class PGPVerifier {
private PGPPublicKeyRingCollection publicKeyRingWithTrustedKeys;
/**
* Verify the specified {@code file}.
* @param file the file to be verified. Must not be <code>null</code>. There must be a second file
* with the same name and the additional suffix ".sig" next to this file (in the same directory).
* This secondary file is a so-called detached signature.
* @throws PGPVerifyException if the given {@code file} could not be verified successfully. Either
* there is no detached-signature-file, or its signature is broken or its signature does not match
* any of the {@linkplain #getPublicKeyRingWithTrustedKeys() trusted keys}.
*/
public void verify(final File file, final File signatureFile) throws PGPVerifyException {
AssertUtil.assertNotNull(file, "file");
AssertUtil.assertNotNull(signatureFile, "signatureFile");
final PGPSignatureList sl = readSignatureFile(signatureFile);
final PGPPublicKeyRingCollection publicKeyRing = getPublicKeyRingWithTrustedKeys();
for (int index = 0; index < sl.size(); ++index) {
try {
final PGPSignature signature = sl.get(index);
signature.init(new BcPGPContentVerifierBuilderProvider(), publicKeyRing.getPublicKey(signature.getKeyID()));
final InputStream contentIn = castStream(file.createInputStream());
try {
final byte[] buf = new byte[16 * 1024];
int len;
while (0 <= (len = contentIn.read(buf))) {
if (len > 0)
signature.update(buf, 0, len);
}
} finally {
contentIn.close();
}
if (signature.verify())
return;
} catch (final Exception e) {
throw new PGPVerifyException(file.getAbsolutePath() + ": " + e, e);
}
}
throw new PGPVerifyException(file.getAbsolutePath());
}
private PGPPublicKeyRingCollection getPublicKeyRingWithTrustedKeys() {
try {
PGPPublicKeyRingCollection ring = publicKeyRingWithTrustedKeys;
if (ring == null) {
// Currently only one single trusted key ;-)
final InputStream publicKeyIn = new BufferedInputStream(PGPVerifier.class.getResourceAsStream("/0x4AB0FBC1.asc"));
try {
ring = new PGPPublicKeyRingCollection(
PGPUtil.getDecoderStream(publicKeyIn), new BcKeyFingerprintCalculator());
} finally {
publicKeyIn.close();
}
publicKeyRingWithTrustedKeys = ring;
}
return ring;
} catch (final RuntimeException x) {
throw x;
} catch (final Exception x) {
throw new RuntimeException(x);
}
}
private PGPSignatureList readSignatureFile(final File signatureFile) throws PGPVerifyException {
AssertUtil.assertNotNull(signatureFile, "signatureFile");
if (!signatureFile.isFile() || !signatureFile.canRead())
throw new PGPVerifyException("The signature-file does not exist or is not readable: " + signatureFile.getAbsolutePath());
try {
final InputStream in = new BufferedInputStream(castStream(signatureFile.createInputStream()));
try {
final PGPObjectFactory objectFactory = new PGPObjectFactory(
PGPUtil.getDecoderStream(in), new BcKeyFingerprintCalculator());
final PGPSignatureList sl = (PGPSignatureList) objectFactory.nextObject();
return sl;
} finally {
in.close();
}
} catch (final Exception e) {
throw new PGPVerifyException(signatureFile.getAbsolutePath() + ": " + e, e);
}
}
}