DKIM is set up for a domain in Office365. A .Net application (currently MVC 4) sends Email through an O365 connector to external parties.
We'd like to sign these using DKIM as well.
I'm not quite clear about the entire process.
MimeKit's Documentation is reasonably clear. I suppose I can use any pub/priv key generator such as Putty to generate a keypair? I would then store the private key in a way that the C# application can read it into
var signer = new DkimSigner ("privatekey.pem") {
SignatureAlgorithm = DkimSignatureAlgorithm.RsaSha1,
AgentOrUserIdentifier = "#eng.example.com",
QueryMethod = "dns/txt",
};
The public key will be published as a DNS record for my domain. Unfortunately, the Office 365 documentation isn't all too clear on the exact how.
Summary Questions
What exactly goes into AgentOrUserIdentifier, if my system sends with the address application#example.org?
How exactly would I publish my generated public key to Office 365?
Any enlightening summary would be greatly appreciated, thanks.
I'll accept #jstedfast's answer (although without really understanding it).
Just in case anyone else is struggling with this, here's the complete walk-through:
Get a public/private key pair. You can use Puttygen or openssl directly, but it's easier to use (oh had I only known beforehand) sth like https://port25.com/dkim-wizard/
Specify your domain name (example.org here) and a "selector" - this could be your application name ("greatapp"). This selector will be the TXT record for the public key in DNS.
Create an additional DNS (TXT) record; leave the Office365 ones intact. Since they rotate keys regularly you want an additional record that you can control.
greatapp._domainkey.example.org IN TXT
"k=rsa\; p=here goes the stuff between -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY-----", so e.g.
"k=rsa\; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhvIwVBomj+dx2CEBbY/ZpSdnQK2Omx6ZNyHsuvC3MMJYNLQ069ajuJo5FP......."
Copy the private key to a file, or use it in your code directly. MimeKit either expects a file or a stream, so for the quick & dirty example here I'm using a string:
var mail = new MimeMessage();
mail.From.Add(new MailboxAddress("Justin Case", "justin#example.org"));
mail.To.Add(new MailboxAddress("Candy Barr", "candy#example.org"));
... subject etc
var privateKey = #"-----BEGIN RSA PRIVATE KEY-----......";
var privateKeyStream = new MemoryStream(Encoding.Default.GetBytes(privateKey));
mail.Sign(new DkimSigner(privateKeyStream, "example.org", "greatapp", DkimSignatureAlgorithm.RsaSha256), new HeaderId[] { HeaderId.From, HeaderId.Subject }, DkimCanonicalizationAlgorithm.Simple, DkimCanonicalizationAlgorithm.Simple);
... Connect client and send.
Thanks to jstedfast something as awesome as MailKit/MimeKit exists, don't forget to donate.
From rfc6376, section 2.6:
2.6. Agent or User Identifier (AUID)
A single identifier that refers to the agent or user on behalf of
whom the Signing Domain Identifier (SDID) has taken responsibility.
The AUID comprises a domain name and an optional <local-part>. The
domain name is the same as that used for the SDID or is a subdomain
of it. For DKIM processing, the domain name portion of the AUID has
only basic domain name semantics; any possible owner-specific
semantics are outside the scope of DKIM. It is specified in
Section 3.5.
Note that acceptable values for the AUID may be constrained via a
flag in the public-key record. (See Section 3.6.1.)
Related
I have a USB token which have a X509Certificate/XmlDigSig on it protected by a pin code. (im not exactly sure what is that)
i am currently using the vendor's Library to sign bytes but the vendor code pops up a windows dialog requesting a pin code.
i would like to avoid the pin code popup and supply the code directly as part of my c#.
public byte[] GetSignedByteArr(byte[] request)
{
var signAndVerifyUtil = new GovIL.SignAndVerifySDK.Providers.XmlDigSig();
var signed = signAndVerifyUtil.Sign(request, GetSignParameters());
return signed.ContentInfo.signedContent;
}
public SignParameters GetSignParameters()
{
return new SignParameters(new CryptoSignatureInfo(Certificate.GetSignCertificateBySerialNumber("xxxxxxxxxxxxxxxxxxxxxxx", true)));
}
thanks to all!
X509Certificate is public certificate which used by SSL/TLS. it's based on public key & private key. every one can encrypt the content using the public key but only people who own the private key can decrypt and verify the content.
So we don't know any information about your hardware key and it's SDK.
If you use it to sign something like Cert Authorities the requested pin is private key and by the best practice you really avoid to hardcode it in your code or database, it's very risky. In this scenario maybe SDK developers forcing users to don't store the private key by exposing API which show dialog to user and don't let the developer to load key from memory or file.
You can do the pin input by simulation trick but again it's so forget it ;)
If you use the SDK for encrypting or signing something that verify somewhere else you use public key and it's wise that SDK developers let you to it by API calls, but we don't know anything about your library, so contact the SDK vendor.
I have to exchange encrypted & signed e-mails with some business partners. Specific algorithms are required, such as :
for signature, RSASSA-PSS as the signature algorithm,
for encryption, RSAES-OAEP for key encryption & AES-128 CBC for content encryption
I am having troubles setting this up with Mailkit, and actually behind it MailKit & BouncyCastle.
Here is where I am so far :
For decryption & signature verification
Decrypting the body is ok, I do it by using a WindowsSecureMimeContext, after setting up my private key in the windows store
Verifying the signature is not ok
case MultipartSigned signedBody:
try
{
using (var ctx = new WindowsSecureMimeContext(StoreLocation.LocalMachine))
{
var verifiedData = signedBody.Verify(ctx);
return verifiedData.All(o => o.Verify());
}
}
catch (Exception e)
{
throw new Exception("Error during signature verification.", e);
}
Certificate of the sender is signed by a common CA, so I'm using again a WindowsSecureMimeContext, but verifiedData.All(o => o.Verify()) throws a DigitalSignatureVerifyException ("Failed to verify digital signature: Unknown error "-1073700864".")
For signature and encryption
Well, that looks tough...
For signature, it seems that I need somewhere a BouncyCastle's PssSigner, which I can get by overriding DkimSigner, and especially the DigestSigner property
class TestSigner : DkimSigner
{
protected TestSigner(string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256)
: base(domain, selector, algorithm)
{
}
public TestSigner(AsymmetricKeyParameter key, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256)
: base(key, domain, selector, algorithm)
{
}
public TestSigner(string fileName, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256)
: base(fileName, domain, selector, algorithm)
{
}
public TestSigner(Stream stream, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256)
: base(stream, domain, selector, algorithm)
{
}
public override ISigner DigestSigner => SignerUtilities.GetSigner(PkcsObjectIdentifiers.IdRsassaPss);
}
However I don't know exactly where to use it. Maybe when using MimeMessage.Sign(), however I am a bit lost with the required parameters in the signature of the method
For encryption, I could find my way up to a RsaesOaepParameters in BouncyCastle's library, by I can't figure out how to use it.
Any help by a mail expert would be much appreciated !
A DkimSigner is used for generating DKIM signatures which is not what you want to do. DKIM signatures have nothing to do with S/MIME.
S/MIME Signing using RSASSA-PSS
Currently, the WindowsSecureMimeContext (which uses System.Security as the backend) does NOT support RSASSA-PSS, so you'll need to use the Bouncy Castle backend.
To use the Bouncy Castle backend, you will need to use one of the BouncyCastleSecureMimeContext derivatives (or create your own). As a temporary solution for playing around with things, I might suggest using the TemporarySecureMimeContext, but for long-term use, I would suggest looking at the DefaultSecureMimeContext - although you will still probably want to subclass that to get it working.
Now that you are using a Bouncy Castle S/MIME context, in order to specify that you want to use RSASSA-PSS padding, you'll need to use the APIs that take a CmsSigner parameter such as MultipartSigned.Create() or ApplicationPkcs7Mime.Sign().
Here's an example code snippet:
var signer = new CmsSigner ("certificate.pfx", "password");
// Specify that we want to use RSASSA-PSS
signer.RsaSignaturePaddingScheme = RsaSignaturePaddingScheme.Pss;
// Sign the message body
var signed = MultipartSigned.Create (ctx, signer, message.Body);
// replace the message body with the signed body
message.Body = signed;
S/MIME Encryption Using AES-128 CBC (or any other specific algorithm) with RSAES-OAEP
First, to encrypt using S/MIME, you'll want to use one of the ApplicationPkcs7Mime.Encrypt() methods.[2]
The Encrypt() methods that take a MailboxAddress will automatically create the CmsRecipients and CmsRecipientCollection for you by doing certificate lookups based on the email address provided (or, if any of those mailboxes are actually a SecureMailboxAddress, the Fingerprint is used instead, which is useful if that user has more than 1 certificate in your database or you want to be extra sure that MimeKit picks the right one).
The other thing that MimeKit will do for you when you feed it a list of MailboxAddresses, is that it will look up the supported encryption algorithms that are stored in the database for said user.
For the WindowsSecureMimeContext, this involves looking at the S/MIME Capabilities X509 Certificate Extension attribute and decoding the supported encryption algorithms. In my experience, however, many times this extension is not present on X509 Certificates in the Windows certificate store and so MimeKit will have to assume that only 3DES CBC is supported.
For the DefaultSecureMimeContext, if you have verified any S/MIME signed message by said recipient, then that user's certificate (chain) and advertised encryption algorithms will be stored in MimeKit's custom SQL database (when you sign a message using S/MIME, it's fairly common practice for clients to include the S/MIME Capabilities attribute in the S/MIME signature data).
Now that you understand how that works, if you want to force the use of AES-128 CBC, the way to do that is to manually construct the CmsRecipientCollection yourself.
Naturally, this involves creating a new CmsRecipient for each recipient. To create this class, all you really need is the X509 certificate for that recipient.
var recipient = new CmsRecipient (certificate);
Since you want to force the use of AES-128 CBC, now you just need to override the encryption algorithms that this recipient supports:
recipient.EncryptionAlgorithms = new EncryptionAlgorithm[] {
EncryptionAlgorithm.Aes128
};
(By default, the EncryptionAlgorithms property will be set to the algorithms listed in the certificate's S/MIME Capabilities Extension attribute (in preferential order), if present, otherwise it'll just contain 3DES CBC).
If you also want to force RSAES-OAEP, you'll need to set:
recipient.RsaEncryptionPadding = RsaEncryptionPadding.OaepSha1;
Add each CmsRecipient to your CmsRecipientCollection and then pass that off to your preferred Encrypt() method and whallah, it will be encrypted using AES-128 CBC.
Notes:
MultipartSigned.Create() will produce a multipart/signed MIME part while ApplicationPkcs7Mime.Sign() will create an application/pkcs7-mime MIME part. Whichever one you want to use is up to you to decide, just keep in mind that your choice may impact compatibility with whatever client your recipients are using (I think most clients support both forms, but you might want to check to make sure).
If you've registered your custom SecureMimeContext class with MimeKit (as briefly described in the README), then you can feel free to use the various Encrypt/Decrypt/Sign/Verify/etc methods that do not take a cryptography context argument as MimeKit will instantiate the default context for you. Otherwise you will need to pass them a context.
I am trying to figure out a way of authentication between two distributed services.
I don't want to have a shared secret distributed on every service host, because it would mean that once one host has been compromised, all hosts are compromised.
So my scenario is:
Host A knows the public key of Host B
Host A encodes and encryptes the jwt using Host B´s public key
Host B receives and decrypts the jwt using its private key, that it only knows itself.
The jose-jwt package:
https://github.com/dvsekhvalnov/jose-jwt
seems like a good option to me. Beside the signing of the jwt, it also supports encryption using private/public keys.
On the page there are the following examples for encoding and decoding a jwt:
Encode:
var publicKey=new X509Certificate2("my-key.p12", "password").PublicKey.Key as RSACryptoServiceProvider;
string token = Jose.JWT.Encode(payload, publicKey, JweAlgorithm.RSA_OAEP, JweEncryption.A256GCM);
Decode:
var privateKey=new X509Certificate2("my-key.p12", "password", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet).PrivateKey as RSACryptoServiceProvider;
string json = Jose.JWT.Decode(token,privateKey);
Now, here is what i don´t understand:
How can I create a .p12 certificate file that only contains the public key information (for the host/service A that encodes the jwt) ?
.. and how can I create a .p12 certificate file that contains both, the public and the private key information (for the host/service B that decodes the jwt) ?
From all the research that I have done, i get the impression that you can either only make a .p12 file that contains both, or one that contains only the public key. But it seems there is no way to create two .p12 files, one with both information and one with only the public key. What am I missing?
Thanks for your answers.
Normally a PKCS12/PFX is not used for public-only, but you can do it if you like.
Assuming that cert.HasPrivateKey is true: cert.Export(X509ContentType.Pkcs12, somePassword) will produce a byte[] that you can write to "publicAndPrivate.p12" (or whatever).
Normally for a public-only certificate you'll write it down just as the X.509 data, either DER-binary or PEM-DER encoded. .NET doesn't make PEM-DER easy, so we'll stick with DER-binary. You can get that data by either cert.RawData, or cert.Export(X509ContentType.Cert) (both will produce identical results, since this export form has no random data in it). (publicOnly.cer)
If you really want a PKCS12 blob which has just the public certificate:
using (X509Certificate2 publicOnly = new X509Certificate2(publicPrivate.RawData))
{
return publicOnly.Export(X509ContentType.Pkcs12, somePassword);
}
The resulting byte[] could then be publicOnly.p12.
I am able to successfully identify client certificates in a .NET thick client app, and the user is able to successfully select one.
X509Store store = new X509Store("MY", StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
var fcollection = store.Certificates.Find(X509FindType.FindByApplicationPolicy, "1.3.6.1.5.5.7.3.2", true);
// other stuff where user selects one of them
Now how do I ask the user to answer the challenge (e.g. PIN in this case)?
I see there's a SignedXML.ComputeSignature() class, but it takes a byte stream, and I'm not sure where that comes from (perhaps in certificate.RawData[]?).
I'm not really as interested in getting the actual pin as I am that the card/pin match.
EDIT:
I tried using the private key from the smart card (and even encrypted from it), but I don't get asked for my PIN.
RSACryptoServiceProvider rsacsp = (RSACryptoServiceProvider)certificate.PrivateKey;
UnicodeEncoding ByteConverter = new UnicodeEncoding();
byte[] dataToEncrypt = ByteConverter.GetBytes("Data to Encrypt");
var encryptedData = RSAEncrypt(dataToEncrypt, rsacsp.ExportParameters(false), false);
Looks like the PIN request happens when I call RSACryptoServiceProvidersa.Decrypt.
Here's example code that worked perfectly for me in a Console app:
http://blog.aggregatedintelligence.com/2010/02/encryptingdecrypting-using.html
Much simpler in ASP.NET (aside from all the IIS config hassles/mysteries ...).
If this is a smartcard, the pin prompt will happen when you try to use the private key of the certificate.
You need to use the certificate somehow, and validate the result. For example, you might use the certificate to sign something. Once that signature operation happens, the pin prompt will appear.
If you don't really need to "use" the certificate, just want to validate that it's there and the user knows the pin, then you need some sort of proof of work. The certificate could be used to sign a challenge, and a remote server could validate the signature uses a key that belongs to a trusted root. Keep in mind this is difficult to get right, such as making sure you aren't open to a replay attack, etc.
I need to store "password like information" in a database field. I would like it to be encrypted but I need to decrypt it before using it. So I can not use a Hash/Salt solution.
Granted if an attacker made it that far into the database it may be too far gone but I figure this would at least stop the mistaken dump of the data.
How to encrypt a value store it into the database and decrypt the same value for use later?
Hashing is not an option (I use it on other parts actually).
Where to store the private key? Users would not supply anything.
This a C# solution so .NET specific stuff would be great. My question is very similar but I am looking for a .net based solution: Two-way encryption: I need to store passwords that can be retrieved
EDIT:
Hogan pretty much answered my question. I found examples out there and they ranged from very complicated to rather simple. It looks like AES is still good so I will be using that method. thank you for all your help.
One solution that does not involve private keys is using DPAPI.
You can use it from .NET via the ProtectedData class.
Here is an example:
public void Test()
{
var password = "somepassword";
var encrypted_password = EncryptPassword(password);
var decrypted_password = DecryptPassword(encrypted_password);
}
public string EncryptPassword(string password)
{
var data = Encoding.UTF8.GetBytes(password);
var encrypted_data = ProtectedData.Protect(data, null, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(encrypted_data);
}
public string DecryptPassword(string encrypted_password)
{
var encrypted_data = Convert.FromBase64String(encrypted_password);
var data = ProtectedData.Unprotect(encrypted_data, null, DataProtectionScope.CurrentUser);
return Encoding.UTF8.GetString(data);
}
Please note that DPAPI in this case depends on the current logged in user account. If you encrypt the password when your application is running as User1, then you can only decrypt the password running under the same user account. Please note that if you change the windows password for User1 in an incorrect way, then you will lose the ability to decrypt the password. See this question for details.
If you don't want use DPAPI, and prefer to have a private key. Then the best place to store such private key is in the user's key store. However, in order to store a private key in the local user store, you need to have a certificate for it. You can create a self signed certificate and store it with its corresponding private key into the local user certificate store.
You can access the user store in code using the X509Store class. You can use it to find the certificate (which is in C# a X509Certificate2 class) that you want to use and then use it to do encryption/decryption.
See this and this for more details.