/// <summary> /// Generates certificate request in PKCS#10 format defined by RFC 2986 /// </summary> /// <param name="session">Session with user logged in</param> /// <param name="publicKeyHandle">Handle of public key that should be contained in generated request</param> /// <param name="privateKeyHandle">Handle of private key that should be used for the creation of request signature</param> /// <param name="subjectDistinguishedName">Subject entity's distinguished name</param> /// <param name="hashAlgorithm">Hash algorihtm used for the creation of request signature</param> /// <returns>Certificate request in PKCS#10 format</returns> public static byte[] GeneratePkcs10(Session session, ObjectHandle publicKeyHandle, ObjectHandle privateKeyHandle, string subjectDistinguishedName, HashAlgorithm hashAlgorithm) { List<CKA> pubKeyAttrsToRead = new List<CKA>(); pubKeyAttrsToRead.Add(CKA.CKA_KEY_TYPE); pubKeyAttrsToRead.Add(CKA.CKA_MODULUS); pubKeyAttrsToRead.Add(CKA.CKA_PUBLIC_EXPONENT); // Read public key attributes List<ObjectAttribute> publicKeyAttributes = session.GetAttributeValue(publicKeyHandle, pubKeyAttrsToRead); if (CKK.CKK_RSA != (CKK)publicKeyAttributes[0].GetValueAsUlong()) throw new NotSupportedException("Currently only RSA keys are supported"); // Create instance of RsaKeyParameters class usable for BouncyCastle BigInteger modulus = new BigInteger(1, publicKeyAttributes[1].GetValueAsByteArray()); BigInteger publicExponent = new BigInteger(1, publicKeyAttributes[2].GetValueAsByteArray()); RsaKeyParameters publicKeyParameters = new RsaKeyParameters(false, modulus, publicExponent); // Determine algorithms Mechanism mechanism = null; string signatureAlgorihtm = null; switch (hashAlgorithm) { case HashAlgorithm.SHA1: mechanism = new Mechanism(CKM.CKM_SHA1_RSA_PKCS); signatureAlgorihtm = PkcsObjectIdentifiers.Sha1WithRsaEncryption.Id; break; case HashAlgorithm.SHA256: mechanism = new Mechanism(CKM.CKM_SHA256_RSA_PKCS); signatureAlgorihtm = PkcsObjectIdentifiers.Sha256WithRsaEncryption.Id; break; case HashAlgorithm.SHA384: mechanism = new Mechanism(CKM.CKM_SHA384_RSA_PKCS); signatureAlgorihtm = PkcsObjectIdentifiers.Sha384WithRsaEncryption.Id; break; case HashAlgorithm.SHA512: mechanism = new Mechanism(CKM.CKM_SHA512_RSA_PKCS); signatureAlgorihtm = PkcsObjectIdentifiers.Sha512WithRsaEncryption.Id; break; } // Generate and sign PKCS#10 request Pkcs10CertificationRequestDelaySigned pkcs10 = new Pkcs10CertificationRequestDelaySigned(signatureAlgorihtm, new X509Name(subjectDistinguishedName), publicKeyParameters, null); byte[] signature = session.Sign(mechanism, privateKeyHandle, pkcs10.GetDataToSign()); pkcs10.SignRequest(new DerBitString(signature)); return pkcs10.GetDerEncoded(); }
/// <summary> /// Generates asymetric key pair /// </summary> /// <param name='session'>Read-write session with user logged in</param> /// <param name='ckaLabel'>Value of CKA_LABEL attribute for generated keys</param> /// <param name='ckaId'>Value of CKA_ID attribute for generated keys</param> /// <param name='publicKeyHandle'>Output parameter for public key object handle</param> /// <param name='privateKeyHandle'>Output parameter for private key object handle</param> public static void GenerateKeyPair(Session session, string ckaLabel, byte[] ckaId, out ObjectHandle publicKeyHandle, out ObjectHandle privateKeyHandle) { // Prepare attribute template of new public key List<ObjectAttribute> publicKeyAttributes = new List<ObjectAttribute>(); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_TOKEN, true)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_PRIVATE, false)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_LABEL, ckaLabel)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_ID, ckaId)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_ENCRYPT, true)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_VERIFY, true)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_VERIFY_RECOVER, true)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_WRAP, true)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_MODULUS_BITS, 1024)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_PUBLIC_EXPONENT, new byte[] { 0x01, 0x00, 0x01 })); // Prepare attribute template of new private key List<ObjectAttribute> privateKeyAttributes = new List<ObjectAttribute>(); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_TOKEN, true)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_PRIVATE, true)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_LABEL, ckaLabel)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_ID, ckaId)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_SENSITIVE, true)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_DECRYPT, true)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_SIGN, true)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_SIGN_RECOVER, true)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_UNWRAP, true)); // Specify key generation mechanism Mechanism mechanism = new Mechanism(CKM.CKM_RSA_PKCS_KEY_PAIR_GEN); // Generate key pair session.GenerateKeyPair(mechanism, publicKeyAttributes, privateKeyAttributes, out publicKeyHandle, out privateKeyHandle); }
/// <summary> /// Imports the certificate into the PKCS#11 compatible device and pairs it with the corresponding private key /// </summary> /// <param name="session">Session with user logged in</param> /// <param name="certificate">Certificate that should be imported</param> /// <returns>Handle of created certificate object</returns> public static ObjectHandle ImportCertificate(Session session, byte[] certificate) { // Parse certificate X509CertificateParser x509CertificateParser = new X509CertificateParser(); X509Certificate x509Certificate = x509CertificateParser.ReadCertificate(certificate); // Get public key from certificate AsymmetricKeyParameter pubKeyParams = x509Certificate.GetPublicKey(); if (!(pubKeyParams is RsaKeyParameters)) throw new NotSupportedException("Currently only RSA keys are supported"); RsaKeyParameters rsaPubKeyParams = (RsaKeyParameters)pubKeyParams; // Find corresponding private key List<ObjectAttribute> privKeySearchTemplate = new List<ObjectAttribute>(); privKeySearchTemplate.Add(new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY)); privKeySearchTemplate.Add(new ObjectAttribute(CKA.CKA_KEY_TYPE, CKK.CKK_RSA)); privKeySearchTemplate.Add(new ObjectAttribute(CKA.CKA_MODULUS, rsaPubKeyParams.Modulus.ToByteArrayUnsigned())); privKeySearchTemplate.Add(new ObjectAttribute(CKA.CKA_PUBLIC_EXPONENT, rsaPubKeyParams.Exponent.ToByteArrayUnsigned())); List<ObjectHandle> foundObjects = session.FindAllObjects(privKeySearchTemplate); if (foundObjects.Count != 1) throw new ObjectNotFoundException("Corresponding RSA private key not found"); ObjectHandle privKeyObjectHandle = foundObjects[0]; // Read CKA_LABEL and CKA_ID attributes of private key List<CKA> privKeyAttrsToRead = new List<CKA>(); privKeyAttrsToRead.Add(CKA.CKA_LABEL); privKeyAttrsToRead.Add(CKA.CKA_ID); List<ObjectAttribute> privKeyAttributes = session.GetAttributeValue(privKeyObjectHandle, privKeyAttrsToRead); // Define attributes of new certificate object List<ObjectAttribute> certificateAttributes = new List<ObjectAttribute>(); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_CERTIFICATE)); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_TOKEN, true)); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_PRIVATE, false)); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_MODIFIABLE, true)); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_LABEL, privKeyAttributes[0].GetValueAsString())); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_CERTIFICATE_TYPE, CKC.CKC_X_509)); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_TRUSTED, false)); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_SUBJECT, x509Certificate.SubjectDN.GetDerEncoded())); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_ID, privKeyAttributes[1].GetValueAsByteArray())); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_ISSUER, x509Certificate.IssuerDN.GetDerEncoded())); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_SERIAL_NUMBER, new DerInteger(x509Certificate.SerialNumber).GetDerEncoded())); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_VALUE, x509Certificate.GetEncoded())); // Create certificate object return session.CreateObject(certificateAttributes); }
/// <summary> /// Imports trusted certificate into the PKCS#11 compatible device /// </summary> /// <param name="session">Session with user logged in</param> /// <param name="certificate">Certificate that should be imported</param> /// <returns>Handle of created certificate object</returns> public static ObjectHandle ImportTrustedCertificate(Session session, byte[] certificate) { // Choose unique values for CKA_LABEL and CKA_ID attributes string ckaLabel = Guid.NewGuid().ToString(); byte[] ckaId = session.GenerateRandom(20); return ImportTrustedCertificate(session, certificate, ckaLabel, ckaId); }
/// <summary> /// Imports trusted certificate into the PKCS#11 compatible device /// </summary> /// <param name="session">Session with user logged in</param> /// <param name="certificate">Certificate that should be imported</param> /// <param name="ckaLabel">Value of CKA_LABEL attribute</param> /// <param name="ckaId">Value of CKA_ID attribute</param> /// <returns>Handle of created certificate object</returns> public static ObjectHandle ImportTrustedCertificate(Session session, byte[] certificate, string ckaLabel, byte[] ckaId) { // Parse certificate X509CertificateParser x509CertificateParser = new X509CertificateParser(); X509Certificate x509Certificate = x509CertificateParser.ReadCertificate(certificate); // Define attributes of new certificate object List<ObjectAttribute> certificateAttributes = new List<ObjectAttribute>(); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_CERTIFICATE)); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_TOKEN, true)); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_PRIVATE, false)); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_MODIFIABLE, true)); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_LABEL, ckaLabel)); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_CERTIFICATE_TYPE, CKC.CKC_X_509)); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_TRUSTED, true)); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_SUBJECT, x509Certificate.SubjectDN.GetDerEncoded())); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_ID, ckaId)); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_ISSUER, x509Certificate.IssuerDN.GetDerEncoded())); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_SERIAL_NUMBER, new DerInteger(x509Certificate.SerialNumber).GetDerEncoded())); certificateAttributes.Add(new ObjectAttribute(CKA.CKA_VALUE, x509Certificate.GetEncoded())); // Create certificate object return session.CreateObject(certificateAttributes); }
/// <summary> /// Creates the data object. /// </summary> /// <param name='session'>Read-write session with user logged in</param> /// <returns>Object handle</returns> public static ObjectHandle CreateDataObject(Session session) { // Prepare attribute template of new data object List<ObjectAttribute> objectAttributes = new List<ObjectAttribute>(); objectAttributes.Add(new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_DATA)); objectAttributes.Add(new ObjectAttribute(CKA.CKA_TOKEN, true)); objectAttributes.Add(new ObjectAttribute(CKA.CKA_APPLICATION, Settings.ApplicationName)); objectAttributes.Add(new ObjectAttribute(CKA.CKA_LABEL, Settings.ApplicationName)); objectAttributes.Add(new ObjectAttribute(CKA.CKA_VALUE, "Data object content")); // Create object return session.CreateObject(objectAttributes); }
/// <summary> /// Generates asymetric key pair. /// </summary> /// <param name='session'>Read-write session with user logged in</param> /// <param name='publicKeyHandle'>Output parameter for public key object handle</param> /// <param name='privateKeyHandle'>Output parameter for private key object handle</param> public static void GenerateKeyPair(Session session, out ObjectHandle publicKeyHandle, out ObjectHandle privateKeyHandle) { // The CKA_ID attribute is intended as a means of distinguishing multiple key pairs held by the same subject byte[] ckaId = session.GenerateRandom(20); // Prepare attribute template of new public key List<ObjectAttribute> publicKeyAttributes = new List<ObjectAttribute>(); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_TOKEN, true)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_PRIVATE, false)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_LABEL, Settings.ApplicationName)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_ID, ckaId)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_ENCRYPT, true)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_VERIFY, true)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_VERIFY_RECOVER, true)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_WRAP, true)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_MODULUS_BITS, 1024)); publicKeyAttributes.Add(new ObjectAttribute(CKA.CKA_PUBLIC_EXPONENT, new byte[] { 0x01, 0x00, 0x01 })); // Prepare attribute template of new private key List<ObjectAttribute> privateKeyAttributes = new List<ObjectAttribute>(); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_TOKEN, true)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_PRIVATE, true)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_LABEL, Settings.ApplicationName)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_ID, ckaId)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_SENSITIVE, true)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_DECRYPT, true)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_SIGN, true)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_SIGN_RECOVER, true)); privateKeyAttributes.Add(new ObjectAttribute(CKA.CKA_UNWRAP, true)); // Specify key generation mechanism Mechanism mechanism = new Mechanism(CKM.CKM_RSA_PKCS_KEY_PAIR_GEN); // Generate key pair session.GenerateKeyPair(mechanism, publicKeyAttributes, privateKeyAttributes, out publicKeyHandle, out privateKeyHandle); }
/// <summary> /// Generates symetric key. /// </summary> /// <param name='session'>Read-write session with user logged in</param> /// <returns>Object handle</returns> public static ObjectHandle GenerateKey(Session session) { // Prepare attribute template of new key List<ObjectAttribute> objectAttributes = new List<ObjectAttribute>(); objectAttributes.Add(new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_SECRET_KEY)); objectAttributes.Add(new ObjectAttribute(CKA.CKA_KEY_TYPE, CKK.CKK_DES3)); objectAttributes.Add(new ObjectAttribute(CKA.CKA_ENCRYPT, true)); objectAttributes.Add(new ObjectAttribute(CKA.CKA_DECRYPT, true)); objectAttributes.Add(new ObjectAttribute(CKA.CKA_DERIVE, true)); objectAttributes.Add(new ObjectAttribute(CKA.CKA_EXTRACTABLE, true)); // Specify key generation mechanism Mechanism mechanism = new Mechanism(CKM.CKM_DES3_KEY_GEN); // Generate key return session.GenerateKey(mechanism, objectAttributes); }
/// <summary> /// Initializes a new instance of the Pkcs11Signature class /// </summary> /// <param name="libraryPath">Path to the unmanaged PCKS#11 library</param> /// <param name="tokenSerial">Serial number of the token (smartcard) that contains signing key. May be null if tokenLabel is specified.</param> /// <param name="tokenLabel">Label of of the token (smartcard) that contains signing key. May be null if tokenSerial is specified.</param> /// <param name="pin">PIN for the token (smartcard)</param> /// <param name="ckaLabel">Label (value of CKA_LABEL attribute) of the private key used for signing. May be null if ckaId is specified.</param> /// <param name="ckaId">Identifier (value of CKA_ID attribute) of the private key used for signing. May be null if ckaLabel is specified.</param> /// <param name="hashAlgorihtm">Hash algorihtm used for the signature creation</param> private void InitializePkcs7RsaSignature(string libraryPath, string tokenSerial, string tokenLabel, byte[] pin, string ckaLabel, byte[] ckaId, HashAlgorithm hashAlgorihtm) { try { if (string.IsNullOrEmpty(libraryPath)) throw new ArgumentNullException("libraryPath"); _pkcs11 = new Pkcs11(libraryPath, true); _slot = FindSlot(tokenSerial, tokenLabel); if (_slot == null) throw new TokenNotFoundException(string.Format("Token with serial \"{0}\" and label \"{1}\" was not found", tokenSerial, tokenLabel)); _session = _slot.OpenSession(true); _session.Login(CKU.CKU_USER, pin); _privateKeyHandle = FindPrivateKey(ckaLabel, ckaId); _ckaLabel = ckaLabel; _ckaId = ckaId; if (!Enum.IsDefined(typeof(HashAlgorithm), hashAlgorihtm)) throw new ArgumentException("Invalid hash algorithm specified"); _hashAlgorihtm = hashAlgorihtm; } catch { if (_session != null) { _session.Dispose(); _session = null; } if (_pkcs11 != null) { _pkcs11.Dispose(); _pkcs11 = null; } throw; } }
/// <summary> /// Disposes object /// </summary> /// <param name="disposing">Flag indicating whether managed resources should be disposed</param> protected virtual void Dispose(bool disposing) { if (!this._disposed) { // Dispose managed objects if (disposing) { _allCertificates = null; _signingCertificate = null; _hashAlgorihtm = HashAlgorithm.SHA512; _ckaId = null; _ckaLabel = null; _privateKeyHandle = null; if (_session != null) { try { _session.Logout(); } catch { // Any exceptions can be safely ignored here } _session.Dispose(); _session = null; } _slot = null; if (_pkcs11 != null) { _pkcs11.Dispose(); _pkcs11 = null; } } // Dispose unmanaged objects _disposed = true; } }
/// <summary> /// Closes a session between an application and a token /// </summary> /// <param name="session">Session</param> public void CloseSession(Session session) { if (session == null) throw new ArgumentNullException("session"); session.CloseSession(); }