/** * This method uses the JCE to provide the HMAC-SHA-1 * algorithm. * HMAC computes a Hashed Message Authentication Code and * in this case SHA1 is the hash algorithm used. * * @param keyBytes the bytes to use for the HMAC-SHA-1 key * @param text the message or text to be authenticated. * * @throws NoSuchAlgorithmException if no provider makes * either HmacSHA1 or HMAC-SHA-1 * digest algorithms available. * @throws InvalidKeyException * The secret provided was not a valid HMAC-SHA-1 key. * */ public static byte[] hmac_sha1(byte[] keyBytes, byte[] text) { using (HMACSHA1 hmac = new HMACSHA1(keyBytes)) { return hmac.ComputeHash(text); } }
//public static string ClearPassword; /* -generate (in order) 1) gen refs in SignedInfo 2) construct SignedInfo and create sig element 1) obtain raw data add wsu:Id to everything apply transforms / canonical calculate digest create ref elem 2) create signed info apply canonical create actual signature value sig element w/SignedInfo, SigVlaue, KeyInfo, ... */ public static XmlDocument SignXml(XmlDocument plainDoc) { if(SigObj == null) return plainDoc; //nothing to sign XmlElement envelope = plainDoc.DocumentElement; XmlElement headerOrBody = (XmlElement) envelope.ChildNodes[0]; XmlElement header; XmlElement body; if(headerOrBody.LocalName == Elem.Body) { header = plainDoc.CreateElement(headerOrBody.Prefix, Elem.Header, headerOrBody.NamespaceURI); envelope.InsertBefore(header, headerOrBody); } header = (XmlElement) envelope.ChildNodes[0]; body = (XmlElement) envelope.ChildNodes[1]; XmlNodeList headers = header.ChildNodes; XmlElement security = null; foreach(XmlNode xn in headers) { if(xn.LocalName == Elem.Security) security = (XmlElement) xn; } if(security == null) { security = plainDoc.CreateElement(Pre.wsse, Elem.Security, Ns.wsseLatest); XmlAttribute mustUnderstand = plainDoc.CreateAttribute(Pre.soap, Attrib.mustUnderstand, Ns.soap); mustUnderstand.Value = "1"; security.Attributes.Append(mustUnderstand); header.AppendChild(security); } XmlElement tokenElem = null; string secTokId = null; string sigAlgVal = null; //add BinarySecurityToken or UsernameToken under Security if(SigObj.BinSecTok != null) { XmlElement binSecTok = SigObj.BinSecTok.WriteXml(plainDoc, security); secTokId = SigObj.BinSecTok.Id; sigAlgVal = Alg.rsaSha1; if(SigObj.AsymmAlg is DSACryptoServiceProvider) sigAlgVal = Alg.dsaSha1; tokenElem = binSecTok; } /* <wsse:UsernameToken xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility" wsu:Id="SecurityToken-344570f1-e3b7-42fc-9b78-f0dcd1f90bd8"> <wsse:Username>Admin</wsse:Username> <wsse:Password Type="wsse:PasswordDigest">W5xVfXpb+NoV9KaPIQXUIslGGak=</wsse:Password> <wsse:Nonce>+7L+k37JW8qQCK1SPopXeQ==</wsse:Nonce> <wsu:Created>2003-10-23T04:40:04Z</wsu:Created> </wsse:UsernameToken> */ if(SigObj.UserTok != null) { XmlElement userTokElem = SigObj.UserTok.WriteXml(plainDoc, security); tokenElem = userTokElem; /* //xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" XmlElement userTokElem = plainDoc.CreateElement(Pre.wsse, Elem.UsernameToken, Ns.wsseLatest); security.AppendChild(userTokElem); XmlAttribute uid = plainDoc.CreateAttribute(Pre.wsu, Attrib.Id, Ns.wsuLatest); uid.Value = SigObj.UserTok.Id; userTokElem.Attributes.Append(uid); XmlElement userElem = plainDoc.CreateElement(Pre.wsse, Elem.Username, Ns.wsseLatest); userElem.InnerText = SigObj.UserTok.Username.Text; userTokElem.AppendChild(userElem); if(SigObj.UserTok.Password != null) { XmlElement passElem = plainDoc.CreateElement(Pre.wsse, Elem.Password, Ns.wsseLatest); XmlAttribute type = plainDoc.CreateAttribute(Attrib.Type); type.Value = SigObj.UserTok.Password.Type; passElem.Attributes.Append(type); passElem.InnerText = SigObj.UserTok.Password.Text; userTokElem.AppendChild(passElem); } XmlElement nonceElem = plainDoc.CreateElement(Pre.wsse, Elem.Nonce, Ns.wsseLatest); nonceElem.InnerText = SigObj.UserTok.Nonce.Text; userTokElem.AppendChild(nonceElem); XmlElement creElem = plainDoc.CreateElement(Pre.wsu, Elem.Created, Ns.wsuLatest); creElem.InnerText = SigObj.UserTok.Created; userTokElem.AppendChild(creElem); userTokElem.Attributes.Append(uid); */ secTokId = SigObj.UserTok.Id; sigAlgVal = Alg.hmacSha1; } if(SigObj.securityContextToken != null) { XmlNode sctNode = LameXpath.SelectSingleNode(header, Elem.SecurityContextToken); if(sctNode == null) { //i need to import this node 1st sctNode = plainDoc.ImportNode(SigObj.securityContextToken, true); string dupeId = sctNode.Attributes[Attrib.Id, Ns.wsuLatest].Value; XmlElement dupeElem = LameXpath.SelectSingleNode(dupeId, security); if(dupeElem == null) security.AppendChild(sctNode); else sctNode = LameXpath.SelectSingleNode(dupeId, security); } //<wsse:SecurityContextToken wsu:Id=\"SecurityToken-feb27552-6eb5-4a27-a831-e1bdfca326e2\"> secTokId = sctNode.Attributes[Attrib.Id, Ns.wsuLatest].Value; sigAlgVal = Alg.hmacSha1; tokenElem = (XmlElement) sctNode; if(SigObj.derKeyTok != null) { XmlNode idElem = LameXpath.SelectSingleNode(sctNode, Elem.Identifier); if(idElem != null) SigObj.derKeyTok.secTokRef.Reference.URI = idElem.InnerText; XmlElement derKeyTokElem = SigObj.derKeyTok.WriteXml(plainDoc, security, (XmlElement) sctNode); secTokId = SigObj.derKeyTok.id; } } //add Signature element, SignedInfo, CanonicalizationMethod and SignatureMethod XmlElement sigElem = plainDoc.CreateElement(Pre.ds, Elem.Signature, Ns.ds); security.AppendChild(sigElem); //just append //add SignedInfo XmlElement sigInfoElem = plainDoc.CreateElement(Pre.ds, Elem.SignedInfo, Ns.ds); sigElem.AppendChild(sigInfoElem); XmlElement canMethElem = plainDoc.CreateElement(Pre.ds, Elem.CanonicalizationMethod, Ns.ds); XmlAttribute canAlg = plainDoc.CreateAttribute(Attrib.Algorithm); canAlg.Value = Alg.xmlExcC14n; canMethElem.Attributes.Append(canAlg); sigInfoElem.AppendChild(canMethElem); XmlElement sigMethElem = plainDoc.CreateElement(Pre.ds, Elem.SignatureMethod, Ns.ds); XmlAttribute sigAlg = plainDoc.CreateAttribute(Attrib.Algorithm); sigAlg.Value = sigAlgVal; sigMethElem.Attributes.Append(sigAlg); sigInfoElem.AppendChild(sigMethElem); //get each Refs element, add Id if missing //canonical, Digest, add ReferenceElement bool comments = false; bool exclusive = true; SHA1CryptoServiceProvider shaCsp = new SHA1CryptoServiceProvider(); foreach(object oRef in SigObj.Refs) { XmlElement refdElem = LameXpath.SelectSingleNode(plainDoc, oRef.ToString()); if(refdElem == null) continue; //cant sign it because it doesnt exist //get or add Id XmlAttribute xaId = null; foreach(XmlAttribute xa in refdElem.Attributes) { if(xa.LocalName == Attrib.Id) { xaId = xa; break; } } if(xaId == null) { xaId = plainDoc.CreateAttribute(Pre.wsu, Attrib.Id, Ns.wsuLatest); string preId = "Id-"; if(oRef.ToString() == Elem.Timestamp) preId = "Timestamp-"; xaId.Value = preId + GuidEx.NewGuid().ToString("D"); refdElem.Attributes.Append(xaId); } XmlDocument xdRefd = new XmlDocument(); xdRefd.LoadXml(refdElem.OuterXml); XmlCanonicalizer xc = new XmlCanonicalizer(comments, exclusive); MemoryStream msRefd = (MemoryStream) xc.Canonicalize(xdRefd); byte [] baRefd = new byte[msRefd.Length]; msRefd.Read(baRefd, 0, baRefd.Length); string debugName = oRef.ToString(); byte [] baDigest = shaCsp.ComputeHash(baRefd); XmlElement refElem = plainDoc.CreateElement(Pre.ds, Elem.Reference, Ns.ds); XmlAttribute refUri = plainDoc.CreateAttribute(Attrib.URI); refUri.Value = "#" + xaId.Value; refElem.Attributes.Append(refUri); sigInfoElem.AppendChild(refElem); XmlElement transsElem = plainDoc.CreateElement(Pre.ds, Elem.Transforms, Ns.ds); refElem.AppendChild(transsElem); XmlElement transElem = plainDoc.CreateElement(Pre.ds, Elem.Transform, Ns.ds); XmlAttribute transAlg = plainDoc.CreateAttribute(Attrib.Algorithm); transAlg.Value = Alg.xmlExcC14n; transElem.Attributes.Append(transAlg); transsElem.AppendChild(transElem); XmlElement digMethElem = plainDoc.CreateElement(Pre.ds, Elem.DigestMethod, Ns.ds); XmlAttribute digMethAlg = plainDoc.CreateAttribute("Algorithm"); digMethAlg.Value = Alg.sha1; digMethElem.Attributes.Append(digMethAlg); refElem.AppendChild(digMethElem); XmlElement digValElem = plainDoc.CreateElement(Pre.ds, Elem.DigestValue, Ns.ds); digValElem.InnerText = OpenNETCF.Security.Cryptography.NativeMethods.Format.GetB64(baDigest); refElem.AppendChild(digValElem); } //canonical SignedInfo, get key, get signature XmlDocument xdSigInfo = new XmlDocument(); xdSigInfo.LoadXml(sigInfoElem.OuterXml); XmlCanonicalizer xcSi = new XmlCanonicalizer(comments, exclusive); MemoryStream msSigInfo = (MemoryStream) xcSi.Canonicalize(xdSigInfo); byte [] baSigInfo = new byte[msSigInfo.Length]; msSigInfo.Read(baSigInfo, 0, baSigInfo.Length); byte [] baSig = null; if(SigObj.BinSecTok != null) { byte [] baUnsigHash = shaCsp.ComputeHash(baSigInfo); baSig = SigObj.AsymmAlg.SignHash(baUnsigHash, "SHA"); } if(SigObj.UserTok != null) { byte [] derKey = P_SHA1.DeriveKey(SigObj.ClearPassword, StrKeyLabel, SigObj.UserTok.Nonce.Text, SigObj.UserTok.Created, NumKeyBytes); HMACSHA1 hs = new HMACSHA1(derKey); baSig = hs.ComputeHash(baSigInfo); } if(SigObj.securityContextToken != null) { //XmlElement createdElem = LameXpath.SelectSingleNode(SigObj.securityContextToken, "Created"); //string strCreated = createdElem.InnerText; //2004-03-05T01:59:49Z //string label = "WS-Security"; ////byte [] baKey = P_SHA1.DeriveKey(password, label, nonce, created, 24); //byte [] baKey = P_SHA1.DeriveKey(String.Empty, label, String.Empty, strCreated, 24); //HMACSHA1 hs = new HMACSHA1(baKey); //baSig = hs.ComputeHash(baSigInfo); byte [] hashKey; if(SigObj.derKeyTok != null) hashKey = SigObj.derKeyTok.derKey; else hashKey = SigObj.securityContextKey; HMACSHA1 hs = new HMACSHA1(hashKey); baSig = hs.ComputeHash(baSigInfo); } //add SignatureValue XmlElement sigValElem = plainDoc.CreateElement(Pre.ds, Elem.SignatureValue, Ns.ds); sigValElem.InnerText = OpenNETCF.Security.Cryptography.NativeMethods.Format.GetB64(baSig); sigElem.AppendChild(sigValElem); //add KeyInfo XmlElement keyInfoElem = plainDoc.CreateElement(Pre.ds, Elem.KeyInfo, Ns.ds); XmlElement secTokRefElem = plainDoc.CreateElement(Pre.wsse, Elem.SecurityTokenReference, Ns.wsseLatest); XmlElement sigRefElem = plainDoc.CreateElement(Pre.wsse, Elem.Reference, Ns.wsseLatest); XmlAttribute uri = plainDoc.CreateAttribute(Attrib.URI); uri.Value = "#" + secTokId; sigRefElem.Attributes.Append(uri); XmlAttribute valueType = plainDoc.CreateAttribute(Attrib.ValueType); valueType.Value = "#" + secTokId; sigRefElem.Attributes.Append(valueType); if(SigObj.UserTok != null) { XmlAttribute valType = plainDoc.CreateAttribute(Attrib.ValueType); valType.Value = Misc.tokenProfUsername + "#UsernameToken"; sigRefElem.Attributes.Append(valType); } if(SigObj.BinSecTok != null) { XmlAttribute valType = plainDoc.CreateAttribute(Attrib.ValueType); valType.Value = Misc.tokenProfX509 + "#X509v3"; sigRefElem.Attributes.Append(valType); } secTokRefElem.AppendChild(sigRefElem); keyInfoElem.AppendChild(secTokRefElem); sigElem.AppendChild(keyInfoElem); //SigObj = null; return plainDoc; }
//or port from openssl.org (tls1_P_hash) public static byte [] DeriveKey(byte [] baPassword, byte [] baLabel, byte [] baNonce, byte [] baTimestamp, int size) { byte [] secret = baPassword; byte [] data = ConcatBa(baLabel, baNonce); data = ConcatBa(data, baTimestamp); HMACSHA1 hs = new HMACSHA1(secret); byte [] seed = (byte[]) data.Clone(); //A0 byte [] A1 = hs.ComputeHash(seed); byte [] A2 = hs.ComputeHash(A1); byte [] A3 = hs.ComputeHash(A2); byte [] A4 = hs.ComputeHash(A3); byte [] hash1 = hs.ComputeHash(ConcatBa(A1, seed)); byte [] hash2 = hs.ComputeHash(ConcatBa(A2, seed)); byte [] hash3 = hs.ComputeHash(ConcatBa(A3, seed)); byte [] hash4 = hs.ComputeHash(ConcatBa(A4, seed)); byte [] baOut = new byte[size]; byte [] baTemp = ConcatBa(hash1, hash2); baTemp = ConcatBa(baTemp, hash3); baTemp = ConcatBa(baTemp, hash4); Array.Copy(baTemp, 0, baOut, 0, size); return baOut; }
public static void VerifySig(XmlDocument sigDoc) { try { XmlElement envelope = sigDoc.DocumentElement; XmlElement securityElem = LameXpath.SelectSingleNode(sigDoc, Elem.Security); if(securityElem != null) { XmlAttribute mustUndAtt = securityElem.Attributes[Attrib.mustUnderstand,Ns.soap]; if(mustUndAtt != null) mustUndAtt.Value = "0"; } XmlElement sigElem = LameXpath.SelectSingleNode(sigDoc, Elem.Signature); if(sigElem == null) return; XmlElement sigValElem = LameXpath.SelectSingleNode(sigElem, Elem.SignatureValue); byte [] baSigVal = OpenNETCF.Security.Cryptography.NativeMethods.Format.GetB64(sigValElem.InnerText); bool comments = false; bool exclusive = true; SHA1CryptoServiceProvider shaCsp = new SHA1CryptoServiceProvider(); XmlElement sigMethElem = LameXpath.SelectSingleNode(sigElem, Elem.SignatureMethod); string segMeth = sigMethElem.Attributes["Algorithm"].Value; XmlElement signedInfoElem = LameXpath.SelectSingleNode(sigElem, Elem.SignedInfo); XmlDocument xdSignedInfo = new XmlDocument(); xdSignedInfo.LoadXml(signedInfoElem.OuterXml); XmlCanonicalizer xc = new XmlCanonicalizer(comments, exclusive); MemoryStream ms = (MemoryStream) xc.Canonicalize(xdSignedInfo); byte [] baMs = new byte[ms.Length]; ms.Read(baMs, 0, baMs.Length); ArrayList keyInfoRefElem = LameXpath.SelectChildNodes(sigElem, Elem.SecurityTokenReference, Elem.Reference); XmlElement keyInfoRef = (XmlElement) keyInfoRefElem[0]; string secTokUri = keyInfoRef.Attributes["URI"].Value; secTokUri = secTokUri.TrimStart(new char[]{'#'}); XmlElement secTokElem = LameXpath.SelectSingleNode(secTokUri, sigDoc); if(secTokElem.LocalName == Elem.UsernameToken) { XmlElement nonce = LameXpath.SelectSingleNode(secTokElem, Elem.Nonce); XmlElement created = LameXpath.SelectSingleNode(secTokElem, Elem.Created); //DerivedKeyGenerator seems to be off by 1? //byte [] baKey = P_SHA1.DeriveKey(ClearPassword, StrKeyLabel, nonce.InnerText, created.InnerText, NumKeyBytes); byte [] baKey = P_SHA1.DeriveKey(SigObj.ClearPassword, StrKeyLabel, nonce.InnerText, created.InnerText, NumKeyBytes); HMACSHA1 hmacSha = new HMACSHA1(baKey); byte [] baSig = hmacSha.ComputeHash(baMs); OpenNETCF.Security.Cryptography.NativeMethods.Format.SameBytes(baSigVal, baSig); } else if(secTokElem.LocalName == Elem.BinarySecurityToken) { byte [] baCert = OpenNETCF.Security.Cryptography.NativeMethods.Format.GetB64(secTokElem.InnerText); X509Certificate cert = new X509Certificate(baCert); //pub key to verify sig. byte [] exponent; byte [] modulus; DecodeCertKey.GetPublicRsaParams(cert, out exponent, out modulus); RSAParameters rsaParam = new RSAParameters(); rsaParam.Exponent = exponent; rsaParam.Modulus = modulus; RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider(); rsaCsp.ImportParameters(rsaParam); byte [] baUnsigHash = shaCsp.ComputeHash(baMs); bool valid = rsaCsp.VerifyHash(baUnsigHash, "SHA", baSigVal); if(valid == false) throw new Exception("signature is not valid"); } else if(secTokElem.LocalName == Elem.SecurityContextToken) { //TODO how to validate signature? } else { throw new Exception("only support Username, BinarySecurity, and SecurityContext Token signature"); } //verify reference hashes string refdName = String.Empty; ArrayList refNodes = LameXpath.SelectChildNodes(sigDoc, Elem.SignedInfo, Elem.Reference); foreach(object oXn in refNodes) { XmlNode xn = (XmlNode) oXn; string uriId = xn.Attributes[Attrib.URI].Value; uriId = uriId.TrimStart(new char[]{'#'}); XmlElement digValElem = LameXpath.SelectSingleNode(xn, Elem.DigestValue); byte [] baDigest = OpenNETCF.Security.Cryptography.NativeMethods.Format.GetB64(digValElem.InnerText); XmlElement refdElem = LameXpath.SelectSingleNode(uriId, sigDoc); XmlDocument xdRefdElem = new XmlDocument(); refdName = refdElem.LocalName; //for debug visibility xdRefdElem.LoadXml(refdElem.OuterXml); //not reusable xc = new XmlCanonicalizer(comments, exclusive); //MemoryStream ms = (MemoryStream) xc.Canonicalize(refdElem); ms = (MemoryStream) xc.Canonicalize(xdRefdElem); baMs = new byte[ms.Length]; ms.Read(baMs, 0, baMs.Length); byte [] baHash = shaCsp.ComputeHash(baMs); try { OpenNETCF.Security.Cryptography.NativeMethods.Format.SameBytes(baDigest, baHash); } catch(Exception ex) { throw new Exception(refdName + ":" + ex.Message, ex); } } } finally { //ClearPassword = null; SigObj = null; } }