pkcs11 0.1.0-x86-mswin32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,90 @@
1
+ module PKCS11
2
+ # Each slot corresponds to a physical reader or other device interface.
3
+ # It may contain a token.
4
+ class Slot
5
+ def initialize(pkcs11, slot) # :nodoc:
6
+ @pk, @slot = pkcs11, slot
7
+ end
8
+
9
+ # The slot handle.
10
+ def to_int
11
+ @slot
12
+ end
13
+ alias to_i to_int
14
+
15
+ def inspect # :nodoc:
16
+ "#<#{self.class} #{@slot.inspect}>"
17
+ end
18
+
19
+ # Obtains information about a particular slot in the system.
20
+ def C_GetSlotInfo
21
+ @pk.C_GetSlotInfo(@slot)
22
+ end
23
+ alias info C_GetSlotInfo
24
+
25
+ # Obtains information about a particular token in the system.
26
+ def C_GetTokenInfo
27
+ @pk.C_GetTokenInfo(@slot)
28
+ end
29
+ alias token_info C_GetTokenInfo
30
+
31
+ # Waits for a slot event, such as token insertion or token removal, to
32
+ # occur. flags determines whether or not the C_WaitForSlotEvent call blocks (i.e., waits
33
+ # for a slot event to occur);
34
+ def C_WaitForSlotEvent(flags)
35
+ @pk.C_WaitForSlotEvent(@slot, flags)
36
+ end
37
+ alias wait_for_event C_WaitForSlotEvent
38
+
39
+ # C_GetMechanismList is used to obtain a list of mechanism types supported by a token.
40
+ def C_GetMechanismList
41
+ @pk.C_GetMechanismList(@slot).map{|mech|
42
+ Mechanism.new MECHANISMS, mech
43
+ }
44
+ end
45
+ alias mechanisms C_GetMechanismList
46
+
47
+ # Obtains information about a particular mechanism possibly
48
+ # supported by a token.
49
+ def C_GetMechanismInfo(mechanism)
50
+ @pk.C_GetMechanismInfo(@slot, Session.hash_to_mechanism(mechanism))
51
+ end
52
+ alias mechanism_info C_GetMechanismInfo
53
+
54
+ # Initializes a token. pin is the SO’s initial PIN; label is the label of the token (max 32-byte). This standard allows PIN
55
+ # values to contain any valid UTF8 character, but the token may impose subset restrictions.
56
+ def C_InitToken(pin, label)
57
+ @pk.C_InitToken(@slot, pin, label.ljust(32, " "))
58
+ end
59
+ alias init_token C_InitToken
60
+
61
+ # Opens a Session between an application and a token in a particular slot.
62
+ #
63
+ # flags:: indicates the type of session. Default is read-only,
64
+ # use <tt>CKF_SERIAL_SESSION | CKF_RW_SESSION</tt> for read-write session.
65
+ #
66
+ # * If called with block, yields the block with the session and closes the session
67
+ # when the is finished.
68
+ # * If called without block, returns the session object.
69
+ def C_OpenSession(flags=CKF_SERIAL_SESSION)
70
+ nr = @pk.C_OpenSession(@slot, flags)
71
+ sess = Session.new @pk, nr
72
+ if block_given?
73
+ begin
74
+ yield sess
75
+ ensure
76
+ sess.close
77
+ end
78
+ else
79
+ sess
80
+ end
81
+ end
82
+ alias open C_OpenSession
83
+
84
+ # Closes all sessions an application has with a token.
85
+ def C_CloseAllSessions
86
+ @pk.C_CloseAllSessions(@slot)
87
+ end
88
+ alias close_all_sessions C_CloseAllSessions
89
+ end
90
+ end
@@ -0,0 +1,90 @@
1
+ require "pkcs11"
2
+ require "openssl"
3
+
4
+ LIBSOFTOKEN3_SO = "libsoftokn3.so"
5
+ LIBNSS_PATHS = %w(
6
+ /usr/lib64 /usr/lib/ /usr/lib64/nss /usr/lib/nss
7
+ )
8
+ unless so_path = ARGV.shift
9
+ paths = LIBNSS_PATHS.collect{|path| File.join(path, LIBSOFTOKEN3_SO) }
10
+ so_path = paths.find{|path| File.exist?(path) }
11
+ end
12
+
13
+ dir = Dir.glob(File.expand_path("~/.mozilla/firefox/*.default")).first
14
+ NSS_INIT_ARGS = [
15
+ "configDir='#{dir}'",
16
+ "secmod='secmod.db'",
17
+ "flags='readOnly'",
18
+ ]
19
+
20
+ args = PKCS11::CK_C_INITIALIZE_ARGS.new
21
+ args.flags = 0
22
+ args.pReserved = NSS_INIT_ARGS.join(" ")
23
+
24
+ pk11 = PKCS11.new(so_path, args)
25
+ info = pk11.C_GetInfo
26
+ p [
27
+ info.cryptokiVersion, info.manufacturerID, info.flags,
28
+ info.libraryDescription, info.libraryVersion
29
+ ]
30
+
31
+ slots = pk11.C_GetSlotList(false)
32
+ p slots
33
+
34
+ slot = 2
35
+ sinfo = pk11.C_GetSlotInfo(slot)
36
+ p [
37
+ sinfo.slotDescription, sinfo.manufacturerID, sinfo.flags,
38
+ sinfo.hardwareVersion, sinfo.firmwareVersion
39
+ ]
40
+ mechanisms = pk11.C_GetMechanismList(slot)
41
+ mechanisms.each do |m|
42
+ p PKCS11::MECHANISMS[m] || m
43
+ end
44
+
45
+ flags = PKCS11::CKF_SERIAL_SESSION | PKCS11::CKF_RW_SESSION
46
+ session = pk11.C_OpenSession(slot, flags)
47
+ p [:session, session]
48
+ pk11.C_Login(session, PKCS11::CKU_USER, "")
49
+
50
+ find_template = [
51
+ PKCS11::CK_ATTRIBUTE.new(PKCS11::CKA_CLASS, PKCS11::CKO_CERTIFICATE),
52
+ ]
53
+ p pk11.C_FindObjectsInit(session, find_template)
54
+ objs = pk11.C_FindObjects(session, 128)
55
+ objs.each do |handle|
56
+ template = [
57
+ PKCS11::CK_ATTRIBUTE.new(PKCS11::CKA_SUBJECT, nil),
58
+ ]
59
+ attrs = pk11.C_GetAttributeValue(session, handle, template)
60
+ attrs.each do |attr|
61
+ p OpenSSL::X509::Name.new(attr.value)
62
+ end
63
+ end
64
+ objs = pk11.C_FindObjectsFinal(session)
65
+
66
+ find_template = [
67
+ PKCS11::CK_ATTRIBUTE.new(PKCS11::CKA_CLASS, PKCS11::CKO_PRIVATE_KEY),
68
+ PKCS11::CK_ATTRIBUTE.new(PKCS11::CKA_KEY_TYPE, PKCS11::CKK_RSA),
69
+ ]
70
+ p pk11.C_FindObjectsInit(session, find_template)
71
+ objs = pk11.C_FindObjects(session, 128)
72
+ objs.each do |handle|
73
+ template = [
74
+ PKCS11::CK_ATTRIBUTE.new(PKCS11::CKA_CLASS, nil),
75
+ PKCS11::CK_ATTRIBUTE.new(PKCS11::CKA_KEY_TYPE, nil),
76
+ PKCS11::CK_ATTRIBUTE.new(PKCS11::CKA_ID, nil),
77
+ PKCS11::CK_ATTRIBUTE.new(PKCS11::CKA_SIGN, nil),
78
+ PKCS11::CK_ATTRIBUTE.new(PKCS11::CKA_SIGN_RECOVER, nil),
79
+ PKCS11::CK_ATTRIBUTE.new(PKCS11::CKA_DECRYPT, nil),
80
+ PKCS11::CK_ATTRIBUTE.new(PKCS11::CKA_EXTRACTABLE, nil),
81
+ ]
82
+ attrs = pk11.C_GetAttributeValue(session, handle, template)
83
+ attrs.each do |attr|
84
+ p [PKCS11::ATTRIBUTES[attr.type], attr.value]
85
+ end
86
+ end
87
+ objs = pk11.C_FindObjectsFinal(session)
88
+
89
+ pk11.C_Logout(session)
90
+ pk11.C_CloseSession(session)
data/sample/nssckbi.rb ADDED
@@ -0,0 +1,51 @@
1
+ require "pkcs11"
2
+ require "openssl"
3
+
4
+ LIBNSSCKBI_SO = "libnssckbi.so"
5
+ LIBNSS_PATHS = %w(
6
+ /usr/lib64 /usr/lib /usr/lib64/nss /usr/lib/nss
7
+ /usr/lib64/xulrunner /usr/lib/xulrunner
8
+ /usr/local/lib64/xulrunner /usr/local/lib/xulrunner
9
+ )
10
+ unless so_name = ARGV[0]
11
+ paths = LIBNSS_PATHS.collect{|path| File.join(path, LIBNSSCKBI_SO) }
12
+ so_name = paths.find{|path| File.exist?(path) }
13
+ end
14
+
15
+ pkcs11 = PKCS11.new(so_name)
16
+ slot = pkcs11.C_GetSlotList(true).first
17
+ session = pkcs11.C_OpenSession(slot, PKCS11::CKF_SERIAL_SESSION)
18
+
19
+ pkcs11.C_FindObjectsInit(session, [
20
+ PKCS11::CK_ATTRIBUTE.new(PKCS11::CKA_CLASS, PKCS11::CKO_CERTIFICATE)
21
+ ])
22
+ handles = pkcs11.C_FindObjects(session, 1000)
23
+ pkcs11.C_FindObjectsFinal(session)
24
+
25
+ attribute_types = [
26
+ PKCS11::CKA_CLASS,
27
+ PKCS11::CKA_TOKEN, PKCS11::CKA_PRIVATE, PKCS11::CKA_MODIFIABLE,
28
+ PKCS11::CKA_LABEL, PKCS11::CKA_CERTIFICATE_TYPE,
29
+ PKCS11::CKA_SUBJECT, PKCS11::CKA_ID, PKCS11::CKA_ISSUER,
30
+ PKCS11::CKA_SERIAL_NUMBER, PKCS11::CKA_VALUE,
31
+ ]
32
+ template = attribute_types.collect{|a| PKCS11::CK_ATTRIBUTE.new(a, nil) }
33
+ handles.each do |handle|
34
+ attributes = pkcs11.C_GetAttributeValue(session, handle, template)
35
+ attributes.each do |attribute|
36
+ type_name = PKCS11::ATTRIBUTES[attribute.type]
37
+ case attribute.type
38
+ when PKCS11::CKA_LABEL
39
+ p [type_name, attribute.value]
40
+ when PKCS11::CKA_SUBJECT, PKCS11::CKA_ISSUER
41
+ p [type_name, OpenSSL::X509::Name.new(attribute.value)]
42
+ when PKCS11::CKA_SERIAL_NUMBER
43
+ serial = OpenSSL::ASN1.decode(attribute.value).value rescue nil
44
+ attribute.value.unpack("w").first
45
+ p [type_name, serial]
46
+ when PKCS11::CKA_VALUE
47
+ cert = OpenSSL::X509::Certificate.new(attribute.value)
48
+ p [cert.serial, cert.not_before, cert.not_after]
49
+ end
50
+ end
51
+ end
Binary file
Binary file
Binary file
data/test/helper.rb ADDED
@@ -0,0 +1,43 @@
1
+ require "openssl"
2
+
3
+ def open_softokn
4
+
5
+ if RUBY_PLATFORM =~ /win32/
6
+ lLIBSOFTOKEN3_SO = "softokn3.dll"
7
+
8
+ # Try to find the firefox path.
9
+ unless ENV['SOFTOKN_PATH']
10
+ require 'win32/registry'
11
+ begin
12
+ firefox_path = Win32::Registry::HKEY_LOCAL_MACHINE.open('SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\firefox.exe'){|reg|
13
+ reg.read('Path')[1]
14
+ }
15
+ rescue Win32::Registry::Error
16
+ end
17
+ if firefox_path
18
+ ENV['Path'] = ENV['Path'] + ";" + firefox_path
19
+ so_path = File.join(firefox_path, lLIBSOFTOKEN3_SO)
20
+ end
21
+ end
22
+ else
23
+ lLIBSOFTOKEN3_SO = "libsoftokn3.so"
24
+ lLIBNSS_PATHS = %w(
25
+ /usr/lib64 /usr/lib/ /usr/lib64/nss /usr/lib/nss
26
+ )
27
+ unless so_path = ENV['SOFTOKN_PATH']
28
+ paths = lLIBNSS_PATHS.collect{|path| File.join(path, lLIBSOFTOKEN3_SO) }
29
+ so_path = paths.find{|path| File.exist?(path) }
30
+ end
31
+ end
32
+
33
+ raise "#{lLIBSOFTOKEN3_SO} not found - please install firefox or set ENV['SOFTOKN_PATH']" unless so_path
34
+
35
+ dir = File.join(File.dirname(__FILE__), 'fixtures/softokn')
36
+ nNSS_INIT_ARGS = [
37
+ "configDir='#{dir}'",
38
+ "secmod='secmod.db'",
39
+ "flags='readWrite'",
40
+ ]
41
+
42
+ pk11 = PKCS11.open(so_path, :flags=>0, :pReserved=>nNSS_INIT_ARGS.join(" "))
43
+ end
@@ -0,0 +1,36 @@
1
+ require "test/unit"
2
+ require "pkcs11"
3
+ require "test/helper"
4
+
5
+ class TestPkcs11 < Test::Unit::TestCase
6
+ def setup
7
+ @pk = open_softokn
8
+ end
9
+
10
+ def teardown
11
+ @pk.close
12
+ @pk = nil
13
+ GC.start
14
+ end
15
+
16
+ def pk
17
+ @pk
18
+ end
19
+
20
+ def test_info
21
+ info = pk.info
22
+ assert info.inspect =~ /cryptokiVersion=/, 'There should be a version in the library info'
23
+ end
24
+
25
+ def test_slots
26
+ slots = pk.active_slots
27
+ assert slots.length>=1, 'Hope there is at least one active slot'
28
+ end
29
+
30
+ def test_close
31
+ pk.close
32
+ assert_raise(PKCS11::Error){ pk.info }
33
+ pk.close
34
+ assert_raise(PKCS11::Error){ pk.active_slots }
35
+ end
36
+ end
@@ -0,0 +1,167 @@
1
+ require "test/unit"
2
+ require "pkcs11"
3
+ require "test/helper"
4
+ require "openssl"
5
+
6
+ class TestPkcs11Crypt < Test::Unit::TestCase
7
+ include PKCS11
8
+
9
+ attr_reader :slots
10
+ attr_reader :slot
11
+ attr_reader :session
12
+ attr_reader :rsa_priv_key
13
+ attr_reader :rsa_pub_key
14
+ attr_reader :secret_key
15
+
16
+ def setup
17
+ $pkcs11 ||= open_softokn
18
+ @slots = pk.active_slots
19
+ @slot = slots.last
20
+ @session = slot.open
21
+ session.login(:USER, "")
22
+
23
+ @rsa_pub_key = session.find_objects(:CLASS => CKO_PUBLIC_KEY,
24
+ :KEY_TYPE => CKK_RSA).first
25
+ @rsa_priv_key = session.find_objects(:CLASS => CKO_PRIVATE_KEY,
26
+ :KEY_TYPE => CKK_RSA).first
27
+ @secret_key = session.create_object(
28
+ :CLASS=>CKO_SECRET_KEY,
29
+ :KEY_TYPE=>CKK_DES2,
30
+ :ENCRYPT=>true, :WRAP=>true, :DECRYPT=>true, :UNWRAP=>true, :TOKEN=>false,
31
+ :VALUE=>'0123456789abcdef',
32
+ :LABEL=>'test_secret_key')
33
+ end
34
+
35
+ def teardown
36
+ @secret_key.destroy
37
+ @session.logout
38
+ @session.close
39
+ end
40
+
41
+ def pk
42
+ $pkcs11
43
+ end
44
+
45
+ def test_endecrypt
46
+ plaintext1 = "secret text"
47
+ cryptogram = session.encrypt( :RSA_PKCS, rsa_pub_key, plaintext1)
48
+ assert cryptogram.length>10, 'The cryptogram should contain some data'
49
+ assert_not_equal cryptogram, plaintext1, 'The cryptogram should be different to plaintext'
50
+
51
+ plaintext2 = session.decrypt( :RSA_PKCS, rsa_priv_key, cryptogram)
52
+ assert_equal plaintext1, plaintext2, 'Decrypted plaintext should be the same'
53
+ end
54
+
55
+ def test_sign_verify
56
+ plaintext = "important text"
57
+ signature = session.sign( :SHA1_RSA_PKCS, rsa_priv_key, plaintext)
58
+ assert signature.length>10, 'The signature should contain some data'
59
+
60
+ signature2 = session.sign( :SHA1_RSA_PKCS, rsa_priv_key){|c|
61
+ c.update(plaintext[0..3])
62
+ c.update(plaintext[4..-1])
63
+ }
64
+ assert_equal signature, signature2, 'results of one-step and two-step signatures should be equal'
65
+
66
+ valid = session.verify( :SHA1_RSA_PKCS, rsa_pub_key, signature, plaintext)
67
+ assert valid, 'The signature should be correct'
68
+
69
+ assert_raise(PKCS11::Error, 'The signature should be invalid on different text') do
70
+ session.verify( :SHA1_RSA_PKCS, rsa_pub_key, signature, "modified text")
71
+ end
72
+ end
73
+
74
+ def create_openssl_cipher(pk11_key)
75
+ rsa = OpenSSL::PKey::RSA.new
76
+ rsa.n = OpenSSL::BN.new pk11_key[:MODULUS], 2
77
+ rsa.e = OpenSSL::BN.new pk11_key[:PUBLIC_EXPONENT], 2
78
+ rsa
79
+ end
80
+
81
+ def test_compare_sign_with_openssl
82
+ signature = session.sign( :SHA1_RSA_PKCS, rsa_priv_key, "important text")
83
+
84
+ osslc = create_openssl_cipher rsa_pub_key
85
+ valid = osslc.verify(OpenSSL::Digest::SHA1.new, signature, "important text")
86
+ assert valid, 'The signature should be correct'
87
+ end
88
+
89
+ def test_compare_endecrypt_with_openssl
90
+ plaintext1 = "secret text"
91
+ osslc = create_openssl_cipher rsa_pub_key
92
+ cryptogram = osslc.public_encrypt(plaintext1)
93
+
94
+ plaintext2 = session.decrypt( :RSA_PKCS, rsa_priv_key, cryptogram)
95
+ assert_equal plaintext1, plaintext2, 'Decrypted plaintext should be the same'
96
+ end
97
+
98
+ def test_digest
99
+ plaintext = "secret text"
100
+ digest1 = session.digest( :SHA_1, plaintext)
101
+ digest2 = OpenSSL::Digest::SHA1.new(plaintext).digest
102
+ assert_equal digest1, digest2, 'Digests should be equal'
103
+ digest3 = session.digest(:SHA_1){|c|
104
+ c.update(plaintext[0..3])
105
+ c.update(plaintext[4..-1])
106
+ }
107
+ assert_equal digest1, digest3, 'Digests should be equal'
108
+
109
+ digest3 = session.digest(:SHA256){|c|
110
+ c.update(plaintext)
111
+ c.digest_key(secret_key)
112
+ }
113
+ end
114
+
115
+ def test_wrap_key
116
+ wrapped_key_value = session.wrap_key(:DES3_ECB, secret_key, secret_key)
117
+ assert_equal 16, wrapped_key_value.length, '112 bit 3DES key should have same size wrapped'
118
+
119
+ unwrapped_key = session.unwrap_key(:DES3_ECB, secret_key, wrapped_key_value, :CLASS=>CKO_SECRET_KEY, :KEY_TYPE=>CKK_DES2, :ENCRYPT=>true, :DECRYPT=>true)
120
+
121
+ secret_key_kcv = session.encrypt( :DES3_ECB, secret_key, "\0"*8)
122
+ unwrapped_key_kcv = session.encrypt( :DES3_ECB, unwrapped_key, "\0"*8)
123
+ assert_equal secret_key_kcv, unwrapped_key_kcv, 'Key check values of original and wrapped/unwrapped key should be equal'
124
+ end
125
+
126
+ def test_wrap_private_key
127
+ wrapped_key_value = session.wrap_key({:DES3_CBC_PAD=>"\0"*8}, secret_key, rsa_priv_key)
128
+ assert wrapped_key_value.length>100, 'RSA private key should have bigger size wrapped'
129
+ end
130
+
131
+ def test_generate_secret_key
132
+ key = session.generate_key(:DES2_KEY_GEN,
133
+ {:ENCRYPT=>true, :WRAP=>true, :DECRYPT=>true, :UNWRAP=>true, :TOKEN=>false, :LOCAL=>true})
134
+ assert_equal true, key[:LOCAL], 'Keys created on the token should be marked as local'
135
+ end
136
+
137
+ def test_generate_key_pair
138
+ pub_key, priv_key = session.generate_key_pair(:RSA_PKCS_KEY_PAIR_GEN,
139
+ {:ENCRYPT=>true, :VERIFY=>true, :WRAP=>true, :MODULUS_BITS=>768, :PUBLIC_EXPONENT=>[3].pack("N"), :TOKEN=>false},
140
+ {:PRIVATE=>true, :SUBJECT=>'test', :ID=>[123].pack("n"),
141
+ :SENSITIVE=>true, :DECRYPT=>true, :SIGN=>true, :UNWRAP=>true, :TOKEN=>false, :LOCAL=>true})
142
+
143
+ assert_equal true, priv_key[:LOCAL], 'Private keys created on the token should be marked as local'
144
+ assert_equal priv_key[:CLASS], CKO_PRIVATE_KEY
145
+ assert_equal pub_key[:CLASS], CKO_PUBLIC_KEY
146
+ assert_equal true, priv_key[:SENSITIVE], 'Private key should be sensitive'
147
+ end
148
+
149
+ def test_derive_key
150
+ # Generate DH key for side 1
151
+ key1 = OpenSSL::PKey::DH.new(512)
152
+
153
+ # Generate key side 2 with same prime and base as side 1
154
+ pub_key2, priv_key2 = session.generate_key_pair(:DH_PKCS_KEY_PAIR_GEN,
155
+ {:PRIME=>key1.p.to_s(2), :BASE=>key1.g.to_s(2), :TOKEN=>false},
156
+ {:VALUE_BITS=>512, :DERIVE=>true, :TOKEN=>false})
157
+
158
+ # Derive secret DES key for side 1 with OpenSSL
159
+ new_key1 = key1.compute_key(OpenSSL::BN.new pub_key2[:VALUE], 2)
160
+
161
+ # Derive secret DES key for side 2 with softokn3
162
+ new_key2 = session.derive_key( {:DH_PKCS_DERIVE=>key1.pub_key.to_s(2)}, priv_key2,
163
+ :CLASS=>CKO_SECRET_KEY, :KEY_TYPE=>CKK_AES, :VALUE_LEN=>16, :ENCRYPT=>true, :DECRYPT=>true, :SENSITIVE=>false )
164
+
165
+ assert_equal new_key1[0,16], new_key2[:VALUE], 'Exchanged session key should be equal'
166
+ end
167
+ end