Exemplo n.º 1
0
        /// <summary>
        /// Load all manifest entries from a given manifest in a JAR
        /// </summary>
        /// <param name="source">source JAR</param>
        /// <param name="manifest">manifest to load</param>
        /// <returns>manifest data</returns>
        public ManifestData Load(IJar source, string path)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (path.IsNullOrEmpty())
            {
                throw new ArgumentNullException(nameof(path));
            }

            // If this file does not exist, obviously we cannot load it
            if (!source.Contains(path))
            {
                throw new ManifestException($"Manifest {path} does not exist");
            }

            ManifestData manifest = new ManifestData
            {
                ManifestDigest = String.Empty,
                Entries        = new List <ManifestEntry>()
            };

            // The manifest digest is supposed to refer to the digest that THIS manifest
            // expects the main manifest to have. Until set otherwise, assume this digest
            // is = to our own hash (which means loading the main manifest populates this
            // value with the main manifest hash for future comparison)
            using (Hasher h = new Hasher())
            {
                manifest.ManifestDigest = source.SHA256(h, path).ToBase64();
            }

            try
            {
                using (StreamReader reader = new StreamReader(source.Open(path)))
                {
                    string[] lines = Unwrap70(reader.ReadToEnd().Split(
                                                  new char[]
                    {
                        (char)10,
                        (char)13
                    },
                                                  StringSplitOptions.RemoveEmptyEntries));

                    Populate(manifest, lines);
                }
            }
            catch (Exception ex)
            {
                throw new ManifestException($"Failed to open or parse manifest {path}", ex);
            }

            return(manifest);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Produce a SHA256 of a file in the JAR. If the file does not exist, an exception is thrown.
        /// </summary>
        /// <param name="this"></param>
        /// <param name="hasher">hasher implementation</param>
        /// <param name="path">filename to generate a hash of</param>
        /// <returns>the byte data of the SHA-256 hash</returns>
        public static byte[] SHA256(this IJar @this, Hasher hasher, string path)
        {
            if (hasher == null)
            {
                throw new ArgumentNullException(nameof(hasher));
            }

            if (path.IsNullOrEmpty())
            {
                throw new ArgumentNullException(nameof(path));
            }

            if ([email protected](path))
            {
                throw new JarException($"File to hash {path} does not exist in JAR");
            }

            using (Stream file = @this.Open(path))
            {
                return(hasher.SHA256(file));
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Perform JAR digital signature verification against a JAR filename on disk
        /// </summary>
        /// <param name="jar">JAR container. The caller is expected to dispose this type themselves - it will not be disposed
        /// by this method</param>
        /// <param name="certificates">certificate to verify / accept against</param>
        /// <param name="nonStandardCountCheck">whether to perform the additional file count verification check against
        /// MANIFEST.MF (recommended if the file is actually an arbitrary ZIP)</param>
        /// <returns>digital signature verification state of the JAR</returns>
        public static VerificationResult Jar(IJar jar, IVerificationCertificates certificates, bool nonStandardCountCheck = true)
        {
            // Unsigned ZIP and probably not even a JAR
            if (!jar.Contains(@"META-INF\MANIFEST.MF"))
            {
                return(new VerificationResult
                {
                    Status = SigningStatus.NotSigned,
                    Valid = false
                });
            }

            IManifestLoader manifestLoader = new ManifestLoader();

            ManifestData centralManifest = manifestLoader.Load(jar, @"META-INF\MANIFEST.MF");

            if (nonStandardCountCheck)
            {
                // Non-standard check: Ensure that no unsigned files have been ADDED
                // to the JAR (file qty. [except signature itself] must match manifest entries)
                //
                int nonManifestFiles = jar.NonSignatureFiles().Count();

                if (centralManifest.Entries.Count != nonManifestFiles)
                {
                    Log.Message($"Expected {centralManifest.Entries.Count} file(s) found {nonManifestFiles}");

                    return(new VerificationResult
                    {
                        Status = SigningStatus.FundamentalHashMismatch,
                        Valid = false
                    });
                }
            }

            // Verify the hashes of every file in the JAR
            //
            using (var h = new Hasher())
            {
                Log.Message($"Central manifest contains {centralManifest.Entries.Count} entries");

                foreach (ManifestEntry e in centralManifest.Entries)
                {
                    Log.Message($"Digest check {e.Path} ({e.Digest})");

                    // Check each file matches the hash in the manifest
                    if (jar.SHA256(h, e.Path).ToBase64() != e.Digest)
                    {
                        Log.Message($"{e.Path} has an incorrect digest");

                        return(new VerificationResult
                        {
                            Status = SigningStatus.FundamentalHashMismatch,
                            Valid = false
                        });
                    }
                }
            }

            // Detect signatures
            //
            //
            ISignatureFinder finder = new SignatureFinder();

            List <Signature> signatures = finder.Find(jar);

            if (!signatures.Any())
            {
                Log.Message("No signatures detected");

                return(new VerificationResult
                {
                    Status = SigningStatus.NotSigned,
                    Valid = false
                });
            }

            Log.Message($"{signatures.Count} signature(s) detected");

            // Verify signatures
            //
            //
            SignatureVerifier ver = new SignatureVerifier();

            if (ver.Verify(jar, centralManifest, signatures, certificates))
            {
                return(new VerificationResult
                {
                    Status = SigningStatus.SignedValid,
                    Valid = true
                });
            }
            else
            {
                return(new VerificationResult
                {
                    Status = SigningStatus.SignedInvalid,
                    Valid = false
                });
            }
        }