/// <summary> /// Signs the specified xml document with the certificate found in /// the local machine matching the provided friendly name and /// referring to the specified target reference ID. /// </summary> /// <param name="certFriendlyName"> /// Friendly Name of the X509Certificate to be retrieved /// from the LocalMachine keystore and used to sign the xml document. /// Be sure to have appropriate permissions set on the keystore. /// </param> /// <param name="xmlDoc"> /// XML document to be signed. /// </param> /// <param name="targetReferenceId"> /// Reference element that will be specified as signed. /// </param> /// <param name="includePublicKey"> /// Flag to determine whether to include the public key in the /// signed xml. /// </param> public static void SignXml(string certFriendlyName, IXPathNavigable xmlDoc, string targetReferenceId, bool includePublicKey) { if (string.IsNullOrEmpty(certFriendlyName)) { throw new Saml2Exception(Resources.SignedXmlInvalidCertFriendlyName); } if (xmlDoc == null) { throw new Saml2Exception(Resources.SignedXmlInvalidXml); } if (string.IsNullOrEmpty(targetReferenceId)) { throw new Saml2Exception(Resources.SignedXmlInvalidTargetRefId); } X509Certificate2 cert = FedletCertificateFactory.GetCertificateByFriendlyName(certFriendlyName); if (cert == null) { throw new Saml2Exception(Resources.SignedXmlCertNotFound); } XmlDocument xml = (XmlDocument)xmlDoc; SignedXml signedXml = new SignedXml(xml); signedXml.SigningKey = cert.PrivateKey; if (includePublicKey) { KeyInfo keyInfo = new KeyInfo(); keyInfo.AddClause(new KeyInfoX509Data(cert)); signedXml.KeyInfo = keyInfo; } Reference reference = new Reference(); reference.Uri = "#" + targetReferenceId; XmlDsigEnvelopedSignatureTransform envelopSigTransform = new XmlDsigEnvelopedSignatureTransform(); reference.AddTransform(envelopSigTransform); signedXml.AddReference(reference); signedXml.ComputeSignature(); XmlElement xmlSignature = signedXml.GetXml(); XmlNamespaceManager nsMgr = new XmlNamespaceManager(xml.NameTable); nsMgr.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl); nsMgr.AddNamespace("saml", Saml2Constants.NamespaceSamlAssertion); nsMgr.AddNamespace("samlp", Saml2Constants.NamespaceSamlProtocol); XmlNode issuerNode = xml.DocumentElement.SelectSingleNode("saml:Issuer", nsMgr); if (issuerNode != null) { xml.DocumentElement.InsertAfter(xmlSignature, issuerNode); } else { // Insert as a child to the target reference id XmlNode targetNode = xml.DocumentElement.SelectSingleNode("//*[@ID='" + targetReferenceId + "']", nsMgr); targetNode.PrependChild(xmlSignature); } }
/// <summary> /// Signs the specified query string with the certificate found in the /// local machine matching the provided friendly name. The algorithm /// is expected to be one of the parameters in the query string. /// </summary> /// <param name="certFriendlyName"> /// Friendly Name of the X509Certificate to be retrieved /// from the LocalMachine keystore and used to sign the xml document. /// Be sure to have appropriate permissions set on the keystore. /// </param> /// <param name="queryString">Query string to sign.</param> /// <returns> /// A signed query string where a digital signature is added. /// </returns> public static string SignQueryString(string certFriendlyName, string queryString) { if (string.IsNullOrEmpty(certFriendlyName)) { throw new Saml2Exception(Resources.SignedQueryStringInvalidCertFriendlyName); } if (string.IsNullOrEmpty(queryString)) { throw new Saml2Exception(Resources.SignedQueryStringInvalidQueryString); } char[] queryStringSep = { '&' }; NameValueCollection queryParams = new NameValueCollection(); foreach (string pairs in queryString.Split(queryStringSep)) { string key = pairs.Substring(0, pairs.IndexOf("=", StringComparison.Ordinal)); string value = pairs.Substring(pairs.IndexOf("=", StringComparison.Ordinal) + 1); queryParams[key] = value; } if (string.IsNullOrEmpty(queryParams[Saml2Constants.SignatureAlgorithm])) { throw new Saml2Exception(Resources.SignedQueryStringSigAlgMissing); } X509Certificate2 cert = FedletCertificateFactory.GetCertificateByFriendlyName(certFriendlyName); if (cert == null) { throw new Saml2Exception(Resources.SignedQueryStringCertNotFound); } if (!cert.HasPrivateKey) { throw new Saml2Exception(Resources.SignedQueryStringCertHasNoPrivateKey); } string encodedSignature = string.Empty; string signatureAlgorithm = HttpUtility.UrlDecode(queryParams[Saml2Constants.SignatureAlgorithm]); if (signatureAlgorithm == Saml2Constants.SignatureAlgorithmRsa) { RSACryptoServiceProvider privateKey = (RSACryptoServiceProvider)cert.PrivateKey; byte[] signature = privateKey.SignData( Encoding.UTF8.GetBytes(queryString.ToString()), new SHA1CryptoServiceProvider()); encodedSignature = Convert.ToBase64String(signature); } else { throw new Saml2Exception(Resources.SignedQueryStringSigAlgNotSupported); } string signedQueryString = queryString + "&" + Saml2Constants.Signature + "=" + HttpUtility.UrlEncode(encodedSignature); return(signedQueryString); }
/// <summary> /// Encrypts the NameID attribute of the AttributeQuery request. /// </summary> /// <param name="certFriendlyName"> /// Friendly Name of the X509Certificate to be retrieved /// from the LocalMachine keystore and used to encrypt generated symmetric key. /// Be sure to have appropriate permissions set on the keystore. /// </param> /// <param name="xmlDoc"> /// XML document to be encrypted. /// </param> /// <param name="symmetricAlgorithmUri"> /// Symmetric algorithm uri used for encryption. /// </param> public static void EncryptAttributeQueryNameID(string certFriendlyName, string symmetricAlgorithmUri, XmlDocument xmlDoc) { if (string.IsNullOrWhiteSpace(certFriendlyName)) { throw new Saml2Exception(Resources.EncryptedXmlInvalidCertFriendlyName); } if (string.IsNullOrWhiteSpace(symmetricAlgorithmUri)) { throw new Saml2Exception(Resources.EncryptedXmlInvalidEncrAlgorithm); } if (xmlDoc == null) { throw new Saml2Exception(Resources.SignedXmlInvalidXml); } X509Certificate2 cert = FedletCertificateFactory.GetCertificateByFriendlyName(certFriendlyName); if (cert == null) { throw new Saml2Exception(Resources.EncryptedXmlCertNotFound); } XmlNamespaceManager nsMgr = new XmlNamespaceManager(xmlDoc.NameTable); nsMgr.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl); nsMgr.AddNamespace("saml", Saml2Constants.NamespaceSamlAssertion); nsMgr.AddNamespace("samlp", Saml2Constants.NamespaceSamlProtocol); string xpath = "/samlp:AttributeQuery/saml:Subject/saml:NameID"; XmlNode root = xmlDoc.DocumentElement; XmlNode node = root.SelectSingleNode(xpath, nsMgr); XmlNode encryptedID = xmlDoc.CreateNode(XmlNodeType.Element, "EncryptedID", Saml2Constants.NamespaceSamlAssertion); node.ParentNode.PrependChild(encryptedID); XmlElement elementToEncrypt = (XmlElement)encryptedID.AppendChild(node.Clone()); if (elementToEncrypt == null) { throw new Saml2Exception(Resources.EncryptedXmlInvalidXml); } encryptedID.ParentNode.RemoveChild(node); SymmetricAlgorithm alg = Saml2Utils.GetAlgorithm(symmetricAlgorithmUri); if (alg == null) { throw new Saml2Exception(Resources.EncryptedXmlInvalidEncrAlgorithm); } alg.GenerateKey(); string encryptionElementID = Saml2Utils.GenerateId(); string encryptionKeyElementID = Saml2Utils.GenerateId(); EncryptedData encryptedData = new EncryptedData(); encryptedData.Type = EncryptedXml.XmlEncElementUrl; encryptedData.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES128Url); encryptedData.Id = encryptionElementID; EncryptedXml encryptedXml = new EncryptedXml(); byte[] encryptedElement = encryptedXml.EncryptData(elementToEncrypt, alg, false); encryptedData.CipherData.CipherValue = encryptedElement; encryptedData.KeyInfo = new KeyInfo(); EncryptedKey encryptedKey = new EncryptedKey(); encryptedKey.Id = encryptionKeyElementID; RSA publicKeyRSA = cert.PublicKey.Key as RSA; encryptedKey.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url); encryptedKey.CipherData = new CipherData(EncryptedXml.EncryptKey(alg.Key, publicKeyRSA, false)); encryptedData.KeyInfo.AddClause(new KeyInfoRetrievalMethod("#" + encryptionKeyElementID, "http://www.w3.org/2001/04/xmlenc#EncryptedKey")); KeyInfoName kin = new KeyInfoName(); kin.Value = cert.SubjectName.Name; encryptedKey.KeyInfo.AddClause(kin); EncryptedXml.ReplaceElement(elementToEncrypt, encryptedData, false); XmlNode importKeyNode = xmlDoc.ImportNode(encryptedKey.GetXml(), true); encryptedID.AppendChild(importKeyNode); }
/// <summary> /// Signs the specified xml document with the certificate found in /// the local machine matching the provided friendly name and /// referring to the specified target reference ID. /// </summary> /// <param name="certFriendlyName"> /// Friendly Name of the X509Certificate to be retrieved /// from the LocalMachine keystore and used to sign the xml document. /// Be sure to have appropriate permissions set on the keystore. /// </param> /// <param name="xmlDoc"> /// XML document to be signed. /// </param> /// <param name="targetReferenceId"> /// Reference element that will be specified as signed. /// </param> /// <param name="includePublicKey"> /// Flag to determine whether to include the public key in the /// signed xml. /// </param> /// <param name="serviceProviderInstance"> /// ServiceProvider instance for retreaving Signature transform /// and canonicalization method /// </param> public static void SignXml(string certFriendlyName, IXPathNavigable xmlDoc, string targetReferenceId, bool includePublicKey, ServiceProvider serviceProviderInstance) { if (string.IsNullOrEmpty(certFriendlyName)) { throw new Saml2Exception(Resources.SignedXmlInvalidCertFriendlyName); } if (xmlDoc == null) { throw new Saml2Exception(Resources.SignedXmlInvalidXml); } if (string.IsNullOrEmpty(targetReferenceId)) { throw new Saml2Exception(Resources.SignedXmlInvalidTargetRefId); } X509Certificate2 cert = FedletCertificateFactory.GetCertificateByFriendlyName(certFriendlyName); if (cert == null) { throw new Saml2Exception(Resources.SignedXmlCertNotFound); } XmlDocument xml = (XmlDocument)xmlDoc; SignedXml signedXml = new SignedXml(xml); signedXml.SigningKey = cert.PrivateKey; if (includePublicKey) { KeyInfo keyInfo = new KeyInfo(); keyInfo.AddClause(new KeyInfoX509Data(cert)); signedXml.KeyInfo = keyInfo; } Reference reference = new Reference(); reference.Uri = "#" + targetReferenceId; //Read the transform type and canonicalization method from sp-extended.xml string transformType = serviceProviderInstance.SignatureTransformMethod; string canonicalizationMethodType = serviceProviderInstance.CanonicalizationMethod; Transform sigTransform; //Implement the gathered data switch (transformType) { case "XmlDsigExcC14NTransform": sigTransform = new XmlDsigExcC14NTransform(); break; case "XmlDsigExcC14NWithCommentsTransform": sigTransform = new XmlDsigExcC14NWithCommentsTransform(); break; default: sigTransform = new XmlDsigEnvelopedSignatureTransform(); break; } if (canonicalizationMethodType != null && (canonicalizationMethodType == SignedXml.XmlDsigExcC14NTransformUrl || canonicalizationMethodType == SignedXml.XmlDsigExcC14NWithCommentsTransformUrl)) { signedXml.Signature.SignedInfo.CanonicalizationMethod = canonicalizationMethodType; } reference.AddTransform(sigTransform); signedXml.AddReference(reference); signedXml.ComputeSignature(); XmlElement xmlSignature = signedXml.GetXml(); XmlNamespaceManager nsMgr = new XmlNamespaceManager(xml.NameTable); nsMgr.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl); nsMgr.AddNamespace("saml", Saml2Constants.NamespaceSamlAssertion); nsMgr.AddNamespace("samlp", Saml2Constants.NamespaceSamlProtocol); XmlNode issuerNode = xml.DocumentElement.SelectSingleNode("saml:Issuer", nsMgr); if (issuerNode != null) { xml.DocumentElement.InsertAfter(xmlSignature, issuerNode); } else { // Insert as a child to the target reference id XmlNode targetNode = xml.DocumentElement.SelectSingleNode("//*[@ID='" + targetReferenceId + "']", nsMgr); targetNode.PrependChild(xmlSignature); } }
/// <summary> /// Signs the specified xml document with the certificate found in /// the local machine matching the provided friendly name and /// referring to the specified target reference ID. /// </summary> /// <param name="certFriendlyName"> /// Friendly Name of the X509Certificate to be retrieved /// from the LocalMachine keystore and used to sign the xml document. /// Be sure to have appropriate permissions set on the keystore. /// </param> /// <param name="xmlDoc"> /// XML document to be signed. /// </param> /// <param name="targetReferenceId"> /// Reference element that will be specified as signed. /// </param> /// <param name="includePublicKey"> /// Flag to determine whether to include the public key in the /// signed xml. /// </param> /// <param name="signatureSigningAlgorithm"> /// The algorithm used to sign the xml. /// </param> /// <param name="digestAlgorithm"> /// The method used to create the message digest. /// </param> /// <param name="serviceProviderInstance"> /// ServiceProvider instance for retreaving Signature transform /// and canonicalization method /// </param> public static void SignXml(string certFriendlyName, IXPathNavigable xmlDoc, string targetReferenceId, bool includePublicKey, string signatureSigningAlgorithm, string digestAlgorithm, ServiceProvider serviceProviderInstance) { if (string.IsNullOrEmpty(certFriendlyName)) { throw new Saml2Exception(Resources.SignedXmlInvalidCertFriendlyName); } if (xmlDoc == null) { throw new Saml2Exception(Resources.SignedXmlInvalidXml); } if (string.IsNullOrEmpty(targetReferenceId)) { throw new Saml2Exception(Resources.SignedXmlInvalidTargetRefId); } X509Certificate2 cert = FedletCertificateFactory.GetCertificateByFriendlyName(certFriendlyName); if (cert == null) { throw new Saml2Exception(Resources.SignedXmlCertNotFound); } XmlDocument xml = (XmlDocument)xmlDoc; SignedXml signedXml = new SignedXml(xml); var cspParams = new CspParameters(24) { KeyContainerName = "XML_DISG_RSA_KEY" }; var key = new RSACryptoServiceProvider(cspParams); key.FromXmlString(cert.PrivateKey.ToXmlString(true)); signedXml.SigningKey = key; if (includePublicKey) { KeyInfo keyInfo = new KeyInfo(); keyInfo.AddClause(new KeyInfoX509Data(cert)); signedXml.KeyInfo = keyInfo; } Reference reference = new Reference(); reference.Uri = "#" + targetReferenceId; //Read the transform type, signature digest and canonicalization method from sp-extended.xml string transformType = serviceProviderInstance.SignatureTransformMethod; string canonicalizationMethodType = serviceProviderInstance.CanonicalizationMethod; Transform sigTransform; //Implement the gathered data switch (transformType) { case "XmlDsigExcC14NTransform": sigTransform = new XmlDsigExcC14NTransform(); break; case "XmlDsigExcC14NWithCommentsTransform": sigTransform = new XmlDsigExcC14NWithCommentsTransform(); break; default: sigTransform = new XmlDsigEnvelopedSignatureTransform(); break; } if (canonicalizationMethodType != null && (canonicalizationMethodType == SignedXml.XmlDsigExcC14NTransformUrl || canonicalizationMethodType == SignedXml.XmlDsigExcC14NWithCommentsTransformUrl)) { signedXml.Signature.SignedInfo.CanonicalizationMethod = canonicalizationMethodType; } reference.AddTransform(sigTransform); bool bIsUsingExtendedAlgorithms = false; if (!String.IsNullOrEmpty(digestAlgorithm)) { reference.DigestMethod = digestAlgorithm; if (!digestAlgorithm.ToLowerInvariant().Contains("xmldsig#sha1")) { bIsUsingExtendedAlgorithms = true; } } signedXml.AddReference(reference); if (!String.IsNullOrEmpty(signatureSigningAlgorithm)) { signedXml.SignedInfo.SignatureMethod = signatureSigningAlgorithm; if (!signatureSigningAlgorithm.ToLowerInvariant().Contains("-sha1")) { bIsUsingExtendedAlgorithms = true; } } if (bIsUsingExtendedAlgorithms && !FedletSignedSignatureSupportSingleton.IsInitialised) { throw new Saml2Exception(Resources.ExtendedAlgorithmsUnavailable); } signedXml.ComputeSignature(); XmlElement xmlSignature = signedXml.GetXml(); XmlNamespaceManager nsMgr = new XmlNamespaceManager(xml.NameTable); nsMgr.AddNamespace("ds", SignedXml.XmlDsigNamespaceUrl); nsMgr.AddNamespace("saml", Saml2Constants.NamespaceSamlAssertion); nsMgr.AddNamespace("samlp", Saml2Constants.NamespaceSamlProtocol); XmlNode issuerNode = xml.DocumentElement.SelectSingleNode("saml:Issuer", nsMgr); if (issuerNode != null) { xml.DocumentElement.InsertAfter(xmlSignature, issuerNode); } else { // Insert as a child to the target reference id XmlNode targetNode = xml.DocumentElement.SelectSingleNode("//*[@ID='" + targetReferenceId + "']", nsMgr); targetNode.PrependChild(xmlSignature); } }
/// <summary> /// Signs the specified query string with the certificate found in the /// local machine matching the provided friendly name. The algorithm /// is expected to be one of the parameters in the query string. /// </summary> /// <param name="certFriendlyName"> /// Friendly Name of the X509Certificate to be retrieved /// from the LocalMachine keystore and used to sign the xml document. /// Be sure to have appropriate permissions set on the keystore. /// </param> /// <param name="queryString">Query string to sign.</param> /// <returns> /// A signed query string where a digital signature is added. /// </returns> public static string SignQueryString(string certFriendlyName, string queryString) { if (string.IsNullOrEmpty(certFriendlyName)) { throw new Saml2Exception(Resources.SignedQueryStringInvalidCertFriendlyName); } if (string.IsNullOrEmpty(queryString)) { throw new Saml2Exception(Resources.SignedQueryStringInvalidQueryString); } char[] queryStringSep = { '&' }; NameValueCollection queryParams = new NameValueCollection(); foreach (string pairs in queryString.Split(queryStringSep)) { string key = pairs.Substring(0, pairs.IndexOf("=", StringComparison.Ordinal)); string value = pairs.Substring(pairs.IndexOf("=", StringComparison.Ordinal) + 1); queryParams[key] = value; } if (string.IsNullOrEmpty(queryParams[Saml2Constants.SignatureAlgorithm])) { throw new Saml2Exception(Resources.SignedQueryStringSigAlgMissing); } X509Certificate2 cert = FedletCertificateFactory.GetCertificateByFriendlyName(certFriendlyName); if (cert == null) { throw new Saml2Exception(Resources.SignedQueryStringCertNotFound); } if (!cert.HasPrivateKey) { throw new Saml2Exception(Resources.SignedQueryStringCertHasNoPrivateKey); } string encodedSignature = string.Empty; string signatureAlgorithm = HttpUtility.UrlDecode(queryParams[Saml2Constants.SignatureAlgorithm]); // Setup the key var cspParams = new CspParameters(24) { KeyContainerName = "XML_DISG_RSA_KEY" }; RSACryptoServiceProvider privateKey = new RSACryptoServiceProvider(cspParams); privateKey.FromXmlString(cert.PrivateKey.ToXmlString(true)); byte[] toBeSigned = Encoding.UTF8.GetBytes(queryString.ToString()); byte[] signature = privateKey.SignData(toBeSigned, GetRequestedCryptoServiceProvider(signatureAlgorithm)); encodedSignature = Convert.ToBase64String(signature); string signedQueryString = queryString + "&" + Saml2Constants.Signature + "=" + HttpUtility.UrlEncode(encodedSignature); return(signedQueryString); }