mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-02 01:02:08 +01:00
Debug signature verification using debug build of bcpg
This commit is contained in:
parent
9fc4b0b42e
commit
4d6ca80e25
5 changed files with 59 additions and 77 deletions
|
@ -8,7 +8,12 @@ dependencies {
|
||||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||||
|
|
||||||
implementation "org.bouncycastle:bcprov-jdk15on:$bouncyCastleVersion"
|
implementation "org.bouncycastle:bcprov-jdk15on:$bouncyCastleVersion"
|
||||||
|
|
||||||
|
//*
|
||||||
api "org.bouncycastle:bcpg-jdk15on:$bouncyCastleVersion"
|
api "org.bouncycastle:bcpg-jdk15on:$bouncyCastleVersion"
|
||||||
|
/*/
|
||||||
|
api files("libs/bcpg-jdk18on-1.70-SNAPSHOT.jar")
|
||||||
|
// */
|
||||||
|
|
||||||
implementation 'org.slf4j:slf4j-api:1.7.32'
|
implementation 'org.slf4j:slf4j-api:1.7.32'
|
||||||
testImplementation 'ch.qos.logback:logback-classic:1.2.5'
|
testImplementation 'ch.qos.logback:logback-classic:1.2.5'
|
||||||
|
|
|
@ -373,8 +373,21 @@ public final class SignatureVerifier {
|
||||||
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signingKey);
|
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signingKey);
|
||||||
int read;
|
int read;
|
||||||
byte[] buf = new byte[8192];
|
byte[] buf = new byte[8192];
|
||||||
|
byte lastByte = -1;
|
||||||
while ((read = signedData.read(buf)) != -1) {
|
while ((read = signedData.read(buf)) != -1) {
|
||||||
signature.update(buf, 0, read);
|
// If we previously omitted a newline, but the stream is not yet empty, add it now
|
||||||
|
if (lastByte == (byte) '\n') {
|
||||||
|
signature.update(lastByte);
|
||||||
|
}
|
||||||
|
lastByte = buf[read - 1];
|
||||||
|
|
||||||
|
if (lastByte == (byte) '\n') {
|
||||||
|
// if last byte in buffer is newline, omit it for now
|
||||||
|
signature.update(buf, 0, read - 1);
|
||||||
|
} else {
|
||||||
|
// otherwise, write buffer as usual
|
||||||
|
signature.update(buf, 0, read);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
throw new SignatureValidationException("Cannot init signature.", e);
|
throw new SignatureValidationException("Cannot init signature.", e);
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package org.pgpainless.signature.cleartext_signatures;
|
package org.pgpainless.signature.cleartext_signatures;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -23,11 +22,7 @@ import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import org.bouncycastle.bcpg.ArmoredInputStream;
|
import org.bouncycastle.bcpg.ArmoredInputStream;
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPObjectFactory;
|
import org.bouncycastle.openpgp.PGPObjectFactory;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
|
||||||
import org.bouncycastle.openpgp.PGPSignatureGenerator;
|
|
||||||
import org.bouncycastle.openpgp.PGPSignatureList;
|
import org.bouncycastle.openpgp.PGPSignatureList;
|
||||||
import org.bouncycastle.util.Strings;
|
import org.bouncycastle.util.Strings;
|
||||||
import org.pgpainless.implementation.ImplementationFactory;
|
import org.pgpainless.implementation.ImplementationFactory;
|
||||||
|
@ -69,19 +64,17 @@ public final class ClearsignedMessageUtil {
|
||||||
if (lookAhead != -1 && in.isClearText()) {
|
if (lookAhead != -1 && in.isClearText()) {
|
||||||
byte[] line = lineOut.toByteArray();
|
byte[] line = lineOut.toByteArray();
|
||||||
out.write(line, 0, getLengthWithoutSeparatorOrTrailingWhitespace(line));
|
out.write(line, 0, getLengthWithoutSeparatorOrTrailingWhitespace(line));
|
||||||
out.write(lineSep);
|
|
||||||
|
|
||||||
while (lookAhead != -1 && in.isClearText()) {
|
while (lookAhead != -1 && in.isClearText()) {
|
||||||
lookAhead = readInputLine(lineOut, lookAhead, in);
|
lookAhead = readInputLine(lineOut, lookAhead, in);
|
||||||
line = lineOut.toByteArray();
|
line = lineOut.toByteArray();
|
||||||
out.write(line, 0, getLengthWithoutSeparatorOrTrailingWhitespace(line));
|
|
||||||
out.write(lineSep);
|
out.write(lineSep);
|
||||||
|
out.write(line, 0, getLengthWithoutSeparatorOrTrailingWhitespace(line));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (lookAhead != -1) {
|
if (lookAhead != -1) {
|
||||||
byte[] line = lineOut.toByteArray();
|
byte[] line = lineOut.toByteArray();
|
||||||
out.write(line, 0, getLengthWithoutSeparatorOrTrailingWhitespace(line));
|
out.write(line, 0, getLengthWithoutSeparatorOrTrailingWhitespace(line));
|
||||||
out.write(lineSep);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -94,38 +87,6 @@ public final class ClearsignedMessageUtil {
|
||||||
return signatures;
|
return signatures;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the given signature by processing the data from the messageData input stream.
|
|
||||||
*
|
|
||||||
* @param signature uninitialized signature
|
|
||||||
* @param signingKey public signing key
|
|
||||||
* @param messageData input stream containing the data to which the signature belongs
|
|
||||||
* @return initialized signature
|
|
||||||
*
|
|
||||||
* @throws PGPException if the signature cannot be initialized
|
|
||||||
* @throws IOException if an IO error happens
|
|
||||||
*/
|
|
||||||
public static PGPSignature initializeSignature(PGPSignature signature, PGPPublicKey signingKey, InputStream messageData)
|
|
||||||
throws PGPException, IOException {
|
|
||||||
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signingKey);
|
|
||||||
|
|
||||||
InputStream sigIn = new BufferedInputStream(messageData);
|
|
||||||
ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
|
|
||||||
int lookAhead = readInputLine(lineOut, sigIn);
|
|
||||||
processLine(signature, lineOut.toByteArray());
|
|
||||||
|
|
||||||
if (lookAhead != -1) {
|
|
||||||
do {
|
|
||||||
lookAhead = readInputLine(lineOut, lookAhead, sigIn);
|
|
||||||
signature.update((byte) '\r');
|
|
||||||
signature.update((byte) '\n');
|
|
||||||
processLine(signature, lineOut.toByteArray());
|
|
||||||
} while (lookAhead != -1);
|
|
||||||
}
|
|
||||||
sigIn.close();
|
|
||||||
return signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
|
public static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
bOut.reset();
|
bOut.reset();
|
||||||
|
@ -190,25 +151,6 @@ public final class ClearsignedMessageUtil {
|
||||||
return nlBytes;
|
return nlBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void processLine(PGPSignature sig, byte[] line) {
|
|
||||||
int length = getLengthWithoutWhiteSpace(line);
|
|
||||||
if (length > 0) {
|
|
||||||
sig.update(line, 0, length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void processLine(OutputStream aOut, PGPSignatureGenerator sGen, byte[] line)
|
|
||||||
throws IOException {
|
|
||||||
// note: trailing white space needs to be removed from the end of
|
|
||||||
// each line for signature calculation RFC 4880 Section 7.1
|
|
||||||
int length = getLengthWithoutWhiteSpace(line);
|
|
||||||
if (length > 0) {
|
|
||||||
sGen.update(line, 0, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
aOut.write(line, 0, line.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getLengthWithoutSeparatorOrTrailingWhitespace(byte[] line) {
|
private static int getLengthWithoutSeparatorOrTrailingWhitespace(byte[] line) {
|
||||||
int end = line.length - 1;
|
int end = line.length - 1;
|
||||||
|
|
||||||
|
@ -223,16 +165,6 @@ public final class ClearsignedMessageUtil {
|
||||||
return b == '\r' || b == '\n';
|
return b == '\r' || b == '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getLengthWithoutWhiteSpace(byte[] line) {
|
|
||||||
int end = line.length - 1;
|
|
||||||
|
|
||||||
while (end >= 0 && isWhiteSpace(line[end])) {
|
|
||||||
end--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return end + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isWhiteSpace(byte b) {
|
private static boolean isWhiteSpace(byte b) {
|
||||||
return isLineEnding(b) || b == '\t' || b == ' ';
|
return isLineEnding(b) || b == '\t' || b == ' ';
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.bouncycastle.openpgp.PGPSignatureList;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.exception.SignatureValidationException;
|
import org.pgpainless.exception.SignatureValidationException;
|
||||||
import org.pgpainless.signature.CertificateValidator;
|
import org.pgpainless.signature.CertificateValidator;
|
||||||
|
import org.pgpainless.signature.SignatureVerifier;
|
||||||
import org.pgpainless.util.ArmoredInputStreamFactory;
|
import org.pgpainless.util.ArmoredInputStreamFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,7 +94,7 @@ public class CleartextSignatureProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ClearsignedMessageUtil.initializeSignature(signature, signingKey, multiPassStrategy.getMessageInputStream());
|
SignatureVerifier.initializeSignatureAndUpdateWithSignedData(signature, multiPassStrategy.getMessageInputStream(), signingKey);
|
||||||
CertificateValidator.validateCertificateAndVerifyInitializedSignature(signature, certificate, PGPainless.getPolicy());
|
CertificateValidator.validateCertificateAndVerifyInitializedSignature(signature, certificate, PGPainless.getPolicy());
|
||||||
return signature;
|
return signature;
|
||||||
} catch (SignatureValidationException e) {
|
} catch (SignatureValidationException e) {
|
||||||
|
|
|
@ -30,15 +30,17 @@ import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.bouncycastle.util.io.Streams;
|
import org.bouncycastle.util.io.Streams;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.key.TestKeys;
|
import org.pgpainless.key.TestKeys;
|
||||||
import org.pgpainless.signature.CertificateValidator;
|
import org.pgpainless.signature.CertificateValidator;
|
||||||
import org.pgpainless.signature.SignatureUtils;
|
import org.pgpainless.signature.SignatureUtils;
|
||||||
import org.pgpainless.signature.cleartext_signatures.ClearsignedMessageUtil;
|
import org.pgpainless.signature.SignatureVerifier;
|
||||||
import org.pgpainless.signature.cleartext_signatures.CleartextSignatureProcessor;
|
import org.pgpainless.signature.cleartext_signatures.CleartextSignatureProcessor;
|
||||||
import org.pgpainless.signature.cleartext_signatures.InMemoryMultiPassStrategy;
|
import org.pgpainless.signature.cleartext_signatures.InMemoryMultiPassStrategy;
|
||||||
import org.pgpainless.signature.cleartext_signatures.MultiPassStrategy;
|
import org.pgpainless.signature.cleartext_signatures.MultiPassStrategy;
|
||||||
|
import org.pgpainless.util.ArmorUtils;
|
||||||
import org.pgpainless.util.TestUtils;
|
import org.pgpainless.util.TestUtils;
|
||||||
|
|
||||||
public class CleartextSignatureVerificationTest {
|
public class CleartextSignatureVerificationTest {
|
||||||
|
@ -48,7 +50,7 @@ public class CleartextSignatureVerificationTest {
|
||||||
"To blazon it, then sweeten with thy breath\n" +
|
"To blazon it, then sweeten with thy breath\n" +
|
||||||
"This neighbor air, and let rich music’s tongue\n" +
|
"This neighbor air, and let rich music’s tongue\n" +
|
||||||
"Unfold the imagined happiness that both\n" +
|
"Unfold the imagined happiness that both\n" +
|
||||||
"Receive in either by this dear encounter.\n";
|
"Receive in either by this dear encounter.";
|
||||||
public static final String MESSAGE_SIGNED = "-----BEGIN PGP SIGNED MESSAGE-----\n" +
|
public static final String MESSAGE_SIGNED = "-----BEGIN PGP SIGNED MESSAGE-----\n" +
|
||||||
"Hash: SHA512\n" +
|
"Hash: SHA512\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -118,11 +120,40 @@ public class CleartextSignatureVerificationTest {
|
||||||
PGPSignature signature = SignatureUtils.readSignatures(SIGNATURE).get(0);
|
PGPSignature signature = SignatureUtils.readSignatures(SIGNATURE).get(0);
|
||||||
PGPPublicKey signingKey = signingKeys.getPublicKey(signature.getKeyID());
|
PGPPublicKey signingKey = signingKeys.getPublicKey(signature.getKeyID());
|
||||||
|
|
||||||
/*
|
|
||||||
SignatureVerifier.initializeSignatureAndUpdateWithSignedData(signature, new ByteArrayInputStream(MESSAGE_BODY.getBytes(StandardCharsets.UTF_8)), signingKey);
|
SignatureVerifier.initializeSignatureAndUpdateWithSignedData(signature, new ByteArrayInputStream(MESSAGE_BODY.getBytes(StandardCharsets.UTF_8)), signingKey);
|
||||||
/*/
|
|
||||||
ClearsignedMessageUtil.initializeSignature(signature, signingKey, new ByteArrayInputStream(MESSAGE_BODY.getBytes(StandardCharsets.UTF_8)));
|
|
||||||
//*/
|
|
||||||
CertificateValidator.validateCertificateAndVerifyInitializedSignature(signature, signingKeys, PGPainless.getPolicy());
|
CertificateValidator.validateCertificateAndVerifyInitializedSignature(signature, signingKeys, PGPainless.getPolicy());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Disabled
|
||||||
|
public void print() throws IOException {
|
||||||
|
// CHECKSTYLE:OFF
|
||||||
|
PGPPublicKeyRing keys = TestKeys.getEmilPublicKeyRing();
|
||||||
|
System.out.println(ArmorUtils.toAsciiArmoredString(keys));
|
||||||
|
System.out.println(MESSAGE_SIGNED);
|
||||||
|
System.out.println(MESSAGE_BODY);
|
||||||
|
System.out.println(SIGNATURE);
|
||||||
|
// CHECKSTYLE:ON
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOutputOfSigVerification() throws IOException, PGPException {
|
||||||
|
PGPSignature signature = SignatureUtils.readSignatures(SIGNATURE).get(0);
|
||||||
|
|
||||||
|
ConsumerOptions options = new ConsumerOptions()
|
||||||
|
.addVerificationCert(TestKeys.getEmilPublicKeyRing())
|
||||||
|
.addVerificationOfDetachedSignature(signature);
|
||||||
|
|
||||||
|
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()
|
||||||
|
.onInputStream(new ByteArrayInputStream(MESSAGE_BODY.getBytes(StandardCharsets.UTF_8)))
|
||||||
|
.withOptions(options);
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
Streams.pipeAll(decryptionStream, out);
|
||||||
|
decryptionStream.close();
|
||||||
|
|
||||||
|
OpenPgpMetadata metadata = decryptionStream.getResult();
|
||||||
|
assertEquals(1, metadata.getVerifiedSignatures().size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue