How to sign public PGP key with Bouncy Castle in C# - c#

I want to create web of trust support in my application, allowing my users to use their private keys, to sign other user's public keys - Using C# and Bouncy Castle.
I've got most things figured out, such as creating PGP keys, submitting them to key servers using HTTP REST, encrypting MIME messages and cryptographically signing them (using MimeKit) - But the one remaining hurdle, is to figure out some piece of code that can use my private key, to sign for another person's public key, using Bouncy Castle.
Since the documentation for BC is horrendous, figuring out these parts, have previously proven close to impossible ...
For the record, I'm using GnuPG as my storage for keys.
If anybody wants to look at my code so far for what I have done, feel free to check it out here.
I am probably not supposed to ask this here, but I'd also love it if some BC gurus out there could have a general look at my code so far, and check if I've made a fool of myself with the stuff I've done so far ...

Found the answer after a lot of trial and error, here it is ...
private static byte[] SignPublicKey(
PgpSecretKey secretKey,
string password,
PgpPublicKey keyToBeSigned,
bool isCertain)
{
// Extracting private key, and getting ready to create a signature.
PgpPrivateKey pgpPrivKey = secretKey.ExtractPrivateKey (password.ToCharArray());
PgpSignatureGenerator sGen = new PgpSignatureGenerator (secretKey.PublicKey.Algorithm, HashAlgorithmTag.Sha1);
sGen.InitSign (isCertain ? PgpSignature.PositiveCertification : PgpSignature.CasualCertification, pgpPrivKey);
// Creating a stream to wrap the results of operation.
Stream os = new MemoryStream();
BcpgOutputStream bOut = new BcpgOutputStream (os);
sGen.GenerateOnePassVersion (false).Encode (bOut);
// Creating a generator.
PgpSignatureSubpacketGenerator spGen = new PgpSignatureSubpacketGenerator();
PgpSignatureSubpacketVector packetVector = spGen.Generate();
sGen.SetHashedSubpackets (packetVector);
bOut.Flush();
// Returning the signed public key.
return PgpPublicKey.AddCertification (keyToBeSigned, sGen.Generate()).GetEncoded();
}

Related

Best way to store RSA key and use it in .Net core(looking for a cross-platform solution)

I'm currently using this link to store my RSA key in windows Key Container (machine-level) and it works fine, but I'm looking for a way that works for both Linux and windows because I will definitely deploy this project on Linux.
public static void StoreRSAKey(string containerName, string xmlKey)
{
#pragma warning disable CA1416 // Validate platform compatibility
var parameters = new CspParameters
{
KeyContainerName = containerName
};
#pragma warning restore CA1416 // Validate platform compatibility
parameters.Flags = CspProviderFlags.UseMachineKeyStore;
using RSACryptoServiceProvider? rsa = new RSACryptoServiceProvider(parameters);
rsa.FromXmlString(xmlKey);
}
I have found several recommendations on the web but I need a more precise solution.
I'd be glad if anyone can help me through this.
Best way to store RSA key and use it in .Net core(looking for a cross-platform solution)
IMO, the question should be, is it possible to use the RSA key in .net core for cross-platform?
I have recently built open-source encryption and decryption library, spending hours investigating the same question you asked. The short answer it is not possible to use CspParameters with Linux, it is for Windows OS (as this answer mentions). And because the answer is not possible, there is no best way.
So to start with, let's see if we can answer the question of using the RSA key in the .net core for cross-platform.
To do that it is very simple, you need to do following:
Rsa = RSA.Create();
Rsa.KeySize = 2048;
This part does not require installing the library, it is part of netstandard2.0.
That is it, now to export and import a key that you generate, you can do the following.
When you RSA.Create() first you can export the key and store it anywhere safe for later usage.
To export private key and it should be kept safe
Rsa.ToXmlString(true);
To export public key, to encrypt with
Rsa.ToXmlString(false);
When you need to import the key from a local store, you can do the following:
Rsa.FromXmlString(asymmetricKey);
This is the cross-platform compatible solution for windows, Linux, or Mac computers.
It is also possible to import certificates from a local computer using X509Certificate2 and use its public key for encryption and private key for decryption.
It is also possible to import private key parameters to RSAParameters, which requires a helper method to translate XML tags from private key file:
<RSAKeyValue>
<Modulus>xxxx...</Modulus>
<Exponent>xxxx</Exponent>
<P>xxxx...</P>
<Q>xxxx...</Q>
<DP>xxxx...</DP>
<DQ>xxxx...</DQ>
<InverseQ>xxxx...</InverseQ>
<D>xxxx...</D>
</RSAKeyValue>
But I find it easier to use FromXmlString and it is part of RSA class when creating RSA.Create(), so no need for a helper method, that said if performance means a lot for your project, you need to make a performance test to compare the results.
So finally I provide a simple example of how to store and load keys:
public static void Main(string[] args)
{
var rsa = RSA.Create();
rsa.KeySize = 2048;
// public key for decrypting
var privateKey = rsa.ToXmlString(true);
SaveKey(#"privateKey", privateKey);
// public key for encrypting
var publicKey = rsa.ToXmlString(false);
SaveKey(#"publicKey", publicKey);
// initialize the private for use on another instance
var rsaAnotherPlace = RSA.Create();
rsaAnotherPlace.KeySize = 2048;
rsaAnotherPlace.FromXmlString(LoadKey(#"privateKey"));
}
// store my keys
public static void SaveKey(string filename, string content)
{
var bytes = Encoding.ASCII.GetBytes(content);
using var fs = new FileStream(filename, FileMode.Create, FileAccess.Write);
fs.Write(bytes, 0, bytes.Length);
}
// load key
public static string LoadKey(string filename)
{
var bytes = File.ReadAllBytes(filename);
return Encoding.ASCII.GetString(bytes);
}
I have tested the solution on Windows and Linux OS and it passes the macOS test on GitHub actions, but I have not tested it yet on macOS.
Disclaimer: this is the open-source library I am working on.

Is there a Microsoft library for ed25519 signature verification?

I've been going in circles trying to get a simple ed25519 signature verification working. Had success with libsodium third party library but then ran into issues with it and am back to the drawing board. I see system.security.cryptography.ecdsa but it's not clear to me if it's possible to get that to work with a ed25519 signature. I'm kind of surprised there aren't code examples of doing this sig verification in .Net because I thought ed25519 was a reasonably common algorithm?
Is there a Microsoft library for ed25519 signature verification? Or can anyone provide an example of how to successfully do this in .Net?
You can use BouncyCastle for this purpose. Below is the code example of how I am doing it. For using this code make sure the publicKey and signature are hex strings.
public Task<bool> VerifySignature(string publicKey, string dataToVerify, string signature)
{
var publicKeyParam = new Ed25519PublicKeyParameters(Hex.DecodeStrict(publicKey));
var dataToVerifyBytes = Encoding.UTF8.GetBytes(dataToVerify);
var signatureBytes = Convert.FromHexString(signature);
var verifier = new Ed25519Signer();
verifier.Init(false, publicKeyParam);
verifier.BlockUpdate(dataToVerifyBytes, 0, dataToVerifyBytes.Length);
var isVerified = verifier.VerifySignature(signatureBytes);
return Task.FromResult(isVerified);
}

Verify Java Card signature

I am writing a Java Card 3.0.2 application on a NXP J3D081 card. I have it both signing and verifying a signature using ALG_ECDSA_SHA_256. The keys have been written to the card by my test app. If I sign 32 bytes of data and pass the signature back to the card the Verify code successfully verifies the signature. If I sign 32 bytes in Bouncy Castle with the Private key and pass to the Verify on the Card it successfully verifies the signature. The bouncy castle Verify Code successfully verifies signatures created from the bouncy castle signing routine.
BUT if I take the returned signature from the Java Card and pass it to the C# bouncy castle code it FAILS to verify the signature. I have checked all input values and they are correct. My code is here (note I pass Public keys as 64 bytes and prepend them with 0x04)
public bool HashAndVerifyDSA(byte[] pb, byte[] inData, byte[] sig)
{
byte[] pub = new byte[65];
pub[0] = 0x4;
Array.Copy(pb, 0, pub, 1, 64);
ECCurve curve = parameters.Curve;
ECPoint q = curve.DecodePoint(pub);
ICipherParameters Public = new ECPublicKeyParameters(algorithm, q, parameters);
ISigner bSigner = SignerUtilities.GetSigner("SHA-256withECDSA");
bSigner.Init(false, Public);
bSigner.BlockUpdate(inData, 0, inData.Length);
return (bSigner.VerifySignature(sig));
}
I should note that the parameters specify the P-256 curve and are used successfully in the encrypted communication to the card. The Public key is successfully created.
I seem to have less hair now then I did two days ago. Any pointers would be welcome.
Apart from steps you have performed to debug the thing, you can check the following also: -
Verify the signature using some online available tool. Do not forget to use same curve parameters and public key generated from javacard.
Verify the same using bouncy castle java library. I perform the same steps in one of my tools and it was matched successfully.

Extract the shared secret from class ECDiffieHellmanCng

I am currently developing an SSH client and it is necessary that said client is able to exchange keys with the server via ECDH KEX (NIST-256, 384 and 521).
I did some (actually a lot) of research, found the .NET class ECDiffieHellmanCng, and was able to extract and import the public key of the server into the class.
The problem, however, is that I can't extract the shared secret without deriving it (ECDiffieHellmanCng.DeriveKeyMaterial(CngKey otherpartyPublicKey)).
Is there a way to directly access the shared secret ("k" as it's called in the RFC papers)?
Here is page 7 from the RFC of the ECDH implementation and why I need the shared secret:
The exchange hash H is computed as the hash of the concatenation of
the following.
string V_C, client's identification string (CR and LF excluded)
string V_S, server's identification string (CR and LF excluded)
string I_C, payload of the client's SSH_MSG_KEXINIT
string I_S, payload of the server's SSH_MSG_KEXINIT
string K_S, server's public host key
string Q_C, client's ephemeral public key octet string
string Q_S, server's ephemeral public key octet string
mpint K, shared secret <-- this is why I need the pure secret
before any derivation
Thanks for any solutions or hints!
You don't actually need k, then, you just need to compute H. The ECDiffieHellman class allows you to do that.
byte[] prepend = Concat(V_C, V_S, I_C, I_S, K_S, Q_C, Q_S);
byte[] exchangeHash = ecdh.DeriveKeyFromHash(otherPublic, new HashAlgorithmName("whatever your hash algorithm is"), prepend, null);
Though that is using .NET 4.6.2 (currently in preview) API: DeriveKeyFromHash
If you are on an older framework it's still possible, but requires using the ECDiffieHellmanCng type specifically:
ecdhCng.SecretPrepend = prepend;
ecdhCng.SecretAppend = null;
ecdhCng.HashAlgorithm = new CngAlgorithm("whatever your hash algorithm is");
ecdhCng.KeyDerivationFunction = ECDiffieHellmanKeyDerivationFunction.Hash;
byte[] exchangeHash = ecdhCng.DeriveKeyMaterial(otherPublic);
Even after a lot of research i couldn't find a way to do it so the answer is no - you can not extract the secret.
My solution for the big picture was to discard the ECDiffieHellmanCng class altogether and instead wrap the OpenSSH library in C#.
Hope this at least helps someone else with the same idea.

DSA and Public Key Exchange

I am trying to implement a licensing solution with DSA Algorithms for my application. Now here is what I have done:
Generated a hardware key, taken its hash.
Generated public and private keys. And encrypted my hash function with private key.
I forward this encrypted value back to client along with the public key.
At client's system, I use the DSASignatureDeformatter's VerifySignature function to validate my encrypted key, and my hardware key. If equal I validate the client.
Now my problem is that how to send the public key over the network. I tried to store and forward various DSAParameters values e.g., J, G, P in a file but since the sizes of keys change, that is not viable. Please see if anyone can guide.
Updated:
When I try to do this at the client's machine
DSAParameters KeyInfo;
using (DSACryptoServiceProvider DSA = new DSACryptoServiceProvider())
{
// Import the key information.
KeyInfo = DSA.ExportParameters(false);
}
The key size it generates for its various members is different from the public key parameters I have sent it back from server.
Okay. A bit late. But maybe other ones will have the same question.
You should just export your key like this:
string publicKey = DSA.ToXmlString(false);
so you can import it like this:
using (DSACryptoServiceProvider dsa = new DSACryptoServiceProvider())
{
dsa.FromXmlString(publicKey);
return dsa.VerifySignature()
}

Categories

Resources