nemid 0.1.0

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,93 @@
1
+ module NemID
2
+ module Authentication
3
+ class Response
4
+ PID_REGEX = /\APID:([0-9-]+)\Z/.freeze
5
+ RID_REGEX = /\ARID:([0-9-]+)\Z/.freeze
6
+
7
+ def initialize(string)
8
+ if string.match?(/\A[A-Za-z0-9+\/\r\n]+={0,2}\z/)
9
+ decoded_string = Base64.decode64(string)
10
+ if decoded_string.start_with? '<?xml'
11
+ @doc = NemID::XMLDSig::Document.new(decoded_string)
12
+ else
13
+ raise error(decoded_string)
14
+ end
15
+ elsif string.start_with? '<?xml'
16
+ @doc = NemID::XMLDSig::Document.new(string)
17
+ else
18
+ raise NemID::Errors::ResponseError
19
+ end
20
+ end
21
+
22
+ def extract_pid
23
+ if has_pid?
24
+ serial_number.match(PID_REGEX)[1]
25
+ end
26
+ end
27
+
28
+ def extract_rid
29
+ if has_rid?
30
+ serial_number.match(RID_REGEX)[1]
31
+ end
32
+ end
33
+
34
+ def extract_pid_or_rid
35
+ serial_number
36
+ end
37
+
38
+ def has_pid?
39
+ serial_number.match?(PID_REGEX)
40
+ end
41
+
42
+ def has_rid?
43
+ serial_number.match?(RID_REGEX)
44
+ end
45
+
46
+ def user_certificate_expired?
47
+ @doc.user_certificate_expired?
48
+ end
49
+
50
+ def user_certificate_revoked?
51
+ @doc.user_certificate_revoked?
52
+ end
53
+
54
+ def valid_certificate_chain?
55
+ @doc.validate_certificate_chain
56
+ end
57
+
58
+ def validate_response
59
+ raise NemID::Errors::InvalidSignature if not valid_signature?
60
+ raise NemID::Errors::InvalidCertificateChain if not valid_certificate_chain?
61
+ raise NemID::Errors::UserCertificateExpired if user_certificate_expired?
62
+ raise NemID::Errors::UserCertificateRevoked if user_certificate_revoked?
63
+
64
+ true
65
+ end
66
+
67
+ def valid_signature?
68
+ @doc.validate_signature
69
+ end
70
+
71
+ private
72
+ def class_exists?(class_name)
73
+ klass = Module.const_get(class_name)
74
+ return klass.is_a?(Class)
75
+ rescue NameError
76
+ return false
77
+ end
78
+
79
+ def error(str)
80
+ klass = "NemID::Errors::#{str}Error"
81
+ if class_exists?(klass)
82
+ return eval(klass)
83
+ else
84
+ return NemID::Errors::ResponseError
85
+ end
86
+ end
87
+
88
+ def serial_number
89
+ @serial_number ||= @doc.extract_pid_or_rid
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,53 @@
1
+ require 'openssl'
2
+
3
+ module NemID
4
+ class Crypto
5
+
6
+ def initialize(certificate, pass)
7
+ certificate = read_file(certificate)
8
+ @pkcs12 = read_pkcs12(certificate, pass)
9
+ @rsa_instance = rsa_keypair(@pkcs12, pass)
10
+ end
11
+
12
+ def base64_encoded_der_representation
13
+ Base64.strict_encode64(@pkcs12.certificate.to_der)
14
+ end
15
+
16
+ def base64_encoded_digest_representation(data)
17
+ Base64.strict_encode64(digest(data))
18
+ end
19
+
20
+ def base64_encoded_rsa_signature(data)
21
+ Base64.strict_encode64(sign(data))
22
+ end
23
+
24
+ def get_certificate
25
+ @pkcs12.certificate
26
+ end
27
+
28
+ def get_key
29
+ @pkcs12.key
30
+ end
31
+
32
+ private
33
+ def digest(data)
34
+ OpenSSL::Digest::SHA256.new.digest(data)
35
+ end
36
+
37
+ def read_file(certificate)
38
+ File.read(certificate)
39
+ end
40
+
41
+ def read_pkcs12(certificate, pass)
42
+ OpenSSL::PKCS12::new(certificate, pass)
43
+ end
44
+
45
+ def rsa_keypair(pkcs12, passphrase)
46
+ OpenSSL::PKey::RSA.new(pkcs12.key, passphrase)
47
+ end
48
+
49
+ def sign(data)
50
+ @rsa_instance.sign(OpenSSL::Digest::SHA256.new, data)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,17 @@
1
+ module NemID
2
+ module Errors
3
+ class ResponseError < StandardError
4
+ attr_reader :da, :en
5
+ end
6
+ end
7
+ end
8
+
9
+ require_relative "errors/app"
10
+ require_relative "errors/auth"
11
+ require_relative "errors/can"
12
+ require_relative "errors/capp"
13
+ require_relative "errors/lib"
14
+ require_relative "errors/lock"
15
+ require_relative "errors/oces"
16
+ require_relative "errors/srv"
17
+ require_relative "errors/validation"
@@ -0,0 +1,49 @@
1
+ module NemID
2
+ module Errors
3
+ class APPError < ResponseError
4
+ def initialize(msg='', sp='support')
5
+ @da = "Der er opstået en teknisk fejl. Forsøg igen. Kontakt #{sp}, hvis " \
6
+ "problemet fortsætter."
7
+ @en = "A technical error has occurred. Please try again. Contact #{sp} if " \
8
+ "the problem persists"
9
+ super(msg)
10
+ end
11
+ end
12
+
13
+ class APP001Error < APPError
14
+ def initialize(msg="Fix the integration issue. [JS Client: See tool at " \
15
+ "/developers/validateparameters.jsp]")
16
+ super
17
+ end
18
+ end
19
+
20
+ class APP002Error < APPError
21
+ def initialize(msg="Correct the sign text. [JS Client: See tool at " \
22
+ "/developers/signtextviewer.jsp]")
23
+ super
24
+ end
25
+ end
26
+
27
+ class APP003Error < APPError; end
28
+
29
+ class APP004Error < APPError; end
30
+
31
+ class APP007Error < APPError
32
+ def initialize(msg="Fix the integration issue. [JS Client: See tool at " \
33
+ "/developers/validateparameters.jsp]")
34
+ super
35
+ end
36
+ end
37
+
38
+ class APP008Error < APPError
39
+ def initialize(msg="Fix the integration issue. [JS Client: See tool at " \
40
+ "/developers/validateparameters.jsp]")
41
+ super
42
+ end
43
+ end
44
+
45
+ class APP009Error < APPError; end
46
+
47
+ class APP010Error < APPError; end
48
+ end
49
+ end
@@ -0,0 +1,180 @@
1
+ module NemID
2
+ module Errors
3
+ class AUTHError < ResponseError
4
+ DA_SUPPORT_URL = '[https://www.nemid.nu/dk-da/support/faa_hjaelp_til_nemid/kontakt/]'
5
+ EN_SUPPORT_URL = '[https://www.nemid.nu/dk-en/support/contact/]'
6
+
7
+ def initialize(msg="The service provider is recommended to refer the user " \
8
+ "to NemID support.")
9
+ super(msg)
10
+ end
11
+ end
12
+
13
+ class AUTH001Error < AUTHError
14
+ def initialize
15
+ @da = "Dit NemID er spærret. Kontakt NemID support " \
16
+ "#{DA_SUPPORT_URL}."
17
+ @en = "Your NemID is blocked. Please contact NemID support. " \
18
+ "#{EN_SUPPORT_URL}"
19
+ super
20
+ end
21
+ end
22
+
23
+ class AUTH003Error < AUTHError
24
+ def initialize(msg='')
25
+ @da = "Login er gennemført korrekt, men du har ikke en bankaftale. " \
26
+ "Kontakt din bank for at høre nærmere."
27
+ @en = "Login succeeded but you have no bank agreement. Please contact " \
28
+ "your bank for more details"
29
+ super
30
+ end
31
+ end
32
+
33
+ class AUTH004Error < AUTHError
34
+ def initialize
35
+ @da = "Dit NemID er midlertidigt låst i 8 timer og du kan ikke logge på " \
36
+ "før spærringen er ophævet"
37
+ @en = "Your NemID is temporarily locked and you cannot log on until the " \
38
+ "8 hour time lock has been lifted."
39
+ super
40
+ end
41
+ end
42
+
43
+ class AUTH005Error < AUTHError
44
+ def initialize
45
+ @da = "Dit NemID er spærret. Kontakt NemID support " \
46
+ "#{DA_SUPPORT_URL}."
47
+ @en = "Your NemID has been blocked. Please contact NemID support. " \
48
+ "#{EN_SUPPORT_URL}"
49
+ super
50
+ end
51
+ end
52
+
53
+ class AUTH006Error < AUTHError
54
+ def initialize
55
+ @da = "Du har brugt alle nøgler på nøglekortet. " \
56
+ "Du kan bestille et nyt på siden Mistet nøglekort. " \
57
+ "[https://service.nemid.nu/dk-da/nemid/noeglekort/mistet_noeglekort/]"
58
+ @en = "You have used all the codes on your code card. " \
59
+ "You can order a new code card on the Lost code card page. " \
60
+ "[https://service.nemid.nu/dk-en/nemid/code_cards/lost_code_card/]"
61
+ super
62
+ end
63
+ end
64
+
65
+ class AUTH007Error < AUTHError
66
+ def initialize
67
+ @da = "Din NemID-adgangskode er spærret på grund af for mange fejlede forsøg. " \
68
+ "Kontakt NemID support #{DA_SUPPORT_URL}."
69
+ @en = "Your NemID password is blocked due to too many failed password attempts. " \
70
+ "Please contact NemID support. #{EN_SUPPORT_URL}"
71
+ super
72
+ end
73
+ end
74
+
75
+ class AUTH008Error < AUTHError
76
+ def initialize
77
+ @da = "Dit NemID er ikke aktivt og du skal bestille en ny midlertidig " \
78
+ "adgangskode til aktivering hos support. Ring til NemID support " \
79
+ "#{DA_SUPPORT_URL}."
80
+ @en = "Your NemID is not active and you need support to issue a new " \
81
+ "activation password to activate. Please call NemID support. " \
82
+ "#{EN_SUPPORT_URL}"
83
+ super
84
+ end
85
+ end
86
+
87
+ class AUTH009Error < AUTHError
88
+ def initialize(msg="The service provider is advised to perform a reload of " \
89
+ "the client so the user can re-authenticate and try again. Also, if the " \
90
+ "problem persists, refer to Support")
91
+ @da = "Der er opstået en teknisk fejl. Forsøg igen."
92
+ @en = "A technical error has occurred. Please try again."
93
+ super(msg)
94
+ end
95
+ end
96
+
97
+ class AUTH010Error < AUTHError
98
+ def initialize(msg="The service provider is advised to perform a reload of " \
99
+ "the client so the user can re-authenticate and try again.")
100
+ @da = "Der er opstået en teknisk fejl. Tjek at kun ét NemID login er " \
101
+ "aktivt og forsøg igen."
102
+ @en = "A technical error has occurred. Please try again, and ensure that " \
103
+ "only one NemID login is running."
104
+ super(msg)
105
+ end
106
+ end
107
+
108
+ class AUTH011Error < AUTHError
109
+ def initialize(msg='')
110
+ @da = "NemID på mobil understøtter ikke brug af midlertidig adgangskode. " \
111
+ "Kontakt NemID support for atfå en ny kode udstedt. " \
112
+ "#{DA_SUPPORT_URL}" \
113
+ "Prøv derefter igen."
114
+ @en = "NemID login on mobile does not support authentication using a " \
115
+ "temporary password. Please contact NemID support to have a new temporary " \
116
+ "password issued. #{EN_SUPPORT_URL} " \
117
+ "Thereafter, please try again."
118
+ super
119
+ end
120
+ end
121
+
122
+ class AUTH012Error < AUTHError
123
+ def initialize(msg="The service provider is advised to perform a reload of " \
124
+ "the client so the user can re-authenticate and try again")
125
+ @da = "Der er opstået en teknisk fejl. Forsøg igen."
126
+ @en = "A technical error has occurred. Please try again."
127
+ super
128
+ end
129
+ end
130
+
131
+ class AUTH013Error < AUTHError
132
+ def initialize(msg="The service provider is advised to perform a reload of " \
133
+ "the client so the user can re-authenticate using regular 2-factor authentication.")
134
+ @da = "Der er opstået en teknisk fejl. Forsøg igen."
135
+ @en = "A technical error has occurred. Please try again."
136
+ super
137
+ end
138
+ end
139
+
140
+ class AUTH017Error < AUTHError
141
+ def initialize(msg="Consider displaying information suggesting how to avoid " \
142
+ "known environmental problems.")
143
+ @da = "En teknisk fejl i browseren gør at NemID ikke kan starte. Forsøg " \
144
+ "at slå unødige plug-ins fra, eller prøv igen med en anden browser."
145
+ @en = "Something in the browser environment has caused NemID to stop " \
146
+ "working. This could be because of an incompatible plug-in, too " \
147
+ "restrictive privacy settings or other environment factors. " \
148
+ "Please try deactivating plugins, resetting your browser settings " \
149
+ "or try using a different browser."
150
+ super
151
+ end
152
+ end
153
+
154
+ class AUTH018Error < AUTHError
155
+ def initialize(msg='')
156
+ @da = "Din nøgleapp er spærret. For at bruge den igen skal den genaktiveres."
157
+ @en = "Your code app is revoked. To use it again please reactivate it."
158
+ super
159
+ end
160
+ end
161
+
162
+ class AUTH019Error < AUTHError
163
+ def initialize(msg='')
164
+ @da = "Det er ikke muligt at logge ind med nøglekort, brug anden løsning " \
165
+ "nøgleapp eller nøgleviser."
166
+ @en = "It is not possible to login with a code card, please use a code app " \
167
+ "or code token."
168
+ super
169
+ end
170
+ end
171
+
172
+ class AUTH020Error < AUTHError
173
+ def initialize(msg='Retry with 2-factor login for user.')
174
+ @da = "Kunne ikke logge ind med 1-faktor, prøv med 2-faktor login."
175
+ @en = "Unable to login with 1-factor, please try with 2-factor login"
176
+ super
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,97 @@
1
+ module NemID
2
+ module Errors
3
+ class CANError < ResponseError
4
+ def initialize(msg='')
5
+ super(msg)
6
+ end
7
+ end
8
+
9
+ class CAN001Error < CANError
10
+ def initialize(msg="Redirect the user to a sensible place, taking into " \
11
+ "account where the user is in the flow.")
12
+ @da = "Du har afbrudt aktiveringen efter du har brugt den midlertidige adgangskode. " \
13
+ "Din midlertidige adgangskode er ikke længere gyldig, og du skal bestille " \
14
+ "en ny midlertidig adgangskode, før du kan aktivere og bruge NemID. " \
15
+ "Den nye, midlertidige adgangskode kan du bestille via NemID-support " \
16
+ "[https://www.nemid.nu/dk-da/support/faa_hjaelp_til_nemid/kontakt/]."
17
+ @en = "You have cancelled the activation of NemID after submitting the activation password. " \
18
+ "Your activation password is no longer valid, and you must request a new " \
19
+ "activation password before you can activate and use NemID. " \
20
+ "You can order your new activation password via NemID Support " \
21
+ "[https://www.nemid.nu/dk-en/support/contact/]"
22
+ super
23
+ end
24
+ end
25
+
26
+ class CAN002Error < CANError
27
+ def initialize(msg="The service provider must send the user to a sensible " \
28
+ "place, taking into account where the user is in the flow.")
29
+ @da = "Du har afbrudt login."
30
+ @en = "You have cancelled login."
31
+ super
32
+ end
33
+ end
34
+
35
+ class CAN003Error < CANError
36
+ def initialize
37
+ @da = "Forbindelsen til applikationen er timet ud eller er blevet " \
38
+ "afbrudt af en anden app. Forsøg igen."
39
+ @en = "The connection to the application has timed out or has been " \
40
+ "interrupted by another app. Please try again."
41
+ super
42
+ end
43
+ end
44
+
45
+ class CAN004Error < CANError
46
+ def initialize
47
+ @da = "Sessionen er afbrudt. Forsøg igen."
48
+ @en = "The session is cancelled. Please try again."
49
+ super
50
+ end
51
+ end
52
+
53
+ class CAN005Error < CANError
54
+ def initialize
55
+ @da = "Det tog for lang tid, før du godkendte den anmodning, du havde " \
56
+ "sendt til din nøgleapp"
57
+ @en = "You took too long to authenticate the request you had sent to " \
58
+ "your code app."
59
+ super
60
+ end
61
+ end
62
+
63
+ class CAN006Error < CANError
64
+ def initialize
65
+ @da = "Du kan højst have ##MAXACTIVENMAS## aktive nøgleapps ad gangen. " \
66
+ "Hvis du vil aktivere en ny nøgleapp, skal du først spærre en af dine " \
67
+ "nuværende på nemid.nu eller ved at kontakte NemID support eller din bank"
68
+ @en = "The maximum number of active code apps you can have at any time " \
69
+ "is ##MAXACTIVENMAS##. If you wish to activate another code app, you must " \
70
+ "first block one of your current code apps at nemid.nu or by contacting NemID " \
71
+ "Support or your bank."
72
+ super
73
+ end
74
+ end
75
+
76
+ class CAN007Error < CANError
77
+ def initialize
78
+ @da = "Du har afvist din anmodning om godkendelse i din nøgleapp. Hvis " \
79
+ "det var en fejl, kan du sende en ny anmodning, når du har afsluttet ved " \
80
+ "at klikke på \"Ok\"."
81
+ @en = "You rejected your code app authentication request. If this was " \
82
+ "incorrect, you can submit a new request after clicking \"OK\" to finish."
83
+ super
84
+ end
85
+ end
86
+
87
+ class CAN008Error < CANError
88
+ def initialize(msg="User should be notified and could have a choice to restart transaction")
89
+ @da = "Du har sendt en ny anmodning til godkendelse i din nøgleapp, som " \
90
+ "overskriver en eksisterende."
91
+ @en = "You sent a new authentication request to your code app overwriting " \
92
+ "an existing one."
93
+ super
94
+ end
95
+ end
96
+ end
97
+ end