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()
}
Related
I am trying to use RSACryptoServiceProvider with CspParameters that point to a global pin.
It works correctly if I use an application pin but when I use the global pin it fails with:
"The card cannot be accessed because the wrong PIN was presented."
Will it work when I use a global pin? Is there an option that tells it what type of pin to look for?
Thanks in advance.
Update:
I am retrieving the discovery object from the smart card if it exists.
This will tell me two things I want to know.
1). If the card has both application and global pins. (first byte of pin usage >= 60)
2). Which pin is considered primary. (second byte 0x10 = app, 0x20 = global)
I have a card, the NIST Test Pivcard 3, which has both pins but the global pin is primary. For this card when I enter the global pin on my test form, I can do a verify against it and it validates the pin correctly. (CLA=0x00, INS=0x20, P1=0x00, P2=0x00, Lc=0x8)
I can do the same for this card if I enter the application pin instead (with P2 set to 0x80) and it verifies it correctly.
After I verify the pin, set the AID and get some other x509 data from the card, I attempt to sign some hashed data using the card's private key.
Using RSACryptoServiceProvider and CspParameters it fails whenever I pass it the global pin. I get "The card cannot be accessed because the wrong PIN was presented."
If I pass it a valid application pin, then it works fine.
My code looks like this:
try
{
SecureString ss = new SecureString();
char[] PINs = PIN.ToCharArray();
foreach (char a in PINs)
{
ss.AppendChar(a);
}
CspParameters csp = new CspParameters(1, "Microsoft Base Smart Card Crypto Provider");
csp.Flags = CspProviderFlags.UseDefaultKeyContainer;
csp.KeyPassword = ss;
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);
byte[] data = File.ReadAllBytes(hashFile);
sig = rsa.SignHash(data, "SHA1");
bool verified = rsa.VerifyHash(data, CryptoConfig.MapNameToOID("SHA1"), sig);
}
catch (Exception ex)
{
txt_msg.Text = ex.Message;
etc...
}
Is there some flag I am missing here to say that the pin being used is a global pin? Or are we not allowed to use a global pin? Or am I missing some other thing here? This is my first attempt to use RSACryptoServiceProvider and I'm probably missing some fundamentals.
Any suggestions would be appreciated.
You seem to assume, that PINs are somewhat exchangeable in the sense, that a global PIN is an appropriate substitute for an application-specific one. This is not true.
While a card could have been set up this way (accepting global PIN say #1 OR application specific PIN #2), your card is obviously not. If the card does not offer the choice, the service provider can't succeed. Even if the service provider uses the PIN you want and the comparison succeeds, the card will not allow usage of the private key.
And no, there is no way that you change the ID of the PIN required for signature according to your liking, since this path would then also be available for an attacker.
(All of this answers assume, that you are not allowed to modify the card content, e. g. you are a normal card holder.)
I am using:
Windows 10 (Version 1709, OS Build 17025.1000)
.net framework 4.7
VS 2017 (version: 15.3.5)
Here is what I did:
Got a self signed ECC certificate using OpenSSL and steps outlined in the script at https://gist.github.com/sidshetye/4759690 with modifications:
a) Used NIST/P-256 curve over a 256 bit prime field
b) Used SHA-256
Load the certificate from file (generated in previous step) into X509Certificate2 object
Imported the PFX file into windows trust store (for testing). This is successful.
Inspection of the imported certificate shows Public Key field as 'ECC (256 Bits)' and Public key parameters as 'ECDSA_P256'.
Next tried to figure out how to encrypt with this certificate.
I am stuck at the last step because all the examples that use X509Certificate2 object predominantly use only RSA and I am using ECC certificate. For RSA certificate, there is a GetRSAPublicKey extention method on X509Certificate2 and RSA class has Encrypt method. However there is no such method for ECC certificates.
Next, I stumbled on this post (Load a Certificate Using X509Certificate2 with ECC Public Key) and tried following (even though it appeared bizarre as to why ECC cert public key is being coerced into RSA type):
RSACryptoServiceProvider csp = (RSACryptoServiceProvider)cert.PublicKey.Key
I got following exception: The certificate key algorithm is not supported.
Next I stumbled on this post (Importing ECC-based certificate from the Windows Certificate Store into CngKey) which basically tried to create CNGKey type and instantiate ECDsaCng with it. However even if I can do it with ECDiffieHellmanCng, there is no Encrypt method on it.
So I am not really sure how can I proceed further to use ECC X509 certificate's public key to encrypt data.
###Background
Asymmetric algorithms have three different purposes (that I know of)
Encryption
RSA is the only "standard" algorithm that can do this directly.
Signature
RSA
DSA
ECDSA
ElGamal Signature
Key Agreement
Diffie-Hellman (DH)
ECDH
ElGamal encryption (the asymmetric startup phase)
MQV
ECMQV
Because RSA encryption is space limited, and was hard for computers in the '90s, RSA encryption's primary use was in "Key Transfer", which is to say that the "encrypted message" was just the symmetric encryption key for DES/3DES (AES not yet having been invented) - https://www.rfc-editor.org/rfc/rfc2313#section-8.
Key agreement (or transfer) schemes always have to be combined with a protocol/scheme to result in an encryption operation. Such schemes include
TLS (nee SSL)
CMS or S/MIME encrypted-data
IES (Integrated Encryption Scheme)
ECIES (Elliptic Curve Integrated Encryption Scheme)
ElGamal encryption (holistically)
PGP encryption
So what you probably want is ECIES.
ECIES.Net
Currently (.NET Framework 4.7.1, .NET Core 2.0) there's no support to get an ECDiffieHellman object from a certificate in .NET.
Game over, right? Well, probably not. Unless a certificate carrying an ECDH key explicitly uses the id-ecDH algorithm identifier (vs the more standard id-ecc one) it can be opened as ECDSA. Then, you can coerce that object into being ECDH:
using (ECDsa ecdsa = cert.GetECDsaPublicKey())
{
return ECDiffieHellman.Create(ecdsa.ExportParameters(false));
}
(a similar thing can be done for a private key, if the key is exportable, otherwise complex things are required, but you shouldn't need it)
Let's go ahead and carve off the recipient public object:
ECDiffieHellmanPublicKey recipientPublic = GetECDHFromCertificate(cert).PublicKey;
ECCurve curve = recipientPublic.ExportParameters().Curve;
So now we turn to http://www.secg.org/sec1-v2.pdf section 5.1 (Elliptic Curve Integrated Encryption Scheme)
###Setup
Choose ANSI-X9.63-KDF with SHA-2-256 as the hash function.
Choose HMAC–SHA-256–256.
Choose AES–256 in CBC mode.
Choose Elliptic Curve Diffie-Hellman Primitive.
You already chose secp256r1.
Hard-coded. Done.
Point compression's annoying, choose not to use it.
I'm omitting SharedInfo. That probably makes me a bad person.
Not using XOR, N/A.
###Encrypt
Make an ephemeral key on the right curve.
ECDiffieHellman ephem = ECDiffieHellman.Create(curve);
We decided no.
ECParameters ephemPublicParams = ephem.ExportParameters(false);
int pointLen = ephemPublicParams.Q.X.Length;
byte[] rBar = new byte[pointLen * 2 + 1];
rBar[0] = 0x04;
Buffer.BlockCopy(ephemPublicParams.Q.X, 0, rBar, 1, pointLen);
Buffer.BlockCopy(ephemPublicParams.Q.Y, 0, rBar, 1 + pointLen, pointLen);
Can't directly do this, moving on.
Can't directly do this, moving on.
Since we're in control here, we'll just do 3, 4, 5, and 6 as one thing.
KDF time.
// This is why we picked AES 256, HMAC-SHA-2-256(-256) and SHA-2-256,
// the KDF is dead simple.
byte[] ek = ephem.DeriveKeyFromHash(
recipientPublic,
HashAlgorithmName.SHA256,
null,
new byte[] { 0, 0, 0, 1 });
byte[] mk = ephem.DeriveKeyFromHash(
recipientPublic,
HashAlgorithmName.SHA256,
null,
new byte[] { 0, 0, 0, 2 });
Encrypt stuff.
byte[] em;
// ECIES uses AES with the all zero IV. Since the key is never reused,
// there's not risk in that.
using (Aes aes = Aes.Create())
using (ICryptoTransform encryptor = aes.CreateEncryptor(ek, new byte[16]))
{
if (!encryptor.CanTransformMultipleBlocks)
{
throw new InvalidOperationException();
}
em = encryptor.TransformFinalBlock(message, 0, message.Length);
}
MAC it
byte[] d;
using (HMAC hmac = new HMACSHA256(mk))
{
d = hmac.ComputeHash(em);
}
Finish
// Either
return Tuple.Create(rBar, em, d);
// Or
return rBar.Concat(em).Concat(d).ToArray();
###Decrypt
Left as an exercise to the reader.
For getting ECDiffieHellman private key from certificate, use the following method:
Install NuGet package Security.Cryptography (CLR Security). (The package is under MIT license.)
Use the following extension method to get the CngKey instance:
CngKey cngKey = certificate.GetCngPrivateKey();
(Note: The extension method certificate.GetECDsaPrivateKey(), natively supported in .NET, returns an ECDsaCng instance; there is no extension method to return ECDiffieHellmanCng.)
The cngKey instance can be used to create either an ECDsaCng or an ECDiffieHellmanCng instance:
var sa = new ECDsaCng(cngKey);
var sa = new ECDiffieHellmanCng(cngKey);
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();
}
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.
I have used PuttyGen to create a public/private key pair as a .ppk file, but I'm having trouble actually using it in my code. Opening the randomly generated ppk file in notepad shows this:
PuTTY-User-Key-File-2: ssh-rsa
Encryption: aes256-cbc
Comment: rsa-key-20151217
Public-Lines: 6
AAAAB3NzaC1yc2EAAAABJQAAAQEAl3m8PRlx/SL7EJrs+hDQbP9mp27XXRY4pztg
v8mxAthI2tMEhF0eyXqFV0/W/M13pPs1hHh3H7wIfAy/XbxF7KPeOeMkThMmF2p1
cwJOcpFuh6TCPT09ScPLCR5bsmQyzvsjPMWahcoDrLhf9MGfc9luQs7k1eMTM1iX
hq6F/ku7mNQ4mgKoOOlXKPhE6dFz9Qhe5k0TE3zIfkXUCCkm+74VqyG5l5vG6/fb
ZmVD0nGM6ErPHB/zQ0WbTm65BMmLIIfNZoVrRwcrQmjj4qJnJg7s+Ar0wfB0Y1kl
91fcxKJ4Mx2uBw7T0T+61DrXKamnqQmTz8g/QsgwwBGLqz9mXQ==
Private-Lines: 14
uvM2bpGRiAQey2PSgAdoQHWn7nv925WBHXx5uSfrUZpx9+HwMjCAy7Y7vDwZUUwT
kYb5PH8acor9aeu7xAaTdUFuYWliHA023pbac9CxdRUE7xMCH8u8rTpo6ZKRKKmS
ExNfEXnk9Veqn2Kr2vbXOd/zc2hiJNrmlMdik1v6RMmOhGfNgHKytlvJbZCF9WDM
gkXpuKBO04cFA241PvJNVC0VdHg+DiNhnqdlUBxW/trR5OieIBgqXLTqeAjGsdt9
066F0DhUCguOrSl5WUlrM3I4ylTqn1Xb9Vr1/7iTnDUaStwGWxjMj9YqgjHjuF/9
Lq0lDdIAriXb9IcvHoU7JWPpOtr5JtGRK0MDAN3YEX4hutPcmFv3F6UHOCFOSBNQ
UTu+Hg+dGzInvN63d0YaZyoEO8n8OpVI8gjrsKsHDgk7crnIyIDi8KKF4skgE4jf
kpVHwW80WBw9zizJlnBFxCnrkigEvI6X+hKTbTM4il5wt7Ixcympgl8EvZc8TaI2
Ksqeg+ONeA2H4Wgi+bzkys111/oTDsC41JfjwWS1lanuKpyYm9SDWTSQf48KV8kH
7fIHQ5O/4RV+f7k19IIWmvCcZTqq9xzjJXtYrjYKbCsptPBq+AQjvXcwkV1DpoGP
q5FKpA0QR4VYlCYqU6h5EdE+EuJpL2r0fEQ1I3pYsH+AUjCvZT1TzB5qvh+0krAw
GI786w9LbuhNUWEGtO8AIS0876bTQq4xLlTlgsZSHG0ElUIHSLlwUQL9RLiN97OZ
sokrwZ/7tFMA4ZK9eOPHXIkgWiF4vnrf1CLN/ePuLKpXsGjYKXnLyQy4cM9tGsEA
cfTBsYqriIvlehgnREdzUiGFxHoJ95WMpK/ESCSwlAAW3EMTs9bZRxyi5hwpmjc8
Private-MAC: 5403e23591ff38245d34db92ef130c86e9789b98
And this is what's in the public key file:
---- BEGIN SSH2 PUBLIC KEY ----
Comment: "rsa-key-20151217"
AAAAB3NzaC1yc2EAAAABJQAAAQEAl3m8PRlx/SL7EJrs+hDQbP9mp27XXRY4pztg
v8mxAthI2tMEhF0eyXqFV0/W/M13pPs1hHh3H7wIfAy/XbxF7KPeOeMkThMmF2p1
cwJOcpFuh6TCPT09ScPLCR5bsmQyzvsjPMWahcoDrLhf9MGfc9luQs7k1eMTM1iX
hq6F/ku7mNQ4mgKoOOlXKPhE6dFz9Qhe5k0TE3zIfkXUCCkm+74VqyG5l5vG6/fb
ZmVD0nGM6ErPHB/zQ0WbTm65BMmLIIfNZoVrRwcrQmjj4qJnJg7s+Ar0wfB0Y1kl
91fcxKJ4Mx2uBw7T0T+61DrXKamnqQmTz8g/QsgwwBGLqz9mXQ==
---- END SSH2 PUBLIC KEY ----
Obviously, I'm not using this key anymore, it's just for illustrative purposes. But how do I read this file into C# to use it to sign and verify a string?
You're making an RSA key in Putty and trying to use that, why not simply just generate the key pair with C#'s RSA encryption?
You can generate a public/private key-pair and save it using this:
//Generate a public/private key pair.
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
//Save the public key information to an RSAParameters structure.
RSAParameters RSAKeyInfo = RSA.ExportParameters(false);
You can see how to use this key pair to encrypt data, example documentation here.