1 people like it.
Like the snippet!
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
More information