title: X509Certificate2: determine if certificate private key belongs to an hardware device, and if it needs a PIN
body: <p>I'm writing a program to do encryption things, using certificates stored on hardware devices (smart cards).</p>
  <ul>
  <li>The program must use .Net Framework 4.5.2 :( Changing this isn't possible.</li>
  <li>The crypto things I perform haven't problems :)</li>
  </ul>
  <p><em><strong>Preamble</strong></em></p>
  <p>As far as I understood, when a smart card, containing one or more certificates, is connected to the computer, the certificates appears on the Windows certificates store (CurrentUser/My).</p>
  <p>This is managed by the smartcard software, that must be installed.</p>
  <p>Since my software must work with any hardware device, the easiest solution is to popup a window, and making the user to choose the certificate to use.</p>
  <p><em><strong>First problem</strong></em></p>
  <p>I don't want to show all the certificates that are on CurrentUser/My, but only the ones that have an associated hardware connected to the computer.</p>
  <p>This is, more or less, the code that I'm using:</p>
  <pre><code>X509Store storeObject = new X509Store(StoreName.My, StoreLocation.CurrentUser);
  
  storeObject.Open(OpenFlags.ReadOnly);
  
  foreach (var certificate in storeObject.Certificates)
  {
      bool isValid = false;
      if (certificate.HasPrivateKey)
      {
          try
          {
              var rsaKey = certificate.PrivateKey as RSACryptoServiceProvider;
              
              if (rsaKey.CspKeyContainerInfo.HardwareDevice)
              {
                  isValid = true;
              }
          }
          catch (Exception ex)
          {
              // just log
          }
      }
  }
  </code></pre>
  <p>Despite this solution is very ugly, it's the best I came up.</p>
  <p>The certificate belongs to a smart card if:</p>
  <ul>
  <li>HasPrivateKey is true.</li>
  <li>The cast of PrivateKey to RSACryptoServiceProvider didn't throw an exception if the smart card is connected.</li>
  <li>HardwareDevice then is true.</li>
  </ul>
  <p>The problem is on the PrivateKey cast: it seems that lot of things are performed by the CryptoApi system during this cast (that includes calls - of course - to the smart card software).</p>
  <p>This is <strong>very slow</strong>, and if the smartcard software has some kind of failures, uncontrollable error messages are shown, ...</p>
  <p><em><strong>Question</strong></em></p>
  <p>Is there any other way to determine if the certificate belongs to an hardware device?
  And to determine if the hardware is currently connected?</p>
  <p><em><strong>Second problem</strong></em></p>
  <p>The certificate may require a PIN.</p>
  <p>I must (it's a prerequisite) avoid the Windows input box asking for the PIN.</p>
  <p>It's not a problem for me to collect the PIN, and to use it during my crypto operations.</p>
  <p>My problem is to understand if the PIN is required or not.</p>
  <p>ANY example I found online assume that if rsaKey.CspKeyContainerInfo.HardwareDevice == true then a PIN is required.</p>
  <p>But I'm not sure about that.</p>
  <p><em><strong>Question</strong></em></p>
  <p>Is there any way I can determine if a certificate requires a PIN code?</p>
  <p><em><strong>Conclusion</strong></em></p>
  <p>I also like external libraries, as long as they are free (preferably open source) and/or come from Microsoft.</p>
  <p>Thank you very much!</p>