IRS-A2A BulkRequestTransmitter消息未正确格式化和/或无法解释

尝试通过BulkRequestTransmitter Web服务提交时,我收到以下错误。 对于此消息,组合指南不太有用,当我将SOAP XML与Composition Guide中的SOAP进行比较时,它们似乎是一对一的。 我希望另一组眼睛能够看到问题所在。

消息格式不正确和/或无法解释。 请查看位于https://www.irs.gov/for-Tax-Pros/Software-Developers/Information-Returns/Affordable-Care-Act-Information的“AIR提交组合和参考指南”第3节中概述的XML标准。 -Return-AIR-Program ,更正任何问题,然后重试。

我试过的:

  • 尝试在SOAP信封中提交(和不提供)空格。
  • 尝试使用XML格式的表单数据XML进行提交。
  • 尝试使用base64string格式的表单数据提交(因为此提交是)。
  • 在签名元素中添加了ds前缀。 使用此SOpost以便为Signature元素添加前缀。
  • 添加了“漂亮打印”格式的表格数据,并根据更新的作文指南(v4.2)添加。
  • 复制组合指南第10.3节中概述的BulkTransmitterService请求的MIME格式。
  • 创建了两个解决方案:1。)手动创建SOAP请求所需的XML并通过HttpWebRequest对象发送; 2.)通过导入到项目的WSDL作为Service Reference发送提交请求,使用自定义编码器进行GZip and Mtom Encoding并手动创建SOAP状态请求所需的XML(通过HttpWebRequest发送)。

更新#1
根据一些新增内容更新了请求。

  • 在签名元素中添加了ds前缀。
  • 添加了“漂亮打印”格式的表格数据,并根据更新的作文指南(v4.2:第5.4.2节)添加。

更新#2
我开始在Visual Studio的新实例中手动创建SOAP .xml文件,根据需要导入模式引用。 我在任何类型的应用程序创建之外这样做

通过这样做,我能够在我通过我的应用程序创建的SOAP找到一些额外的错误(感谢intellisense!) 。 我发现的错误是在Manifest XML中,因为它们不符合IRS模式。

我将在接下来的24小时内查看这些内容并进行相应更新。

  • urn:MailingAddressGrp应该有一个urn:USAddressGrpurn:ForeignAddressGrp 。 那个孩子应该包含适当的地址元素。 我的代码目前缺少urn:MailingAddressGrp的直接子代urn:MailingAddressGrp
  • Form1094C_Request_[TCC]_yyyyMMddThhmmssfffZ.xml urn1:DocumentSystemFileNm的值不正确。 我还不完全确定它应该是什么。
  • urn1:BulkExchangeFile元素存在与xop:Include元素相关的问题。 架构需要base64Binary类型。

更新#2.5

  • 更新了我的XML生成过程以包含USAddressGrp元素。
  • 发现我在毫秒内有一个额外的字符(四个而不是三个)。 一旦我更正了这一点,同时从文件名的开头删除字符串“Form”, urn1:DocumentSystemFileNm的值就能够成功validation模式。

更新#3

  • 根据我所做的更新更新了完整请求。 此时,我无法推断出我的请求有什么问题。 如果有人看到任何明显的东西,请帮忙!

更新#4

  • 根据所做的其他更新更新了完整请求。 根据其他SO用户的反馈从签名中删除了ds前缀。 此用户已使这些请求工作,而无需在事后将ds前缀附加到签名并重新计算签名。

    SO用户还确认他的请求正在使用元素设置为元素的子元素。

  • 根据“合成指南”第10.3节中的示例确认MIME标头是正确的。

更新#5

  • 我目前有两个解决方案:一个是手动创建SOAP请求所需的XML并通过HttpWebRequest发送; 以及使用下面列出的自定义编码器的提交请求的WSDL Service Reference ,并手动创建状态的SOAP请求所需的XML。

    在此更新时,解决方案1在提交提交请求时给出了上述错误,并在进行状态请求时给出了以下错误。 但是,在使用解决方案2时,两个请求(提交和状态)都会在下面给出错误。

    我正在研究可能的证书问题,看看他们是否在这些解决方案中取得任何进展。

更新#6

我遇到了一些问题导致我被推迟。 我将免除你的细节,但是,缺点是我们没有在IRS系统注册的安全证书,也没有正确安装证书,以便我可以通过X509Store访问信息。 最后这些事情已经完成,我能够测试从服务器向IRS提交数据(与我没有正确证书的本地机器相比)。 不幸的是,我仍然收到下面详述的WS-Security错误。 我已使用我目前发送的内容更新了完整请求。

消息发生错误:消息中的WS安全标头无效。 请查看位于https://www.irs.gov/for-Tax-Pros/Software-Developers/Information-Returns/Affordable-Care-Act-Information的“AIR提交作文和参考指南”第5节中概述的传输说明。 -Return-AIR-Program ,更正任何问题,然后重试。


MIME标头中的所有换行符都是原样,我相信换行符是预期的。 FormData附件以Pretty Print的forms发送,而SOAP Envelope则不是; 此post中的SOAP信封的格式是为了便于阅读。

更新#7:

感谢用户:jstill和fatherOfWine以及他们在下面发布的内容,以及Bon对此项目的早期帮助。 我已经突破了一条墙,让提交工作。 它现在正在运作。 状态请求也正常。 但是,我需要弄清楚如何处理它以便从中提取状态和附件(错误数据文件)。

完整要求:

 Content-Encoding: gzip Accept-Encoding: gzip, deflate Content-Type: multipart/related; type="application/xop+xml"; start=""; start-info="text/xml"; boundary="MIME_boundary" SOAPAction: BulkRequestTransmitter MIME-Version: 1.0 Host: la.www4.irs.gov --MIME_Boundary Content-Type: application/xop+xml; charset=UTF-8; type="text/xml" Content-Transfer-Encoding: 8bit Content-Id:           [TimestampDigestValue]    [ACABusinessHeaderDigestValue]    [ManifestDigestValue]   [SignatureValue]   [KeyIdentifier]     2016-05-18T09:51:05.856Z 2016-05-18T10:01:05.856Z    2015 0 000000301 O T      Selitestthree   6689 Willow Court Beverly Hills CA 90211    Rose Lincoln  5559876543   I  ContactFirstName ContactLastName  ContactPhoneNumber  3 1 PPACA 1094/1095C application/xml 6b2512ce28f603f76261923d297738e5 14076 1094C_Request_[TCC]_20160518T215105716Z.xml   51958882-c653-4eab-8dfb-287ecc555aaa:SYS12:[TCC]::T 2016-05-18T14:51:05.8568594-07:00           --MIME_Boundary Content-Type: text/xml; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Id:  Content-Disposition: attachment; name="1094C_Request_[TCC]_20160518T215105716Z.xml" [PrettyPrintFormDataXml] --MIME_boundary-- 

首先,快速免责声明。 这个答案是由fatherOfWine,Russ和Bon在这个和其他SO问题上提供的伟大的东西成为可能的。 我真正做的就是将他们中的一些东西结合起来,并解决我仍然遇到的问题,直到它起作用。 更重要的是,这里提供的代码是BAD ,可能不应该按原样使用。 我计划清理这一点,因为我知道什么有用,我建议任何人都使用它来做同样的事情。 一个很大的事情可能会跳到任何看到这个的人身上的是过多的静态变量,我用它来快速解决所有问题。 说真的, 不要在生产过程中使用它,它是几个小时的产品,只是在墙上扔东西直到出现问题,但它应该提供一个良好的起点,让事情变得更好。


这里有太多的代码要真正包含它,所以我将通过一些重点和一般发现然后包含VS解决方案的链接。

  1. 确保您已经设置了TCC和各种其他ID,您已购买了正确类型的证书( 本文档的第41页 )并且您已正确注册证书( 请参阅此文档 )
  2. 我发现删除肥皂信封中的所有CR-LF是必要的,以便接受消息。 有了他们在那里,我会在“意外的子元素”或类似的东西上得到一些错误。
  3. 文档在几个地方自相矛盾(参见第74和84页,看看他们说BulkExchangeFile元素应该包含哪些例子),wsdl / xsds也是直接错误的。 也许我只是以某种方式拥有旧的,但我必须做出改变并尝试一些事情,直到我发现他们这边的服务实际上会接受。
  4. 正确地将keyinfo添加到signedxml部分是非常重要的 ,那里的所有引用都是正确构建的并包含正确的InclusiveNamespaces列表,并且一旦调用ComputeSignature,您对信封所做的唯一更改就是添加签名它的元素。
  5. 说到签名元素,如果它出现在安全元素内的时间戳元素之后,IRS系统将返回错误。 它必须是第一个。
  6. 因为命名空间前缀在生成签名引用时非常重要,所以我选择了手工构建信封xml的路径,这样我就可以确定所有内容都与他们想要的完全匹配。 即便如此,有几个元素的前缀我必须在我测试时改变,因为文档中的XSD或某些页面应该是什么并不是他们的服务实际上想要的。 幸运的是,服务返回的错误实际上通过指示它期望值来自哪个命名空间来提供一些帮助。
  7. 除了获得所有gzip和MTOM设置之外(再次,感谢一百万到fatherOfWine的帮助),最终为我工作的大部分内容都是在单个通用类(我巧妙地称之为“General”)中完成的。 同样,这是糟糕的代码,只是需要得到一些东西(任何东西!)才能正常工作。 我会继续把它包含在答案中,但万一它提供了一个快速的“啊哈!” 对任何其他人解决这个问题。

     using System; using System.IO; using System.Net; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.Xml; using System.ServiceModel; using System.ServiceModel.Channels; using System.Text; using System.Xml; using IrsAcaClient.ACABulkRequestTransmitterService; namespace IrsAcaClient { public class General { /***************************************************** * * What I'm doing here (with static vars) is VERY BAD but this whole thing is just a dirty hack for now. * Hopefully I can clean this up later. * - JRS 2016-05-10 * *****************************************************/ public const string SecurityTimestampStringFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffZ"; public const string EnvelopeContentID = ""; public static string AttachmentFilePath; public static string AttachmentFileName { get { return Path.GetFileName(General.AttachmentFilePath); } } public static string AttachmentContentID {get { return string.Format("<{0}>", General.AttachmentFileName); }} public const string MIMEBoundary = "MIME_boundary"; public static string TCCode; public static Guid TransmissionGuid; public static string UniqueTransmissionId { get { return string.Format("{0}:SYS12:{1}::T", TransmissionGuid, TCCode); } } public static string SecurityTimeStampWsuId; public static string ManifestWsuId; public static string BusinessHeaderWsuId; public static string SignatureWsuId; public static string CertificatePath; public static string CertificatePassword; public static DateTime SecurityTimestampUTC; private static string _replacementSoapEnvelope; public static string ReplacementSoapEnvelope{get { return _replacementSoapEnvelope; }} private static void GenerateReference(string elementID, string inclusivePrefixList, SignedXmlWithId xSigned) { var reference = new Reference() { Uri = "#" + elementID }; XmlDsigExcC14NTransform env = new XmlDsigExcC14NTransform(); env.InclusiveNamespacesPrefixList = inclusivePrefixList; reference.AddTransform(env); xSigned.AddReference(reference); } public static string GetAttachmentFileContent() { //probably not ideal return File.ReadAllText(AttachmentFilePath); } public static string GetFileName() { //TODO: this may need to be tweaked slightly from the real filename return General.AttachmentFileName; } public static string GenerateWsuId(string prefix) { return string.Format("{0}-{1}", prefix, Guid.NewGuid().ToString().Replace("-", "").ToUpper()); } internal static void GenerateReplacementSoapEnvelope(ACABulkRequestTransmitterService.SecurityHeaderType securityHeader, ACABulkRequestTransmitterService.ACABulkBusinessHeaderRequestType businessHeader, ACABulkRequestTransmitterService.ACATrnsmtManifestReqDtlType manifest, ACABulkRequestTransmitterService.ACABulkRequestTransmitterType bulkTrans) { //load the base envelope xml var doc = new XmlDocument(); doc.PreserveWhitespace = false; doc.Load("BaseSoapEnvelope.xml"); /* Need a bunch of namespaces defined * xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" * xmlns:urn="urn:us:gov:treasury:irs:ext:aca:air:7.0" * xmlns:urn1="urn:us:gov:treasury:irs:common" * xmlns:urn2="urn:us:gov:treasury:irs:msg:acabusinessheader" * xmlns:urn3="urn:us:gov:treasury:irs:msg:irsacabulkrequesttransmitter" * xmlns:wsa="http://www.w3.org/2005/08/addressing" * xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" * xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" * xmlns:ds="http://www.w3.org/2000/09/xmldsig#" * xmlns:xop="http://www.w3.org/2004/08/xop/include" */ XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable); nsMgr.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/"); nsMgr.AddNamespace("urn", "urn:us:gov:treasury:irs:ext:aca:air:7.0"); nsMgr.AddNamespace("urn1", "urn:us:gov:treasury:irs:common"); nsMgr.AddNamespace("urn2", "urn:us:gov:treasury:irs:msg:acabusinessheader"); nsMgr.AddNamespace("urn3", "urn:us:gov:treasury:irs:msg:irsacabulkrequesttransmitter"); nsMgr.AddNamespace("wsa", "http://www.w3.org/2005/08/addressing"); nsMgr.AddNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); nsMgr.AddNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); nsMgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#"); nsMgr.AddNamespace("xop","http://www.w3.org/2004/08/xop/include"); //start replacing values in it //for securityHeader, should have the following /* * securityHeader.Signature.Id * securityHeader.Timestamp.Id * securityHeader.Timestamp.Created.Value * securityHeader.Timestamp.Expires.Value */ //doc.SelectSingleNode("//wsse:Security/ds:Signature", nsMgr).Attributes["Id"].Value = securityHeader.Signature.Id; doc.SelectSingleNode("//wsse:Security/wsu:Timestamp", nsMgr).Attributes["wsu:Id"].Value = securityHeader.Timestamp.Id; doc.SelectSingleNode("//wsse:Security/wsu:Timestamp/wsu:Created", nsMgr).InnerText = securityHeader.Timestamp.Created.Value; doc.SelectSingleNode("//wsse:Security/wsu:Timestamp/wsu:Expires", nsMgr).InnerText = securityHeader.Timestamp.Expires.Value; //for businessHeader, should have the following /* * businessHeader.UniqueTransmissionId * businessHeader.Timestamp * businessHeader.Id */ doc.SelectSingleNode("//urn2:ACABusinessHeader", nsMgr).Attributes["wsu:Id"].Value = businessHeader.Id; doc.SelectSingleNode("//urn2:ACABusinessHeader/urn:UniqueTransmissionId", nsMgr).InnerText = businessHeader.UniqueTransmissionId; doc.SelectSingleNode("//urn2:ACABusinessHeader/urn1:Timestamp", nsMgr).InnerText = businessHeader.Timestamp.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"); //for manifest, should have the following, some of which will need some conversions /* * manifest.Id * manifest.BinaryFormatCd - convert from enum * manifest.PaymentYr * manifest.PriorYearDataInd - convert from enum * manifest.EIN * manifest.TransmissionTypeCd - convert from enum * manifest.TestFileCd * manifest.TransmitterNameGrp.BusinessNameLine1Txt * manifest.CompanyInformationGrp.CompanyNm * manifest.CompanyInformationGrp.MailingAddressGrp.Item.AddressLine1Txt * manifest.CompanyInformationGrp.MailingAddressGrp.Item.CityNm * manifest.CompanyInformationGrp.MailingAddressGrp.Item.USStateCd - convert from enum * manifest.CompanyInformationGrp.MailingAddressGrp.Item.USZIPCd * manifest.CompanyInformationGrp.ContactNameGrp.PersonFirstNm * manifest.CompanyInformationGrp.ContactNameGrp.PersonLastNm * manifest.CompanyInformationGrp.ContactPhoneNum * manifest.VendorInformationGrp.VendorCd * manifest.VendorInformationGrp.ContactNameGrp.PersonFirstNm * manifest.VendorInformationGrp.ContactNameGrp.PersonLastNm * manifest.VendorInformationGrp.ContactPhoneNum * manifest.TotalPayeeRecordCnt * manifest.TotalPayerRecordCnt * manifest.SoftwareId * manifest.FormTypeCd - convert from enum * manifest.ChecksumAugmentationNum * manifest.AttachmentByteSizeNum * manifest.DocumentSystemFileNm */ doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl", nsMgr).Attributes["wsu:Id"].Value = manifest.Id; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:PaymentYr", nsMgr).InnerText = manifest.PaymentYr; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:PriorYearDataInd", nsMgr).InnerText = manifest.PriorYearDataInd.GetXmlEnumAttributeValueFromEnum(); doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn1:EIN", nsMgr).InnerText = manifest.EIN; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:TransmissionTypeCd", nsMgr).InnerText = manifest.TransmissionTypeCd.ToString(); doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:TestFileCd", nsMgr).InnerText = manifest.TestFileCd; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:TransmitterNameGrp/urn:BusinessNameLine1Txt", nsMgr).InnerText = manifest.TransmitterNameGrp.BusinessNameLine1Txt; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:CompanyNm", nsMgr).InnerText = manifest.CompanyInformationGrp.CompanyNm; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:MailingAddressGrp/urn:USAddressGrp/urn:AddressLine1Txt", nsMgr).InnerText = ((USAddressGrpType)manifest.CompanyInformationGrp.MailingAddressGrp.Item).AddressLine1Txt; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:MailingAddressGrp/urn:USAddressGrp/urn1:CityNm", nsMgr).InnerText = ((USAddressGrpType)manifest.CompanyInformationGrp.MailingAddressGrp.Item).CityNm; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:MailingAddressGrp/urn:USAddressGrp/urn:USStateCd", nsMgr).InnerText = ((USAddressGrpType)manifest.CompanyInformationGrp.MailingAddressGrp.Item).USStateCd.ToString(); doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:MailingAddressGrp/urn:USAddressGrp/urn1:USZIPCd", nsMgr).InnerText = ((USAddressGrpType)manifest.CompanyInformationGrp.MailingAddressGrp.Item).USZIPCd; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:ContactNameGrp/urn:PersonFirstNm", nsMgr).InnerText = manifest.CompanyInformationGrp.ContactNameGrp.PersonFirstNm; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:ContactNameGrp/urn:PersonLastNm", nsMgr).InnerText = manifest.CompanyInformationGrp.ContactNameGrp.PersonLastNm; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:CompanyInformationGrp/urn:ContactPhoneNum", nsMgr).InnerText = manifest.CompanyInformationGrp.ContactPhoneNum; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:VendorInformationGrp/urn:VendorCd", nsMgr).InnerText = manifest.VendorInformationGrp.VendorCd; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:VendorInformationGrp/urn:ContactNameGrp/urn:PersonFirstNm", nsMgr).InnerText = manifest.VendorInformationGrp.ContactNameGrp.PersonFirstNm; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:VendorInformationGrp/urn:ContactNameGrp/urn:PersonLastNm", nsMgr).InnerText = manifest.VendorInformationGrp.ContactNameGrp.PersonLastNm; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:VendorInformationGrp/urn:ContactPhoneNum", nsMgr).InnerText = manifest.VendorInformationGrp.ContactPhoneNum; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:TotalPayeeRecordCnt", nsMgr).InnerText = manifest.TotalPayeeRecordCnt; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:TotalPayerRecordCnt", nsMgr).InnerText = manifest.TotalPayerRecordCnt; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:SoftwareId", nsMgr).InnerText = manifest.SoftwareId; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:FormTypeCd", nsMgr).InnerText = manifest.FormTypeCd.GetXmlEnumAttributeValueFromEnum(); doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn1:BinaryFormatCd", nsMgr).InnerText = manifest.BinaryFormatCd.GetXmlEnumAttributeValueFromEnum(); doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn1:ChecksumAugmentationNum", nsMgr).InnerText = manifest.ChecksumAugmentationNum; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn1:AttachmentByteSizeNum", nsMgr).InnerText = manifest.AttachmentByteSizeNum; doc.SelectSingleNode("//urn:ACATransmitterManifestReqDtl/urn:DocumentSystemFileNm", nsMgr).InnerText = manifest.DocumentSystemFileNm; //for bulkTrans, should have the following /* * bulkTrans.BulkExchangeFile.Include.href */ doc.SelectSingleNode("//urn3:ACABulkRequestTransmitter/urn1:BulkExchangeFile/xop:Include", nsMgr).Attributes["href"].Value = bulkTrans.BulkExchangeFile.Include.href; //now do some more security setup var cert = new X509Certificate2(CertificatePath, CertificatePassword, X509KeyStorageFlags.MachineKeySet); var exported = cert.Export(X509ContentType.Cert, CertificatePassword); var base64 = Convert.ToBase64String(exported); //now compute all the signing stuff var xSigned = new SignedXmlWithId(doc); xSigned.Signature.Id = securityHeader.Signature.Id; // Add the key to the SignedXml document. xSigned.SigningKey = cert.PrivateKey; xSigned.Signature.Id = SignatureWsuId; xSigned.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NWithCommentsTransformUrl; var keyInfo = new KeyInfo { Id = GenerateWsuId("KI") }; //need to get the keyinfo into the signed xml stuff before we compute sigs, and because it is using some stuff that //doesn't appear to be supported out of the box we'll work around it by adding a node directly var sbKeyInfo = new StringBuilder(); sbKeyInfo.Append(""); sbKeyInfo.Append(""); sbKeyInfo.Append("" + base64.ToString()); sbKeyInfo.Append(""); sbKeyInfo.Append(""); sbKeyInfo.Append(""); XmlDocument tempDoc = new XmlDocument(); tempDoc.LoadXml(sbKeyInfo.ToString()); keyInfo.AddClause(new KeyInfoNode((XmlElement)tempDoc.FirstChild.FirstChild)); xSigned.KeyInfo = keyInfo; GenerateReference(SecurityTimeStampWsuId, "wsse wsa soapenv urn urn1 urn2 urn3", xSigned); GenerateReference(BusinessHeaderWsuId, "wsa soapenv urn urn1 urn3", xSigned); GenerateReference(ManifestWsuId, "wsa soapenv urn1 urn2 urn3", xSigned); // Compute the Signature. xSigned.ComputeSignature(); //signing stuff must come before the timestamp or the IRS service complains doc.SelectSingleNode("//wsse:Security", nsMgr).InsertBefore(xSigned.GetXml(), doc.SelectSingleNode("//wsse:Security", nsMgr).FirstChild); // _replacementSoapEnvelope = doc.OuterXml; } public static ACABulkRequestTransmitterResponseType Run(ACABulkRequestTransmitterService.SecurityHeaderType securityHeader, ACABulkRequestTransmitterService.ACABulkBusinessHeaderRequestType businessHeader, ACABulkRequestTransmitterService.ACATrnsmtManifestReqDtlType manifest, ACABulkRequestTransmitterService.ACABulkRequestTransmitterType bulkTrans) { //had some issues early on with the cert on the IRS server, this should probably be removed and retested without it ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) => true; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; var acaSecurityHeader = new ACABulkRequestTransmitterService.TransmitterACASecurityHeaderType(); //leave this empty for transmitting via ISS-A2A var requestClient = new ACABulkRequestTransmitterService.BulkRequestTransmitterPortTypeClient("BulkRequestTransmitterPort"); requestClient.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.None; //var vs = requestClient.Endpoint.Behaviors.Where((i) => i.GetType().Namespace.Contains("VisualStudio")); //if (vs != null) // requestClient.Endpoint.Behaviors.Remove((System.ServiceModel.Description.IEndpointBehavior)vs.Single()); //generate the real envelope we want GenerateReplacementSoapEnvelope(securityHeader, businessHeader, manifest, bulkTrans); using (var scope = new OperationContextScope(requestClient.InnerChannel)) { //Adding proper HTTP Header to an outgoing requqest. HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty(); requestMessage.Headers["Content-Encoding"] = "gzip"; requestMessage.Headers["Content-Type"] = string.Format(@"multipart/related; type=""application/xop+xml"";start=""{0}"";start-info=""text/xml"";boundary=""{1}""", General.EnvelopeContentID, General.MIMEBoundary); OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage; var response = requestClient.BulkRequestTransmitter(acaSecurityHeader, securityHeader, ref businessHeader, manifest, bulkTrans); //we got a response! now do something with it return response; } } } 

Here is the complete solution , just needs all of your own data supplied (including the complete attachment file with all the payee and payer records, which is outside the scope of this but should be pretty easy to generate). Also note that this is submission of forms only, not status checks. When I get that working I’ll try to remember to return and update this answer (but if someone else already has it and wants to share, that’d be pretty rad as well).


Edit for Status Service

I’ve combined a cleaned up version of the classes generated from the wsdl and my own junk code to get messages through and process the responses. Note that this isn’t 100% tested yet, needs sanity checks, etc. but like the previous stuff should at least help anyone else struggling with this mess. Usage here is pretty straightforward:

 var statusResponse = StatusService.CheckStatus(receipt, tCCode, certificatePath, certificatePassword, "https://la.www4.irs.gov/airp/aca/a2a/1095BC_Status_Request_AATS2016"); 

And here is the full class (with bonus generated classes namespace):

See my second answer for the status service code

Don’t know if it will resolve your issue, but nevertheless i give it a shot. Sometimes help comes from very unexpected sources 🙂

  1. First of all timestamp fields are in a wrong format: one in businessheader should NOT contain milliseconds at all. I know it for a fact.
  2. In security header timestamp only 3 digits are allowed to represent milliseconds.
  3. Remove empty elements like “OriginalReceiptId” from ACATransmitterManifestReqDtl element: they don’t like those.
  4. I hope you are providing them with proper software id, because you have it empty in the payload, but I am sure they would love to have it, imho.:)
  5. And I think the message you’ve got in the response also have something to do with Signature element. I think they want Signature element to have some prefix(“ds” preferably, I guess). But here I am not sure on 100%.

    You see, I am battling same battle as you. And my message security timestamp has prefix “u” and they do not complain about it. Though they didn’t like binarysecuritytoken ever.:) I am struggling to generate signature to the IRS liking. WCF is very secretive and does not allow easy prefix changing on soap envelope or allow to choose CanonicalizationMethod algorithm for a signature.

UPDATE : Been able to successfully send request to the service. Tell you at once: prefixes are unimportant. What was important: CorrectedInd tag must be present in Form1095BUpstreamDetail and attributes recordType=”String” lineNum=”0″ also must be present.

UPDATE2 : Another thing that I’ve changed I’ve placed ACABusinessHeader before ManifestDtl. Here are my settings: I am using WCF as carrier and SignedXml to generate signature. Also I am using custom gZip encoder(for obvious reasons0 and custom MtomEncoder to read response from service(yes, yes it’s MTOMed:)) can you believe those pokemons?!?!?) and that’s not all: they send response as multipart document with only 1 part!:)) I had to adjust my encoder to handle that. And voilà, service started to behave. Hope it might help.

UPDATE3 First of all make sure data in attachment file correspond to the test scenario you are using as guinea pig. I, probably, sound like a broken record, but that’s REALLY important. Now I’ll cut the stuff and present what I have. It’s a bit crude, but it does the trick.:)

1.Here is config file portion:
1.1.Make sure system.serviceModel element contains following portion:

      

1.2。 Make sure binding element contains this:

        

1.3。 Change binding of BulkRequestTransmitterPort endpoit under client element to “customBinding”(and change binding name to the name of the custom binding as well) and make sure it contains following portion:

     

Also client element should contain following portion:

       
  1. GZip encoder you could get from following link: https://msdn.microsoft.com/en-us/library/cc138373(v=vs.90).aspx Just download WCF example and dully move whole GZipMessageEncoder project under your project.

  2. Get MTOMEncoder(which I renamed from SwaEncoder for clarity reasons) from this link: Soap-with-Attachments Move following classes into GZipMessageEncoder project:
    MimeContent, MimeParser, MimePart, MTOMEncoder

  3. Modify GZipMessageEncoder class like this:
    4.1. Add following code at the beginning of the class:

      //------------------- MTOM related stuff. Begin. --------------------- const string ATTCHMNT_PROP = "attachment_file_content"; const string ATTCHMNT_CONTENT_ID = "Here goes content id"; private string _ContentType; private string _MediaType; protected MimeContent _MyContent; protected MimePart _SoapMimeContent; protected MimePart _AttachmentMimeContent; protected GZipMessageEncoderFactory _Factory; protected MimeParser _MimeParser; private void SetupMTOM(GZipMessageEncoderFactory factory) { // _ContentType = "multipart/related"; _MediaType = _ContentType; // // Create owned objects // _Factory = factory; _MimeParser = new MimeParser(); // // Create object for the mime content message // _SoapMimeContent = new MimePart() { ContentTypeStart = "application/xop+xml", ContentType = "text/xml", ContentId = "Here goes envelope MIME id from HTTP Content-Type header", // TODO: make content id dynamic or configurable? CharSet = "UTF-8", // TODO: make charset configurable? TransferEncoding = "8bit" // TODO: make transfer-encoding configurable? }; _AttachmentMimeContent = new MimePart() { ContentType = "application/xml", // TODO: AttachmentMimeContent.ContentType configurable? ContentId = ATTCHMNT_CONTENT_ID, // TODO: AttachmentMimeContent.ContentId configurable/dynamic? TransferEncoding = "7bit" // TODO: AttachmentMimeContent.TransferEncoding dynamic/configurable? }; _MyContent = new MimeContent() { Boundary = "here goes boundary id" // TODO: MimeContent.Boundary configurable/dynamic? }; _MyContent.Parts.Add(_SoapMimeContent); _MyContent.Parts.Add(_AttachmentMimeContent); _MyContent.SetAsStartPart(_SoapMimeContent); } //------------------- MTOM related stuff. End. ---------------------- 

4.2. Modify Method WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset) like this:

 public override ArraySegment WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset) { ArraySegment buffer = innerEncoder.WriteMessage(message, maxMessageSize, bufferManager, 0); var requestSOAPEnvelopeXml = System.Text.Encoding.UTF8.GetString(buffer.Array); //Here you create Security node and sign the request. For ex: requestSOAPEnvelopeXml = SigngEnvelope(requestSOAPEnvelopeXml); //Here you are getting 1094\1095 forms xml payload. string fileContent = GetAttachmentFileContent(); //Here comes the MTOMing... _SoapMimeContent.Content = System.Text.Encoding.UTF8.GetBytes(requestSOAPEnvelopeXml); _AttachmentMimeContent.Content = System.Text.Encoding.UTF8.GetBytes(fileContent); _MyContent.Parts.Where(m=> m.ContentId!=null && m.ContentId.Equals(ATTCHMNT_CONTENT_ID)).Single().ContentDisposition = GetFileName(envelope); // Now create the message content for the stream byte[] MimeContentBytes = _MimeParser.SerializeMimeContent(_MyContent); int MimeContentLength = MimeContentBytes.Length; // Write the mime content into the section of the buffer passed into the method byte[] TargetBuffer = bufferManager.TakeBuffer(MimeContentLength + messageOffset); Array.Copy(MimeContentBytes, 0, TargetBuffer, messageOffset, MimeContentLength); // Return the segment of the buffer to the framework return CompressBuffer(new ArraySegment(TargetBuffer, messageOffset, MimeContentLength), bufferManager, messageOffset); } 

4.3. Override couple more methods like this:

 public override Message ReadMessage(ArraySegment buffer, BufferManager bufferManager, string contentType) { ArraySegment decompressedBuffer = DecompressBuffer(buffer, bufferManager); MtomEncoder mtomEncoder = new MtomEncoder(innerEncoder, _Factory); Message returnMessage = mtomEncoder.ReadMessage(buffer, bufferManager, contentType); returnMessage.Properties.Encoder = mtomEncoder; return returnMessage; } public override bool IsContentTypeSupported(string contentType) { return true; } 

4.4. Make sure GZipMessage constructor looks like this:

  internal GZipMessageEncoder(MessageEncoder messageEncoder, GZipMessageEncoderFactory factory) : base() { if (messageEncoder == null) throw new ArgumentNullException("messageEncoder", "A valid message encoder must be passed to the GZipEncoder"); innerEncoder = messageEncoder; SetupMTOM(factory); } 

5. Make sure GZipMessageEncodingBindingElement class has following method:

  public override void ApplyConfiguration(BindingElement bindingElement) { GZipMessageEncodingBindingElement binding = (GZipMessageEncodingBindingElement)bindingElement; PropertyInformationCollection propertyInfo = this.ElementInformation.Properties; if (propertyInfo["innerMessageEncoding"].ValueOrigin != PropertyValueOrigin.Default) { switch (this.InnerMessageEncoding) { case "textMessageEncoding": binding.InnerMessageEncodingBindingElement = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8); break; case "binaryMessageEncoding": binding.InnerMessageEncodingBindingElement = new BinaryMessageEncodingBindingElement(); break; } } } 
  1. Modify MTOMEncoder class. Make sure that following method looks like this:

     public override Message ReadMessage(System.IO.Stream stream, int maxSizeOfHeaders, string contentType) { VerifyOperationContext(); if (contentType.ToLower().StartsWith("multipart/related")) { byte[] ContentBytes = new byte[stream.Length]; stream.Read(ContentBytes, 0, ContentBytes.Length); MimeContent Content = _MimeParser.DeserializeMimeContent(contentType, ContentBytes); if (Content.Parts.Count >= 1) { MemoryStream ms = new MemoryStream(Content.Parts[0].Content); //At least for now IRS is sending SOAP envelope as 1st part(and only part(sic!) of MULTIpart response) as xml. Message Msg = ReadMessage(ms, int.MaxValue, "text/xml");//Content.Parts[0].ContentType); if( Content.Parts.Count>1 ) Msg.Properties.Add(ATTCHMNT_PROP, Content.Parts[1].Content); return Msg; } else { throw new ApplicationException("Invalid mime message sent! Soap with attachments makes sense, only, with at least 2 mime message content parts!"); } } else if (contentType.ToLower().StartsWith("text/xml")) { XmlReader Reader = XmlReader.Create(stream); return Message.CreateMessage(Reader, maxSizeOfHeaders, MessageVersion); } else { throw new ApplicationException( string.Format( "Invalid content type for reading message: {0}! Supported content types are multipart/related and text/xml!", contentType)); } } 
  2. GZipMessageEncoderFactory class constructor should look like this:

      public GZipMessageEncoderFactory(MessageEncoderFactory messageEncoderFactory) { if (messageEncoderFactory == null) throw new ArgumentNullException("messageEncoderFactory", "A valid message encoder factory must be passed to the GZipEncoder"); encoder = new GZipMessageEncoder(messageEncoderFactory.Encoder, this); } 
  3. This is how I call the service:

      var requestClient = new BulkRequestTransmitterPortTypeClient("BulkRequestTransmitterPort"); requestClient.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.None; #if DEBUG var vs = requestClient.Endpoint.Behaviors.Where((i) => i.GetType().Namespace.Contains("VisualStudio")); if( vs!=null ) requestClient.Endpoint.Behaviors.Remove((System.ServiceModel.Description.IEndpointBehavior)vs.Single()); #endif using (var scope = new OperationContextScope(requestClient.InnerChannel)) { //Adding proper HTTP Header to an outgoing requqest. HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty(); requestMessage.Headers["Content-Encoding"] = "gzip"; requestMessage.Headers["Content-Type"] = @"multipart/related; type=""application/xop+xml"";start="""";start-info=""text/xml"";boundary=""here goes boundary id"""; OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage; response = requestClient.BulkRequestTransmitter(request.ACASecurityHeader, request.Security, ref request.ACABusinessHeader, request.ACATransmitterManifestReqDtl, request.ACABulkRequestTransmitter); } 
  4. Modify Mime Part:

9.1. Add new method:

  public void GetHeader(StringBuilder Builder) { if (string.IsNullOrEmpty(ContentId) && string.IsNullOrEmpty(ContentType) && string.IsNullOrEmpty(TransferEncoding)) return; if (!string.IsNullOrEmpty(ContentTypeStart)) { Builder.Append(string.Format("Content-Type: {0}", ContentTypeStart)); Builder.Append(string.Format("; type=\"{0}\"", ContentType)); } else Builder.Append(string.Format("Content-Type: {0}", ContentType)); if (!string.IsNullOrEmpty(CharSet)) Builder.Append(string.Format("; charset={0}", CharSet)); Builder.Append(new char[] { '\r', '\n' }); Builder.Append(string.Format("Content-Transfer-Encoding: {0}", TransferEncoding)); Builder.Append(new char[] { '\r', '\n' }); Builder.Append(string.Format("Content-Id: {0}", ContentId)); Builder.Append(new char[] { '\r', '\n' }); if (!string.IsNullOrEmpty(ContentDisposition)) Builder.Append(string.Format("Content-Disposition: attachment; filename=\"{0}\"", ContentDisposition)); } 

9.2. Add property:

  public string ContentDisposition { get; set; } 
  1. Modify MimeParser SerializeMimeContent() method: replace this block of code:

      Builder.Append(string.Format("Content-Type: {0}", item.ContentType)); if (!string.IsNullOrEmpty(item.CharSet)) Builder.Append(string.Format("; charset={0}", item.CharSet)); Builder.Append(new char[] { '\r', '\n' }); Builder.Append(string.Format("Content-Transfer-Encoding: {0}", item.TransferEncoding)); Builder.Append(new char[] { '\r', '\n' }); Builder.Append(string.Format("Content-Id: {0}", item.ContentId)); 

with this:

 item.GetHeader(Builder); 

And that’s should be it! Kick off your shoes and dig the blues!:)))

Seconds answer to include status service, rather than just another link that could disappear.

Here is the main class:

 using System; using System.IO; using System.IO.Compression; using System.Net; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.Xml; using System.Text; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Serialization; namespace IrsAcaClient { public class StatusService { private const string SecurityTimestampStringFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffZ"; public static ACABulkRequestStatusService.ACABulkRequestTransmitterStatusDetailResponseType CheckStatus(string receiptID, string tCCode, string certificatePath, string certificatePassword, string statusServiceUrl) { //go ahead and generate some of the ids and timestamps we'll need var securityTimeStampWsuId = GenerateWsuId("TS"); var businessHeaderWsuId = GenerateWsuId("id"); var detailRequestWsuId = GenerateWsuId("id"); var signatureWsuId = GenerateWsuId("SIG"); var securityTimestampUTC = DateTime.UtcNow; var securityTimestampCreated = securityTimestampUTC.ToString(SecurityTimestampStringFormat); var securityTimestampExpires = securityTimestampUTC.AddMinutes(10).ToString(SecurityTimestampStringFormat); //build the envelope //load the base envelope xml var doc = new XmlDocument(); doc.PreserveWhitespace = false; doc.Load("BaseStatusRequestEnvelope.xml"); /* Need a bunch of namespaces defined * xmlns:oas1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" * xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" * xmlns:urn="urn:us:gov:treasury:irs:msg:irstransmitterstatusrequest" * xmlns:urn1="urn:us:gov:treasury:irs:ext:aca:air:7.0" * xmlns:urn2="urn:us:gov:treasury:irs:common" * xmlns:urn3="urn:us:gov:treasury:irs:msg:acasecurityheader" * xmlns:wsa="http://www.w3.org/2005/08/addressing" * xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" * xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" * xmlns:ds="http://www.w3.org/2000/09/xmldsig#"); */ XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable); nsMgr.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/"); nsMgr.AddNamespace("urn", "urn:us:gov:treasury:irs:msg:irstransmitterstatusrequest"); nsMgr.AddNamespace("urn1", "urn:us:gov:treasury:irs:ext:aca:air:7.0"); nsMgr.AddNamespace("urn2", "urn:us:gov:treasury:irs:common"); nsMgr.AddNamespace("urn3", "urn:us:gov:treasury:irs:msg:acasecurityheader"); nsMgr.AddNamespace("wsa", "http://www.w3.org/2005/08/addressing"); nsMgr.AddNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); nsMgr.AddNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); nsMgr.AddNamespace("oas1", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); nsMgr.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#"); //start replacing values in it //for securityHeader, should have the following /* * securityHeader.Timestamp.Id * securityHeader.Timestamp.Created.Value * securityHeader.Timestamp.Expires.Value */ doc.SelectSingleNode("//wsse:Security/wsu:Timestamp", nsMgr).Attributes["wsu:Id"].Value = securityTimeStampWsuId; doc.SelectSingleNode("//wsse:Security/wsu:Timestamp/wsu:Created", nsMgr).InnerText = securityTimestampCreated; doc.SelectSingleNode("//wsse:Security/wsu:Timestamp/wsu:Expires", nsMgr).InnerText = securityTimestampExpires; //for businessHeader, should have the following /* * businessHeader.UniqueTransmissionId * businessHeader.Timestamp * businessHeader.Id */ doc.SelectSingleNode("//urn:ACABusinessHeader", nsMgr).Attributes["wsu:Id"].Value = businessHeaderWsuId; doc.SelectSingleNode("//urn:ACABusinessHeader/urn1:UniqueTransmissionId", nsMgr).InnerText = GetUniqueTransmissionId(Guid.NewGuid(), tCCode); doc.SelectSingleNode("//urn:ACABusinessHeader/urn2:Timestamp", nsMgr).InnerText = securityTimestampUTC.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"); //for ACABulkRequestTransmitterStatusDetailRequest, should have the following /* * ACABulkRequestTransmitterStatusDetailRequest.Id * ACABulkRequestTransmitterStatusDetailRequest.ACABulkReqTrnsmtStsReqGrpDtl.ReceiptId */ doc.SelectSingleNode("//urn:ACABulkRequestTransmitterStatusDetailRequest", nsMgr).Attributes["wsu:Id"].Value = detailRequestWsuId; doc.SelectSingleNode("//urn:ACABulkRequestTransmitterStatusDetailRequest/urn1:ACABulkReqTrnsmtStsReqGrpDtl/urn2:ReceiptId", nsMgr).InnerText = receiptID; //now do some more security setup var cert = new X509Certificate2(certificatePath, certificatePassword, X509KeyStorageFlags.MachineKeySet); var exported = cert.Export(X509ContentType.Cert, certificatePassword); var base64 = Convert.ToBase64String(exported); //now compute all the signing stuff var xSigned = new SignedXmlWithId(doc); // Add the key to the SignedXml document. xSigned.SigningKey = cert.PrivateKey; xSigned.Signature.Id = signatureWsuId; xSigned.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NWithCommentsTransformUrl; var keyInfo = new KeyInfo { Id = GenerateWsuId("KI") }; //need to get the keyinfo into the signed xml stuff before we compute sigs, and because it is using some stuff that //doesn't appear to be supported out of the box we'll work around it by adding a node directly var sbKeyInfo = new StringBuilder(); sbKeyInfo.Append(""); sbKeyInfo.Append(""); sbKeyInfo.Append("" + base64.ToString()); sbKeyInfo.Append(""); sbKeyInfo.Append(""); sbKeyInfo.Append(""); XmlDocument tempDoc = new XmlDocument(); tempDoc.LoadXml(sbKeyInfo.ToString()); keyInfo.AddClause(new KeyInfoNode((XmlElement)tempDoc.FirstChild.FirstChild)); xSigned.KeyInfo = keyInfo; GenerateReference(securityTimeStampWsuId, "wsse wsa oas1 soapenv urn urn1 urn2 urn3", xSigned); GenerateReference(businessHeaderWsuId, "wsa oas1 soapenv urn1 urn2 urn3", xSigned); GenerateReference(detailRequestWsuId, "oas1 soapenv urn1 urn2 urn3", xSigned); // Compute the Signature. xSigned.ComputeSignature(); //signing stuff must come before the timestamp or the IRS service complains doc.SelectSingleNode("//wsse:Security", nsMgr).InsertBefore(xSigned.GetXml(), doc.SelectSingleNode("//wsse:Security", nsMgr).FirstChild); //get the completed envelope var envelope = doc.OuterXml; //start the webrequest //get the request object var request = CreateWebRequest(statusServiceUrl); //get the request stream and then get a writer on it using (var stream = request.GetRequestStream()) using (var gz = new GZipStream(stream, CompressionMode.Compress)) using (var writer = new StreamWriter(gz)) { //start by writing the soap envelope to the stream writer.WriteLine(envelope); writer.Close(); stream.Close(); } //get the response WebResponse response; //let an exception get thrown up the stack response = request.GetResponse(); //get the response stream, get a reader on it, and read the response as text using (var responseStream = response.GetResponseStream()) using (var reader = new StreamReader(responseStream, Encoding.UTF8)) { var responseText = reader.ReadToEnd(); //rip the one element (and children) we need out var match = Regex.Match(responseText, @"<(?'prefix'[\w\d]*):ACABulkRequestTransmitterStatusDetailResponse.*<\/\k:ACABulkRequestTransmitterStatusDetailResponse>"); return Deserialize(match.ToString()); } } private static string GetUniqueTransmissionId(Guid transmissionGuid, string tCCode) { return string.Format("{0}:SYS12:{1}::T", transmissionGuid, tCCode); } private static string GenerateWsuId(string prefix) { return string.Format("{0}-{1}", prefix, Guid.NewGuid().ToString().Replace("-", "").ToUpper()); } private static void GenerateReference(string elementID, string inclusivePrefixList, SignedXmlWithId xSigned) { var reference = new Reference() { Uri = "#" + elementID }; XmlDsigExcC14NTransform env = new XmlDsigExcC14NTransform(); env.InclusiveNamespacesPrefixList = inclusivePrefixList; reference.AddTransform(env); xSigned.AddReference(reference); } ///  /// creates a webrequest object and prefills some required headers and such ///  ///  ///  private static HttpWebRequest CreateWebRequest(string url) { //setup a web request with all the headers and such that the service requires var webRequest = (HttpWebRequest)WebRequest.Create(url); webRequest.Method = "POST"; webRequest.ProtocolVersion = HttpVersion.Version11; webRequest.Headers.Add(HttpRequestHeader.ContentEncoding, "gzip"); webRequest.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip, deflate"); webRequest.ContentType = "text/xml;charset=UTF-8"; webRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; webRequest.Headers.Add("SOAPAction", "RequestSubmissionStatusDetail"); webRequest.KeepAlive = true; return webRequest; } ///  /// deserializes the xml string into an object ///  ///  ///  public static T Deserialize(string xmlString) where T : class { //if the string is empty, just return null if (xmlString.Length <= 0) { return null; } //create a serializer var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T)); T output; //create the reader that the serializer will read from, passing it the string using (var reader = new System.IO.StringReader(xmlString)) { //rebuild the list object output = (T)serializer.Deserialize(reader); } //return the list return output; } } } 

Here is the relevant base xml:

      XXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXX     XXXXXXXXXXXXXXXXXX  XXXXXXXXXXXXXXXXXX   RequestSubmissionStatusDetail     XXXXXXXXXXXXXXXXXX     

For this one, the main change I needed to make to the WSDL-generated classes was the following:

  [System.SerializableAttribute()] [XmlRoot("ACABulkRequestTransmitterStatusDetailResponse", Namespace = "urn:us:gov:treasury:irs:msg:irstransmitterstatusrequest")] public class ACABulkRequestTransmitterStatusDetailResponseType { private ACABulkRequestTransmitterResponseType aCABulkRequestTransmitterResponseField; private ACABulkReqTrnsmtStsRespGrpDtlType aCABulkReqTrnsmtStsRespGrpDtlField; private string versionField; public ACABulkRequestTransmitterStatusDetailResponseType() { this.versionField = "1.0"; } [System.Xml.Serialization.XmlElementAttribute(Namespace = "urn:us:gov:treasury:irs:ext:aca:air:7.0", Order = 0)] public ACABulkRequestTransmitterResponseType ACABulkRequestTransmitterResponse { get { return this.aCABulkRequestTransmitterResponseField; } set { this.aCABulkRequestTransmitterResponseField = value; } } [System.Xml.Serialization.XmlElementAttribute(Namespace = "urn:us:gov:treasury:irs:ext:aca:air:7.0", Order = 1)] public ACABulkReqTrnsmtStsRespGrpDtlType ACABulkReqTrnsmtStsRespGrpDtl { get { return this.aCABulkReqTrnsmtStsRespGrpDtlField; } set { this.aCABulkReqTrnsmtStsRespGrpDtlField = value; } } [System.Xml.Serialization.XmlAttributeAttribute()] public string version { get { return this.versionField; } set { this.versionField = value; } } } 

Adding the ds prefix is actually what is breaking this. The SignatureValue element contains the x509 hash of the serialized SignedInfo element (this is where having consistent whitespace matters).

By modifying the SignedInfo element, the authentication will fail when the IRS compares the SignatureValue hash you send against their computation of the SignedInfo hash using the certificate you uploaded and associated with the provided TCC.

Just remove your modification of the SignedInfo element and all should be good. 这个对我有用。

This was an edit made to the above post, which added more information to jstill’s post. Unfortunately, peer reviewers rejected it.

In addition to the changes jstill made to the Status’ Reference.cs file, I also had to include the BulkExchangeFileType and IncludeFileType updates he made to the Submission’s Reference.cs file in order to get the Deserializer method to function partially .

The Deserializer method will return the TransmissionStatusCd and the ReceiptId elements as expected, however, it will not populate the ErrorDataFile element properly.

Since, at this time, I am unable to get the ErrorDataFile object populated properly, I am not utilizing the ACABulkRequestTransmitterStatusDetailResponseType object to capture the response returned from the Status Web Service. Instead, I have chosen to read the ResponseStream into a string object and parse the (up to) two MIME parts of the response, and process those as necessary.


Additional changes to the Status’ Reference.cs

 [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.34283")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(Namespace = "urn:us:gov:treasury:irs:common")] public partial class BulkExchangeFileType : object, System.ComponentModel.INotifyPropertyChanged { private IncludeType includeField; ///  [System.Xml.Serialization.XmlElement(Order = 0, Namespace = "http://www.w3.org/2004/08/xop/include")] public IncludeType Include { get { return this.includeField; } set { this.includeField = value; this.RaisePropertyChanged("Include"); } } public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName) { System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged; if (propertyChanged != null) { propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } } [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.0.30319.34283")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://www.w3.org/2004/08/xop/include")] public partial class IncludeType : object, System.ComponentModel.INotifyPropertyChanged { private System.Xml.XmlNode[] anyField; private string hrefField; ///  [System.Xml.Serialization.XmlTextAttribute()] [System.Xml.Serialization.XmlAnyElementAttribute(Order = 0)] public System.Xml.XmlNode[] Any { get { return this.anyField; } set { this.anyField = value; this.RaisePropertyChanged("Any"); } } ///  [System.Xml.Serialization.XmlAttributeAttribute(DataType = "string")] public string href { get { return this.hrefField; } set { this.hrefField = value; this.RaisePropertyChanged("href"); } } public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string propertyName) { System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged; if (propertyChanged != null) { propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } } 

For those running into the following error:

AIRMF3002 Rejected transmission – Unable to process your request because validation failure occurs in the Attachment Byte Size Number

It appears there is an issue on the IRS side with the size they are expecting (as per the Documentation) and the size they actually accept. Originally, I had the following code:

 // Size in Bytes of File: This code returns the "Size" located on the File's Property Page. // Result: TRANSMISSION REJECTED ON INCORRECT FILE SIZE! manifestHeader.AttachmentByteSizeNum = new FileInfo(FormDataFilePath).Length.ToString(); 

I replaced the above code with the following, and the error I was receiving was resolved.

 // Read the contents of the file, and retrieve the length of the content of the file itself.. // Result: TRANSMISSION WAS ACCEPTED USING THIS FILE SIZE. manifestHeader.AttachmentByteSizeNum = File.ReadAllText(FormDataFilePath).Length.ToString(); 

It appears as though the Web Service is actually expecting the size of the file content and not the size of the actual file. The difference in size pertaining to the test scenarios was approximately 3 bytes. I assume that is because retreiving the size of the file adds some additional file-related information not belonging to the actual content.

I have notified the IRS about this issue regarding their documentation.