X509Certificate2 request PIN - c#

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.

Related

how to sign bytes using USB token with pin code?

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.

How to work with (and create) X509 Certificates for private/public key encryption of JWT Tokens

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.

How to store information like passwords encrypted but not in hash

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.

C# Generate a non self signed client CX509Certificate Request without a CA using the certenroll.dll

I have a self signed root certificate that I generated in C# using CERTENROLL.dll's CX509CertificateRequest Certificate functionality.
I would like to write a function that generates client certificates signed by my root using the same API. However the only CertEnroll option I can find that does not generate a self signed certificate requires a authenticated CA.
There seems to be a flag for setting a SignerCertificate but it always fails to initialize.
//Initialize cert
var cert = new CX509CertificateRequestCertificate();
//take care of signer
cert.Issuer = issuen;
CSignerCertificate sc = new CSignerCertificate();
var raw = SEScert.GetRawCertData();
var rawStr=Convert.ToBase64String(raw);
sc.Initialize(false, X509PrivateKeyVerify.VerifyNone,
EncodingType.XCN_CRYPT_STRING_BASE64, rawStr); //fails here
cert.SignerCertificate = sc;
Does anyone know how I can generate a client CX509CertificateRequest signed by my root?
Any help or advice would be greatly appreciated.
I was able to solve this.
The encoding of SEScert is a hex string not base64 also the machine context should be set to true not false the correct code looks as follows:
ISignerCertificate signerCertificate = new CSignerCertificate();
signerCertificate.Initialize(true, X509PrivateKeyVerify.VerifyNone,EncodingType.XCN_CRYPT_STRING_HEX, SEScert.GetRawCertDataString());
cert.SignerCertificate = (CSignerCertificate)signerCertificate;
Hope this helps others in the future.

Use X509Certificate without requiring passphrase

I have a certificate that I need to use in order to access a web service. The problem is that whenever I try to use the X509 certificate it asks for a passphrase (PIN). Is there any way of providing the passphrase directly, without it popping up the same window every time?
The certificate uses a dongle made by Oberthur Technologies, if it's of any help. Here's the code I use to get the certificate:
X509Store store = new X509Store("MY",StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
if(collection.Count != 0)
userCert = collection[0]; // everything's ok up to here
And here's where I use the certificate:
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri(url));
req.ClientCertificates.Add(userCert); // add the certificate I just got
// ...
WebResponse ret = req.GetResponse(); // here's where it asks me for my passphrase
The certificate's private key is stored on the Oberthur token. It is enforcing the user to enter the PIN to gain access to certificates private key. This is by design and cannot be overridden.
I am searching for a way to do it with managed code but haven't yet found one.
In the past I have just called the CAPI function CryptSetProvParam with option PP_SIGNATURE_PIN and the PIN as the data. This prevents the CSP from prompting the user for a PIN. It's kinda hard for a system service to enter that PIN :)
So, you will have to pinvoke CryptSetProvParam to do it.

Categories

Resources