Share this link

    Verify the Chain Yourself

    2067 0 Created on 2020-10-09 10:22:09; Last updated on 2022-07-01 13:01:14

    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 support@babelway.com to receive theses files.

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

    package com.babelway.tools;
    
    import java.io.IOException;
    import java.io.StringReader;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.security.Security;
    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;
    import au.com.bytecode.opencsv.CSVReader;
    
    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"));
            assertChunkFileIsSelfConsistent(chunk);
            System.out.println("The chunk is self consistant!");
    
            // # 2. Validate the related timestamp
            // The timestamp
            byte[] timestamp = Files.readAllBytes(Paths.get("/tmp/timestamp.txt"));
            assertTimestampIsValid(timestamp);
            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  ) {
                        sb.append(row[j]).append(';');
                    }
                    sb.append(chainHash);
                    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