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
//! Example to test if SKI can be used to fetch certificates from the certs endpoint
//!
//! This tests whether the Subject Key Identifier (SKI) found in signatures
//! can be used to retrieve the actual certificate.
use ribbit_client::{Endpoint, ProtocolVersion, Region, RibbitClient};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize logging
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.init();
let client = RibbitClient::new(Region::US).with_protocol_version(ProtocolVersion::V1);
println!("=== Certificate Fetching by SKI Test ===\n");
// First, get a signature to find the SKI
let endpoint = Endpoint::ProductVersions("wow".to_string());
println!("1. Getting signature from: {}", endpoint.as_path());
let ski = match client.request(&endpoint).await {
Ok(response) => {
if let Some(mime_parts) = &response.mime_parts {
if let Some(sig_content) = &mime_parts.signature {
match ribbit_client::cms_parser::parse_cms_signature(sig_content) {
Ok(cms_info) => {
if let Some(first_signer) = cms_info.signers.first() {
// Extract SKI from the identifier
if first_signer
.identifier
.issuer
.starts_with("SubjectKeyIdentifier:")
{
let ski = &first_signer.identifier.serial_number;
println!("✓ Found SKI: {ski}");
Some(ski.clone())
} else {
println!("✗ Signer doesn't use SKI");
None
}
} else {
println!("✗ No signers found");
None
}
}
Err(e) => {
println!("✗ Failed to parse signature: {e}");
None
}
}
} else {
println!("✗ No signature in response");
None
}
} else {
println!("✗ No MIME parts in response");
None
}
}
Err(e) => {
println!("✗ Request failed: {e}");
None
}
};
if let Some(ski) = ski {
println!("\n2. Testing different certificate endpoint formats with SKI: {ski}\n");
// Test different possible endpoint formats
let test_endpoints = [
// Try the SKI directly
Endpoint::Cert(ski.clone()),
// Try uppercase
Endpoint::Cert(ski.to_uppercase()),
// Try with common certificate fingerprint hashes
Endpoint::Cert("5168ff90af0207753cccd9656462a212b859723b".to_string()), // Known working cert
];
for (i, cert_endpoint) in test_endpoints.iter().enumerate() {
println!("Test #{}: {}", i + 1, cert_endpoint.as_path());
match client.request(cert_endpoint).await {
Ok(response) => {
if let Some(data) = &response.data {
if data.contains("-----BEGIN CERTIFICATE-----") {
println!("✓ Got PEM certificate!");
// Try to parse the certificate
if let Some(cert_start) = data.find("-----BEGIN CERTIFICATE-----") {
if let Some(cert_end) = data.find("-----END CERTIFICATE-----") {
let cert_pem = &data[cert_start..cert_end + 25];
// Extract base64 content
let lines: Vec<&str> = cert_pem
.lines()
.filter(|line| !line.contains("-----"))
.collect();
let base64_content = lines.join("");
// Decode from base64
use base64::Engine;
if let Ok(cert_der) = base64::engine::general_purpose::STANDARD
.decode(&base64_content)
{
// Parse certificate
use der::Decode;
use x509_cert::Certificate;
match Certificate::from_der(&cert_der) {
Ok(cert) => {
println!(
" Subject: {}",
cert.tbs_certificate.subject
);
// Try to extract SKI from certificate extensions
if let Some(extensions) =
&cert.tbs_certificate.extensions
{
for ext in extensions.iter() {
// Subject Key Identifier OID is 2.5.29.14
if ext.extn_id.to_string() == "2.5.29.14" {
let ski_bytes =
ext.extn_value.as_bytes();
// SKI is typically OCTET STRING containing the identifier
// Skip the OCTET STRING tag and length
if ski_bytes.len() > 2
&& ski_bytes[0] == 0x04
{
let ski_hex =
hex::encode(&ski_bytes[2..]);
println!(
" Certificate SKI: {ski_hex}"
);
if ski_hex == ski {
println!(
" 🎯 SKI MATCHES! This is the signer's certificate!"
);
}
}
}
}
}
}
Err(e) => {
println!(" Failed to parse certificate: {e}");
}
}
}
}
}
} else if data.contains("Product") || data.contains("Region") {
println!("✗ Got product data instead of certificate");
} else {
println!(
"✗ Response doesn't contain certificate (length: {} bytes)",
data.len()
);
}
} else {
println!("✗ Empty response");
}
}
Err(e) => {
println!("✗ Request failed: {e}");
}
}
println!();
}
// Also test if there's a pattern for certificate endpoints
println!("\n3. Analyzing certificate endpoint patterns:\n");
// Get a few known certificates to see the pattern
let known_certs = vec![
"5168ff90af0207753cccd9656462a212b859723b",
"28458c5833cf2cf050900c3ddc956011de3a8fce",
];
for cert_hash in known_certs {
println!("Fetching known cert: {cert_hash}");
let cert_endpoint = Endpoint::Cert(cert_hash.to_string());
if let Ok(response) = client.request(&cert_endpoint).await {
if let Some(data) = &response.data {
if data.contains("-----BEGIN CERTIFICATE-----") {
println!("✓ Success - appears to be SHA-1 fingerprint");
}
}
}
}
println!("\nConclusion: The certs endpoint likely expects SHA-1 fingerprints, not SKIs");
println!("The SKI {ski} cannot be directly used with the certs endpoint");
}
Ok(())
}