msidp-endpoint 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c3063389a5690ccaabdb90a88d5b829e4d679c75535e557abcd2894b5b7423e
4
- data.tar.gz: 496d1e0b37bd567c2c955f716360e451b8ce768454095a3746364a423c1ee2ec
3
+ metadata.gz: 37385ee2c6bd0f32d4cc2a176e7140ba05a1f756a97e5539c963dcb1b4a9c075
4
+ data.tar.gz: d586b59889dbe8b58f4aaeba41abc5dc52fbbd07ed06367607c0b2bd703b603a
5
5
  SHA512:
6
- metadata.gz: 7f70b2959ca814ec3d3c967b46183b5f79d3c9ccf642a30929b741d6321f22da91b918b5937ec943086c94cf8647108916b515674c44e27213d35d6cf3f9d93b
7
- data.tar.gz: c6f5d2f9e19039707aad84c62f7b863d4b93b2c03ea21a4a4208ec6571ecddd825b8659eccec51aff3b7ad137b3357b9e2f49dd641781bf96318679175737897
6
+ metadata.gz: 7c7cdd5a0bad6a7da2ce4f43c63683c8b9d81308053490bdd1cc842195f6f1d7bc2dc02b1d65c6bb1009fc2ed734444c8e3007dcb9b6793edc6a51c5796b80f3
7
+ data.tar.gz: 7efc055c1962d6a7eeda5742cb8845303f4ea728ec30fd103d291748a603c99d7412843d7eb832527c564631a3e585cee18fb8fbe98105c94de8f899f1351309
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'msidp/certificate_credential'
5
+
6
+ if ARGV.length < 4
7
+ progname = File.basename($PROGRAM_NAME)
8
+ warn <<~USAGE
9
+ Compute a client assetion for a certificate pair.
10
+ Usage:
11
+ #{progname} certificate_file private_key_file tenant_id client_id
12
+ certificate_file: public certificate file
13
+ private_key_file: private key file
14
+ tenant_id: tenant ID in GUID or domain-name format
15
+ client_id: applicaiton (client) ID
16
+ USAGE
17
+ exit 64
18
+ end
19
+
20
+ certificate, private_key, tenant_id, client_id = ARGV
21
+
22
+ cert = OpenSSL::X509::Certificate.new(File.binread(certificate))
23
+ key = OpenSSL::PKey::RSA.new(File.binread(private_key))
24
+
25
+ cred = MSIDP::CertificateCredential.new(
26
+ cert, key, tenant_id: tenant_id, client_id: client_id
27
+ )
28
+
29
+ puts cred.assertion
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+ require 'base64'
5
+ require 'json'
6
+
7
+ module MSIDP
8
+ # Certificate credential for application authentication
9
+ class CertificateCredential
10
+ # @return [String] tenant a directory tenant in GUID or domain-name format.
11
+ attr_accessor :tenant
12
+ # @return [String] client_id the assigned applicaiton (client) ID.
13
+ attr_accessor :client_id
14
+
15
+ # Initialize an instance
16
+ #
17
+ # @param [OpenSSL::X509::Certificate] cert a certificate.
18
+ # @param [OpenSSL::PKey] cert the private key paired with the certificate.
19
+ # @param [String] tenant a directory tenant in GUID or domain-name format.
20
+ # @param [String] client_id the assigned applicaiton (client) ID.
21
+ def initialize(cert, key, tenant:, client_id:)
22
+ @cert = cert
23
+ @key = key
24
+ @tenant = tenant
25
+ @client_id = client_id
26
+ end
27
+
28
+ # Computes the JWT assertion.
29
+ #
30
+ # @return [String] JWT assertion string.
31
+ def assertion
32
+ header_base64 = base64url_encode(header)
33
+ payload_base64 = base64url_encode(payload)
34
+ signature = @key.sign('sha256', "#{header_base64}.#{payload_base64}")
35
+ sign_base64 = base64url_encode(signature)
36
+ "#{header_base64}.#{payload_base64}.#{sign_base64}"
37
+ end
38
+
39
+ # JOSE header of the JWT.
40
+ #
41
+ # @return [String] JSON string of the JOSE header.
42
+ def header
43
+ digest = OpenSSL::Digest::SHA1.digest(@cert.to_der)
44
+ x5t = Base64.urlsafe_encode64(digest)
45
+ header = { alg: 'RS256', typ: 'JWT', x5t: x5t.to_s }
46
+ JSON.dump(header)
47
+ end
48
+
49
+ # JWS payload of the JWT claim.
50
+ #
51
+ # @return [String] JSON string of the JWS payload.
52
+ def payload
53
+ not_after = @cert.not_after.to_i
54
+ not_before = @cert.not_before.to_i
55
+ jti = make_jwt_id
56
+ payload = {
57
+ aud: "https://login.microsoftonline.com/#{tenant}/v2.0",
58
+ exp: not_after, iss: client_id, jti: jti,
59
+ nbf: not_after, sub: client_id, iat: not_before
60
+ }
61
+ JSON.dump(payload)
62
+ end
63
+
64
+ private
65
+
66
+ def base64url_encode(data)
67
+ Base64.urlsafe_encode64(data, padding: false)
68
+ end
69
+
70
+ def make_jwt_id
71
+ data = @cert.to_der
72
+ data << tenant
73
+ data << client_id
74
+ sha1hash_uuid(OpenSSL::Digest::SHA1.digest(data))
75
+ end
76
+
77
+ # rubocop:disable Style/FormatStringToken
78
+ def sha1hash_uuid(hash)
79
+ bytes = hash.bytes[0..15]
80
+ bytes[6] = bytes[6] & 0x0F | 0x50
81
+ bytes[8] = bytes[6] & 0x3F | 0x80
82
+ format(
83
+ '%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x',
84
+ *bytes
85
+ )
86
+ end
87
+ # rubocop:enable Style/FormatStringToken
88
+ end
89
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module MSIDP
4
4
  module Endpoint
5
- VERSION = '0.1.1'
5
+ VERSION = '0.2.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,25 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: msidp-endpoint
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SHIMAYOSHI, Takao
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-18 00:00:00.000000000 Z
11
+ date: 2022-03-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Simple class library for Microsoft Identity Platform endpoints.
14
14
  email:
15
15
  - simayosi@cc.kyushu-u.ac.jp
16
- executables: []
16
+ executables:
17
+ - msidp-cert2assertion
17
18
  extensions: []
18
19
  extra_rdoc_files: []
19
20
  files:
20
21
  - LICENSE.txt
21
22
  - README.md
23
+ - exe/msidp-cert2assertion
22
24
  - lib/msidp/access_token.rb
25
+ - lib/msidp/certificate_credential.rb
23
26
  - lib/msidp/endpoint.rb
24
27
  - lib/msidp/endpoint/version.rb
25
28
  - lib/msidp/error.rb