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 +4 -4
- data/exe/msidp-cert2assertion +29 -0
- data/lib/msidp/certificate_credential.rb +89 -0
- data/lib/msidp/endpoint/version.rb +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 37385ee2c6bd0f32d4cc2a176e7140ba05a1f756a97e5539c963dcb1b4a9c075
|
4
|
+
data.tar.gz: d586b59889dbe8b58f4aaeba41abc5dc52fbbd07ed06367607c0b2bd703b603a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- SHIMAYOSHI, Takao
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
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
|