private bool VerifySignatureDsa() { byte[] signatureBlockBytes = JarUtils.ReadBytes(ArchivePath, SignatureBlockFilePath); byte[] signatureFileBytes = JarUtils.ReadBytes(ArchivePath, SignatureFilePath); SHA1Managed sha = new SHA1Managed(); // lgtm [cs/weak-crypto] Hash algorithm specified by signature algorithm byte[] hash = sha.ComputeHash(signatureFileBytes); ContentInfo ci = new ContentInfo(signatureFileBytes); SignedCms cms = new SignedCms(ci, detached: true); cms.Decode(signatureBlockBytes); try { cms.CheckSignature(verifySignatureOnly: true); } catch (CryptographicException ce) { JarError.AddError(ce.Message); return(false); } // If there were no exceptions logged then signature verification should be good. return(true); }
/// <summary> /// Verifies each individual entry's x-Digest against the computed digest of the individual section of the entry in /// the manifest. /// </summary> /// <param name="manifestFile">The manifest file to use when computing individual digests.</param> /// <returns>true if verifications was successful, false otherwise.</returns> public bool VerifySignatureSourceFileDigests(JarManifestFile manifestFile) { foreach (JarIndividualEntry signatureFileEntry in IndividualSection) { JarIndividualEntry manifestFileEntry = manifestFile.IndividualSection.FirstOrDefault( i => String.Equals(i.Name, signatureFileEntry.Name)); if (manifestFileEntry != null) { string computedDigest = JarUtils.GetHashDigest(manifestFileEntry.RawText, signatureFileEntry.HashAlgorithmName); if (!String.Equals(computedDigest, signatureFileEntry.DigestValue)) { JarError.AddError(String.Format(JarResources.SignatureFileEntryDigestMismatch, signatureFileEntry.Name, SignatureFilePath, computedDigest, signatureFileEntry.DigestValue)); return(false); } } else { // Signature file contains an entry that's not present in the MANIFEST.MF file JarError.AddError(String.Format(JarResources.MissingManifestEntry, signatureFileEntry.Name, SignatureFilePath)); return(false); } } // If we make it out of the loop we're all good return(true); }
/// <summary> /// Verifies whether or not the JAR file is signed. /// </summary> /// <returns>True if the JAR file is signed, false otherwise.</returns> public bool IsSigned() { // If there are no signature files or a manifest then don't bother doing anything further. if (!(HasSignatureFile && HasManifestFile)) { JarError.AddError(JarResources.MissingSignatureOrManifestFile); return(false); } // Verify the file based on the spec at https://docs.oracle.com/javase/7/docs/technotes/guides/jar/jar.html // // STEP 1: Verify the signature over the signature file when the manifest is first parsed. For // efficiency, this verification can be remembered. Note that this verification only validates // the signature directions themselves, not the actual archive files. // // Note: There can be multiple signature (.SF) files, e.g. as new files are added to the archive after it was signed. if (!SignatureFiles.All(sf => sf.VerifySignature())) { return(false); } // STEP 2: If an x-Digest-Manifest attribute exists in the signature file, verify the value against a digest calculated // over the entire manifest. If more than one x-Digest-Manifest attribute exists in the signature file, // verify that at least one of them matches the calculated digest value. // Get all the signature files that failed to verify the x-Digest-Manifest attributes IEnumerable <JarSignatureFile> signatureFilesFailedVerifyDigestManifest = from sf in SignatureFiles where !sf.VerifyDigestManifest(Manifest) select sf; if (signatureFilesFailedVerifyDigestManifest.Count() > 0) { // STEP 3: If an x-Digest-Manifest attribute does not exist in the signature file or none of the digest values calculated // in the previous step match, then a less optimized verification is performed: // * If an x-Digest-Manifest-Main-Attributes entry exists in the signature file, verify the value against // a digest calculated over the main attributes in the manifest file. If this calculation fails, then JAR // file verification fails. This decision can be remembered for efficiency. If an x-Digest-Manifest-Main-Attributes // entry does not exist in the signature file, its nonexistence does not affect JAR file verification and the // manifest main attributes are not verified. // * Verify the digest value in each source file information section in the signature file against a digest value // calculated against the corresponding entry in the manifest file. If any of the digest values don't match, then // JAR file verification fails. if (!signatureFilesFailedVerifyDigestManifest.All(sf => sf.VerifyDigestManifestMain(Manifest))) { return(false); } } // STEP 4: For each entry in the manifest, verify the digest value in the manifest file against // a digest calculated over the actual data referenced in the "Name:" attribute, which // specifies either a relative file path or URL. If any of the digest values don't match, // then JAR file verification fails. if (!Manifest.VerifyManifestEntries()) { return(false); } return(true); }
private bool Verify(JarIndividualEntry entry, ZipArchiveEntry archiveEntry) { using (Stream stream = archiveEntry.Open()) { HashAlgorithm ha = HashAlgorithm.Create(entry.HashAlgorithmName); byte[] computedHash = ha.ComputeHash(stream); string hashDigest = Convert.ToBase64String(computedHash); // Compare the computed hash digest against the value provided in the manifest file. if (!String.Equals(entry.DigestValue, hashDigest)) { JarError.AddError(String.Format(JarResources.ManifestEntryDigestMismatch, entry.Name, entry.DigestValue, hashDigest)); return(false); } return(true); } }
/// <summary> /// Verify the signature over the signature file. /// </summary> /// <returns>True if the signature was verified successfully, false otherwise.</returns> public bool VerifySignature() { if (String.IsNullOrEmpty(SignatureBlockFilePath)) { JarError.AddError(String.Format(JarResources.MissingSignatureBlockFile, BaseFilename + ".RSA", BaseFilename + ".DSA")); return(false); } if (String.Equals(Path.GetExtension(SignatureBlockFilePath), ".RSA", StringComparison.OrdinalIgnoreCase)) { return(VerifySignatureRsa()); } if (String.Equals(Path.GetExtension(SignatureBlockFilePath), ".DSA", StringComparison.OrdinalIgnoreCase)) { return(VerifySignatureDsa()); } return(false); }
/// <summary> /// Verify the signature file, e.g. x.SF using the corresponding signature block, e.g. x.RSA /// </summary> /// <returns>True if the verification is successful, false otherwise.</returns> private bool VerifySignatureRsa() { Timestamps.Clear(); byte[] signatureBlockBytes = JarUtils.ReadBytes(ArchivePath, SignatureBlockFilePath); byte[] signatureFileBytes = JarUtils.ReadBytes(ArchivePath, SignatureFilePath); SHA256Managed sha = new SHA256Managed(); byte[] hash = sha.ComputeHash(signatureFileBytes); ContentInfo ci = new ContentInfo(signatureFileBytes); SignedCms cms = new SignedCms(ci, detached: true); cms.Decode(signatureBlockBytes); try { cms.CheckSignature(verifySignatureOnly: true); // See if we can retrieve a timestamp foreach (SignerInfo signerInfo in cms.SignerInfos) { foreach (CryptographicAttributeObject unsignedAttribute in signerInfo.UnsignedAttributes) { if (String.Equals(unsignedAttribute.Oid.Value, WinCrypt.szOID_SIGNATURE_TIMESTAMP_ATTRIBUTE, StringComparison.OrdinalIgnoreCase)) { Pkcs9AttributeObject timestampAttribute = new Pkcs9AttributeObject(unsignedAttribute.Values[0]); SignedCms timestampCms = new SignedCms(); timestampCms.Decode(timestampAttribute.RawData); TstInfo timestampToken = TstInfo.Read(timestampCms.ContentInfo.Content); foreach (SignerInfo timestampSigner in timestampCms.SignerInfos) { foreach (CryptographicAttributeObject sa in timestampSigner.SignedAttributes) { if (String.Equals(sa.Oid.Value, WinCrypt.szOID_RSA_signingTime, StringComparison.OrdinalIgnoreCase)) { var signingTime = (Pkcs9SigningTime)sa.Values[0]; X509Certificate2 timestampSignerCert = timestampSigner.Certificate; Timestamps.Add(new Timestamp { SignedOn = signingTime.SigningTime.ToLocalTime(), EffectiveDate = Convert.ToDateTime(timestampSignerCert.GetEffectiveDateString()).ToLocalTime(), ExpiryDate = Convert.ToDateTime(timestampSignerCert.GetExpirationDateString()).ToLocalTime(), SignatureAlgorithm = timestampSignerCert.SignatureAlgorithm.FriendlyName }); } } } } } } } catch (CryptographicException ce) { JarError.AddError(ce.Message); return(false); } // If there were no exceptions logged then signature verification should be good. return(true); }