Verify the Chain Yourself

    13751 0 Created on 2020-10-09 10:22:09; Last updated on 2022-06-25 19:00:40

    To verify the message yourself, you will need 3 set of files. First, you need the secure chain chunk containing the message, then you need the corresponding timestamp and finally the manual signature of the chunk. Contact to receive theses files.

    The following java code, using bouncycastle and CSVReader, can be used to validate the chain.

    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.util.Base64;
    import java.util.Collection;
    import org.bouncycastle.cert.X509CertificateHolder;
    import org.bouncycastle.cms.CMSProcessableByteArray;
    import org.bouncycastle.cms.CMSSignedData;
    import org.bouncycastle.cms.CMSSignerDigestMismatchException;
    import org.bouncycastle.cms.CMSVerifierCertificateNotValidException;
    import org.bouncycastle.cms.SignerInformation;
    import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
    import org.bouncycastle.jcajce.provider.digest.SHA3;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import org.bouncycastle.util.encoders.Hex;
    public class SecureChainValidator {
        public static void main(String[] args) throws Exception {
            Security.addProvider(new BouncyCastleProvider());
            // # 1. Validate that the part of the secure chain (i.e. the chunk) that you want to verify is self-consistant
            //      The CSV file that contains a part of the secure chain that you want to verify.
            byte[] chunk = Files.readAllBytes(Paths.get("/tmp/chunk.csv"));
            System.out.println("The chunk is self consistant!");
            // # 2. Validate the related timestamp
            // The timestamp
            byte[] timestamp = Files.readAllBytes(Paths.get("/tmp/timestamp.txt"));
            System.out.println("The timstamp is valid!");
            // 2. Validate the related manual signature
            // The text file (in JSON format) that contains all the information about all the chunks that
            // were manually signed together, including the target chunk
            byte[] signatureContent = Files.readAllBytes(Paths.get("/tmp/signatureContent.txt"));
            // The signature itself
            byte[] signature = Files.readAllBytes(Paths.get("/tmp/signature.txt"));
            assertSignatureIsValid(signature, signatureContent);
            System.out.println("The signature is valid!");
            System.out.println("Verification finished successfully");
        public static void assertChunkFileIsSelfConsistent(byte[] chunk) {
            try (CSVReader reader = new CSVReader(new StringReader(new String(chunk)), ';', '"', '\\')) {
                String[] row = reader.readNext();
                if (row == null) {
                    throw new IllegalArgumentException("Empty chunk file");
                //chaining = chainId;creationMoment;hubId;messageKey;step;hash;previousChainHash
                String chainHash = row[6];
                StringBuilder sb;
                while ((row = reader.readNext()) != null) {
                    sb = new StringBuilder();
                    for (int j = 0; j < 6; j  ) {
                    chainHash = hash(sb.toString().getBytes());
                    if (!chainHash.equals(row[6])) {
                        throw new RuntimeException("Chunk is not consitant: expected chain hash "   chainHash);
            } catch (IOException e) {
                throw new RuntimeException("Unable to read chunk content");
        public static String hash(byte[] bytes) {
            if (bytes == null) {
                throw new IllegalArgumentException("Argument should not be null");
            try {
                SHA3.DigestSHA3 md = new SHA3.Digest512();
                return new String(Hex.encode(md.digest(bytes)), "UTF8");
            } catch (Exception e) {
                throw new RuntimeException(e);
        public static void assertTimestampIsValid(byte[] timestamp) {
            try {
                CMSSignedData cmsSignedData = new CMSSignedData(Base64.getDecoder().decode(new String(timestamp)));
                SignerInformation timestampSigner = cmsSignedData.getSignerInfos().getSigners().iterator().next();
                X509CertificateHolder timestampSigningCertificate = getSignerCertificate(cmsSignedData, timestampSigner);
                if (!timestampSigner.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME).build(timestampSigningCertificate))) {
                    throw new RuntimeException("Timestamp status is not OK");
            } catch (Exception e) {
                throw new RuntimeException("Unable to validate timestamp: "   e.getMessage(), e);
        public static void assertSignatureIsValid(byte[] signature, byte[] signatureContent) {
            try {
                CMSSignedData signedData = new CMSSignedData(new CMSProcessableByteArray(signatureContent), Base64.getDecoder().decode(new String(signature)));
                SignerInformation signer = signedData.getSignerInfos().getSigners().iterator().next();
                X509CertificateHolder signerCertificate = getSignerCertificate(signedData, signer);
                if (!signer.verify((new JcaSimpleSignerInfoVerifierBuilder()).setProvider("BC").build(signerCertificate))) {
                    throw new RuntimeException("Signature is not valid");
            } catch (CMSVerifierCertificateNotValidException | CMSSignerDigestMismatchException e) {
                throw new RuntimeException("Signature is not valid", e);
            } catch (Exception e) {
                throw new RuntimeException("Unable to verify signature.", e);
        private static X509CertificateHolder getSignerCertificate(CMSSignedData signedData, SignerInformation signerInfo) {
            Collection matches = signedData.getCertificates().getMatches(signerInfo.getSID());
            if (matches != null && !matches.isEmpty()) {
                return (X509CertificateHolder) matches.iterator().next();
            } else {
                throw new RuntimeException("No certificate found for the signer");


    0 people found this helpful.

    Related Articles