Closed Sicos1977 closed 2 years ago
Also for other people that come here and need to know how to make a (self signed) certificate. See the code below. It took me a while to find and figure out all the code I needed. So I hope this message is helpfull for you.
public static X509Certificate2 GenerateCertificate(
int keyLength,
int durationYears,
string subjectName,
string friendlyName,
string password,
out string privateKey,
out string publicKey)
{
// Prepare random number generation.
var cryptoApiRandomGenerator = new CryptoApiRandomGenerator();
var secureRandom = new SecureRandom(cryptoApiRandomGenerator);
// Create asymmetric key
var keyGenerationParameters = new KeyGenerationParameters(secureRandom, keyLength);
var rsaKeyPairGenerator = new RsaKeyPairGenerator();
rsaKeyPairGenerator.Init(keyGenerationParameters);
var asymmetricCipherKeyPair = rsaKeyPairGenerator.GenerateKeyPair();
var x509V3CertificateGenerator = new X509V3CertificateGenerator();
// Generate a serial number
var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), secureRandom);
x509V3CertificateGenerator.SetSerialNumber(serialNumber);
// Assign the subject name
var subjectDn = new X509Name($"CN={subjectName}");
x509V3CertificateGenerator.SetIssuerDN(subjectDn);
x509V3CertificateGenerator.SetSubjectDN(subjectDn);
// Set valid dates
var notBefore = DateTime.UtcNow.Date;
var notAfter = notBefore.AddYears(durationYears);
x509V3CertificateGenerator.SetNotBefore(notBefore);
x509V3CertificateGenerator.SetNotAfter(notAfter);
// Set key usage for digital signatures and key encipherment
var keyUsage = new KeyUsage(KeyUsage.DigitalSignature | KeyUsage.KeyEncipherment);
x509V3CertificateGenerator.AddExtension(X509Extensions.KeyUsage, true, keyUsage);
var oids = new List<DerObjectIdentifier>
{
new("1.3.6.1.5.5.7.3.4"), // Secure Email
new("1.3.6.1.4.1.6449.1.3.5.2") // Email Protection
};
// Load the OIDs passed in and specify enhanced key usages
x509V3CertificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(oids));
// Assign the public key
x509V3CertificateGenerator.SetPublicKey(asymmetricCipherKeyPair.Public);
// Self-sign the certificate using SHA-512.
ISignatureFactory signatureFactory = new Asn1SignatureFactory("SHA512WITHRSA", asymmetricCipherKeyPair.Private, secureRandom);
var bouncyCastleCertificate = x509V3CertificateGenerator.Generate(signatureFactory);
// Convert from BouncyCastle private key format to System.Security.Cryptography format.
var x509Certificate = new X509Certificate2(DotNetUtilities.ToX509Certificate(bouncyCastleCertificate))
{
PrivateKey = ToDotNetKey((RsaPrivateCrtKeyParameters)asymmetricCipherKeyPair.Private),
FriendlyName = friendlyName
};
using var privateTextWriter = new StringWriter();
var privatePemWriter = new PemWriter(privateTextWriter);
if (!string.IsNullOrEmpty(password))
privatePemWriter.WriteObject(asymmetricCipherKeyPair.Private, "AES-128", password.ToCharArray(), new SecureRandom());
else
privatePemWriter.WriteObject(asymmetricCipherKeyPair.Private);
privatePemWriter.Writer.Flush();
privateKey = privatePemWriter.ToString();
using var publicTextWriter = new StringWriter();
var publicPemWriter = new PemWriter(publicTextWriter);
publicPemWriter.WriteObject(asymmetricCipherKeyPair.Public);
publicKey = publicTextWriter.ToString();
return x509Certificate;
}
/// <summary>
/// Convert from BouncyCastle private key format to System.Security.Cryptography format.
/// </summary>
/// <param name="privateKey">BouncyCastle private key.</param>
/// <returns>System.Security.Cryptography representation of the key.</returns>
private static AsymmetricAlgorithm ToDotNetKey(RsaPrivateCrtKeyParameters privateKey)
{
var cspParams = new CspParameters
{
KeyContainerName = Guid.NewGuid().ToString(),
KeyNumber = (int)KeyNumber.Exchange,
Flags = CspProviderFlags.UseMachineKeyStore
};
var rsaProvider = new RSACryptoServiceProvider(cspParams);
var parameters = new RSAParameters
{
Modulus = privateKey.Modulus.ToByteArrayUnsigned(),
P = privateKey.P.ToByteArrayUnsigned(),
Q = privateKey.Q.ToByteArrayUnsigned(),
DP = privateKey.DP.ToByteArrayUnsigned(),
DQ = privateKey.DQ.ToByteArrayUnsigned(),
InverseQ = privateKey.QInv.ToByteArrayUnsigned(),
D = privateKey.Exponent.ToByteArrayUnsigned(),
Exponent = privateKey.PublicExponent.ToByteArrayUnsigned()
};
rsaProvider.ImportParameters(parameters);
return rsaProvider;
}
And also some code to generate Open PGP keys
internal static void GeneratePgpKey(int keyLength, string identifier, string password, out string privateKey, out string publicKey)
{
// Genereer een nieuwe RSA key pair
var rsaKeyPairGenerator = new RsaKeyPairGenerator();
rsaKeyPairGenerator.Init(new RsaKeyGenerationParameters(BigInteger.ValueOf(0x101), new SecureRandom(), keyLength, 80));
var asymmetricCipherKeyPair = rsaKeyPairGenerator.GenerateKeyPair();
// Maak PGP sub packet
var hashedGen = new PgpSignatureSubpacketGenerator();
hashedGen.SetKeyFlags(true, PgpKeyFlags.CanSign | PgpKeyFlags.CanEncryptCommunications);
hashedGen.SetPreferredCompressionAlgorithms(false, new[] { (int)CompressionAlgorithmTag.Zip });
hashedGen.SetPreferredHashAlgorithms(false, new[] { (int)HashAlgorithmTag.Sha512 });
hashedGen.SetPreferredSymmetricAlgorithms(false, new[] { (int)SymmetricKeyAlgorithmTag.Aes256 });
var unhashedGen = new PgpSignatureSubpacketGenerator();
// Maak PGP sleutel
var secretKey = new PgpSecretKey(
PgpSignature.DefaultCertification,
PublicKeyAlgorithmTag.RsaGeneral,
asymmetricCipherKeyPair.Public,
asymmetricCipherKeyPair.Private,
DateTime.Now,
identifier,
SymmetricKeyAlgorithmTag.Cast5,
password?.ToCharArray(),
hashedGen.Generate(),
unhashedGen.Generate(),
new SecureRandom());
var info = new Dictionary<string, string> { { "Tool", "<Your tool name>" }, { "Identifier", identifier } };
// Extract the keys
using (var privateMemoryStream = new MemoryStream())
{
using (var privateArmoredOutputStream = new ArmoredOutputStream(privateMemoryStream, info))
secretKey.Encode(privateArmoredOutputStream);
privateKey = Encoding.ASCII.GetString(privateMemoryStream.ToArray());
}
using (var publicMemoryStream = new MemoryStream())
{
using (var publicArmoredOutputStream = new ArmoredOutputStream(publicMemoryStream, info))
secretKey.PublicKey.Encode(publicArmoredOutputStream);
publicKey = Encoding.ASCII.GetString(publicMemoryStream.ToArray());
}
}
I did some digging around in the Cryptography name space and found this class MimeKit.Cryptography.BouncyCastleSecureMimeContext, is this the one to work with certificates?
If you want to use the BouncyCastle backend for S/MIME, then yes.
The SecureMimeContext is the most abstract of the S/MIME classes. The BouncyCastleSecureMimeContext is the subclass that uses BouncyCastle for crypto operations. The WindowsSecureMimeContext is the subclass that sues System.Security for crypto operations.
And also probably a very dumb question, how does MimeKit know if an e-mail is encrypted or signed with a certificate instead of for example a OpenPGP key?
PGP/MIME and S/MIME use different MIME types.
Do I need to do a check like this or is there some code in MimeKit that figures it out?
If you get a MultipartSigned, then calling Verify() will figure out the PGP vs S/MIME stuff for you.
If you get an ApplicationPkcs7Mime part, then that can only be S/MIME and you would check the SecureMimeType property to know if it is signed or encrypted (aka enveloped).
If you get an ApplicationPkcs7Signature, then that is a S/MIME signed-only part.
If you get a MultipartEncrypted, that will generally be a PGP/MIME encrypted part (S/MIME doesn't really use that), but the Decrypt() method will figure out which to use for you.
I now use the abstract class
MimeKit.Cryptography.OpenPgpContext
to work with Open PGP keys. This works as expected. I also have an requirement to work with X509Certificate2 certificates? So is there a class simular to what the OpenPgpContext class does?I did some digging around in the Cryptography name space and found this class
MimeKit.Cryptography.BouncyCastleSecureMimeContext
, is this the one to work with certificates?And also probably a very dumb question, how does MimeKit know if an e-mail is encrypted or signed with a certificate instead of for example a OpenPGP key?
Do I need to do a check like this or is there some code in MimeKit that figures it out?