Issues (234)

src/Key/PublicKey.php (1 issue)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace SimpleSAML\XMLSecurity\Key;
6
7
use SimpleSAML\Assert\Assert;
8
use SimpleSAML\XMLSecurity\CryptoEncoding\PEM;
9
10
use function base64_encode;
11
use function chr;
12
use function chunk_split;
13
use function ord;
14
use function pack;
15
use function sprintf;
16
17
/**
18
 * A class modeling public keys for their use in asymmetric algorithms.
19
 *
20
 * @package simplesamlphp/xml-security
21
 */
22
class PublicKey extends AsymmetricKey
23
{
24
    public const int ASN1_TYPE_INTEGER = 0x02; // 2
0 ignored issues
show
A parse error occurred: Syntax error, unexpected T_STRING, expecting '=' on line 24 at column 21
Loading history...
25
26
    public const int ASN1_TYPE_BIT_STRING = 0x03; // 3
27
28
    public const int ASN1_TYPE_SEQUENCE = 0x30; // 16
29
30
    public const int ASN1_SIZE_128 = 0x80; // 128
31
32
    public const int ASN1_SIZE_256 = 0x0100; // 256
33
34
    public const int ASN1_SIZE_65535 = 0x010000; // 65535
35
36
37
    /**
38
     * Create a new public key from the PEM-encoded key material.
39
     *
40
     * @param \SimpleSAML\XMLSecurity\CryptoEncoding\PEM $key The PEM-encoded key material.
41
     */
42
    final public function __construct(
43
        #[\SensitiveParameter]
44
        PEM $key,
45
    ) {
46
        Assert::oneOf(
47
            $key->type(),
48
            [PEM::TYPE_PUBLIC_KEY, PEM::TYPE_RSA_PUBLIC_KEY],
49
            "PEM structure has the wrong type %s.",
50
        );
51
52
        parent::__construct($key);
53
    }
54
55
56
    /**
57
     * Encode data in ASN.1.
58
     *
59
     * @param int $type The type of data.
60
     * @param string $string The data to encode.
61
     *
62
     * @return null|string The encoded data, or null if it was too long.
63
     */
64
    protected static function makeASN1Segment(int $type, string $string): ?string
65
    {
66
        switch ($type) {
67
            case self::ASN1_TYPE_INTEGER:
68
                if (ord($string) > self::ASN1_SIZE_128 - 1) {
69
                    $string = chr(0) . $string;
70
                }
71
                break;
72
            case self::ASN1_TYPE_BIT_STRING:
73
                $string = chr(0) . $string;
74
                break;
75
        }
76
77
        $length = strlen($string);
78
        Assert::lessThan($length, self::ASN1_SIZE_65535);
79
80
        if ($length < self::ASN1_SIZE_128) {
81
            $output = sprintf("%c%c%s", $type, $length, $string);
82
        } elseif ($length < self::ASN1_SIZE_256) {
83
            $output = sprintf("%c%c%c%s", $type, self::ASN1_SIZE_128 + 1, $length, $string);
84
        } else { // ($length < self::ASN1_SIZE_65535)
85
            $output = sprintf(
86
                "%c%c%c%c%s",
87
                $type,
88
                self::ASN1_SIZE_128 + 2,
89
                $length / 0x0100,
90
                $length % 0x0100,
91
                $string,
92
            );
93
        }
94
95
        return $output;
96
    }
97
98
99
    /**
100
     * Create a new public key from its RSA details (modulus and exponent).
101
     *
102
     * @param string $modulus The modulus of the given key.
103
     * @param string $exponent The exponent of the given key.
104
     *
105
     * @return \SimpleSAML\XMLSecurity\Key\PublicKey A new public key with the given modulus and exponent.
106
     */
107
    public static function fromDetails(string $modulus, string $exponent): PublicKey
108
    {
109
        return new static(PEM::fromString(
110
            "-----BEGIN PUBLIC KEY-----\n" .
111
            chunk_split(
112
                base64_encode(
113
                    self::makeASN1Segment(
114
                        self::ASN1_TYPE_SEQUENCE,
115
                        pack("H*", "300D06092A864886F70D0101010500") . // RSA alg id
116
                        self::makeASN1Segment( // bitstring
117
                            self::ASN1_TYPE_BIT_STRING,
118
                            self::makeASN1Segment( // sequence
119
                                self::ASN1_TYPE_SEQUENCE,
120
                                self::makeASN1Segment(self::ASN1_TYPE_INTEGER, $modulus)
121
                                . self::makeASN1Segment(self::ASN1_TYPE_INTEGER, $exponent),
122
                            ),
123
                        ),
124
                    ),
125
                ),
126
                64,
127
                "\n",
128
            ) .
129
            "-----END PUBLIC KEY-----\n",
130
        ));
131
    }
132
133
134
    /**
135
     * Get a new public key from a file.
136
     *
137
     * @param string $file The file where the PEM-encoded private key is stored.
138
     *
139
     * @return static A new public key.
140
     *
141
     * @throws \SimpleSAML\XMLSecurity\Exception\InvalidArgumentException If the file cannot be read.
142
     */
143
    public static function fromFile(string $file): static
144
    {
145
        return new static(PEM::fromFile($file));
146
    }
147
}
148