<%= Html.Span(Formatter.Format(a.CreateDate), new { title = a.CreateDate }) %>
+
<%= Html.Span(Formatter.Format(a.UpdateDate), new { @class = "update-date", title = a.UpdateDate }) %>
+
+ <%= Html.ActionLink("View", "Details", new { id = a.ID }, new { @class = "view-details" }) %> |
+ <%= Html.ActionLink("Edit", "Edit", new { id = a.ID }) %> |
+ <% if (a.IsEnabled) { %>
+ <%= Html.ActionLink("Disable", "Disable", new { id = a.ID }, new { @class = "enable-disable-action" }) %>
+ <% } else { %>
+ <%= Html.ActionLink("Enable", "Enable", new { id = a.ID }, new { @class = "enable-disable-action" }) %>
+ <% } %>
+ |
+ <%= Html.ActionLink("Delete", "Delete", new { id = a.ID }, new { @class = "toolbar-button delete-action" }) %>
+
+
+ <% } %>
+
+
-<%= Html.Pager((IPagination)Model) %>
+<% var paged = Model as Health.Direct.Admin.Console.Models.Pagination.PaginatedList; %>
+<% if (paged != null) { %>
+
<%= Html.P(a.Thumbprint, new { title = a.Thumbprint, @class = "thumbprint" }) %>
+
<%= a.Status %>
+
<%= Html.Span(Formatter.Format(a.CreateDate), new { title = a.CreateDate }) %>
+
<%= Html.Span(Formatter.Format(a.ValidStartDate), new { title = a.ValidStartDate }) %>
+
<%= Html.Span(Formatter.Format(a.ValidEndDate), new { title = a.ValidEndDate }) %>
+
<%= a.Purpose %>
+
+ <%= Html.ActionLink("View", "Details", new { a.ID }, new { @class = "view-details" }) %> |
+ <% if (a.IsEnabled) { %>
+ <%= Html.ActionLink("Disable", "Disable", new { a.ID }, new { @class = "enable-disable-action" }) %>
+ <% } else { %>
+ <%= Html.ActionLink("Enable", "Enable", new { a.ID }, new { @class = "enable-disable-action" }) %>
+ <% } %>
+ |
+ <%= Html.ActionLink("Delete", "Delete", new { a.ID }, new { @class = "toolbar-button delete-action" }) %>
+
+
+ <% } %>
+
+
-<%= Html.Pager((IPagination)Model) %>
+<% var paged = Model as Health.Direct.Admin.Console.Models.Pagination.PaginatedList; %>
+<% if (paged != null) { %>
+
+<% } %>
\ No newline at end of file
diff --git a/csharp/agent/agent.csproj b/csharp/agent/agent.csproj
index f8ccad4..66ed7a7 100644
--- a/csharp/agent/agent.csproj
+++ b/csharp/agent/agent.csproj
@@ -45,6 +45,6 @@
-
+
\ No newline at end of file
diff --git a/csharp/build/DirectProject.sln b/csharp/build/DirectProject.sln
index abc30cf..ffd4f72 100644
--- a/csharp/build/DirectProject.sln
+++ b/csharp/build/DirectProject.sln
@@ -84,8 +84,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "hsmCryptographer.tests", ".
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "smtpEventHandler", "..\gateway\smtpEventHandler\smtpEventHandler.vcxproj", "{1C4EFA2C-943E-4ABA-9EDC-F92BD561EAC2}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdminMvc", "..\admin\AdminMvc\AdminMvc.csproj", "{8E284E54-B6E7-4F16-B362-719C34E95762}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "xd.tests", "..\unittests\xdtests\xd.tests.csproj", "{C6AF9782-1F93-4B72-9281-754456D053A8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "xdm.tests", "..\unittests\xdm.tests\xdm.tests.csproj", "{2C3A196B-AA5A-4D35-8DE3-953F8DD3501F}"
@@ -122,6 +120,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "external", "external", "{FD
..\external\microsoft\vcredist\vcredist_x86.exe = ..\external\microsoft\vcredist\vcredist_x86.exe
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdminMvc", "..\admin\AdminMvc\AdminMvc.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -256,10 +256,6 @@ Global
{1C4EFA2C-943E-4ABA-9EDC-F92BD561EAC2}.Debug|Any CPU.Build.0 = Debug|x64
{1C4EFA2C-943E-4ABA-9EDC-F92BD561EAC2}.Release|Any CPU.ActiveCfg = Release|x64
{1C4EFA2C-943E-4ABA-9EDC-F92BD561EAC2}.Release|Any CPU.Build.0 = Release|x64
- {8E284E54-B6E7-4F16-B362-719C34E95762}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {8E284E54-B6E7-4F16-B362-719C34E95762}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {8E284E54-B6E7-4F16-B362-719C34E95762}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {8E284E54-B6E7-4F16-B362-719C34E95762}.Release|Any CPU.Build.0 = Release|Any CPU
{C6AF9782-1F93-4B72-9281-754456D053A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C6AF9782-1F93-4B72-9281-754456D053A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C6AF9782-1F93-4B72-9281-754456D053A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -320,6 +316,10 @@ Global
{533AA11A-7C3C-4F3D-9901-534301D3F036}.Debug|Any CPU.Build.0 = Debug|Any CPU
{533AA11A-7C3C-4F3D-9901-534301D3F036}.Release|Any CPU.ActiveCfg = Release|Any CPU
{533AA11A-7C3C-4F3D-9901-534301D3F036}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/csharp/common/Cryptography/LegacySMIMECryptographer.cs b/csharp/common/Cryptography/LegacySMIMECryptographer.cs
new file mode 100644
index 0000000..2f959a5
--- /dev/null
+++ b/csharp/common/Cryptography/LegacySMIMECryptographer.cs
@@ -0,0 +1,647 @@
+/*
+ Copyright (c) 2010, Direct Project
+ All rights reserved.
+
+ Authors:
+ Umesh Madan umeshma@microsoft.com
+ Joe Shook Joseph.Shook@Surescripts.com
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+using System;
+using System.Net.Mime;
+using System.Security.Cryptography.Pkcs;
+using System.Security.Cryptography.X509Certificates;
+using Health.Direct.Common.Extensions;
+using Health.Direct.Common.Mail;
+using Health.Direct.Common.Mime;
+
+namespace Health.Direct.Common.Cryptography
+{
+ ///
+ /// Encapsulates use of S/MIME PKCS7 (CMS) cryptography
+ ///
+ public class LegacySMIMECryptographer : SMIMECryptographerBase, ISmimeCryptographer
+ {
+ ///
+ /// The default set of cryptographic algorithms.
+ ///
+ public static readonly ISmimeCryptographer Default = new LegacySMIMECryptographer();
+
+ ///
+ public event Action Error;
+
+ ///
+ public event Action Warning
+ {
+ add { }
+ remove { }
+ }
+
+ ///
+ /// This is the default cryptographer so always return self.
+ ///
+ public ISmimeCryptographer DefaultCryptographer { get { return this; } set { } }
+
+ ///
+ /// Initializes an instance with the default set of encryption and digest algorithms.
+ ///
+ public LegacySMIMECryptographer()
+ : this(EncryptionAlgorithm.AES128, DigestAlgorithm.SHA256)
+ {
+ }
+
+ ///
+ /// Initializes an instance, specifying the encryption and digest algorithm to use.
+ ///
+ /// The to use in this cryptographer
+ /// The to use in this cryptographer
+ public LegacySMIMECryptographer(EncryptionAlgorithm encryptionAlgorithm, DigestAlgorithm digestAlgorithm)
+ {
+ EncryptionAlgorithm = encryptionAlgorithm;
+ DigestAlgorithm = digestAlgorithm;
+ IncludeMultipartEpilogueInSignature = true;
+ IncludeCertChainInSignature = X509IncludeOption.EndCertOnly;
+ }
+
+ //-----------------------------------------------------
+ //
+ // Encryption
+ //
+ //-----------------------------------------------------
+ ///
+ /// Takes a MIME entity and returns a new encrypted MIME entity.
+ ///
+ /// The including content headers
+ /// The certificate used for encrytion
+ /// The encrypted
+ public MimeEntity Encrypt(MimeEntity entity, X509Certificate2 encryptingCertificate)
+ {
+ try
+ {
+ return Encrypt(entity, new X509Certificate2Collection(encryptingCertificate));
+ }
+ catch (Exception ex)
+ {
+ this.Error.NotifyEvent(this, ex);
+ throw;
+ }
+ }
+
+ ///
+ /// Takes a MIME entity and returns a new encrypted MIME entity.
+ ///
+ ///
+ /// As specified in the S/MIME and CMS RFCs, encryption uses symetric encryption to encrypt the body, and
+ /// certificate-based asymetric encryption to encrypt the encryption key used. With multiple certificates,
+ /// there will be multiple copies of the encrypted encryption key.
+ ///
+ /// The including content headers
+ /// The certificates used for encryption
+ /// The encrypted
+ public MimeEntity Encrypt(MimeEntity entity, X509Certificate2Collection encryptingCertificates)
+ {
+ if (entity == null)
+ {
+ throw new EncryptionException(EncryptionError.NullEntity);
+ }
+
+ try
+ {
+ byte[] messageBytes = DefaultSerializer.Default.SerializeToBytes(entity); // Serialize message out as ASCII encoded...
+
+ byte[] encryptedBytes = Encrypt(messageBytes, encryptingCertificates);
+
+ MimeEntity encryptedEntity = new MimeEntity
+ {
+ ContentType = SMIMEStandard.EncryptedEnvelopeContentTypeHeaderValue,
+ ContentTransferEncoding = TransferEncoding.Base64.AsString(),
+ Body = new Body(Convert.ToBase64String(encryptedBytes,
+ Base64FormattingOptions.InsertLineBreaks))
+ };
+
+ encryptedEntity.ContentDisposition = SMIMEStandard.EncryptedEnvelopeDisposition;
+ return encryptedEntity;
+ }
+ catch (Exception ex)
+ {
+ this.Error.NotifyEvent(this, ex);
+ throw;
+ }
+ }
+
+ ///
+ /// Encrypts raw data and returns the encrypted raw data (without content headers)
+ ///
+ /// The content to encrypt
+ /// The certificate used for encrytion
+ /// The encrypted raw data.
+ private byte[] Encrypt(byte[] content, X509Certificate2 encryptingCertificate)
+ {
+ return Encrypt(content, new X509Certificate2Collection(encryptingCertificate));
+ }
+
+ ///
+ /// Encrypts raw data and returns the encrypted raw data (without content headers)
+ ///
+ /// The content to encrypt
+ /// The collection of certificate used for encrytion
+ /// The encrypted raw data.
+ private byte[] Encrypt(byte[] content, X509Certificate2Collection encryptingCertificates)
+ {
+ EnvelopedCms envelope = CreateEncryptedEnvelope(content, encryptingCertificates);
+ return envelope.Encode();
+ }
+
+
+ ///
+ /// Encrypts raw data and returns a instance with the encrypted data.
+ ///
+ /// The content to encrypt
+ /// The collection of certificate used for encrytion
+ /// The encrypted instance.
+ private EnvelopedCms CreateEncryptedEnvelope(byte[] content, X509Certificate2Collection encryptingCertificates)
+ {
+ if (content == null)
+ {
+ throw new EncryptionException(EncryptionError.NullContent);
+ }
+ if (encryptingCertificates == null || encryptingCertificates.Count == 0)
+ {
+ throw new EncryptionException(EncryptionError.NoCertificates);
+ }
+
+ CmsRecipientCollection recipients = new CmsRecipientCollection(SubjectIdentifierType.IssuerAndSerialNumber, encryptingCertificates);
+ EnvelopedCms dataEnvelope = new EnvelopedCms(CreateDataContainer(content), ToAlgorithmID(EncryptionAlgorithm));
+ dataEnvelope.Encrypt(recipients);
+
+ return dataEnvelope;
+ }
+
+ //-----------------------------------------------------
+ //
+ // Decryption
+ //
+ //-----------------------------------------------------
+ //
+ // Cryptography is performed only on the Mime portions of the message, not the RFC822 headers
+ //
+
+ ///
+ /// Decrypts a with a certificate
+ ///
+ /// The to decrypt
+ /// The that encrypted the message
+ /// A holding the decrypted message
+ private MimeEntity Decrypt(Message message, X509Certificate2 decryptingCertificate)
+ {
+ return Decrypt(message.ExtractMimeEntity(), decryptingCertificate);
+ }
+
+ ///
+ /// Decrypts a with a certificate
+ ///
+ /// The to decrypt
+ /// The that encrypted the message
+ /// A holding the decrypted message
+ private MimeEntity Decrypt(MimeEntity encryptedEntity, X509Certificate2 decryptingCertificate)
+ {
+ return this.DecryptEntity(this.GetEncryptedBytes(encryptedEntity), decryptingCertificate);
+ }
+
+ ///
+ /// Decrypt the given encryptedByte array into a MimeEntity
+ ///
+ /// source encrypted bytes
+ /// The that encrypted the message
+ /// A holding the decrypted message
+ public MimeEntity DecryptEntity(byte[] encryptedBytes, X509Certificate2 decryptingCertificate)
+ {
+ if (encryptedBytes.IsNullOrEmpty())
+ {
+ throw new ArgumentException("encryptedBytes");
+ }
+
+ if (decryptingCertificate == null)
+ {
+ throw new EncryptionException(EncryptionError.NoCertificates);
+ }
+
+ if (!decryptingCertificate.HasPrivateKey)
+ {
+ throw new EncryptionException(EncryptionError.NoPrivateKey);
+ }
+
+ try
+ {
+ byte[] decryptedBytes = Decrypt(encryptedBytes, decryptingCertificate);
+
+ //
+ // And turn the encrypted bytes back into an entity
+ //
+ return DefaultSerializer.Default.Deserialize(decryptedBytes);
+ }
+ catch (Exception ex)
+ {
+ this.Error.NotifyEvent(this, ex);
+ throw;
+ }
+ }
+
+ ///
+ /// Decrypts encrypted raw content with a certificate
+ ///
+ /// The raw data to decrypt
+ /// The that encrypted the message
+ /// A byte array holding the decrypted content
+ private byte[] Decrypt(byte[] encryptedContent, X509Certificate2 decryptingCertificate)
+ {
+ return Decrypt(encryptedContent, new X509Certificate2Collection(decryptingCertificate));
+ }
+
+ ///
+ /// Decrypts encrypted raw content with a collection of certificates, at least one of which can decrypt the encryption key.
+ ///
+ ///
+ /// See for more information on underlying processing.
+ ///
+ /// The raw data to decrypt
+ /// The of certificates, at least one of which encrypted the message
+ /// A byte array holding the decrypted content
+ private byte[] Decrypt(byte[] encryptedContent, X509Certificate2Collection decryptingCertificates)
+ {
+ if (decryptingCertificates == null || decryptingCertificates.Count == 0)
+ {
+ throw new EncryptionException(EncryptionError.NoCertificates);
+ }
+
+ EnvelopedCms dataEnvelope = DeserializeEncryptionEnvelope(encryptedContent);
+
+ dataEnvelope.Decrypt(decryptingCertificates);
+
+ ContentInfo contentInfo = dataEnvelope.ContentInfo;
+
+ if (!IsDataContainer(contentInfo))
+ {
+ throw new EncryptionException(EncryptionError.ContentNotDataContainer);
+ }
+
+ return contentInfo.Content;
+ }
+
+ ///
+ /// Deserializes an encrypted byte array of encrypted data as a instance
+ ///
+ /// The raw encrypted data
+ /// An instance of containing the encrypted data
+ private EnvelopedCms DeserializeEncryptionEnvelope(byte[] encryptedContent)
+ {
+ if (encryptedContent == null)
+ {
+ throw new EncryptionException(EncryptionError.NullContent);
+ }
+
+ EnvelopedCms dataEnvelope = new EnvelopedCms();
+ dataEnvelope.Decode(encryptedContent);
+ return dataEnvelope;
+ }
+
+ internal ContentInfo CreateDataContainer(byte[] content)
+ {
+ return new ContentInfo(CryptoOids.ContentType_Data, content);
+ }
+
+ internal bool IsDataContainer(ContentInfo contentInfo)
+ {
+ return (contentInfo.ContentType.Value == CryptoOids.ContentType_Data.Value);
+ }
+
+ //-----------------------------------------------------
+ //
+ // Signatures
+ //
+ //-----------------------------------------------------
+ //
+ // Cryptography is performed only on the Mime portions of the message, not the RFC822 headers
+ // Some mail readers ignore the epilogue when calculating signatures!
+ //
+
+
+ ///
+ /// Creates a detached signed entity from a message and a collection of signing certificate
+ ///
+ ///
+ /// Cryptography is performed only on the Mime portions of the message, not the RFC822 headers
+ /// Some mail readers ignore the epilogue when calculating signatures!
+ ///
+ /// The entity to sign
+ /// The certificates with which to sign.
+ /// A instance holding the signatures.
+ public SignedEntity Sign(Message message, X509Certificate2Collection signingCertificates)
+ {
+ try
+ {
+ return Sign(message.ExtractEntityForSignature(IncludeMultipartEpilogueInSignature), signingCertificates);
+ }
+ catch (Exception ex)
+ {
+ this.Error.NotifyEvent(this, ex);
+ throw;
+ }
+ }
+
+ ///
+ /// Creates a detached signed entity from a and a signing certificate
+ ///
+ ///
+ /// Cryptography is performed only on the Mime portions of the message, not the RFC822 headers
+ /// Some mail readers ignore the epilogue when calculating signatures!
+ ///
+ /// The to sign
+ /// The certificate with which to sign.
+ /// A instance holding the signature.
+ public SignedEntity Sign(MimeEntity entity, X509Certificate2 signingCertificate)
+ {
+ try
+ {
+ return Sign(entity, new X509Certificate2Collection(signingCertificate));
+ }
+ catch (Exception ex)
+ {
+ this.Error.NotifyEvent(this, ex);
+ throw;
+ }
+ }
+
+ ///
+ /// Creates a detatched signed entity from a and collection of a signing certificates
+ ///
+ ///
+ /// Cryptography is performed only on the Mime portions of the message, not the RFC822 headers
+ /// Some mail readers ignore the epilogue when calculating signatures!
+ ///
+ /// The to sign
+ /// The certificates with which to sign.
+ /// A instance holding the signatures.
+ public SignedEntity Sign(MimeEntity entity, X509Certificate2Collection signingCertificates)
+ {
+ if (entity == null)
+ {
+ throw new SignatureException(SignatureError.NullEntity);
+ }
+
+ try
+ {
+ byte[] entityBytes = DefaultSerializer.Default.SerializeToBytes(entity);
+ MimeEntity signature = CreateSignatureEntity(entityBytes, signingCertificates);
+
+ return new SignedEntity(DigestAlgorithm, entity, signature);
+ }
+ catch (Exception ex)
+ {
+ this.Error.NotifyEvent(this, ex);
+ throw;
+ }
+ }
+
+
+ ///
+ /// Creates a signed data from source raw data and a collection of signing certificates
+ ///
+ ///
+ /// Cryptography is performed only on the Mime portions of the message, not the RFC822 headers
+ /// Some mail readers ignore the epilogue when calculating signatures!
+ ///
+ /// The byte array to sign
+ /// The certificates with which to sign.
+ /// Raw data holding the signatures.
+ private byte[] Sign(byte[] content, X509Certificate2Collection signingCertificates)
+ {
+ SignedCms signature = CreateSignature(content, signingCertificates);
+ return signature.Encode();
+ }
+
+
+ ///
+ /// Creates a signed entity from source raw data and a collection of signing certificates
+ ///
+ ///
+ /// Cryptography is performed only on the Mime portions of the message, not the RFC822 headers
+ /// Some mail readers ignore the epilogue when calculating signatures!
+ ///
+ /// The byte array to sign
+ /// The certificates with which to sign.
+ /// The holding the signatures.
+ private MimeEntity CreateSignatureEntity(byte[] content, X509Certificate2Collection signingCertificates)
+ {
+ byte[] signatureBytes = Sign(content, signingCertificates);
+ //
+ // We create an entity to hold a detached signature
+ //
+ MimeEntity signature = new MimeEntity
+ {
+ ContentType = SMIMEStandard.SignatureContentTypeHeaderValue,
+ ContentTransferEncoding = TransferEncoding.Base64.AsString(),
+ Body = new Body(Convert.ToBase64String(signatureBytes))
+ };
+ signature.ContentDisposition = SMIMEStandard.SignatureDisposition;
+ return signature;
+ }
+
+ ///
+ /// Creates for raw content and a collection of signing certificate
+ ///
+ /// The byte array to sign
+ /// The certificates with which to sign.
+ /// An instance of holdling the signatures
+ public SignedCms CreateSignature(byte[] content, X509Certificate2Collection signingCertificates)
+ {
+ if (content == null)
+ {
+ throw new SignatureException(SignatureError.NullContent);
+ }
+
+ if (signingCertificates == null)
+ {
+ throw new SignatureException(SignatureError.NoCertificates);
+ }
+
+ try
+ {
+ SignedCms signature = new SignedCms(CreateDataContainer(content), true);
+ // true: Detached Signature
+
+ for (int i = 0, count = signingCertificates.Count; i < count; ++i)
+ {
+ CmsSigner signer = CreateSigner(signingCertificates[i]);
+ signature.ComputeSignature(signer, true); // true: don't prompt the user
+ }
+
+ return signature;
+ }
+ catch (Exception ex)
+ {
+ this.Error.NotifyEvent(this, ex);
+ throw;
+ }
+ }
+
+ //-----------------------------------------------------
+ //
+ // Signature Validation
+ //
+ //-----------------------------------------------------
+
+ ///
+ /// Checks that a signature was signed by the signer certificate.
+ ///
+ /// The signed entity to check
+ /// The signer certificaet that purports to sign the entity
+ /// If the entity was not signed by the claimed certificate
+ private void CheckSignature(SignedEntity signedEntity, X509Certificate2 signerCertificate)
+ {
+ SignedCms signatureEnvelope = DeserializeDetachedSignature(signedEntity);
+ CheckSignature(signatureEnvelope.SignerInfos, signerCertificate);
+ }
+
+ ///
+ /// Checks that a collection of signature was signed by the signer certificate.
+ ///
+ /// The collection of to check
+ /// The signer certificate that purports to sign the entity
+ /// If the entity was not signed by the claimed certificate
+ private void CheckSignature(SignerInfoCollection signers, X509Certificate2 signerCertificate)
+ {
+ if (signerCertificate == null)
+ {
+ throw new SignatureException(SignatureError.NoCertificates);
+ }
+ if (signers == null || signers.Count == 0)
+ {
+ throw new SignatureException(SignatureError.NoSigners);
+ }
+ //
+ // Find the signer
+ //
+ SignerInfo signer = signers.FindByThumbprint(signerCertificate.Thumbprint);
+ if (signer == null)
+ {
+ throw new SignatureException(SignatureError.NoSigners);
+ }
+
+ signer.CheckSignature(true);
+ }
+
+ ///
+ /// Transforms a to the associated instance
+ ///
+ /// The to deserialize
+ /// The corresponding
+ public SignedCms DeserializeDetachedSignature(SignedEntity entity)
+ {
+ if (entity == null)
+ {
+ throw new SignatureException(SignatureError.NullEntity);
+ }
+
+ try
+ {
+ // Serialize entity out as ASCII encoded...
+ byte[] contentBytes = DefaultSerializer.Default.SerializeToBytes(entity.Content);
+ byte[] signatureBytes = Convert.FromBase64String(entity.Signature.Body.Text);
+
+ return DeserializeDetachedSignature(contentBytes, signatureBytes);
+ }
+ catch (Exception ex)
+ {
+ this.Error.NotifyEvent(this, ex);
+ throw;
+ }
+ }
+
+ ///
+ /// Transforms a detached signature, represented as the entity and the signature raw data
+ /// to the associated instance
+ ///
+ /// The raw content of the entity
+ /// the raw content of the signature
+ /// The corresponding
+ private SignedCms DeserializeDetachedSignature(byte[] content, byte[] signatureBytes)
+ {
+ SignedCms signature = new SignedCms(CreateDataContainer(content), true);
+ signature.Decode(signatureBytes);
+
+ return signature;
+ }
+
+ ///
+ /// Tranforms an enveloped signature to the corresponding
+ ///
+ /// The entity containing the enveloped signature
+ /// the corresponding
+ public SignedCms DeserializeEnvelopedSignature(MimeEntity envelopeEntity)
+ {
+ if (envelopeEntity == null)
+ {
+ throw new SignatureException(SignatureError.NullEntity);
+ }
+
+ if (!SMIMEStandard.IsSignedEnvelope(envelopeEntity))
+ {
+ throw new SignatureException(SignatureError.NotSignatureEnvelope);
+ }
+
+ try
+ {
+ byte[] envelopeBytes = Convert.FromBase64String(envelopeEntity.Body.Text);
+
+ return DeserializeEnvelopedSignature(envelopeBytes);
+ }
+ catch (Exception ex)
+ {
+ this.Error.NotifyEvent(this, ex);
+ throw;
+ }
+ }
+
+ ///
+ /// Tranforms an enveloped signature to the corresponding
+ ///
+ /// The raw data containing the enveloped signature
+ /// the corresponding
+ private SignedCms DeserializeEnvelopedSignature(byte[] envelopeBytes)
+ {
+ SignedCms signature = new SignedCms();
+ signature.Decode(envelopeBytes);
+
+ if (!IsDataContainer(signature.ContentInfo))
+ {
+ throw new SignatureException(SignatureError.ContentNotDataContainer);
+ }
+
+ return signature;
+ }
+
+ private CmsSigner CreateSigner(X509Certificate2 cert)
+ {
+ CmsSigner signer = new CmsSigner(cert)
+ {
+ IncludeOption = IncludeCertChainInSignature,
+ DigestAlgorithm = ToDigestAlgorithmOid(DigestAlgorithm)
+ };
+
+ Pkcs9SigningTime signingTime = new Pkcs9SigningTime();
+ signer.SignedAttributes.Add(signingTime);
+
+ return signer;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/csharp/common/Cryptography/SMIMECryptographer.cs b/csharp/common/Cryptography/SMIMECryptographer.cs
index f3f92e2..42aa6ca 100644
--- a/csharp/common/Cryptography/SMIMECryptographer.cs
+++ b/csharp/common/Cryptography/SMIMECryptographer.cs
@@ -1,5 +1,5 @@
-/*
- Copyright (c) 2010, Direct Project
+/*
+ Copyright (c) 2010 -2025, Direct Project
All rights reserved.
Authors:
@@ -12,9 +12,10 @@ Joe Shook Joseph.Shook@Surescripts.com
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
+
*/
+
using System;
using System.Net.Mime;
using System.Security.Cryptography.Pkcs;
@@ -22,6 +23,14 @@ Neither the name of The Direct Project (directproject.org) nor the names of its
using Health.Direct.Common.Extensions;
using Health.Direct.Common.Mail;
using Health.Direct.Common.Mime;
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Cms;
+using Org.BouncyCastle.Crypto.Operators;
+using Org.BouncyCastle.Security;
+using ContentInfo = System.Security.Cryptography.Pkcs.ContentInfo;
+
namespace Health.Direct.Common.Cryptography
{
@@ -54,7 +63,7 @@ public event Action Warning
/// Initializes an instance with the default set of encryption and digest algorithms.
///
public SMIMECryptographer()
- : this(EncryptionAlgorithm.AES128, DigestAlgorithm.SHA1)
+ : this(EncryptionAlgorithm.AES128, DigestAlgorithm.SHA256)
{
}
@@ -156,33 +165,46 @@ private byte[] Encrypt(byte[] content, X509Certificate2 encryptingCertificate)
/// The encrypted raw data.
private byte[] Encrypt(byte[] content, X509Certificate2Collection encryptingCertificates)
{
- EnvelopedCms envelope = CreateEncryptedEnvelope(content, encryptingCertificates);
- return envelope.Encode();
+ CmsEnvelopedDataGenerator gen = new CmsEnvelopedDataGenerator();
+
+ // OAEP parameters for RSA (SHA-256, MGF1 with SHA-256, pSource=PSpecified with empty string)
+ Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier oaepParams = new Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier(
+ PkcsObjectIdentifiers.IdRsaesOaep,
+ new RsaesOaepParameters(
+ new Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier(NistObjectIdentifiers.IdSha256),
+ new Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier(PkcsObjectIdentifiers.IdMgf1, new Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier(NistObjectIdentifiers.IdSha256)),
+ new Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier(PkcsObjectIdentifiers.IdPSpecified, new DerOctetString(new byte[0]))
+ )
+ );
+
+ foreach (X509Certificate2 cert in encryptingCertificates)
+ {
+ var bcCert = DotNetUtilities.FromX509Certificate(cert);
+ // Use Asn1KeyWrapper with OAEP algorithm and public key
+ var keyWrapper = new Asn1KeyWrapper(oaepParams, bcCert.GetPublicKey());
+ var recipGen = new KeyTransRecipientInfoGenerator(bcCert, keyWrapper);
+ gen.AddRecipientInfoGenerator(recipGen);
+ }
+
+ string symAlgTag = GetSymmetricEncryptionTag(EncryptionAlgorithm);
+ CmsProcessableByteArray data = new CmsProcessableByteArray(content);
+ var encrypted = gen.Generate(data, symAlgTag);
+ return encrypted.GetEncoded();
}
-
- ///
- /// Encrypts raw data and returns a instance with the encrypted data.
- ///
- /// The content to encrypt
- /// The collection of certificate used for encrytion
- /// The encrypted instance.
- private EnvelopedCms CreateEncryptedEnvelope(byte[] content, X509Certificate2Collection encryptingCertificates)
+ private static string GetSymmetricEncryptionTag(EncryptionAlgorithm alg)
{
- if (content == null)
- {
- throw new EncryptionException(EncryptionError.NullContent);
- }
- if (encryptingCertificates == null || encryptingCertificates.Count == 0)
+ switch (alg)
{
- throw new EncryptionException(EncryptionError.NoCertificates);
+ case EncryptionAlgorithm.AES128:
+ return CmsEnvelopedGenerator.Aes128Cbc;
+ case EncryptionAlgorithm.AES192:
+ return CmsEnvelopedGenerator.Aes192Cbc;
+ case EncryptionAlgorithm.AES256:
+ return CmsEnvelopedGenerator.Aes256Cbc;
+ default:
+ return CmsEnvelopedGenerator.Aes128Cbc;
}
-
- CmsRecipientCollection recipients = new CmsRecipientCollection(SubjectIdentifierType.IssuerAndSerialNumber, encryptingCertificates);
- EnvelopedCms dataEnvelope = new EnvelopedCms(CreateDataContainer(content), ToAlgorithmID(EncryptionAlgorithm));
- dataEnvelope.Encrypt(recipients);
-
- return dataEnvelope;
}
//-----------------------------------------------------
@@ -270,7 +292,7 @@ private byte[] Decrypt(byte[] encryptedContent, X509Certificate2 decryptingCerti
/// Decrypts encrypted raw content with a collection of certificates, at least one of which can decrypt the encryption key.
///
///
- /// See for more information on underlying processing.
+ /// See for more information on underlying processing.
///
/// The raw data to decrypt
/// The of certificates, at least one of which encrypted the message
@@ -313,12 +335,12 @@ private EnvelopedCms DeserializeEncryptionEnvelope(byte[] encryptedContent)
return dataEnvelope;
}
- internal ContentInfo CreateDataContainer(byte[] content)
+ internal System.Security.Cryptography.Pkcs.ContentInfo CreateDataContainer(byte[] content)
{
- return new ContentInfo(CryptoOids.ContentType_Data, content);
+ return new System.Security.Cryptography.Pkcs.ContentInfo(CryptoOids.ContentType_Data, content);
}
- internal bool IsDataContainer(ContentInfo contentInfo)
+ internal bool IsDataContainer(System.Security.Cryptography.Pkcs.ContentInfo contentInfo)
{
return (contentInfo.ContentType.Value == CryptoOids.ContentType_Data.Value);
}
@@ -514,7 +536,7 @@ private void CheckSignature(SignedEntity signedEntity, X509Certificate2 signerCe
///
/// Checks that a collection of signature was signed by the signer certificate.
///
- /// The collection of to check
+ /// The collection of to check
/// The signer certificate that purports to sign the entity
/// If the entity was not signed by the claimed certificate
private void CheckSignature(SignerInfoCollection signers, X509Certificate2 signerCertificate)
@@ -530,7 +552,7 @@ private void CheckSignature(SignerInfoCollection signers, X509Certificate2 signe
//
// Find the signer
//
- SignerInfo signer = signers.FindByThumbprint(signerCertificate.Thumbprint);
+ System.Security.Cryptography.Pkcs.SignerInfo signer = signers.FindByThumbprint(signerCertificate.Thumbprint);
if (signer == null)
{
throw new SignatureException(SignatureError.NoSigners);
diff --git a/csharp/common/Cryptography/SMIMECryptographerBase.cs b/csharp/common/Cryptography/SMIMECryptographerBase.cs
index 726018f..79d5025 100644
--- a/csharp/common/Cryptography/SMIMECryptographerBase.cs
+++ b/csharp/common/Cryptography/SMIMECryptographerBase.cs
@@ -88,7 +88,7 @@ public static class CryptoOids
{
// documentation for these is silly.
#pragma warning disable 1591
- public static readonly Oid SHA1 = new Oid("1.3.14.3.2.26");
+ // public static readonly Oid SHA1 = new Oid("1.3.14.3.2.26"); // Obsolete
public static readonly Oid SHA256 = new Oid("2.16.840.1.101.3.4.2.1");
public static readonly Oid SHA384 = new Oid("2.16.840.1.101.3.4.2.2");
public static readonly Oid SHA512 = new Oid("2.16.840.1.101.3.4.2.3");
@@ -123,10 +123,10 @@ public static Oid ToDigestAlgorithmOid(DigestAlgorithm type)
switch (type)
{
default:
- throw new NotSupportedException();
+ throw new NotSupportedException($"{type.ToString()} DigestAlgorithm is not supported.");
- case DigestAlgorithm.SHA1:
- return CryptoOids.SHA1;
+ // case DigestAlgorithm.SHA1: // obsolete
+ // return CryptoOids.SHA1;
case DigestAlgorithm.SHA256:
return CryptoOids.SHA256;
diff --git a/csharp/common/common.csproj b/csharp/common/common.csproj
index 890c7e3..7f87695 100644
--- a/csharp/common/common.csproj
+++ b/csharp/common/common.csproj
@@ -49,7 +49,7 @@
-
+
diff --git a/csharp/config/client/PasswordHash.partial.cs b/csharp/config/client/PasswordHash.partial.cs
index bb86428..ebbc65d 100644
--- a/csharp/config/client/PasswordHash.partial.cs
+++ b/csharp/config/client/PasswordHash.partial.cs
@@ -36,17 +36,14 @@ public PasswordHash(Administrator user, string password)
{
if (user == null) throw new ArgumentNullException(nameof(user));
if (password == null) throw new ArgumentNullException(nameof(password));
- this.HashedPassword = ComputeHash(user.Username, password);
+ this.HashedPassword = ComputeHash(user, password);
}
- static string ComputeHash(string username, string password)
+ static string ComputeHash(Administrator user, string password)
{
- // Replace with the exact original server algorithm if it differs.
- using (var sha = SHA256.Create())
- {
- var material = Encoding.UTF8.GetBytes((username ?? string.Empty).ToLowerInvariant() + ":" + password);
- return Convert.ToBase64String(sha.ComputeHash(material));
- }
+ // Must match the server-side algorithm in config.store\PasswordHash.cs
+ var source = user.Username.ToLower() + "|" + user.CreateDate.ToString("yyyyMMdd'T'HHmmss") + "|" + password;
+ return Convert.ToBase64String(SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(source)));
}
}
}
\ No newline at end of file
diff --git a/csharp/config/console/config.console.csproj b/csharp/config/console/config.console.csproj
index a6e4a4c..82aae26 100644
--- a/csharp/config/console/config.console.csproj
+++ b/csharp/config/console/config.console.csproj
@@ -49,7 +49,7 @@
-
+
diff --git a/csharp/config/store/PasswordHash.cs b/csharp/config/store/PasswordHash.cs
index 3304268..3a44f74 100644
--- a/csharp/config/store/PasswordHash.cs
+++ b/csharp/config/store/PasswordHash.cs
@@ -33,7 +33,7 @@ public PasswordHash(Administrator user, string password)
private static string HashPassword(Administrator user, string password)
{
var source = user.Username.ToLower() + "|" + user.CreateDate.ToString("yyyyMMdd'T'HHmmss") + "|" + password;
- return Convert.ToBase64String(SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(source)));
+ return Convert.ToBase64String(SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(source)));
}
[DataMember(IsRequired = true)]
diff --git a/csharp/context.receiver.tests/App.config b/csharp/context.receiver.tests/App.config
deleted file mode 100644
index 122c732..0000000
--- a/csharp/context.receiver.tests/App.config
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/csharp/context.receiver.tests/ContextTestFiles/BadContext/ContextMissing.eml b/csharp/context.receiver.tests/ContextTestFiles/BadContext/ContextMissing.eml
deleted file mode 100644
index ea130f5..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/BadContext/ContextMissing.eml
+++ /dev/null
@@ -1,21 +0,0 @@
-From: Joe
-Date: Sat, 08 Jul 2017 10:55:39 -0700
-Subject: Need more memory
-Message-Id: <34C73MNYE0U4.6M64WULRWE982@SSORDSK-JSHO02>
-To: "Jean"
-X-Direct-Context: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="=-zYT9GDXFXEDRaBnZ8nRxKw=="
-
---=-zYT9GDXFXEDRaBnZ8nRxKw==
-Content-Type: text/plain; charset=utf-8
-
-Simple Body
---=-zYT9GDXFXEDRaBnZ8nRxKw==
-Content-Type: application/pdf; name=report.pdf
-Content-Disposition: attachment; filename=report.pdf
-Content-Transfer-Encoding: base64
-
-RmFrZSBQREY=
-
---=-zYT9GDXFXEDRaBnZ8nRxKw==--
\ No newline at end of file
diff --git a/csharp/context.receiver.tests/ContextTestFiles/BadContext/ContextUnparsablePatient.eml b/csharp/context.receiver.tests/ContextTestFiles/BadContext/ContextUnparsablePatient.eml
deleted file mode 100644
index 972ca9e..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/BadContext/ContextUnparsablePatient.eml
+++ /dev/null
@@ -1,34 +0,0 @@
-From: Joe
-Date: Sat, 08 Jul 2017 10:55:39 -0700
-Subject: Need more memory
-Message-Id: <34C73MNYE0U4.6M64WULRWE982@SSORDSK-JSHO02>
-To: "Jean"
-X-Direct-Context: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="=-zYT9GDXFXEDRaBnZ8nRxKw=="
-
---=-zYT9GDXFXEDRaBnZ8nRxKw==
-Content-Type: text/plain; charset=utf-8
-
-Simple Body
---=-zYT9GDXFXEDRaBnZ8nRxKw==
-Content-Type: text/plain
-Content-Disposition: attachment; filename=metadata.txt
-Content-ID: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-
-version: 1.0
-id: <2142848@direct.example.com>
-patient-id: 2.16.840.1.113883.19.999999:123456;
- 2.16.840.1.113883.19.888888:75774
-type: radiology/report
-purpose: research
-patient: Name=John; surname=Doe; middleName=Jacob; dateOfBirth=1961-12-31; gender=M; postalCode=12345
-
---=-zYT9GDXFXEDRaBnZ8nRxKw==
-Content-Type: application/pdf; name=report.pdf
-Content-Disposition: attachment; filename=report.pdf
-Content-Transfer-Encoding: base64
-
-RmFrZSBQREY=
-
---=-zYT9GDXFXEDRaBnZ8nRxKw==--
\ No newline at end of file
diff --git a/csharp/context.receiver.tests/ContextTestFiles/BadContext/ContextUnparsableType.eml b/csharp/context.receiver.tests/ContextTestFiles/BadContext/ContextUnparsableType.eml
deleted file mode 100644
index 8f561b7..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/BadContext/ContextUnparsableType.eml
+++ /dev/null
@@ -1,34 +0,0 @@
-From: Joe
-Date: Sat, 08 Jul 2017 10:55:39 -0700
-Subject: Need more memory
-Message-Id: <34C73MNYE0U4.6M64WULRWE982@SSORDSK-JSHO02>
-To: "Jean"
-X-Direct-Context: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="=-zYT9GDXFXEDRaBnZ8nRxKw=="
-
---=-zYT9GDXFXEDRaBnZ8nRxKw==
-Content-Type: text/plain; charset=utf-8
-
-Simple Body
---=-zYT9GDXFXEDRaBnZ8nRxKw==
-Content-Type: text/plain
-Content-Disposition: attachment; filename=metadata.txt
-Content-ID: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-
-version: 1.0
-id: <2142848@direct.example.com>
-patient-id: 2.16.840.1.113883.19.999999:123456;
- 2.16.840.1.113883.19.888888:75774
-type: bad/report
-purpose: research
-patient: givenName=John; surname=Doe; middleName=Jacob; dateOfBirth=1961-12-31; gender=M; postalCode=12345
-
---=-zYT9GDXFXEDRaBnZ8nRxKw==
-Content-Type: application/pdf; name=report.pdf
-Content-Disposition: attachment; filename=report.pdf
-Content-Transfer-Encoding: base64
-
-RmFrZSBQREY=
-
---=-zYT9GDXFXEDRaBnZ8nRxKw==--
\ No newline at end of file
diff --git a/csharp/context.receiver.tests/ContextTestFiles/BadContext/DSN.eml b/csharp/context.receiver.tests/ContextTestFiles/BadContext/DSN.eml
deleted file mode 100644
index 8b1a9e1..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/BadContext/DSN.eml
+++ /dev/null
@@ -1,31 +0,0 @@
-MIME-Version: 1.0
-To: Joe
-From: "Jean"
-Content-Transfer-Encoding: 7bit
-Content-Type: multipart/report;
- boundary="fb90c8abb88a4d37b6f7f600b3512ac5";
- report-type=disposition-notification
-Subject: dispatched:Test Subject 9e273dc5-bc56-4720-8232-021d981a9788
-Message-ID: <1b3f199374444a39948dc7b22b13b709@direct.west.hobo.lab>
-Content-Class: urn:content-classes:message
-Date: Sat, 14 Oct 2017 04:02:24 -0700
-
-This is a multi-part message in MIME format.
-
---fb90c8abb88a4d37b6f7f600b3512ac5
-Content-Transfer-Encoding: 7bit
-Content-Type: text/plain;
- charset="iso-8859-1"
-
-Message Delivery Notification
---fb90c8abb88a4d37b6f7f600b3512ac5
-Content-Transfer-Encoding: 7bit
-Content-Type: message/disposition-notification
-
-Disposition:automatic-action/MDN-sent-automatically;dispatched
-X-DIRECT-FINAL-DESTINATION-DELIVERY:
-Reporting-UA:direct.west.hobo.lab;Direct Security Agent
-Original-Message-ID:
-Final-Recipient:rfc822;"Jean"
-
---fb90c8abb88a4d37b6f7f600b3512ac5--
diff --git a/csharp/context.receiver.tests/ContextTestFiles/BadContext/MDN.eml b/csharp/context.receiver.tests/ContextTestFiles/BadContext/MDN.eml
deleted file mode 100644
index 50c27e6..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/BadContext/MDN.eml
+++ /dev/null
@@ -1,29 +0,0 @@
-MIME-Version: 1.0
-To: Joe
-From: "Jean"
-Content-Transfer-Encoding: 7bit
-Content-Type: multipart/report;
- boundary="1c1983cc46bd440eaa7aebab441cf876";
- report-type=disposition-notification
-Subject: processed:Test Subject 40bec126-77d3-4364-9e63-b98c282209ba
-Message-ID:
-Date: Sat, 14 Oct 2017 04:02:25 -0700
-
-This is a multi-part message in MIME format.
-
---1c1983cc46bd440eaa7aebab441cf876
-Content-Transfer-Encoding: 7bit
-Content-Type: text/plain;
- charset="iso-8859-1"
-
-Message Delivery Notification
---1c1983cc46bd440eaa7aebab441cf876
-Content-Transfer-Encoding: 7bit
-Content-Type: message/disposition-notification
-
-Disposition:automatic-action/MDN-sent-automatically;processed
-Reporting-UA:Direct.West.Hobo.Lab;Direct Security Agent
-Original-Message-ID:
-Final-Recipient:rfc822;Jean@opsstation.lab
-
---1c1983cc46bd440eaa7aebab441cf876--
diff --git a/csharp/context.receiver.tests/ContextTestFiles/BadContext/NoDirectContextHeader.eml b/csharp/context.receiver.tests/ContextTestFiles/BadContext/NoDirectContextHeader.eml
deleted file mode 100644
index 7c18ff2..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/BadContext/NoDirectContextHeader.eml
+++ /dev/null
@@ -1,9 +0,0 @@
-From: Joe
-Date: Sat, 08 Jul 2017 10:55:39 -0700
-Subject: Need more memory
-Message-Id: <34C73MNYE0U4.6M64WULRWE982@SSORDSK-JSHO02>
-To: "Jean"
-MIME-Version: 1.0
-Content-Type: text/plain
-
-Simple Body
diff --git a/csharp/context.receiver.tests/ContextTestFiles/ContextHL7.Default.txtBase64 b/csharp/context.receiver.tests/ContextTestFiles/ContextHL7.Default.txtBase64
deleted file mode 100644
index cb0546d..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/ContextHL7.Default.txtBase64
+++ /dev/null
@@ -1,37 +0,0 @@
-From: Joe
-Date: Wed, 30 Aug 2017 22:17:46 -0700
-Subject: Need more memory
-Message-Id: <34C73MNYE0U4.6M64WULRWE982@SSORDSK-JSHO02>
-To: Jean
-X-Direct-Context: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="=-UM9iEJGuFrn47U1X1JORSw=="
-
---=-UM9iEJGuFrn47U1X1JORSw==
-Content-Type: text/plain; charset=utf-8
-
-Simple Body
---=-UM9iEJGuFrn47U1X1JORSw==
-Content-Type: text/plain
-Content-Disposition: attachment; filename=metadata.txt
-Content-ID: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-
-version: 1.0
-id: <2142848@direct.example.com>
-encapsulation: hl7v2
-
---=-UM9iEJGuFrn47U1X1JORSw==
-Content-Type: application/x-direct-encapsulated+hl7v2; name=adt-sample.hl7
-Content-Disposition: attachment; filename=adt-sample.hl7
-Content-Transfer-Encoding: base64
-
-TVNIIHxeIH5cJnwgU0VORElOR19BUFBMSUNBVElPTiB8IFNFTkRJTkdfRkFDSUxJVFkgfCBS
-RUNFSVZJTkdfQVBQTElDQVRJT04gfCBSRUNFSVZJTkdfRkFDSUxJVFkgfCAyMDExMDYxMzA4
-MzYxNyB8fCBBRFQgXiBBMDEgfCA5MzQ1NzYxMjAxMTA2MTMwODM2MTcgfCBQIHwgMi4zIHx8
-fHwNCkVWTiB8IEEwMSB8IDIwMTEwNjEzMDgzNjE3IHx8fA0KUElEIHwgMSB8fCAxMzU3Njkg
-fHwgTU9VU0UgXiBNSUNLRVkgXnx8IDE5MjgxMTE4IHwgTSB8fHwgMTIzIE1haW4gU3QuXl4g
-TGFrZSBCdWVuYSBWaXN0YSBeIEZMIF4gMzI4MzAgfHwgKDQwNyk5MzkgLSAxMjg5IF5eXiB0
-aGVNYWluTW91c2VAZGlzbmV5LmNvbSB8fHx8fCAxNzE5IHwgOTk5OTk5OTkgfHx8fHx8fHx8
-fHx8fHx8fHx8fHwNClBWMSB8IDEgfCBPIHx8fHx8Xl5eXl5eXl58Xl5eXl5eXl4=
-
---=-UM9iEJGuFrn47U1X1JORSw==--
\ No newline at end of file
diff --git a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple.PatienIdOnly.txtDefault b/csharp/context.receiver.tests/ContextTestFiles/ContextSimple.PatienIdOnly.txtDefault
deleted file mode 100644
index e20da1c..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple.PatienIdOnly.txtDefault
+++ /dev/null
@@ -1,32 +0,0 @@
-From: Joe
-Date: Sat, 08 Jul 2017 10:55:39 -0700
-Subject: Need more memory
-Message-Id: <34C73MNYE0U4.6M64WULRWE982@SSORDSK-JSHO02>
-To: Jean
-X-Direct-Context: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="=-zYT9GDXFXEDRaBnZ8nRxKw=="
-
---=-zYT9GDXFXEDRaBnZ8nRxKw==
-Content-Type: text/plain; charset=utf-8
-
-Simple Body
---=-zYT9GDXFXEDRaBnZ8nRxKw==
-Content-Type: text/plain
-Content-Disposition: attachment; filename=metadata.txt
-Content-ID: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-
-version: 1.0
-id: <2142848@direct.example.com>
-patient-id: 2.16.840.1.113883.19.999999:123456
-type: radiology/report
-purpose: research
-
---=-zYT9GDXFXEDRaBnZ8nRxKw==
-Content-Type: application/pdf; name=report.pdf
-Content-Disposition: attachment; filename=report.pdf
-Content-Transfer-Encoding: base64
-
-RmFrZSBQREY=
-
---=-zYT9GDXFXEDRaBnZ8nRxKw==--
\ No newline at end of file
diff --git a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtBase64 b/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtBase64
deleted file mode 100644
index 3fcc03c..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtBase64
+++ /dev/null
@@ -1,34 +0,0 @@
-From: Joe
-Date: Sat, 08 Jul 2017 11:26:18 -0700
-Subject: Need more memory
-Message-Id: <34C73MNYE0U4.6M64WULRWE982@SSORDSK-JSHO02>
-To: Jean
-X-Direct-Context: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="=-+EBGwupd+08aVN7a5xg65w=="
-
---=-+EBGwupd+08aVN7a5xg65w==
-Content-Type: text/plain; charset=utf-8
-
-Simple Body
---=-+EBGwupd+08aVN7a5xg65w==
-Content-Type: text/plain
-Content-Disposition: attachment; filename=metadata.txt
-Content-Transfer-Encoding: base64
-Content-ID: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-
-dmVyc2lvbjogMS4wDQppZDogPDIxNDI4NDhAZGlyZWN0LmV4YW1wbGUuY29tPg0KcGF0aWVu
-dC1pZDogMi4xNi44NDAuMS4xMTM4ODMuMTkuOTk5OTk5OjEyMzQ1NjsNCiAyLjE2Ljg0MC4x
-LjExMzg4My4xOS44ODg4ODg6NzU3NzQNCnR5cGU6IHJhZGlvbG9neS9yZXBvcnQNCnB1cnBv
-c2U6IHJlc2VhcmNoDQpwYXRpZW50OiBnaXZlbk5hbWU9Sm9objsgc3VybmFtZT1Eb2U7IG1p
-ZGRsZU5hbWU9SmFjb2I7IGRhdGVPZkJpcnRoPTE5NjEtMTItMzE7IGdlbmRlcj1NOyBwb3N0
-YWxDb2RlPTEyMzQ1DQo=
-
---=-+EBGwupd+08aVN7a5xg65w==
-Content-Type: application/pdf; name=report.pdf
-Content-Disposition: attachment; filename=report.pdf
-Content-Transfer-Encoding: base64
-
-RmFrZSBQREY=
-
---=-+EBGwupd+08aVN7a5xg65w==--
\ No newline at end of file
diff --git a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtBinary b/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtBinary
deleted file mode 100644
index 9034b26..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtBinary
+++ /dev/null
@@ -1,35 +0,0 @@
-From: Joe
-Date: Sat, 08 Jul 2017 11:30:57 -0700
-Subject: Need more memory
-Message-Id: <34C73MNYE0U4.6M64WULRWE982@SSORDSK-JSHO02>
-To: Jean
-X-Direct-Context: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="=-qOObRauiSrZluuB5HiV5sQ=="
-
---=-qOObRauiSrZluuB5HiV5sQ==
-Content-Type: text/plain; charset=utf-8
-
-Simple Body
---=-qOObRauiSrZluuB5HiV5sQ==
-Content-Type: text/plain
-Content-Disposition: attachment; filename=metadata.txt
-Content-Transfer-Encoding: binary
-Content-ID: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-
-version: 1.0
-id: <2142848@direct.example.com>
-patient-id: 2.16.840.1.113883.19.999999:123456;
- 2.16.840.1.113883.19.888888:75774
-type: radiology/report
-purpose: research
-patient: givenName=John; surname=Doe; middleName=Jacob; dateOfBirth=1961-12-31; gender=M; postalCode=12345
-
---=-qOObRauiSrZluuB5HiV5sQ==
-Content-Type: application/pdf; name=report.pdf
-Content-Disposition: attachment; filename=report.pdf
-Content-Transfer-Encoding: base64
-
-RmFrZSBQREY=
-
---=-qOObRauiSrZluuB5HiV5sQ==--
\ No newline at end of file
diff --git a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtDefault b/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtDefault
deleted file mode 100644
index 0a13407..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtDefault
+++ /dev/null
@@ -1,34 +0,0 @@
-From: Joe
-Date: Sat, 08 Jul 2017 10:55:39 -0700
-Subject: Need more memory
-Message-Id: <34C73MNYE0U4.6M64WULRWE982@SSORDSK-JSHO02>
-To: "Jean"
-X-Direct-Context: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="=-zYT9GDXFXEDRaBnZ8nRxKw=="
-
---=-zYT9GDXFXEDRaBnZ8nRxKw==
-Content-Type: text/plain; charset=utf-8
-
-Simple Body
---=-zYT9GDXFXEDRaBnZ8nRxKw==
-Content-Type: text/plain
-Content-Disposition: attachment; filename=metadata.txt
-Content-ID: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-
-version: 1.0
-id: <2142848@direct.example.com>
-patient-id: 2.16.840.1.113883.19.999999:123456;
- 2.16.840.1.113883.19.888888:75774
-type: radiology/report
-purpose: research
-patient: givenName=John; surname=Doe; middleName=Jacob; dateOfBirth=1961-12-31; gender=M; postalCode=12345
-
---=-zYT9GDXFXEDRaBnZ8nRxKw==
-Content-Type: application/pdf; name=report.pdf
-Content-Disposition: attachment; filename=report.pdf
-Content-Transfer-Encoding: base64
-
-RmFrZSBQREY=
-
---=-zYT9GDXFXEDRaBnZ8nRxKw==--
\ No newline at end of file
diff --git a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtEightBit b/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtEightBit
deleted file mode 100644
index 96b82d4..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtEightBit
+++ /dev/null
@@ -1,35 +0,0 @@
-From: Joe
-Date: Sat, 08 Jul 2017 11:27:24 -0700
-Subject: Need more memory
-Message-Id: <34C73MNYE0U4.6M64WULRWE982@SSORDSK-JSHO02>
-To: Jean
-X-Direct-Context: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="=-ewU/G6Fvh7cR9G7PSTn7PA=="
-
---=-ewU/G6Fvh7cR9G7PSTn7PA==
-Content-Type: text/plain; charset=utf-8
-
-Simple Body
---=-ewU/G6Fvh7cR9G7PSTn7PA==
-Content-Type: text/plain
-Content-Disposition: attachment; filename=metadata.txt
-Content-Transfer-Encoding: 8bit
-Content-ID: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-
-version: 1.0
-id: <2142848@direct.example.com>
-patient-id: 2.16.840.1.113883.19.999999:123456;
- 2.16.840.1.113883.19.888888:75774
-type: radiology/report
-purpose: research
-patient: givenName=John; surname=Doe; middleName=Jacob; dateOfBirth=1961-12-31; gender=M; postalCode=12345
-
---=-ewU/G6Fvh7cR9G7PSTn7PA==
-Content-Type: application/pdf; name=report.pdf
-Content-Disposition: attachment; filename=report.pdf
-Content-Transfer-Encoding: base64
-
-RmFrZSBQREY=
-
---=-ewU/G6Fvh7cR9G7PSTn7PA==--
\ No newline at end of file
diff --git a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtQuotedPrintable b/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtQuotedPrintable
deleted file mode 100644
index 5d5d51e..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtQuotedPrintable
+++ /dev/null
@@ -1,36 +0,0 @@
-From: Joe
-Date: Sat, 08 Jul 2017 11:30:20 -0700
-Subject: Need more memory
-Message-Id: <34C73MNYE0U4.6M64WULRWE982@SSORDSK-JSHO02>
-To: Jean
-X-Direct-Context: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="=-1GehjmVTXFcPRv+tjzABIA=="
-
---=-1GehjmVTXFcPRv+tjzABIA==
-Content-Type: text/plain; charset=utf-8
-
-Simple Body
---=-1GehjmVTXFcPRv+tjzABIA==
-Content-Type: text/plain
-Content-Disposition: attachment; filename=metadata.txt
-Content-Transfer-Encoding: quoted-printable
-Content-ID: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-
-version: 1.0
-id: <2142848@direct.example.com>
-patient-id: 2.16.840.1.113883.19.999999:123456;
- 2.16.840.1.113883.19.888888:75774
-type: radiology/report
-purpose: research
-patient: givenName=3DJohn; surname=3DDoe; middleName=3DJacob; dateOfBi=
-rth=3D1961-12-31; gender=3DM; postalCode=3D12345
-
---=-1GehjmVTXFcPRv+tjzABIA==
-Content-Type: application/pdf; name=report.pdf
-Content-Disposition: attachment; filename=report.pdf
-Content-Transfer-Encoding: base64
-
-RmFrZSBQREY=
-
---=-1GehjmVTXFcPRv+tjzABIA==--
\ No newline at end of file
diff --git a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtSevenBit b/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtSevenBit
deleted file mode 100644
index 0a84e73..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtSevenBit
+++ /dev/null
@@ -1,35 +0,0 @@
-From: Joe
-Date: Sat, 08 Jul 2017 11:28:08 -0700
-Subject: Need more memory
-Message-Id: <34C73MNYE0U4.6M64WULRWE982@SSORDSK-JSHO02>
-To: Jean
-X-Direct-Context: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="=-jXtmR+ujFqamxwK5xzs6tA=="
-
---=-jXtmR+ujFqamxwK5xzs6tA==
-Content-Type: text/plain; charset=utf-8
-
-Simple Body
---=-jXtmR+ujFqamxwK5xzs6tA==
-Content-Type: text/plain
-Content-Disposition: attachment; filename=metadata.txt
-Content-Transfer-Encoding: 7bit
-Content-ID: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-
-version: 1.0
-id: <2142848@direct.example.com>
-patient-id: 2.16.840.1.113883.19.999999:123456;
- 2.16.840.1.113883.19.888888:75774
-type: radiology/report
-purpose: research
-patient: givenName=John; surname=Doe; middleName=Jacob; dateOfBirth=1961-12-31; gender=M; postalCode=12345
-
---=-jXtmR+ujFqamxwK5xzs6tA==
-Content-Type: application/pdf; name=report.pdf
-Content-Disposition: attachment; filename=report.pdf
-Content-Transfer-Encoding: base64
-
-RmFrZSBQREY=
-
---=-jXtmR+ujFqamxwK5xzs6tA==--
\ No newline at end of file
diff --git a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtUUEncode b/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtUUEncode
deleted file mode 100644
index e536a22..0000000
--- a/csharp/context.receiver.tests/ContextTestFiles/ContextSimple1.txtUUEncode
+++ /dev/null
@@ -1,38 +0,0 @@
-From: Joe
-Date: Sat, 08 Jul 2017 11:29:39 -0700
-Subject: Need more memory
-Message-Id: <34C73MNYE0U4.6M64WULRWE982@SSORDSK-JSHO02>
-To: Jean
-X-Direct-Context: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-MIME-Version: 1.0
-Content-Type: multipart/mixed; boundary="=-8Oju9BPJiN86XvhLX7iekQ=="
-
---=-8Oju9BPJiN86XvhLX7iekQ==
-Content-Type: text/plain; charset=utf-8
-
-Simple Body
---=-8Oju9BPJiN86XvhLX7iekQ==
-Content-Type: text/plain
-Content-Disposition: attachment; filename=metadata.txt
-Content-Transfer-Encoding: x-uuencode
-Content-ID: <2ff6eaec83894520bbb872e5671ff49e@hobo.lab>
-
-begin 0644 metadata.txt
-M=F5R2]R97!O", mimeMessage.Headers["X-Direct-Context"]);
- }
-
- [Theory]
- [InlineData("ContextTestFiles\\BadContext\\MDN.eml")]
- public void TestConstructContextIgnoreMDN(string file)
- {
- var smtpMessage = new CDOSmtpMessage(SmtpAgent.Extensions.LoadCDOMessage(file));
- var testFileName = SmtpAgent.Extensions.CreateUniqueFileName();
-
- var receiver = new LoopBackContext
- {
- TestFilename = testFileName
- };
-
- var settings = new PongContextSettings()
- {
- PickupFolder = TestPickupFolder
- };
-
- receiver.Settings = settings;
- Assert.True(receiver.Receive(smtpMessage));
-
- Assert.False(File.Exists(Path.Combine(
- settings.PickupFolder,
- testFileName)));
-
- }
-
- [Theory]
- [InlineData("ContextTestFiles\\BadContext\\DSN.eml")]
- public void TestConstructContextIgnorDSN(string file)
- {
- var smtpMessage = new CDOSmtpMessage(SmtpAgent.Extensions.LoadCDOMessage(file));
- var testFileName = SmtpAgent.Extensions.CreateUniqueFileName();
-
- var receiver = new LoopBackContext
- {
- TestFilename = testFileName
- };
-
- var settings = new PongContextSettings()
- {
- PickupFolder = TestPickupFolder
- };
-
- receiver.Settings = settings;
- Assert.True(receiver.Receive(smtpMessage));
-
- Assert.False(File.Exists(Path.Combine(
- settings.PickupFolder,
- testFileName)));
-
- }
-
-
- [Theory]
- [InlineData("ContextTestFiles\\BadContext\\ContextMissing.eml")]
- public void TestConstructContextNoContext(string file)
- {
- var smtpMessage = new CDOSmtpMessage(SmtpAgent.Extensions.LoadCDOMessage(file));
- var testFileName = SmtpAgent.Extensions.CreateUniqueFileName();
-
- var receiver = new LoopBackContext
- {
- TestFilename = testFileName
- };
-
- var settings = new PongContextSettings()
- {
- PickupFolder = TestPickupFolder
- };
-
- receiver.Settings = settings;
- Assert.True(receiver.Receive(smtpMessage));
-
- var resultMessage = MimeMessage.Load(
- Path.Combine(
- settings.PickupFolder,
- testFileName));
-
- Assert.Equal("Object reference not set to an instance of an object.", resultMessage.TextBody);
-
- }
-
- [Theory]
- [InlineData("ContextTestFiles\\BadContext\\NoDirectContextHeader.eml")]
- public void TestConstructContextNoContextId(string file)
- {
- var smtpMessage = new CDOSmtpMessage(SmtpAgent.Extensions.LoadCDOMessage(file));
- var testFileName = SmtpAgent.Extensions.CreateUniqueFileName();
-
- var receiver = new LoopBackContext
- {
- TestFilename = testFileName
- };
-
- var settings = new PongContextSettings()
- {
- PickupFolder = TestPickupFolder
- };
-
- receiver.Settings = settings;
- Assert.True(receiver.Receive(smtpMessage));
-
- var resultMessage = MimeMessage.Load(
- Path.Combine(
- settings.PickupFolder,
- testFileName));
-
- Assert.Equal("No Context found", resultMessage.TextBody);
- }
-
-
- [Theory]
- [InlineData("ContextTestFiles\\BadContext\\ContextUnparsableType.eml")]
- public void TestConstructContextUnParsableType(string file)
- {
- var smtpMessage = new CDOSmtpMessage(SmtpAgent.Extensions.LoadCDOMessage(file));
- var testFileName = SmtpAgent.Extensions.CreateUniqueFileName();
-
- var receiver = new LoopBackContext
- {
- TestFilename = testFileName
- };
-
- var settings = new PongContextSettings()
- {
- PickupFolder = TestPickupFolder
- };
-
- receiver.Settings = settings;
- Assert.True(receiver.Receive(smtpMessage));
-
- var resultMessage = MimeMessage.Load(
- Path.Combine(
- settings.PickupFolder,
- testFileName));
-
- Assert.Equal("Context Error=InvalidType", resultMessage.TextBody);
- }
-
- [Theory]
- [InlineData("ContextTestFiles\\BadContext\\ContextUnparsablePatient.eml")]
- public void TestConstructContextUnParsablePatient(string file)
- {
- var smtpMessage = new CDOSmtpMessage(SmtpAgent.Extensions.LoadCDOMessage(file));
- var testFileName = SmtpAgent.Extensions.CreateUniqueFileName();
-
- var receiver = new LoopBackContext
- {
- TestFilename = testFileName
- };
-
- var settings = new PongContextSettings()
- {
- PickupFolder = TestPickupFolder
- };
-
- receiver.Settings = settings;
- Assert.True(receiver.Receive(smtpMessage));
-
- var resultMessage = MimeMessage.Load(
- Path.Combine(
- settings.PickupFolder,
- testFileName));
-
- Assert.Equal("Context Error=InvalidPatient", resultMessage.TextBody);
- }
- }
-}
diff --git a/csharp/context.receiver.tests/context.loopback.receiver.tests.csproj b/csharp/context.receiver.tests/context.loopback.receiver.tests.csproj
deleted file mode 100644
index 34ec897..0000000
--- a/csharp/context.receiver.tests/context.loopback.receiver.tests.csproj
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
- net48
- Library
- Health.Direct.Context.Loopback.Receiver.tests
- Health.Direct.Context.Loopback.Receiver.tests
- false
-
-
-
- ..\build\libraries\smtpagent_com_interop\Interop.CDO.dll
- True
-
-
-
-
-
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/csharp/context.receiver/Extensions.cs b/csharp/context.receiver/Extensions.cs
deleted file mode 100644
index 5d07b97..0000000
--- a/csharp/context.receiver/Extensions.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using System.IO;
-
-namespace Health.Direct.Context.Loopback.Receiver
-{
- public static class Extensions
- {
- public static Stream ToStream(this string str)
- {
- var stream = new MemoryStream();
- var writer = new StreamWriter(stream);
- writer.Write(str);
- writer.Flush();
- stream.Position = 0;
- return stream;
- }
- }
-}
\ No newline at end of file
diff --git a/csharp/context.receiver/LoopBackContext.cs b/csharp/context.receiver/LoopBackContext.cs
deleted file mode 100644
index aff925f..0000000
--- a/csharp/context.receiver/LoopBackContext.cs
+++ /dev/null
@@ -1,186 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Net.Mail;
-using Health.Direct.Common.Container;
-using Health.Direct.Common.Diagnostics;
-using Health.Direct.Common.Mail.DSN;
-using Health.Direct.Common.Mail.Notifications;
-using Health.Direct.Common.Routing;
-using Health.Direct.SmtpAgent;
-using MimeKit;
-
-namespace Health.Direct.Context.Loopback.Receiver
-{
- ///
- /// Test only receiver.
- /// The LoopBack receiver receives messages to a specific email address and parses context if it exists. Then is rebuilds the context and sends a reply to the sender with the same context.
- /// The value of such a receiver is to allow testing partners to test a context implementation with with the .net RI context implemenation.
- ///
- /*
-
-
- LoopBackContext
-
- Health.Direct.Context.Loopback.Receiver.LoopBackContext, Health.Direct.Context.Loopback.Receiver
-
- c:\inetpub\mailroot\pickup
-
-
-
-
- ...
-
-
- */
- public class LoopBackContext : IReceiver, IPlugin
- {
- PongContextSettings m_settings;
-
- public PongContextSettings Settings
- {
- get
- {
- return m_settings;
- }
- set
- {
- if (value == null)
- {
- throw new SmtpAgentException(SmtpAgentError.InvalidPluginRoute);
- }
-
- value.Validate();
- m_settings = value;
- }
- }
-
- public void Init(PluginDefinition pluginDef)
- {
- Settings = pluginDef.DeserializeSettings();
- }
-
- public bool Receive(ISmtpMessage data)
- {
- var message = data.GetEnvelope().Message;
-
- if (message.IsDSN())
- {
- Log.For().Debug("Ignore DSN");
-
- return true;
- }
-
- if (message.IsMDN())
- {
- Log.For().Debug("Ignore MDN");
-
- return true;
- }
-
- var directMessage = MimeMessage.Load(message.ToString().ToStream());
-
- try
- {
- if (! directMessage.ContainsDirectContext())
- {
- Log.For()
- .Warn($"Message does not contain context. MessageId={directMessage.MessageId}");
-
- var dsnMessage = ReturnNoContextMessage(directMessage, @"No Context found");
- DropMessage(dsnMessage);
-
- return true;
- }
-
- var pongMessage = EchoContext.Process(directMessage);
- DropMessage(pongMessage);
-
- Log.For()
- .Info($"Message context pong response. MessageId={pongMessage.MessageId} RelatesToMessageId={message.ID}");
- }
- catch (Exception ex)
- {
- Log.For()
- .Error($"Message exception. MessageId={message.ID} :: Exception: {ex}");
-
- var dsnMessage = ReturnNoContextMessage(directMessage, ex.Message);
- DropMessage(dsnMessage);
-
- }
-
- return true;
- }
-
- private void DropMessage(MimeMessage pongMessage)
- {
- using (Stream stream = File.OpenWrite(
- Path.Combine(
- Settings.PickupFolder,
- TestFilename)))
- {
- pongMessage.WriteTo(stream);
- }
- }
-
- private void DropMessage(DSNMessage dsnMessage)
- {
- using (Stream stream = File.OpenWrite(
- Path.Combine(
- Settings.PickupFolder,
- TestFilename)))
- {
- dsnMessage.Save(stream);
- }
- }
-
- private DSNMessage ReturnNoContextMessage(MimeMessage directMessage, string bodyMessage)
- {
- var to = new MailAddress(directMessage.From.Mailboxes.Single().ToString());
-
- var perMessage = new DSNPerMessage(
- to.Host,
- directMessage.MessageId);
-
- var dsnPerRecipients = new List();
-
- foreach (var mailboxAddress in directMessage.To.Mailboxes)
- {
- var dsnPerRecipient = new DSNPerRecipient(
- DSNStandard.DSNAction.Failed,
- DSNStandard.DSNStatus.Permanent,
- "3.3",
- new MailAddress(mailboxAddress.ToString())
- );
-
- dsnPerRecipients.Add(dsnPerRecipient);
- }
-
- var dsn = new DSN(perMessage, dsnPerRecipients);
- dsn.Explanation = bodyMessage;
- var postMaster = new MailAddress("Postmaster@" + to.Host);
-
- var statusMessage = new DSNMessage(to.Address, postMaster.Address, dsn );
-
- return statusMessage;
- }
-
- private string m_fileName;
-
- ///
- /// Set a known filename per test
- ///
- public string TestFilename
- {
- get
- {
- var fileName = m_fileName ?? SmtpAgent.Extensions.CreateUniqueFileName();
- m_fileName = null;
-
- return fileName;
- }
- set { m_fileName = value; }
- }
- }
-}
diff --git a/csharp/context.receiver/PongContextSettings.cs b/csharp/context.receiver/PongContextSettings.cs
deleted file mode 100644
index 0dc34fe..0000000
--- a/csharp/context.receiver/PongContextSettings.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using System.IO;
-using System.Xml.Serialization;
-using Health.Direct.SmtpAgent;
-
-namespace Health.Direct.Context.Loopback.Receiver
-{
- [XmlType("PongContextSettings")]
- public class PongContextSettings
- {
- public PongContextSettings()
- {
- }
-
- ///
- /// Smtp pickup folder
- ///
- [XmlElement("PickupFolder")]
- public string PickupFolder
- {
- get;
- set;
- }
-
- [XmlIgnore]
- internal bool HasPickupFolder
- {
- get
- {
- return !(string.IsNullOrEmpty(PickupFolder));
- }
- }
-
- public void Validate()
- {
- if (!this.HasPickupFolder)
- {
- return;
- }
-
- if (!Directory.Exists(PickupFolder))
- {
- throw new SmtpAgentException(SmtpAgentError.MailPickupFolderDoesNotExist);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/csharp/context.receiver/Properties/AssemblyInfo.cs b/csharp/context.receiver/Properties/AssemblyInfo.cs
deleted file mode 100644
index 8338130..0000000
--- a/csharp/context.receiver/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("context.receiver")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("53ff4765-58b8-4747-93e7-6ee9416e2036")]
-
diff --git a/csharp/context.receiver/README.md b/csharp/context.receiver/README.md
deleted file mode 100644
index 365b2be..0000000
--- a/csharp/context.receiver/README.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# Health.Direct.Context.Loopback.Receiver
-
-## What is Health.Direct.Context.Loopback.Receiver
-This is a plugin receiver test receiver.
-The LoopBack receiver receives messages and parses context if it exists. Then is rebuilds the context and sends a reply to the sender with the same context. The value of such a receiver is to allow testing partners to test a context implementation with with the .net RI context implemenation. If no context exists a failure DSN with a description is returned. All other failures will result failure DSN's with the best attempt to indicated the failure.
-
-
-## Licesnse Information
-
-Copyright (c) 2010-2017, Direct Project
- All rights reserved.
-
- Authors:
- Joseph Shook Joseph.Shook@Surescripts.com
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-## Installing
-Included with Direct-1.3.0.7-NET45_Beta installer.
-
-### Configuring
-Ensure the following two assemblies are in the Direct Project .NET Gateway folder, typically in C:\Program Files\Direct Project .NET Gateway
-
-- Health.Direct.Context.Loopback.Receiver.dll
-- MimeKitLite.dll
-
-In SmtpAgentConfig.xml add the Health.Direct.Context.Loopback.Receiver plugin reciever to the IncomingRoutes section. Notice the AddressType is LoopBackContext.
-
-```xml
-
-
- SMTP
- C:\inetpub\mailroot\Gateway\incoming
-
-
- LoopBackContext
-
- Health.Direct.Context.Loopback.Receiver.LoopBackContext, Health.Direct.Context.Loopback.Receiver
-
- c:\inetpub\mailroot\pickup
-
-
-
-
-```
-
-Now run the cli tool, "ConfigConsole.exe". The following two commands will add a route for only the PingPong@Direct.North.Hobo.Lab email address to the Health.Direct.Context.Loopback.Receiver plugin receiver and set it's status to enabled. After this restart with IISReset.
-
-Address_Add PingPong@Direct.North.Hobo.Lab LoopBackContext
-Address_Status_Set PingPong@Direct.North.Hobo.Lab Enabled
diff --git a/csharp/context.receiver/context.loopback.receiver.csproj b/csharp/context.receiver/context.loopback.receiver.csproj
deleted file mode 100644
index f8ea153..0000000
--- a/csharp/context.receiver/context.loopback.receiver.csproj
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
- net48
- Library
- Health.Direct.Context.Loopback.Receiver
- Health.Direct.Context.Loopback.Receiver
- false
-
-
-
-
-
-
-
-
- Properties\GlobalAssemblyInfo.cs
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/csharp/hsmCryptographer/HsmCryptographer.csproj b/csharp/hsmCryptographer/HsmCryptographer.csproj
index 39133dc..222509e 100644
--- a/csharp/hsmCryptographer/HsmCryptographer.csproj
+++ b/csharp/hsmCryptographer/HsmCryptographer.csproj
@@ -24,7 +24,7 @@
-
+
\ No newline at end of file
diff --git a/csharp/policy/policy.csproj b/csharp/policy/policy.csproj
index 80135b0..c015c72 100644
--- a/csharp/policy/policy.csproj
+++ b/csharp/policy/policy.csproj
@@ -28,6 +28,6 @@
-
+
\ No newline at end of file
diff --git a/csharp/tools/admin.console/App.config b/csharp/tools/admin.console/App.config
index 0a9c00b..041c3cc 100644
--- a/csharp/tools/admin.console/App.config
+++ b/csharp/tools/admin.console/App.config
@@ -17,7 +17,7 @@
-
+
diff --git a/csharp/tools/trust.bundler/bundle/trust.bundle.csproj b/csharp/tools/trust.bundler/bundle/trust.bundle.csproj
index fb87a35..7f5395d 100644
--- a/csharp/tools/trust.bundler/bundle/trust.bundle.csproj
+++ b/csharp/tools/trust.bundler/bundle/trust.bundle.csproj
@@ -23,7 +23,7 @@
-
+
diff --git a/csharp/tools/trust.bundler/unittests/bundle.tests/trust.bundle.tests.csproj b/csharp/tools/trust.bundler/unittests/bundle.tests/trust.bundle.tests.csproj
index 8f2ab53..823c2bd 100644
--- a/csharp/tools/trust.bundler/unittests/bundle.tests/trust.bundle.tests.csproj
+++ b/csharp/tools/trust.bundler/unittests/bundle.tests/trust.bundle.tests.csproj
@@ -32,7 +32,7 @@
-
+
diff --git a/csharp/unittests/agent/BasicAgentTests.cs b/csharp/unittests/agent/BasicAgentTests.cs
index ab2bbce..85dc657 100644
--- a/csharp/unittests/agent/BasicAgentTests.cs
+++ b/csharp/unittests/agent/BasicAgentTests.cs
@@ -90,7 +90,7 @@ public void BuildOutgoingFile()
public void TestOutgoing(string fileName)
{
m_tester.AgentA.Cryptographer.EncryptionAlgorithm = EncryptionAlgorithm.AES128;
- m_tester.AgentA.Cryptographer.DigestAlgorithm = DigestAlgorithm.SHA1;
+ m_tester.AgentA.Cryptographer.DigestAlgorithm = DigestAlgorithm.SHA256;
OutgoingMessage message = null;
Assert.Null(Record.Exception(() => message = m_tester.ProcessOutgoingFile(fileName)));
@@ -121,7 +121,8 @@ public void TestOutgoingUntrustedFully(string fileName)
//
// All recipients are untrusted. The agent should reject the message completely
//
- Assert.Throws(() => m_tester.ProcessOutgoingFileToString(fileName));
+ var ex = Assert.Throws(() => m_tester.ProcessOutgoingFileToString(fileName));
+ Assert.False(string.IsNullOrEmpty(ex.Message));
}
//
@@ -131,6 +132,7 @@ public void TestOutgoingUntrustedFully(string fileName)
[MemberData("OutgoingUntrustedFiles")]
public void OutgoingUntrusted(string fileName)
{
+ m_tester.AgentA.Cryptographer.DigestAlgorithm = DigestAlgorithm.SHA256;
OutgoingMessage outgoing = m_tester.ProcessOutgoingFile(fileName);
Assert.True(outgoing.RejectedRecipients.Count > 0);
}
@@ -143,7 +145,8 @@ public void TestWithMessageObjects()
{
var message = MimeSerializer.Default.Deserialize(m_tester.ReadMessageText("simple.eml"));
- var outgoing = new OutgoingMessage(message);
+ var outgoing = new OutgoingMessage(message);
+ m_tester.AgentA.Cryptographer.DigestAlgorithm = DigestAlgorithm.SHA256;
outgoing = m_tester.AgentA.ProcessOutgoing(outgoing);
Assert.True(outgoing.Message.HasHeader(MailStandard.Headers.Date));
@@ -192,6 +195,14 @@ public void TestEndToEnd(string fileName, EncryptionAlgorithm encryptionAlgorith
m_tester.AgentA.Cryptographer.DigestAlgorithm = digestAlgorithm;
m_tester.AgentB.Cryptographer.EncryptionAlgorithm = encryptionAlgorithm;
m_tester.AgentB.Cryptographer.DigestAlgorithm = digestAlgorithm;
+
+ if (digestAlgorithm == DigestAlgorithm.SHA1)
+ {
+ var ex = Assert.Throws(() => m_tester.TestEndToEndFile(fileName));
+ Assert.Equal("SHA1 DigestAlgorithm is not supported.", ex.Message);
+ return;
+ }
+
m_tester.TestEndToEndFile(fileName);
}
@@ -204,6 +215,14 @@ public void TestEndToEndCompat(string fileName, EncryptionAlgorithm encryptionAl
{
m_tester.AgentA.Cryptographer.EncryptionAlgorithm = encryptionAlgorithm;
m_tester.AgentA.Cryptographer.DigestAlgorithm = digestAlgorithm;
+
+ if (digestAlgorithm == DigestAlgorithm.SHA1)
+ {
+ var ex = Assert.Throws(() => m_tester.TestEndToEndFile(fileName));
+ Assert.Equal("SHA1 DigestAlgorithm is not supported.", ex.Message);
+ return;
+ }
+
m_tester.TestEndToEndFile(fileName);
}
diff --git a/csharp/unittests/agent/CryptographerTests.cs b/csharp/unittests/agent/CryptographerTests.cs
index 01b7b0d..01158ef 100644
--- a/csharp/unittests/agent/CryptographerTests.cs
+++ b/csharp/unittests/agent/CryptographerTests.cs
@@ -82,10 +82,17 @@ public void TestSignatureOIDs(DigestAlgorithm algo)
{
string messageText = m_tester.ReadMessageText("simple.eml");
m_cryptographer.DigestAlgorithm = algo;
- SignedCms signedData = null;
-
- signedData = m_cryptographer.CreateSignature(Encoding.ASCII.GetBytes(messageText), new X509Certificate2Collection(m_cert));
-
+
+ if (algo == DigestAlgorithm.SHA1)
+ {
+ var ex = Assert.Throws(() =>
+ m_cryptographer.CreateSignature(Encoding.ASCII.GetBytes(messageText), new X509Certificate2Collection(m_cert))
+ );
+ Assert.Equal("SHA1 DigestAlgorithm is not supported.", ex.Message);
+ return;
+ }
+
+ SignedCms signedData = m_cryptographer.CreateSignature(Encoding.ASCII.GetBytes(messageText), new X509Certificate2Collection(m_cert));
Assert.True(signedData.SignerInfos.Count == 1);
Assert.True(signedData.SignerInfos[0].DigestAlgorithm.Value == SMIMECryptographer.ToDigestAlgorithmOid(algo).Value);
}
@@ -96,6 +103,7 @@ public void TestDispositionHeaders()
string messageText = m_tester.ReadMessageText("simple.eml");
Message message = MimeSerializer.Default.Deserialize(messageText);
+ m_cryptographer.DigestAlgorithm = DigestAlgorithm.SHA256;
SignedEntity signedEntity = m_cryptographer.Sign(message, m_cert);
string disposition = signedEntity.Signature.ContentDisposition;
Assert.True(!string.IsNullOrEmpty(disposition));
diff --git a/csharp/unittests/common/Certificates/CertificateFacts.cs b/csharp/unittests/common/Certificates/CertificateFacts.cs
index 4dd74e3..69a4686 100644
--- a/csharp/unittests/common/Certificates/CertificateFacts.cs
+++ b/csharp/unittests/common/Certificates/CertificateFacts.cs
@@ -29,7 +29,7 @@ public void AnchorMetadataSerialize()
{
AnchorMetadata metadata = new AnchorMetadata();
metadata.RequiredOids = new Oid[] {
- new Oid(SMIMECryptographer.CryptoOids.SHA1.Value, "SHA1 Digest Algorithm"),
+ // new Oid(SMIMECryptographer.CryptoOids.SHA1.Value, "SHA1 Digest Algorithm"),
new Oid(SMIMECryptographer.CryptoOids.SHA256.Value, "SHA256 Digest Algorithm"),
};
metadata.BundleSource = "Toby's bundle";
diff --git a/csharp/unittests/common/Cryptography/OaepEncryptionFacts.cs b/csharp/unittests/common/Cryptography/OaepEncryptionFacts.cs
new file mode 100644
index 0000000..333d57f
--- /dev/null
+++ b/csharp/unittests/common/Cryptography/OaepEncryptionFacts.cs
@@ -0,0 +1,219 @@
+/*
+ Copyright (c) 2025, Direct Project
+ All rights reserved.
+
+ Authors:
+ GitHub Copilot
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of The Direct Project (directproject.org) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+using System;
+using System.Net.Mime;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Pkcs;
+using System.Security.Cryptography.X509Certificates;
+using Health.Direct.Common.Cryptography;
+using Health.Direct.Common.Mime;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Health.Direct.Common.Tests.Cryptography
+{
+ public class OaepEncryptionFacts : TestingBase
+ {
+ private readonly ITestOutputHelper _output;
+
+ public OaepEncryptionFacts(ITestOutputHelper output)
+ {
+ _output = output;
+ }
+
+ private static X509Certificate2 GenerateSelfSignedEnciphermentCert(string subjectName)
+ {
+ using (var rsa = RSA.Create(2048))
+ {
+ var req = new CertificateRequest(
+ new X500DistinguishedName($"CN={subjectName}"),
+ rsa,
+ HashAlgorithmName.SHA256,
+ RSASignaturePadding.Pkcs1);
+
+ var keyUsage = new X509KeyUsageExtension(
+ X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DataEncipherment,
+ critical: true);
+ req.CertificateExtensions.Add(keyUsage);
+
+ var basicConstraints = new X509BasicConstraintsExtension(false, false, 0, true);
+ req.CertificateExtensions.Add(basicConstraints);
+
+ var notBefore = DateTimeOffset.UtcNow.AddDays(-1);
+ var notAfter = DateTimeOffset.UtcNow.AddYears(3);
+ var cert = req.CreateSelfSigned(notBefore, notAfter);
+
+ return cert;
+ }
+ }
+
+ private static MimeEntity CreatePlainTextEntity(string text)
+ {
+ return new MimeEntity
+ {
+ ContentType = MimeStandard.MediaType.TextPlain,
+ ContentTransferEncoding = TransferEncoding.SevenBit.AsString(),
+ Body = new Body(text)
+ };
+ }
+
+
+ [Fact]
+ public void BcCryptographer_OAEP_Uses_SHA256_Params()
+ {
+ var recipient = GenerateSelfSignedEnciphermentCert("OAEP-SHA256-Params");
+ var entity = CreatePlainTextEntity("Hello OAEP SHA256");
+
+ var bc = new SMIMECryptographer();
+ var encryptedEntity = bc.Encrypt(entity, recipient);
+ var encryptedBytes = bc.GetEncryptedBytes(encryptedEntity);
+
+ var asn1 = Org.BouncyCastle.Asn1.Asn1Object.FromByteArray(encryptedBytes);
+ var contentInfo = Org.BouncyCastle.Asn1.Cms.ContentInfo.GetInstance(asn1);
+ var envelopedData = Org.BouncyCastle.Asn1.Cms.EnvelopedData.GetInstance(contentInfo.Content);
+ var recipientInfo = Org.BouncyCastle.Asn1.Cms.RecipientInfo.GetInstance(envelopedData.RecipientInfos[0]);
+ var ktri = Org.BouncyCastle.Asn1.Cms.KeyTransRecipientInfo.GetInstance(recipientInfo.Info);
+ var algId = Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier.GetInstance(ktri.KeyEncryptionAlgorithm);
+
+ Assert.Equal(PkcsObjectIdentifiers.IdRsaesOaep.Id, algId.Algorithm.Id);
+
+ var oaep = Org.BouncyCastle.Asn1.Pkcs.RsaesOaepParameters.GetInstance(algId.Parameters);
+ var hashAlg = Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier.GetInstance(oaep.HashAlgorithm);
+ var mgfAlg = Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier.GetInstance(oaep.MaskGenAlgorithm);
+
+ _output.WriteLine($"Bc OAEP Hash: {hashAlg.Algorithm.Id}");
+ _output.WriteLine($"Bc OAEP MGF: {mgfAlg.Algorithm.Id}");
+
+ Assert.Equal(NistObjectIdentifiers.IdSha256.Id, hashAlg.Algorithm.Id);
+ // MGF1 OID with SHA-256 parameter
+ Assert.Equal(PkcsObjectIdentifiers.IdMgf1.Id, mgfAlg.Algorithm.Id);
+ var mgfParam = Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier.GetInstance(mgfAlg.Parameters);
+ Assert.Equal(NistObjectIdentifiers.IdSha256.Id, mgfParam.Algorithm.Id);
+ }
+
+ [Fact]
+ public void DefaultSmimeCryptographer_OAEP_Uses_SHA1()
+ {
+ var recipient = GenerateSelfSignedEnciphermentCert("Default-OAEP-Params");
+ var entity = CreatePlainTextEntity("Hello Default OAEP Params");
+
+ var def = LegacySMIMECryptographer.Default;
+ var encryptedEntity = def.Encrypt(entity, recipient);
+ var encryptedBytes = def.GetEncryptedBytes(encryptedEntity);
+
+ var asn1 = Org.BouncyCastle.Asn1.Asn1Object.FromByteArray(encryptedBytes);
+ var contentInfo = Org.BouncyCastle.Asn1.Cms.ContentInfo.GetInstance(asn1);
+ var envelopedData = Org.BouncyCastle.Asn1.Cms.EnvelopedData.GetInstance(contentInfo.Content);
+ var recipientInfo = Org.BouncyCastle.Asn1.Cms.RecipientInfo.GetInstance(envelopedData.RecipientInfos[0]);
+ var ktri = Org.BouncyCastle.Asn1.Cms.KeyTransRecipientInfo.GetInstance(recipientInfo.Info);
+ var algId = Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier.GetInstance(ktri.KeyEncryptionAlgorithm);
+
+ var keyAlgOid = algId.Algorithm.Id;
+ _output.WriteLine($"Default KeyEncAlg OID: {keyAlgOid}");
+
+
+ Assert.Equal(PkcsObjectIdentifiers.IdRsaesOaep.Id, keyAlgOid);
+ var oaep = Org.BouncyCastle.Asn1.Pkcs.RsaesOaepParameters.GetInstance(algId.Parameters);
+ var hashAlg = Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier.GetInstance(oaep.HashAlgorithm);
+ var mgfAlg = Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier.GetInstance(oaep.MaskGenAlgorithm);
+
+ _output.WriteLine($"Default OAEP Hash: {hashAlg.Algorithm.Id}");
+ _output.WriteLine($"Default OAEP MGF: {mgfAlg.Algorithm.Id}");
+
+ // Expect legacy SHA-1-based OAEP if using OAEP
+ Assert.Equal("1.3.14.3.2.26", hashAlg.Algorithm.Id); // SHA-1
+ Assert.Equal(PkcsObjectIdentifiers.IdMgf1.Id, mgfAlg.Algorithm.Id);
+ var mgfParam = Org.BouncyCastle.Asn1.X509.AlgorithmIdentifier.GetInstance(mgfAlg.Parameters);
+ Assert.Equal("1.3.14.3.2.26", mgfParam.Algorithm.Id); // SHA-1
+ }
+
+ ///
+ /// Encrypt with BouncyCastle OAEP-SHA256, decrypt with Default SMIMECryptographer
+ ///
+ [Fact]
+ public void BcEncrypt_OAEP_SHA256_DefaultDecrypt_Roundtrip()
+ {
+ var recipient = GenerateSelfSignedEnciphermentCert("OAEP-SHA256-Roundtrip");
+ var entity = CreatePlainTextEntity("Hello OAEP Roundtrip");
+
+ var bc = new SMIMECryptographer();
+ var encryptedEntity = bc.Encrypt(entity, recipient);
+ var encryptedBytes = bc.GetEncryptedBytes(encryptedEntity);
+
+ var cms = new EnvelopedCms();
+ cms.Decode(encryptedBytes);
+ Assert.True(cms.RecipientInfos.Count > 0, "No recipient infos present");
+ var keyAlgOid = cms.RecipientInfos[0].KeyEncryptionAlgorithm.Oid.Value;
+ _output.WriteLine($"Encrypted with Bc; KeyEncAlg OID: {keyAlgOid} (OAEP expected)");
+
+ var ex = Record.Exception(() =>
+ {
+ var decrypted = LegacySMIMECryptographer.Default.DecryptEntity(encryptedBytes, recipient);
+ Assert.NotNull(decrypted);
+ Assert.Equal("Hello OAEP Roundtrip", decrypted.Body.Text);
+ });
+
+ if (ex != null)
+ {
+ _output.WriteLine($"Default Decrypt failed: {ex.GetType().Name} - {ex.Message}");
+ Assert.Fail("Default SMIMECryptographer could not decrypt OAEP-SHA256 wrapped content key.");
+ }
+ else
+ {
+ _output.WriteLine("Default Decrypt succeeded with OAEP-SHA256.");
+ }
+ }
+
+ ///
+ /// Encrypt with Default SMIMECryptographer OAEP-SHA1, decrypt with BouncyCastle
+ ///
+ [Fact]
+ public void SMIMECryptographer_OAEP_SHA1_DefaultDecrypt_BCSMIMECryptographer_Roundtrip()
+ {
+ var recipient = GenerateSelfSignedEnciphermentCert("OAEP-SHA256-Roundtrip");
+ var entity = CreatePlainTextEntity("Hello OAEP Roundtrip");
+
+ var sc = LegacySMIMECryptographer.Default;
+ var encryptedEntity = sc.Encrypt(entity, recipient);
+ var encryptedBytes = sc.GetEncryptedBytes(encryptedEntity);
+
+ var cms = new EnvelopedCms();
+ cms.Decode(encryptedBytes);
+ Assert.True(cms.RecipientInfos.Count > 0, "No recipient infos present");
+ var keyAlgOid = cms.RecipientInfos[0].KeyEncryptionAlgorithm.Oid.Value;
+ _output.WriteLine($"Encrypted with Sc; KeyEncAlg OID: {keyAlgOid} (OAEP expected)");
+
+ var ex = Record.Exception(() =>
+ {
+ var decrypted = new SMIMECryptographer().DecryptEntity(encryptedBytes, recipient);
+ Assert.NotNull(decrypted);
+ Assert.Equal("Hello OAEP Roundtrip", decrypted.Body.Text);
+ });
+
+ if (ex != null)
+ {
+ _output.WriteLine($"Default Decrypt failed: {ex.GetType().Name} - {ex.Message}");
+ Assert.Fail("Default SMIMECryptographer could not decrypt OAEP-SHA256 wrapped content key.");
+ }
+ else
+ {
+ _output.WriteLine("Default Decrypt succeeded with OAEP-SHA1.");
+ }
+ }
+ }
+}
diff --git a/csharp/unittests/common/common.tests.csproj b/csharp/unittests/common/common.tests.csproj
index b8156d8..639d38d 100644
--- a/csharp/unittests/common/common.tests.csproj
+++ b/csharp/unittests/common/common.tests.csproj
@@ -93,7 +93,7 @@
-
+
diff --git a/csharp/unittests/hsmCryptographer.tests/hsmCryptographer.tests.csproj b/csharp/unittests/hsmCryptographer.tests/hsmCryptographer.tests.csproj
index eb443a0..18b2b56 100644
--- a/csharp/unittests/hsmCryptographer.tests/hsmCryptographer.tests.csproj
+++ b/csharp/unittests/hsmCryptographer.tests/hsmCryptographer.tests.csproj
@@ -42,7 +42,7 @@
-
+
diff --git a/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentAuditConfig.xml b/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentAuditConfig.xml
index 3fcfa7b..1ded06c 100644
--- a/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentAuditConfig.xml
+++ b/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentAuditConfig.xml
@@ -22,7 +22,7 @@
AES128
- SHA1
+ SHA256
diff --git a/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentAuditConfig_BadAuditor_Defaults.xml b/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentAuditConfig_BadAuditor_Defaults.xml
index 6e001a1..b2eddf9 100644
--- a/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentAuditConfig_BadAuditor_Defaults.xml
+++ b/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentAuditConfig_BadAuditor_Defaults.xml
@@ -22,7 +22,7 @@
AES128
- SHA1
+ SHA256
diff --git a/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentConfig.xml b/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentConfig.xml
index 6f0a40f..32c8a29 100644
--- a/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentConfig.xml
+++ b/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentConfig.xml
@@ -22,7 +22,7 @@
AES128
- SHA1
+ SHA256
diff --git a/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentConfigWithCertPolicy.xml b/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentConfigWithCertPolicy.xml
index 8042acf..172e79d 100644
--- a/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentConfigWithCertPolicy.xml
+++ b/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentConfigWithCertPolicy.xml
@@ -22,7 +22,7 @@
AES128
- SHA1
+ SHA256
diff --git a/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentConfig_InternalRelay.xml b/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentConfig_InternalRelay.xml
index 3e56cb7..1540714 100644
--- a/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentConfig_InternalRelay.xml
+++ b/csharp/unittests/smtpAgent/SmtpAgentTestFiles/TestSmtpAgentConfig_InternalRelay.xml
@@ -16,7 +16,7 @@
AES128
- SHA1
+ SHA256