Test Failed
Push — master ( 19e7e7...e85ccb )
by
unknown
07:48
created

TokenGenerator::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 3
dl 0
loc 12
rs 10
1
<?php
2
namespace Kendox;
3
4
use Exception;
5
6
class TokenGenerator
7
{
8
9
    /**
10
     * Token version
11
     * @var string
12
     */
13
    private $TokenVersion = "1";
14
15
    /**
16
     * Token lifetime in seconds
17
     * @var int
18
     */
19
    private $TokenLifeTime = 99000000;
20
21
    /**
22
     * Certificate data (read from PFX-File)
23
     * @var string
24
     */
25
    private $Certificate = null;
26
27
    /**
28
     * Private key of certificate (read from PFX-file)
29
     * @var string
30
     */
31
    private $CertPrivateKey = null;
32
33
    /**
34
     * @param string $issuer
35
     * @param string $pfxFile
36
     * @param string $pfxPassword
37
     */
38
    function __construct(/**
39
     * Issuer host name
40
     */
41
    private $Issuer, /**
42
     * Full filename of PFX-File (Certificate)
43
     */
44
    private $PfxFile, /**
45
     * Password for PFX-File (Certificate)
46
     */
47
    private $PfxPassword)
48
    {
49
        $this->loadCertificateFromPfx();        
50
    }
51
52
    /**
53
     * Token generation
54
     * 
55
     * @param string $userEMail The e-mail of the user to generate a token
56
     * 
57
     * @return string Token in XML format
58
     */
59
    public function generateToken($userEMail)
60
    {
61
        try {
62
            $now = new \DateTime("now", new \DateTimeZone("utc"));
63
            $guid = $this->createGUID();
64
            $writerSignedInfo = xmlwriter_open_memory();        
65
            $this->writeSignedInfo($writerSignedInfo, $userEMail, $now, $guid);
66
            $signedInfo = xmlwriter_output_memory($writerSignedInfo);
67
            $writer = xmlwriter_open_memory();
68
            xmlwriter_set_indent($writer, false);
69
            xmlwriter_start_element($writer, "InfoShareToken");
70
                $this->writeSignedInfo($writer, $userEMail, $now, $guid);
71
                xmlwriter_start_element($writer, "SignatureValue");
72
                    xmlwriter_text($writer, $this->signXmlString($signedInfo));
73
                xmlwriter_end_element($writer);                
74
            xmlwriter_end_element($writer);
75
            return xmlwriter_output_memory($writer);
76
        } catch(\Exception $ex) {
77
            throw new \Exception("Generating token failed: ".$ex->getMessage());
78
        }
79
    }   
80
81
    /**
82
     * Loads the X509-certificate from PFX-File
83
     */
84
    private function loadCertificateFromPfx()
85
    {
86
        if ($this->PfxFile == null) throw new \Exception("No PFX-File available.");
87
        if (!file_exists($this->PfxFile)) throw new \Exception("PFX-File not found.");
0 ignored issues
show
Bug introduced by
$this->PfxFile of type mixed is incompatible with the type string expected by parameter $filename of file_exists(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

87
        if (!file_exists(/** @scrutinizer ignore-type */ $this->PfxFile)) throw new \Exception("PFX-File not found.");
Loading history...
88
        if (empty($this->PfxPassword)) throw new \Exception("Password not set for PFX-File.");
89
        $pfxContent = file_get_contents($this->PfxFile);
0 ignored issues
show
Bug introduced by
$this->PfxFile of type mixed is incompatible with the type string expected by parameter $filename of file_get_contents(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

89
        $pfxContent = file_get_contents(/** @scrutinizer ignore-type */ $this->PfxFile);
Loading history...
90
        $results = [];
91
        $read = openssl_pkcs12_read($pfxContent, $results, $this->PfxPassword);
92
        if ($read == false) throw new \Exception("Error on reading PFX-File: ".openssl_error_string());
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
93
        $this->Certificate = $results['pkey'].$results['cert'];
94
        $this->CertPrivateKey = $results['pkey'];
95
    }
96
97
    private function writeSignedInfo($writer, $userEMail, $time, $uniqueId)
98
    {
99
        $utcTime = $time->format('Y-m-d H:i:s');
100
        $utcTime = str_replace(" ", "T", $utcTime)."Z";
101
        xmlwriter_start_element($writer, "SignedInfo");
102
            xmlwriter_start_element($writer, "UserPrincipalName");
103
            xmlwriter_text($writer, $userEMail);
104
            xmlwriter_end_element($writer);    
105
            xmlwriter_start_element($writer, "UniqueId");
106
            xmlwriter_text($writer, $uniqueId);
107
            xmlwriter_end_element($writer);            
108
            xmlwriter_start_element($writer, "Version");
109
            xmlwriter_text($writer, $this->TokenVersion);
110
            xmlwriter_end_element($writer);      
111
            xmlwriter_start_element($writer, "TimeStampUTC");
112
            xmlwriter_text($writer, $utcTime);
113
            xmlwriter_end_element($writer);                    
114
            xmlwriter_start_element($writer, "LifeTimeSeconds");
115
            xmlwriter_text($writer, $this->TokenLifeTime);
116
            xmlwriter_end_element($writer);             
117
            xmlwriter_start_element($writer, "IssueServer");
118
            xmlwriter_text($writer, $this->Issuer);
119
            xmlwriter_end_element($writer);     
120
            xmlwriter_start_element($writer, "CertificateFingerprint");
121
            xmlwriter_text($writer, strtoupper(openssl_x509_fingerprint($this->Certificate)));
122
            xmlwriter_end_element($writer);  
123
            xmlwriter_start_element($writer, "HashAlgorithm");
124
            xmlwriter_text($writer, "SHA512");
125
            xmlwriter_end_element($writer);    
126
            xmlwriter_start_element($writer, "Attributes");
127
            xmlwriter_text($writer, "");
128
            xmlwriter_end_element($writer);                                                                                                                                                 
129
        xmlwriter_end_element($writer);     
130
    }
131
132
    /**
133
     * Signing a XML-String and returns the result
134
     * 
135
     * @param string $xml XML-String
136
     * 
137
     * @return string
138
     */
139
    function SignXmlString($signedInfoXml)
140
    {
141
        try {
142
            $data = iconv('utf-8', 'utf-16le', $signedInfoXml);    
143
            $privateKey = \phpseclib3\Crypt\RSA::loadFormat('PKCS8', $this->CertPrivateKey)
144
                            ->withPadding(\phpseclib3\Crypt\RSA::SIGNATURE_PKCS1)
145
                            ->withHash('sha512');
146
            $base64 = base64_encode($privateKey->sign($data));
0 ignored issues
show
Bug introduced by
The method sign() does not exist on phpseclib3\Crypt\RSA. It seems like you code against a sub-type of phpseclib3\Crypt\RSA such as phpseclib3\Crypt\RSA\PrivateKey. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

146
            $base64 = base64_encode($privateKey->/** @scrutinizer ignore-call */ sign($data));
Loading history...
147
            return $base64;
148
        } catch (\Exception $ex) {
149
            throw new \Exception("XML signing failed: ".$ex->getMessage());
150
        }
151
    }
152
153
    /**
154
     * Creates a unique ID
155
     * @return string
156
     */
157
    private function createGUID(){
158
        if (function_exists('com_create_guid')){
159
            return com_create_guid();
160
        }
161
        else {
162
            mt_srand((double)microtime()*10000);
0 ignored issues
show
Bug introduced by
(double)microtime() * 10000 of type double is incompatible with the type integer expected by parameter $seed of mt_srand(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

162
            mt_srand(/** @scrutinizer ignore-type */ (double)microtime()*10000);
Loading history...
163
            $charid = strtoupper(md5(uniqid(random_int(0, mt_getrandmax()), true)));
164
            $hyphen = chr(45);// "-"
165
            $uuid = substr($charid, 0, 8).$hyphen
166
                .substr($charid, 8, 4).$hyphen
167
                .substr($charid,12, 4).$hyphen
168
                .substr($charid,16, 4).$hyphen
169
                .substr($charid,20,12);
170
            return $uuid;
171
        }
172
    }
173
174
}
175
176
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...