Source code for the AS2 Server C# example program posted at EDIINT AS2. A detailed explanation of the algorithm can be read at "An AS2 Server example".
namespace As2ServerNet { public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { //Get Http header so that it can be included in the oMailDoc object during LoadContent string sHttpHeader = ""; // Load ServerVariable collection into NameValueCollection object. NameValueCollection coll = Request.ServerVariables; // Get names of all keys into a string array. String[] arr1 = coll.AllKeys; for (int i = 0; i < arr1.Length; i++) { if (arr1[i].StartsWith("ALL_RAW")) { String[] arr2 = coll.GetValues(arr1[i]); for (int ii = 0; ii < arr2.Length; ii++) { if (ii != 0) { sHttpHeader = sHttpHeader + "<br>"; } sHttpHeader = sHttpHeader + arr1[i] + ": " + arr2[ii]; } } } //saves AS2 message into a file. string uploadFileName = SaveAS2ToFile(Request, sHttpHeader); //processes the uploaded AS2 message, and then replies back an MDN syncronously or asyncronously string MDNResult = ProcMDN(uploadFileName, sHttpHeader); } } protected string SaveAS2ToFile(HttpRequest Request, string sHttpHeader) { if (!IsPostBack) { try { //path of where the received AS2 message will be saved string uploadFileName = ConfigurationManager.AppSettings["uploadDirectory"] + "\\" + Session.SessionID.Trim() + ".bin"; //read the stream of the file that is beig uploaded. Note that the stream does not include headers. BinaryReader reader = new BinaryReader(Request.InputStream); //save the stream to a file if (reader != null) { FileStream fsAs2 = new FileStream(uploadFileName, FileMode.Create, FileAccess.Write); BinaryWriter writerAs2 = new BinaryWriter(fsAs2); int buffSize = 512; bool done = false; byte[] bits = new byte[buffSize]; while (!done) { bits = reader.ReadBytes(buffSize); if (bits.Length == 0) { done = true; } else { writerAs2.Write(bits); } } writerAs2.Close(); reader.Close(); fsAs2.Close(); writerAs2.Dispose(); reader.Dispose(); fsAs2.Dispose(); } // save headers to a file (not necessary, but useful for troubleshooting) if (sHttpHeader != "") { string uploadFileNameHeader = ConfigurationManager.AppSettings["uploadDirectory"] + "\\" + Session.SessionID.Trim() + ".hdr.txt"; StreamWriter oWriter; oWriter = new StreamWriter(uploadFileNameHeader); oWriter.Write(sHttpHeader); oWriter.Close(); oWriter.Dispose(); } return uploadFileName; } finally { } } else { return ""; } } protected string ProcMDN(string AS2FileName, string sHttpHeader) { if (!IsPostBack) { //Get path of app string sPath = AppDomain.CurrentDomain.BaseDirectory; //path where MDN and log files are saved string sSaveFilePath = ConfigurationManager.AppSettings["processDirectory"] + "\\" + Session.SessionID.Trim(); //instantiate oEdiDoc ediDocument oEdiDoc = new ediDocument(); //full path to log FREDI component internal execution oEdiDoc.set_Property(DocumentPropertyIDConstants.Property_Execution_Log, sSaveFilePath + "_log.txt"); //Use certificates in Machine Key Sets Certificate Store. Enable this when certificates are installed on servers. oEdiDoc.set_Option(DocumentOptionIDConstants.OptDocument_MachineKeySet, 1); //set cursor type to forward only oEdiDoc.CursorType = DocumentCursorTypeConstants.Cursor_ForwardOnly; //security settings. Use the eSecurity Console Utility to view where the certificates are located. ediSecurities oSecurities = oEdiDoc.GetSecurities(); oSecurities.DefaultProviderName = "Microsoft Strong Cryptographic Provider"; oSecurities.DefaultCertSystemStoreLocation = "LocalMachine"; //certificates installed on servers are located in LocalMachine; workstations are located in CurrentUser oSecurities.DefaultCertSystemStoreName = "My"; oSecurities.DefaultKeyContainer = "test_cert_key"; //Disable internal standard reference library ediSchemas oSchemas = oEdiDoc.GetSchemas(); oSchemas.EnableStandardReference = false; // Load into RAM only the SEF file that is being used oSchemas.set_Option(SchemasOptionIDConstants.OptSchemas_SetOnDemand, 1); //instantiate oMailDoc mailDocument oMailDoc = oEdiDoc.GetMailDocument(); // RFC4130. The address in the "Disposition-Notification-To" is not used to identify where to // return the MDN. Receiving applications MUST ignore the value and MUST not complain about // RFC 2822 address syntax violations. For AS2, this field is redefined as generic text so that // any string is accepted. oMailDoc.AddDefinedHeader("Disposition-Notification-To", MailMessageHeaderTypeConstants.HeaderType_FieldText, RequirementTypeConstants.Requirement_Optional); //oMailDoc.AddDefinedHeader("Receipt-delivery-option", MailMessageHeaderTypeConstants.HeaderType_FieldText, RequirementTypeConstants.Requirement_Optional); oMailDoc.AddDefinedHeader("Message-ID", MailMessageHeaderTypeConstants.HeaderType_FieldText, RequirementTypeConstants.Requirement_Mandatory); oMailDoc.AddDefinedHeader("Original-Message-ID", MailMessageHeaderTypeConstants.HeaderType_FieldText, RequirementTypeConstants.Requirement_Mandatory); //oMailDoc.AddDefinedHeader("Cookie", MailMessageHeaderTypeConstants.HeaderType_FieldText, RequirementTypeConstants.Requirement_Optional) ////change default properties of boundaries //oMailDoc.set_Property(MailDocPropertyIDConstants.MailDocProp_Boundary, "CompanyX.Electronic.Document.Boundary"); //oMailDoc.set_Property(MailDocPropertyIDConstants.MailDocProp_BoundaryMdn, "CompanyX.Electronic.Document.Boundary.mdn"); //oMailDoc.set_Property(MailDocPropertyIDConstants.MailDocProp_BoundarySign, "CompanyX.Electronic.Document.Boundary.sign"); mailMessage oMDN = oMailDoc.GetMDN(); //you can add your own MDN headers at this time //oMDN.set_HeaderFieldValue("Received-Content-MIC", " "); //oMDN.set_HeaderFieldValue("Final-Receipient", "1234"); //certificate for signing MDN ediSecurity oMdnSecurity = oMDN.GetSecurity(); oMdnSecurity.CertificateSignerName = "as2_test_cert"; // Prevent any processing at this point. Just load the raw document. oMailDoc.set_Option(MailDocOptionIDConstants.MailDocOpt_Auto, 0); //This option means not to process the EDI file in the content when the process method is called. We only // want to process the mail document upto a point where we can determine the content type. oMailDoc.set_Option(MailDocOptionIDConstants.MailDocOpt_EnableEdi, 0); // Use certificates in the cretificate store only. (Not in files.) Must enable this to verify signed messages. oMailDoc.set_Option(MailDocOptionIDConstants.MailDocOpt_UseCertStoreOnly, 1); // Load the mail file that was just received from the client. If the client had requested that an MDN be generated then // the MDN is also generated internally at this point. //Load the http headers with the As2 file if (oMailDoc.LoadContent(AS2FileName, sHttpHeader) != 1) { // "Failed to load AS2" } else { //Saving the mail document at this point is not necessary, but is saved for troubleshooting if need be. oMailDoc.Save(sSaveFilePath + "_MailDoc.bin"); mailMessage oMessage = oMailDoc.GetMessageContent(); // Obtain the values from the message headers before the document is processed because they may not exist after. // Check to see if there is a value in the header 'Receipt-Delivery-Option', and if there is then this is an asynchronous session. string sReceiptDeliveryOption = oMessage.get_HeaderFieldValue("Receipt-delivery-option"); mailInternetURI oReceiptURI = oMessage.GetHeaderTypeURI("Receipt-delivery-option"); //ediSecurity oMsgSecurity = oMessage.GetSecurity(); //oMsgSecurity.ProviderName = "Microsoft Strong Cryptographic Provider"; //oMsgSecurity.DefaultCertSystemStoreLocation = "LocalMachine"; //'certificates installed on servers are located in LocalMachine; workstations are located in CurrentUser //oMsgSecurity.DefaultCertSystemStoreName = "My"; //// The component automatically detects what algorithm was used to encrypt the message and uses the same algorithm to decrypt the it. //// If you wish to know what algorithm was used to encrypt the message, then call the DecryptAlgorithm method. //ediSecurity oMsgSecurity = oMessage.GetSecurity(); //ediSecurityEncryptionCfg oSecurityEncryptionCfg = oMsgSecurity.GetEncryptionConfig(); //string sCfgDecryptAlgorithm = oSecurityEncryptionCfg.DecryptAlgorithm; //string sDecryptAlgorithm = oMsgSecurity.DecryptAlgorithm; //string sSignDigest = oMsgSecurity.GetSignDigest(EncodingMechanismTypeConstants.EncodeType_Base64); // It's not necessary to process in steps, but good for troubleshooting. To process in steps, use the ProcessStep method. string sContentTransferEncoding = oMessage.get_HeaderFieldValue("Content-Transfer-Encoding").ToLower(); // If the file was encoded in base64, this step will decode it only. if (sContentTransferEncoding == "base64") { oMessage.ProcessStep(); oMessage.Save(sSaveFilePath + "_MessageAfterBase64Process.bin"); } sContentTransferEncoding = oMessage.get_HeaderFieldValue("Content-Transfer-Encoding").ToLower(); // If the file was encrypted, this step will decrypt it only. if (sContentTransferEncoding == "binary") { oMessage.ProcessStep(); oMessage.Save(sSaveFilePath + "_MessageAfterBinaryProcessProcess.bin"); } // Do all other processes to get to the Content Type of the file. oMailDoc.Process(); oMailDoc.Save(sSaveFilePath + "_ContentType.bin"); //Get body from message so that we can extract the file from it. mailBody oMailBody = oMessage.GetBody(); //Get the content type of the EDI message so that it can be processed appropriately string sTypeMessage = oMessage.get_HeaderFieldValue("Content-Type"); if (sTypeMessage.ToLower() == "application/edi-x12") //EDI is X12 { //Export the EDI part of the message into a file oMailBody.Export(sSaveFilePath + ".X12"); //Load all pertaining X12 SEF files here. oEdiDoc.LoadSchema(sPath + "SEF/sample_850_X12-4010.SEF", SchemaTypeIDConstants.Schema_Standard_Exchange_Format); oEdiDoc.LoadSchema(sPath + "SEF/sample_837_X098.SEF", SchemaTypeIDConstants.Schema_Standard_Exchange_Format); } else if (sTypeMessage.ToLower() == "application/edifact") //EDI is EDIFACT { //Export the EDI part of the message into a file oMailBody.Export(sSaveFilePath + ".edi"); //UNA segment allowed oEdiDoc.set_Option(DocumentOptionIDConstants.OptDocument_ServiceSegment, 1); //Load all pertaining EDIFACT SEF files here. oEdiDoc.LoadSchema(sPath + "SEF/sample_ORDERS_S93A.SEF", SchemaTypeIDConstants.Schema_Standard_Exchange_Format); } else if (sTypeMessage.ToLower() == "application/xml") //XML { //Export the XML part of the message into a file oMailBody.Export(sSaveFilePath + ".xml.txt"); } else //unknown { //Export the body part of the message into a file oMailBody.Export(sSaveFilePath + ".stream.bin"); } if (!sReceiptDeliveryOption.Equals("")) // SEND MDN ASYNCHRONOUSLY { // Disconnect the client connection because it is not needed in an asyncronous MDN request while server continues to process // the received AS2 message, and then send back an MDN with a new connection. Response.Flush(); Response.Close(); string sMdnTempFile; string sHttpResponse; // Enable the EDI process and everything else (auto) in the next Process method. // These options are not necessary if you don't want to include the EDI validation results // in the MDN oMailDoc.set_Option(MailDocOptionIDConstants.MailDocOpt_EnableEdi, 1); oMailDoc.set_Option(MailDocOptionIDConstants.MailDocOpt_Auto, 1); if (oMailDoc.Process() != 1) { //Failed to process } else { ediTransports oTransports = oMailDoc.GetMdnTransports(); ediTransport oTransport = oTransports.CreateTransport(); //Get a unique temp file ediFileSystem oFileSystem = oEdiDoc.GetFileSystem(); sMdnTempFile = oFileSystem.CreateTempFile(""); // Because the cursor type was set to forward only, we have to iterate thru each segment of the EDI file so that they can // be validated. This is not necessary if you do not wish to include the validation results in the MDN. ediDataSegment oSegment = oEdiDoc.FirstDataSegment; while (oSegment != null) { ediDataSegment.Set(ref oSegment, oEdiDoc.NextDataSegment); } //while (oSegment != null) // Refresh the MDN to include the result of the EDI validation if (oMailDoc.RefreshMDN() != 1) { // "MDN failed to refresh" } else { if (oMDN.Save(sSaveFilePath + ".mdn1.async.bin") != 1) { //failed to save MDN file } else { string sTimeStamp = (DateTime.Now.ToString("yyMMddhhmm")); string sAsyncSchema = oReceiptURI.SchemeName.Trim().ToUpper(); string sAsyncHost = oReceiptURI.HostName; string sAsyncUserInfo = oReceiptURI.UserInfo; string sAsyncUrlPath = oReceiptURI.UrlPath; string sAsyncPort = oReceiptURI.Port.ToString(); string sAsyncValue = oReceiptURI.Value; //oMessage.HeaderFieldValue("Receipt-Delivery-Option") //Send the MDN back to the sender using the scheme and address //specified in the header 'Receipt-Delivery-Option' if (sAsyncSchema == "HTTP") { oTransport.SetHTTP(); oTransport.InternetUrl = sAsyncValue.Trim() + "/"; ediHttpCfg oHttpCfg = oTransport.GetHttpCfg(); oHttpCfg.SendVerb = "POST"; oHttpCfg.EnableProcessResponse = false; try { oTransport.SendFile(sSaveFilePath + ".mdn1.async.bin"); sHttpResponse = oTransport.LastResponseInfo; //if (oTransport.Send(sTimeStamp + ".mdn1.async.bin") != 1) //{ } //else //{ } } finally { } oHttpCfg.Dispose(); } else if (sAsyncSchema == "HTTPS") { oTransport.SetHTTPS(); oTransport.InternetUrl = sAsyncValue.Trim() + "/"; ediHttpCfg oHttpCfg = oTransport.GetHttpCfg(); oHttpCfg.SendVerb = "POST"; oHttpCfg.EnableProcessResponse = false; try { oTransport.SendFile(sSaveFilePath + ".mdn1.async.bin"); sHttpResponse = oTransport.LastResponseInfo; //if (oTransport.Send(sTimeStamp + ".mdn1.async.bin") != 1) //{ } //else //{ } } finally { } oHttpCfg.Dispose(); } else if (sAsyncSchema == "FTP") { oTransport.SetFTP(); oTransport.Address = sAsyncHost; oTransport.TargetPath = sAsyncUrlPath; oTransport.ServerPort = Convert.ToInt16(sAsyncPort); //if (oTransport.Send(sTimeStamp + ".mdn1.async.bin") != 1) //{ } //else //{ } } else if (sAsyncSchema == "MAILTO") { oTransport.SetSMTP(); ediSmtpCfg oSmtpCfg = oTransport.GetSmtpCfg(); oSmtpCfg.ServerName = "smtp.comp.domain.com"; oSmtpCfg.User = "userid"; oSmtpCfg.Password = "password"; oSmtpCfg.AddTo(sAsyncUserInfo); oSmtpCfg.From = "support@domain.com"; oSmtpCfg.FromDisplay = "FrameworkEDI Interoperability test"; oSmtpCfg.Subject = "Interoperability Test Document"; oSmtpCfg.MessageText = "Please find attached text document"; //if (oTransport.Send(sTimeStamp + ".mdn1.async.bin") != 1) //{} //else //{} oSmtpCfg.Dispose(); } //sAsyncSchema } //if (oMDN.Save } // if (oMailDoc.RefreshMDN oFileSystem.Dispose(); oTransport.Dispose(); oTransports.Dispose(); } // if (oMailDoc.Process() } else //SEND MDN SYNCHRONOUSLY { // Enable the EDI process and everything else (auto) in the next Process method. // These options are not necessary if you don't want to include the EDI validation results // in the MDN oMailDoc.set_Option(MailDocOptionIDConstants.MailDocOpt_EnableEdi, 1); oMailDoc.set_Option(MailDocOptionIDConstants.MailDocOpt_Auto, 1); if (oMailDoc.Process() != 1) { //Failed to process } else { // Save MDN to file. This is not necessary because the transport object sends the mdn from the content of // the object (and not from the file) oMDN.Save(sSaveFilePath + ".mdn1.bin"); // Because the cursor type was set to forward only, we have to iterate thru each segment of the EDI file so that they can // be validated. This is not necessary if you do not wish to include the validation results in the MDN. ediDataSegment oSegment = oEdiDoc.FirstDataSegment; while (oSegment != null) { ediDataSegment.Set(ref oSegment, oEdiDoc.NextDataSegment); } //while (oSegment != null) // Refresh the MDN to include the result of the EDI validation if (oMailDoc.RefreshMDN() != 1) { // "MDN failed to refresh" } else { oMDN.Save(sSaveFilePath + ".mdn2.bin"); //You can still add and edit the header of an MDN at this point, but not the message. //oMDN.set_HeaderFieldValue("AS2-Version", "1.0"); //oMDN.set_HeaderFieldValue("Message-ID", "12345demo@companyX.com"); mailHeaders oHeaders = oMDN.GetHeaders(); //add the headers of the MDN file into the http headers mailHeader oHeader = null; for (int i = 1; i <= oHeaders.Count; i++) { mailHeader.Set(ref oHeader, oHeaders.GetHeaderByIndex(i)); Response.AddHeader(oHeader.Name, oHeader.Value); } mailMessages oMdnMsgs = new mailMessages(); // Get the MDN message only (not the headers) because the headers will automatically be included when the MDN is sent. oMdnMsgs = oMDN.GetMessages(); //save the message to a file so that we can read it back as binary oMdnMsgs.Export(sSaveFilePath + ".msgs.mdn"); FileStream oMdnFs = new FileStream(sSaveFilePath + ".msgs.mdn", FileMode.Open); BinaryReader oMdnReader = new BinaryReader(oMdnFs); //read the generated .msgs.mdn file and write it back to the response stream int buffSize = 512; bool done = false; byte[] bits = new byte[buffSize]; while (!done) { bits = oMdnReader.ReadBytes(buffSize); if (bits.Length == 0) { done = true; } else { Response.BinaryWrite(bits); } } oMdnReader.Close(); oMdnFs.Close(); oMdnReader.Dispose(); oMdnFs.Dispose(); oMdnMsgs.Dispose(); oHeaders.Dispose(); } //oMailDoc.RefreshMDN() <> 1 } //oMailDoc.Process() } //url MDN synchronously oMailBody.Dispose(); //oMsgSecurity.Dispose(); oReceiptURI.Dispose(); oMessage.Dispose(); } //oMailDoc.Load(AS2FileName) <> 1 oMdnSecurity.Dispose(); oMDN.Dispose(); oMailDoc.Dispose(); oSchemas.Dispose(); oSecurities.Dispose(); oEdiDoc.Dispose(); } // if (!IsPostBack) return ""; } } }