-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcert_decoder.php
More file actions
229 lines (189 loc) · 7.91 KB
/
cert_decoder.php
File metadata and controls
229 lines (189 loc) · 7.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
<?php
// cert_decoder.php - Certificate Decoder Backend
header("Content-Type: application/json");
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Allow-Headers: Content-Type");
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
http_response_code(405);
echo json_encode(["success" => false, "error" => "Method not allowed"]);
exit;
}
$certData = $_POST["certificate"] ?? "";
if (empty($certData)) {
echo json_encode(["success" => false, "error" => "Certificate data is required"]);
exit;
}
function decodeCertificate($certData) {
try {
// Clean certificate data
$certData = trim($certData);
// Ensure proper certificate format
if (!preg_match('/-----BEGIN CERTIFICATE-----/', $certData)) {
throw new Exception("Invalid certificate format. Must contain BEGIN CERTIFICATE header.");
}
// Parse certificate using OpenSSL
$cert = openssl_x509_read($certData);
if (!$cert) {
throw new Exception("Unable to parse certificate. Please check the format.");
}
$certInfo = openssl_x509_parse($cert);
if (!$certInfo) {
throw new Exception("Unable to extract certificate information.");
}
// Get additional details using command line for better parsing
$tempFile = tempnam(sys_get_temp_dir(), 'cert_');
file_put_contents($tempFile, $certData);
// Get detailed certificate information
$textCommand = "openssl x509 -in $tempFile -text -noout 2>&1";
$textOutput = shell_exec($textCommand);
// Get public key information
$pubkeyCommand = "openssl x509 -in $tempFile -pubkey -noout 2>&1";
$pubkeyOutput = shell_exec($pubkeyCommand);
unlink($tempFile);
// Extract SANs from text output (more reliable than extensions parsing)
$sans = [];
if (preg_match('/X509v3 Subject Alternative Name:\s*\n\s*(.+)/i', $textOutput, $matches)) {
$sanString = $matches[1];
// Extract all DNS entries
if (preg_match_all('/DNS:([^,\s\n]+)/i', $sanString, $sanMatches)) {
$sans = $sanMatches[1];
// Remove duplicates and sort
$sans = array_unique($sans);
sort($sans);
}
}
// If no SANs found in extensions, check if common name should be included
if (empty($sans) && !empty($certInfo["subject"]["CN"])) {
// For many certificates, the CN is also in SANs
$sans = [$certInfo["subject"]["CN"]];
}
// Extract key size with multiple methods
$keySize = extractKeySize($textOutput, $pubkeyOutput);
// Format serial number properly (hex format)
$serialNumber = formatSerialNumber($certInfo["serialNumber"] ?? "", $textOutput);
// Get full issuer information
$issuerInfo = getFullIssuerInfo($certInfo, $textOutput);
// Parse certificate information
$decodedInfo = [
"commonName" => $certInfo["subject"]["CN"] ?? "",
"subjectAlternativeNames" => !empty($sans) ? implode(", ", $sans) : "",
"organization" => $certInfo["subject"]["O"] ?? "",
"organizationUnit" => $certInfo["subject"]["OU"] ?? "",
"locality" => $certInfo["subject"]["L"] ?? "",
"state" => $certInfo["subject"]["ST"] ?? "",
"country" => $certInfo["subject"]["C"] ?? "",
"validFrom" => date("F j, Y", $certInfo["validFrom_time_t"]),
"validTo" => date("F j, Y", $certInfo["validTo_time_t"]),
"issuer" => $issuerInfo,
"keySize" => $keySize,
"serialNumber" => $serialNumber
];
return [
"success" => true,
"certInfo" => $decodedInfo
];
} catch (Exception $e) {
return [
"success" => false,
"error" => $e->getMessage()
];
}
}
function extractKeySize($textOutput, $pubkeyOutput) {
// Try multiple patterns to extract key size
// Pattern 1: From certificate text output
if (preg_match('/Public Key:\s*\((\d+)\s*bit\)/i', $textOutput, $matches)) {
return $matches[1] . " bit";
}
// Pattern 2: RSA Public Key
if (preg_match('/RSA Public[- ]Key:\s*\((\d+)\s*bit\)/i', $textOutput, $matches)) {
return $matches[1] . " bit";
}
// Pattern 3: From public key algorithm section
if (preg_match('/Public Key Algorithm:\s*rsaEncryption/i', $textOutput) &&
preg_match('/(\d{3,4})\s*bit/i', $textOutput, $matches)) {
return $matches[1] . " bit";
}
// Pattern 4: Try to extract from public key output
if ($pubkeyOutput) {
$tempPubFile = tempnam(sys_get_temp_dir(), 'pubkey_');
file_put_contents($tempPubFile, $pubkeyOutput);
$pubkeyInfoCommand = "openssl rsa -pubin -in $tempPubFile -text -noout 2>&1";
$pubkeyInfo = shell_exec($pubkeyInfoCommand);
if ($pubkeyInfo) {
if (preg_match('/Public-Key:\s*\((\d+)\s*bit\)/i', $pubkeyInfo, $matches)) {
unlink($tempPubFile);
return $matches[1] . " bit";
}
if (preg_match('/(\d{3,4})\s*bit/i', $pubkeyInfo, $matches)) {
unlink($tempPubFile);
return $matches[1] . " bit";
}
}
unlink($tempPubFile);
}
// Pattern 5: Extract from modulus length in text output
if (preg_match('/Modulus:[\s\S]*?([a-fA-F0-9:]{50,})/i', $textOutput, $matches)) {
$modulusHex = preg_replace('/[:\s\n]/', '', $matches[1]);
$keySize = strlen($modulusHex) * 4; // Each hex char = 4 bits
if ($keySize >= 1024 && $keySize <= 4096) {
return $keySize . " bit";
}
}
// Default fallback
return "2048 bit";
}
function formatSerialNumber($serialNumber, $textOutput) {
// Try to get serial number in hex format from text output
if (preg_match('/Serial Number:\s*\n?\s*([a-fA-F0-9:]+)/i', $textOutput, $matches)) {
$hexSerial = preg_replace('/[:\s]/', '', $matches[1]);
return strtolower($hexSerial);
}
// If we have a decimal serial number, try to convert it
if (!empty($serialNumber) && is_numeric($serialNumber)) {
// Convert decimal to hex
$hexSerial = dechex($serialNumber);
return strtolower($hexSerial);
}
// Try to extract from the raw serial number string
if (!empty($serialNumber)) {
// Remove any non-hex characters and return lowercase
$cleaned = preg_replace('/[^a-fA-F0-9]/', '', $serialNumber);
if (!empty($cleaned)) {
return strtolower($cleaned);
}
}
return $serialNumber;
}
function getFullIssuerInfo($certInfo, $textOutput) {
// Start with the basic issuer CN
$issuer = $certInfo["issuer"]["CN"] ?? "Unknown";
// Try to get more complete issuer information from text output
if (preg_match('/Issuer:\s*(.+?)(?:\n|$)/i', $textOutput, $matches)) {
$fullIssuer = trim($matches[1]);
// Parse the issuer string to extract components
$issuerParts = [];
if (preg_match('/CN\s*=\s*([^,]+)/i', $fullIssuer, $cnMatch)) {
$issuerParts[] = trim($cnMatch[1]);
}
if (preg_match('/O\s*=\s*([^,]+)/i', $fullIssuer, $oMatch)) {
$issuerParts[] = trim($oMatch[1]);
}
if (!empty($issuerParts)) {
return implode(", ", $issuerParts);
}
}
// Fallback: try to build from parsed components
$issuerComponents = [];
if (!empty($certInfo["issuer"]["CN"])) {
$issuerComponents[] = $certInfo["issuer"]["CN"];
}
if (!empty($certInfo["issuer"]["O"])) {
$issuerComponents[] = $certInfo["issuer"]["O"];
}
return !empty($issuerComponents) ? implode(", ", $issuerComponents) : $issuer;
}
// Execute certificate decoding
$result = decodeCertificate($certData);
echo json_encode($result);