Пример #1
0
        /// <summary>
        /// Encrypts the entire XML document and digitally signs it.
        /// </summary>
        /// <param name="container"></param>
        /// <param name="document">The document to encrypt and sign. It will be modified in-place by this method.</param>
        /// <param name="encryptFor">Certificate of the document's recipient. Must contain RSA public key for a 2048-bit key pair or larger.</param>
        /// <param name="signWith">Certificate of the document's signer. Must be linked to RSA private key at least 2048 bits in length.</param>
        /// <remarks>
        /// The XML document may be left in a corrupted state if an exception is thrown.
        /// </remarks>
        public static void EncryptAndSign(this HelpersContainerClasses.ProtectedXml container, XmlDocument document, X509Certificate2 encryptFor, X509Certificate2 signWith)
        {
            Helpers.Argument.ValidateIsNotNull(document, nameof(document));
            Helpers.Argument.ValidateIsNotNull(encryptFor, nameof(encryptFor));
            Helpers.Argument.ValidateIsNotNull(signWith, nameof(signWith));

            VerifyCertificateIsSaneAndUsable(encryptFor);
            VerifyCertificateAndPrivateKeyIsSaneAndUsable(signWith);

            // Part 1: encrypt. Default settings are secure and nice, surprisingly.
            var encryptor     = new EncryptedXml();
            var encryptedData = encryptor.Encrypt(document.DocumentElement, encryptFor);

            EncryptedXml.ReplaceElement(document.DocumentElement, encryptedData, false);

            // Part 2: sign.
            using (var signingKey = signWith.GetRSAPrivateKey())
            {
                var signedXml = new SignedXml(document)
                {
                    SigningKey = signingKey
                };

                var whatToSign = new Reference
                {
                    // The entire document is signed.
                    Uri = "",

                    // A nice strong algorithm without known weaknesses that are easily exploitable.
                    DigestMethod = Sha512Algorithm
                };

                // This signature (and other signatures) are inside the signed data, so exclude them.
                whatToSign.AddTransform(new XmlDsigEnvelopedSignatureTransform());

                signedXml.AddReference(whatToSign);

                // A nice strong algorithm without known weaknesses that are easily exploitable.
                signedXml.SignedInfo.SignatureMethod = SignedXml.XmlDsigRSASHA512Url;

                // Canonical XML 1.0 (omit comments); I suppose it works fine, no deep thoughts about this.
                signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigCanonicalizationUrl;

                // Signer certificate must be delivered with the signature.
                signedXml.KeyInfo.AddClause(new KeyInfoX509Data(signWith));

                // Ready to sign! Let's go!
                signedXml.ComputeSignature();

                // Now stick the Signature element it generated back into the document and we are done.
                var signature = signedXml.GetXml();
                document.DocumentElement.AppendChild(document.ImportNode(signature, true));
            }
        }
Пример #2
0
        /// <summary>
        /// Decrypts an XML document encrypted using <see cref="EncryptAndSign"/> after verifying the digital signature on it.
        /// </summary>
        /// <param name="container"></param>
        /// <param name="document">The encrypted and signed document. It will be modified in-place by this method.</param>
        /// <param name="signedBy">The certificate of the document's signer will be output into this variable.</param>
        /// <param name="recipientCertificates">The set of certificates whose key pairs may be used for decryption. The correct certificate and key pair will be selected automatically (if it is among the set).</param>
        /// <exception cref="CryptographicException">
        /// Thrown if a cryptographic operation fails (e.g. because you do not have the correct decryption key).
        /// </exception>
        /// <remarks>
        /// The XML document may be left in a corrupted state if an exception is thrown.
        /// </remarks>
        public static void VerifyAndDecrypt(this HelpersContainerClasses.ProtectedXml container, XmlDocument document, out X509Certificate2 signedBy, params X509Certificate2[] recipientCertificates)
        {
            Helpers.Argument.ValidateIsNotNull(document, nameof(document));
            Helpers.Argument.ValidateIsNotNull(recipientCertificates, nameof(recipientCertificates));

            foreach (var certificate in recipientCertificates)
            {
                if (certificate == null)
                {
                    throw new ArgumentException("Recipient certificate list cannot contain null values.", nameof(recipientCertificates));
                }

                VerifyCertificateAndPrivateKeyIsSaneAndUsable(certificate);
            }

            var namespaces = new XmlNamespaceManager(document.NameTable);

            namespaces.AddNamespace("ds", XmlDigitalSignatureNamespace);
            namespaces.AddNamespace("enc", XmlEncryptionNamespace);

            if (document.SelectSingleNode("/enc:EncryptedData", namespaces) == null)
            {
                throw new ArgumentException("The document is not an encrypted XML document.", nameof(document));
            }

            var signatureNodes = document.SelectNodes("/enc:EncryptedData/ds:Signature", namespaces);

            if (signatureNodes.Count != 1)
            {
                throw new ArgumentException("The document not carry exactly 1 XML digital signature.", nameof(document));
            }

            // Verify signature.
            var signatureNode = (XmlElement)signatureNodes[0];

            var signedXml = new SignedXml(document);

            signedXml.LoadXml(signatureNode);

            if (!signedXml.CheckSignature())
            {
                throw new SecurityException("Signature failed to verify - the XML document has been tampered with!");
            }

            var referenceUris = signedXml.SignedInfo.References.Cast <Reference>().Select(r => r.Uri).ToArray();

            // It must be a whole-document signature.
            if (referenceUris.Length != 1 || referenceUris.Single() != "")
            {
                throw new SecurityException("The digital signature was not scoped to the entire XML document.");
            }

            // The signature must include a certificate for the signer in order to be categorized.
            var certificateElement = signatureNode.SelectSingleNode("ds:KeyInfo/ds:X509Data/ds:X509Certificate", namespaces);

            if (certificateElement == null)
            {
                throw new SecurityException("The digital signature did not contain the certificate of the signer.");
            }

            signedBy = new X509Certificate2(Convert.FromBase64String(certificateElement.InnerText));

            // We do not accept signatures from weak certificates.
            VerifyCertificateIsSaneAndUsable(signedBy);

            // Decrypt.
            var decryptor = new EncryptedXmlWithCustomDecryptionCertificates(document)
            {
                DecryptionCertificates = recipientCertificates
            };

            decryptor.DecryptDocument();
        }