1 people like it.

Using PKCS#11 interface via F#, Pkcs11Interop and Soft-HSM2

Initial example of using PKCS#11 from F#

  1: 
  2: 
  3: 
  4: 
  5: 
  6: 
  7: 
  8: 
  9: 
 10: 
 11: 
 12: 
 13: 
 14: 
 15: 
 16: 
 17: 
 18: 
 19: 
 20: 
 21: 
 22: 
 23: 
 24: 
 25: 
 26: 
 27: 
 28: 
 29: 
 30: 
 31: 
 32: 
 33: 
 34: 
 35: 
 36: 
 37: 
 38: 
 39: 
 40: 
 41: 
 42: 
 43: 
 44: 
 45: 
 46: 
 47: 
 48: 
 49: 
 50: 
 51: 
 52: 
 53: 
 54: 
 55: 
 56: 
 57: 
 58: 
 59: 
 60: 
 61: 
 62: 
 63: 
 64: 
 65: 
 66: 
 67: 
 68: 
 69: 
 70: 
 71: 
 72: 
 73: 
 74: 
 75: 
 76: 
 77: 
 78: 
 79: 
 80: 
 81: 
 82: 
 83: 
 84: 
 85: 
 86: 
 87: 
 88: 
 89: 
 90: 
 91: 
 92: 
 93: 
 94: 
 95: 
 96: 
 97: 
 98: 
 99: 
100: 
// Install Soft-HSM2, e.g. https://github.com/disig/SoftHSM2-for-Windows/releases/download/v2.5.0/SoftHSM2-2.5.0.msi
// Easiest way is to copy driver from lib-folder and config from etc-folder, all to bin-folder
// then create user and pin, e.g. 1234
// softhsm2-util.exe --init-token --slot 0 --label testing
let userPin = "1234" 

// Reference @"Pkcs11Interop.dll", e.g.:
//#r "nuget:Pkcs11Interop"

open System
open Net.Pkcs11Interop.Common
open Net.Pkcs11Interop.HighLevelAPI

let pkcs11LibraryPath = @"C:\SoftHSM2\bin\softhsm2-x64.dll"
let factories = new Pkcs11InteropFactories()
let applicationName = "test"

let publicKeyAttributes(ckaId:byte[]) = 
    [   factories.ObjectAttributeFactory.Create(CKA.CKA_TOKEN, true)
        factories.ObjectAttributeFactory.Create(CKA.CKA_PRIVATE, false)
        factories.ObjectAttributeFactory.Create(CKA.CKA_LABEL, applicationName)
        factories.ObjectAttributeFactory.Create(CKA.CKA_ID, ckaId)
        factories.ObjectAttributeFactory.Create(CKA.CKA_ENCRYPT, true)
        factories.ObjectAttributeFactory.Create(CKA.CKA_VERIFY, true)
        factories.ObjectAttributeFactory.Create(CKA.CKA_VERIFY_RECOVER, true)
        factories.ObjectAttributeFactory.Create(CKA.CKA_WRAP, true)
        factories.ObjectAttributeFactory.Create(CKA.CKA_MODULUS_BITS, 1024UL)
        factories.ObjectAttributeFactory.Create(CKA.CKA_PUBLIC_EXPONENT, [| 0x01uy; 0x00uy; 0x01uy |])
    ] |> ResizeArray

let privateKeyAttributes(ckaId:byte[]) = 
    [   factories.ObjectAttributeFactory.Create(CKA.CKA_TOKEN, true)
        factories.ObjectAttributeFactory.Create(CKA.CKA_PRIVATE, true)
        factories.ObjectAttributeFactory.Create(CKA.CKA_LABEL, applicationName)
        factories.ObjectAttributeFactory.Create(CKA.CKA_ID, ckaId)
        factories.ObjectAttributeFactory.Create(CKA.CKA_SENSITIVE, true)
        factories.ObjectAttributeFactory.Create(CKA.CKA_DECRYPT, true)
        factories.ObjectAttributeFactory.Create(CKA.CKA_SIGN, true)
        factories.ObjectAttributeFactory.Create(CKA.CKA_SIGN_RECOVER, true)
        factories.ObjectAttributeFactory.Create(CKA.CKA_UNWRAP, true)
    ] |> ResizeArray

/// https://github.com/Pkcs11Interop/Pkcs11Interop/blob/master/doc/GETTING_STARTED.md
let displayLibraryInfo() =
    use pkcs11Library = factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, pkcs11LibraryPath, AppType.MultiThreaded)
    let libraryInfo = pkcs11Library.GetInfo()
    printfn "Library Manufacturer: %s" libraryInfo.ManufacturerId
    printfn "Library Description: %s" libraryInfo.LibraryDescription
    printfn "Library Version: %s" libraryInfo.LibraryVersion
    let slots = pkcs11Library.GetSlotList(SlotsType.WithOrWithoutTokenPresent)
    for slot in slots do
        let slotInfo = slot.GetSlotInfo()
        printfn "   Slot Manufacturer: %s" slotInfo.ManufacturerId
        printfn "   Slot Description: %s" slotInfo.SlotDescription
        printfn "   Slot Token present: %b" slotInfo.SlotFlags.TokenPresent
        printfn "   Mechanisms: %s" (String.Join(",", slot.GetMechanismList() |> Seq.toArray))

let signHSM (session:ISession) privateKey content =
    let mechanism = session.Factories.MechanismFactory.Create CKM.CKM_SHA1_RSA_PKCS
    let sourceData = content |> ConvertUtils.Utf8StringToBytes
    let signature = session.Sign(mechanism, privateKey, sourceData)
    signature

let verifyHSM (session:ISession) publicKey content signature =
    let mechanism = session.Factories.MechanismFactory.Create CKM.CKM_SHA1_RSA_PKCS
    let sourceData = content |> ConvertUtils.Utf8StringToBytes
    let isValid = session.Verify(mechanism, publicKey, sourceData, signature)
    if not isValid then
        failwith "Wrong signature"

let authenticateAndRun content =
    use pkcs11Library = factories.Pkcs11LibraryFactory.LoadPkcs11Library(factories, pkcs11LibraryPath, AppType.MultiThreaded)
    let slots = pkcs11Library.GetSlotList(SlotsType.WithOrWithoutTokenPresent)
    let slot = slots.[0]
    use session = slot.OpenSession SessionType.ReadWrite
    do session.Login(CKU.CKU_USER, userPin)
    let ckaId = session.GenerateRandom 20
    let publicKey, privateKey = 
        session.GenerateKeyPair(
            factories.MechanismFactory.Create(CKM.CKM_RSA_PKCS_KEY_PAIR_GEN),
            publicKeyAttributes ckaId, privateKeyAttributes ckaId)

    let signature = signHSM session privateKey content

    // do verifyHSM session publicKey content signature

    session.DestroyObject privateKey
    session.DestroyObject publicKey
    session.Logout()
    signature

[<EntryPoint>]
let main argv =
    do displayLibraryInfo()
    printfn "-----------------------"
    let payload =  "Hello World!"
    let signature = authenticateAndRun payload
    printfn "-----------------------"
    printfn "Signature: %O" (System.Text.Encoding.UTF8.GetString(signature, 0, signature.Length))
    0
val userPin : string
namespace System
Multiple items
namespace System.Net

--------------------
namespace Net
namespace Net.Pkcs11Interop
namespace Net.Pkcs11Interop.Common
namespace Net.Pkcs11Interop.HighLevelAPI
val pkcs11LibraryPath : string
val factories : Pkcs11InteropFactories
Multiple items
type Pkcs11InteropFactories =
  new : unit -> Pkcs11InteropFactories + 1 overload
  member MechanismFactory : IMechanismFactory
  member MechanismParamsFactory : IMechanismParamsFactory
  member ObjectAttributeFactory : IObjectAttributeFactory
  member ObjectHandleFactory : IObjectHandleFactory
  member Pkcs11LibraryFactory : IPkcs11LibraryFactory
  member SessionFactory : ISessionFactory
  member SlotFactory : ISlotFactory

--------------------
Pkcs11InteropFactories() : Pkcs11InteropFactories
Pkcs11InteropFactories(pkcs11LibraryFactory: Factories.IPkcs11LibraryFactory, slotFactory: Factories.ISlotFactory, sessionFactory: Factories.ISessionFactory, objectAttributeFactory: Factories.IObjectAttributeFactory, objectHandleFactory: Factories.IObjectHandleFactory, mechanismFactory: Factories.IMechanismFactory, mechanismParamsFactory: Factories.IMechanismParamsFactory) : Pkcs11InteropFactories
val applicationName : string
val publicKeyAttributes : ckaId:byte [] -> ResizeArray<IObjectAttribute>
val ckaId : byte []
Multiple items
val byte : value:'T -> byte (requires member op_Explicit)

--------------------
type byte = Byte
type CKA =
  | CKA_CLASS = 0u
  | CKA_TOKEN = 1u
  | CKA_PRIVATE = 2u
  | CKA_LABEL = 3u
  | CKA_APPLICATION = 16u
  | CKA_VALUE = 17u
  | CKA_OBJECT_ID = 18u
  | CKA_CERTIFICATE_TYPE = 128u
  | CKA_ISSUER = 129u
  | CKA_SERIAL_NUMBER = 130u
  ...
field CKA.CKA_TOKEN: CKA = 1u
field CKA.CKA_PRIVATE: CKA = 2u
field CKA.CKA_LABEL: CKA = 3u
field CKA.CKA_ID: CKA = 258u
field CKA.CKA_ENCRYPT: CKA = 260u
field CKA.CKA_VERIFY: CKA = 266u
field CKA.CKA_VERIFY_RECOVER: CKA = 267u
field CKA.CKA_WRAP: CKA = 262u
field CKA.CKA_MODULUS_BITS: CKA = 289u
field CKA.CKA_PUBLIC_EXPONENT: CKA = 290u
type ResizeArray<'T> = Collections.Generic.List<'T>
val privateKeyAttributes : ckaId:byte [] -> ResizeArray<IObjectAttribute>
field CKA.CKA_SENSITIVE: CKA = 259u
field CKA.CKA_DECRYPT: CKA = 261u
field CKA.CKA_SIGN: CKA = 264u
field CKA.CKA_SIGN_RECOVER: CKA = 265u
field CKA.CKA_UNWRAP: CKA = 263u
val displayLibraryInfo : unit -> unit


 https://github.com/Pkcs11Interop/Pkcs11Interop/blob/master/doc/GETTING_STARTED.md
val pkcs11Library : IPkcs11Library
type AppType =
  | MultiThreaded = 0
  | SingleThreaded = 1
field AppType.MultiThreaded: AppType = 0
val libraryInfo : ILibraryInfo
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
val slots : Collections.Generic.List<ISlot>
type SlotsType =
  | WithTokenPresent = 0
  | WithOrWithoutTokenPresent = 1
field SlotsType.WithOrWithoutTokenPresent: SlotsType = 1
val slot : ISlot
val slotInfo : ISlotInfo
Multiple items
type String =
  new : value:char[] -> string + 8 overloads
  member Chars : int -> char
  member Clone : unit -> obj
  member CompareTo : value:obj -> int + 1 overload
  member Contains : value:string -> bool + 3 overloads
  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
  member EndsWith : value:string -> bool + 3 overloads
  member EnumerateRunes : unit -> StringRuneEnumerator
  member Equals : obj:obj -> bool + 2 overloads
  member GetEnumerator : unit -> CharEnumerator
  ...

--------------------
String(value: char []) : String
String(value: nativeptr<char>) : String
String(value: nativeptr<sbyte>) : String
String(value: ReadOnlySpan<char>) : String
String(c: char, count: int) : String
String(value: char [], startIndex: int, length: int) : String
String(value: nativeptr<char>, startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int) : String
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : String
String.Join(separator: string, values: Collections.Generic.IEnumerable<string>) : string
String.Join<'T>(separator: string, values: Collections.Generic.IEnumerable<'T>) : string
String.Join(separator: string, [<ParamArray>] values: obj []) : string
String.Join(separator: string, [<ParamArray>] value: string []) : string
String.Join<'T>(separator: char, values: Collections.Generic.IEnumerable<'T>) : string
String.Join(separator: char, [<ParamArray>] values: obj []) : string
String.Join(separator: char, [<ParamArray>] value: string []) : string
String.Join(separator: string, value: string [], startIndex: int, count: int) : string
String.Join(separator: char, value: string [], startIndex: int, count: int) : string
module Seq

from Microsoft.FSharp.Collections
val toArray : source:seq<'T> -> 'T []
val signHSM : session:ISession -> privateKey:IObjectHandle -> content:string -> byte []
val session : ISession
type ISession =
  inherit IDisposable
  member CancelFunction : unit -> unit
  member CloseSession : unit -> unit
  member CloseWhenDisposed : bool
  member CopyObject : objectHandle:IObjectHandle * attributes:List<IObjectAttribute> -> IObjectHandle
  member CreateObject : attributes:List<IObjectAttribute> -> IObjectHandle
  member Decrypt : mechanism:IMechanism * keyHandle:IObjectHandle * encryptedData:byte[] -> byte[] + 2 overloads
  member DecryptDigest : digestingMechanism:IMechanism * decryptionMechanism:IMechanism * keyHandle:IObjectHandle * inputStream:Stream * outputStream:Stream -> byte[] + 2 overloads
  member DecryptVerify : verificationMechanism:IMechanism * verificationKeyHandle:IObjectHandle * decryptionMechanism:IMechanism * decryptionKeyHandle:IObjectHandle * data:byte[] * signature:byte[] * decryptedData:byte[] * isValid:bool -> unit + 2 overloads
  member DeriveKey : mechanism:IMechanism * baseKeyHandle:IObjectHandle * attributes:List<IObjectAttribute> -> IObjectHandle
  member DestroyObject : objectHandle:IObjectHandle -> unit
  ...
val privateKey : IObjectHandle
val content : string
val mechanism : IMechanism
type CKM =
  | CKM_RSA_PKCS_KEY_PAIR_GEN = 0u
  | CKM_RSA_PKCS = 1u
  | CKM_RSA_9796 = 2u
  | CKM_RSA_X_509 = 3u
  | CKM_MD2_RSA_PKCS = 4u
  | CKM_MD5_RSA_PKCS = 5u
  | CKM_SHA1_RSA_PKCS = 6u
  | CKM_RIPEMD128_RSA_PKCS = 7u
  | CKM_RIPEMD160_RSA_PKCS = 8u
  | CKM_RSA_PKCS_OAEP = 9u
  ...
field CKM.CKM_SHA1_RSA_PKCS: CKM = 6u
val sourceData : byte []
type ConvertUtils =
  static member Base64StringToBytes : value:string -> byte[]
  static member BoolToBytes : value:bool -> byte[]
  static member BytesToBase64String : value:byte[] -> string
  static member BytesToBool : value:byte[] -> bool
  static member BytesToHexString : value:byte[] -> string
  static member BytesToUtf8String : value:byte[] -> string + 2 overloads
  static member HexStringToBytes : value:string -> byte[]
  static member UInt32FromBytes : value:byte[] -> uint32
  static member UInt32FromCKA : value:CKA -> uint32
  static member UInt32FromCKC : value:CKC -> uint32
  ...
ConvertUtils.Utf8StringToBytes(value: string) : byte []
ConvertUtils.Utf8StringToBytes(value: string, outputLength: int, paddingByte: byte) : byte []
val signature : byte []
val verifyHSM : session:ISession -> publicKey:IObjectHandle -> content:string -> signature:byte [] -> unit
val publicKey : IObjectHandle
val isValid : bool
val not : value:bool -> bool
val failwith : message:string -> 'T
val authenticateAndRun : content:string -> byte []
type SessionType =
  | ReadOnly = 0
  | ReadWrite = 1
field SessionType.ReadWrite: SessionType = 1
type CKU =
  | CKU_SO = 0u
  | CKU_USER = 1u
  | CKU_CONTEXT_SPECIFIC = 2u
field CKU.CKU_USER: CKU = 1u
field CKM.CKM_RSA_PKCS_KEY_PAIR_GEN: CKM = 0u
Multiple items
type EntryPointAttribute =
  inherit Attribute
  new : unit -> EntryPointAttribute

--------------------
new : unit -> EntryPointAttribute
val main : argv:string [] -> int
val argv : string []
val payload : string
namespace System.Text
type Encoding =
  member BodyName : string
  member Clone : unit -> obj
  member CodePage : int
  member DecoderFallback : DecoderFallback with get, set
  member EncoderFallback : EncoderFallback with get, set
  member EncodingName : string
  member Equals : value:obj -> bool
  member GetByteCount : chars:char[] -> int + 5 overloads
  member GetBytes : chars:char[] -> byte[] + 7 overloads
  member GetCharCount : bytes:byte[] -> int + 3 overloads
  ...
property Text.Encoding.UTF8: Text.Encoding with get
Raw view Test code New version

More information

Link:http://fssnip.net/7ZJ
Posted:3 years ago
Author:Tuomas Hietanen
Tags: cryptography , pkcs#11