/// <summary> /// Validate a file with a MinisignSignature and a MinisignPublicKey object. /// </summary> /// <param name="message">The message to validate.</param> /// <param name="signature">A valid MinisignSignature object.</param> /// <param name="publicKey">A valid MinisignPublicKey object.</param> /// <returns><c>true</c> if valid; otherwise, <c>false</c>.</returns> /// <exception cref="OverflowException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static bool ValidateSignature(byte[] message, MinisignSignature signature, MinisignPublicKey publicKey) { if (message == null) { throw new ArgumentException("missing signature input", nameof(message)); } if (signature == null) { throw new ArgumentException("missing signature input", nameof(signature)); } if (publicKey == null) { throw new ArgumentException("missing publicKey input", nameof(publicKey)); } if (!ArrayHelpers.ConstantTimeEquals(signature.KeyId, publicKey.KeyId)) { return(false); } // verify the signature if (PublicKeyAuth.VerifyDetached(signature.Signature, message, publicKey.PublicKey)) { // verify the trusted comment return(PublicKeyAuth.VerifyDetached(signature.GlobalSignature, ArrayHelpers.ConcatArrays(signature.Signature, signature.TrustedComment), publicKey.PublicKey)); } return(false); }
/// <summary> /// Load a signature into a MinisignSignature object. /// </summary> /// <param name="signature">A valid signature.</param> /// <param name="trustedComment">The associated trustedComment.</param> /// <param name="globalSignature">The associated globalSignature.</param> /// <returns>A MinisignSignature object.</returns> /// <exception cref="OverflowException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static MinisignSignature LoadSignature(byte[] signature, byte[] trustedComment, byte[] globalSignature) { if (signature == null) { throw new ArgumentException("missing signature input", nameof(signature)); } if (trustedComment == null) { throw new ArgumentException("missing trustedComment input", nameof(trustedComment)); } if (globalSignature == null) { throw new ArgumentException("missing globalSignature input", nameof(globalSignature)); } var minisignSignature = new MinisignSignature { SignatureAlgorithm = ArrayHelpers.SubArray(signature, 0, 2), KeyId = ArrayHelpers.SubArray(signature, 2, 8), Signature = ArrayHelpers.SubArray(signature, 10), TrustedComment = trustedComment, GlobalSignature = globalSignature }; return(minisignSignature); }
/// <summary> /// Validate a file with a MinisignSignature and a MinisignPublicKey object. /// </summary> /// <param name="filePath">The full path to the file.</param> /// <param name="signature">A valid MinisignSignature object.</param> /// <param name="publicKey">A valid MinisignPublicKey object.</param> /// <returns><c>true</c> if valid; otherwise, <c>false</c>.</returns> /// <exception cref="FileNotFoundException"></exception> /// <exception cref="OverflowException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public static bool ValidateSignature(string filePath, MinisignSignature signature, MinisignPublicKey publicKey) { if (filePath != null && !File.Exists(filePath)) { throw new FileNotFoundException("could not find filePath"); } if (signature == null) { throw new ArgumentException("missing signature input", nameof(signature)); } if (publicKey == null) { throw new ArgumentException("missing publicKey input", nameof(publicKey)); } if (!ArrayHelpers.ConstantTimeEquals(signature.KeyId, publicKey.KeyId)) { return(false); } // load the file into memory var file = LoadMessageFile(filePath); // verify the signature if (PublicKeyAuth.VerifyDetached(signature.Signature, file, publicKey.PublicKey)) { // verify the trusted comment return(PublicKeyAuth.VerifyDetached(signature.GlobalSignature, ArrayHelpers.ConcatArrays(signature.Signature, signature.TrustedComment), publicKey.PublicKey)); } return(false); }
/// <summary> /// Sign a file with a MinisignPrivateKey. /// </summary> /// <param name="fileToSign">The full path to the file.</param> /// <param name="minisignPrivateKey">A valid MinisignPrivateKey to sign.</param> /// <param name="untrustedComment">An optional untrusted comment.</param> /// <param name="trustedComment">An optional trusted comment.</param> /// <param name="outputFolder">The folder to write the signature (optional).</param> /// <returns>The full path to the signed file.</returns> /// <exception cref="FileNotFoundException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> /// <exception cref="OverflowException"></exception> /// <exception cref="DirectoryNotFoundException"></exception> /// <exception cref="IOException"></exception> /// <exception cref="UnauthorizedAccessException"></exception> /// <exception cref="SecurityException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="PathTooLongException"></exception> /// <exception cref="NotSupportedException"></exception> public static string Sign(string fileToSign, MinisignPrivateKey minisignPrivateKey, string untrustedComment = "", string trustedComment = "", string outputFolder = "") { if (fileToSign != null && !File.Exists(fileToSign)) { throw new FileNotFoundException("could not find fileToSign"); } if (minisignPrivateKey == null) { throw new ArgumentException("missing minisignPrivateKey input", nameof(minisignPrivateKey)); } if (string.IsNullOrEmpty(untrustedComment)) { untrustedComment = DefaultComment; } if (string.IsNullOrEmpty(trustedComment)) { var timestamp = GetTimestamp(); var filename = Path.GetFileName(fileToSign); trustedComment = "timestamp: " + timestamp + " file: " + filename; } if ((CommentPrefix + untrustedComment).Length > CommentMaxBytes) { throw new ArgumentOutOfRangeException(nameof(untrustedComment), "untrustedComment too long"); } if ((TrustedCommentPrefix + trustedComment).Length > TrustedCommentMaxBytes) { throw new ArgumentOutOfRangeException(nameof(trustedComment), "trustedComment too long"); } if (string.IsNullOrEmpty(outputFolder)) { outputFolder = Path.GetDirectoryName(fileToSign); } //validate the outputFolder if (string.IsNullOrEmpty(outputFolder) || !Directory.Exists(outputFolder)) { throw new DirectoryNotFoundException("outputFolder must exist"); } if (outputFolder.IndexOfAny(Path.GetInvalidPathChars()) > -1) { throw new ArgumentException("The given path to the output folder contains invalid characters!"); } var file = LoadMessageFile(fileToSign); var minisignSignature = new MinisignSignature { KeyId = minisignPrivateKey.KeyId, SignatureAlgorithm = Encoding.UTF8.GetBytes(Sigalg) }; var signature = PublicKeyAuth.SignDetached(file, minisignPrivateKey.SecretKey); minisignSignature.Signature = signature; var binarySignature = ArrayHelpers.ConcatArrays( minisignSignature.SignatureAlgorithm, minisignSignature.KeyId, minisignSignature.Signature ); // sign the signature and the trusted comment with a global signature var globalSignature = PublicKeyAuth.SignDetached( ArrayHelpers.ConcatArrays(minisignSignature.Signature, Encoding.UTF8.GetBytes(trustedComment)), minisignPrivateKey.SecretKey); // prepare the file lines var signatureFileContent = new[] { CommentPrefix + untrustedComment, Convert.ToBase64String(binarySignature), TrustedCommentPrefix + trustedComment, Convert.ToBase64String(globalSignature) }; var outputFile = fileToSign + SigSuffix; File.WriteAllLines(outputFile, signatureFileContent); return(outputFile); }