diff --git a/sw/host/ot_certs/src/asn1/x509.rs b/sw/host/ot_certs/src/asn1/x509.rs
index b12c063260066f..d89ba4dcf45d6e 100644
--- a/sw/host/ot_certs/src/asn1/x509.rs
+++ b/sw/host/ot_certs/src/asn1/x509.rs
@@ -153,8 +153,12 @@ impl X509 {
                         if let Some(key_usage) = &cert.key_usage {
                             Self::push_key_usage_ext(builder, key_usage)?;
                         }
-                        Self::push_auth_key_id_ext(builder, &cert.authority_key_identifier)?;
-                        Self::push_subject_key_id_ext(builder, &cert.subject_key_identifier)?;
+                        if let Some(auth_key_id) = &cert.authority_key_identifier {
+                            Self::push_auth_key_id_ext(builder, auth_key_id)?;
+                        }
+                        if let Some(subj_key_id) = &cert.subject_key_identifier {
+                            Self::push_subject_key_id_ext(builder, subj_key_id)?;
+                        }
                         for ext in &cert.private_extensions {
                             Self::push_cert_extension(builder, ext)?
                         }
diff --git a/sw/host/ot_certs/src/template/mod.rs b/sw/host/ot_certs/src/template/mod.rs
index 87e24970611d6c..32f933ae21b049 100644
--- a/sw/host/ot_certs/src/template/mod.rs
+++ b/sw/host/ot_certs/src/template/mod.rs
@@ -70,9 +70,9 @@ pub struct Certificate {
     /// X509 certificate's public key.
     pub subject_public_key_info: SubjectPublicKeyInfo,
     /// X509 certificate's authority key identifier.
-    pub authority_key_identifier: Value<Vec<u8>>,
+    pub authority_key_identifier: Option<Value<Vec<u8>>>,
     /// X509 certificate's public key identifier.
-    pub subject_key_identifier: Value<Vec<u8>>,
+    pub subject_key_identifier: Option<Value<Vec<u8>>>,
     // X509 basic constraints extension, optional.
     pub basic_constraints: Option<BasicConstraints>,
     pub key_usage: Option<KeyUsage>,
diff --git a/sw/host/ot_certs/src/x509.rs b/sw/host/ot_certs/src/x509.rs
index a3d48593138c04..dad493d0d35e9e 100644
--- a/sw/host/ot_certs/src/x509.rs
+++ b/sw/host/ot_certs/src/x509.rs
@@ -7,9 +7,7 @@ use indexmap::IndexMap;
 use num_bigint_dig::BigUint;
 
 use foreign_types::ForeignTypeRef;
-use openssl::asn1::{
-    Asn1IntegerRef, Asn1ObjectRef, Asn1OctetStringRef, Asn1StringRef, Asn1TimeRef,
-};
+use openssl::asn1::{Asn1IntegerRef, Asn1ObjectRef, Asn1StringRef, Asn1TimeRef};
 use openssl::bn::{BigNum, BigNumContext, BigNumRef};
 use openssl::ec::{EcGroupRef, EcKey};
 use openssl::ecdsa::EcdsaSig;
@@ -84,10 +82,6 @@ fn asn1str_to_str(field: &str, s: &Asn1StringRef) -> Result<Value<String>> {
     ))
 }
 
-fn asn1octets_to_vec(s: &Asn1OctetStringRef) -> Value<Vec<u8>> {
-    Value::literal(s.as_slice().to_vec())
-}
-
 fn asn1time_to_string(time: &Asn1TimeRef) -> Result<Value<String>> {
     // OpenSSL guarantees that an ASN1_TIME is in fact just a typedef for ASN1_STRING
     // https://www.openssl.org/docs/man1.1.1/man3/ASN1_TIME_to_generalizedtime.html
@@ -222,6 +216,8 @@ pub fn parse_certificate(cert: &[u8]) -> Result<template::Certificate> {
     let mut private_extensions = Vec::new();
     let mut basic_constraints = None;
     let mut key_usage: Option<KeyUsage> = None;
+    let mut auth_key_id = None;
+    let mut subj_key_id = None;
     for ext in raw_extensions {
         match ext.object.nid() {
             // Ignore extensions that are standard and handled by openssl.
@@ -240,9 +236,24 @@ pub fn parse_certificate(cert: &[u8]) -> Result<template::Certificate> {
                     extension::parse_key_usage(&ext).context("could not parse X509 key usage")?,
                 );
             }
-            Nid::AUTHORITY_KEY_IDENTIFIER => (),
+            Nid::AUTHORITY_KEY_IDENTIFIER => {
+                // Although OpenSSL will parse the authority key identifier correctly, it will sometimes
+                // pretend that it doesn't exist if the certificate is unusual (i.e. all key usage bit
+                // set to false) without showing any error. Since this is very confusing, we may as well
+                // parse it ourselves.
+                auth_key_id = Some(Value::Literal(
+                    extension::parse_authority_key_id(&ext)
+                        .context("could not parse authority key identifier")?,
+                ));
+            }
             Nid::SUBJECT_ALT_NAME => (),
-            Nid::SUBJECT_KEY_IDENTIFIER => (),
+            Nid::SUBJECT_KEY_IDENTIFIER => {
+                // Same issue as with authority key identifier.
+                subj_key_id = Some(Value::Literal(
+                    extension::parse_subject_key_id(&ext)
+                        .context("could not parse subject key identifier")?,
+                ));
+            }
             _ => private_extensions
                 .push(extension::parse_extension(&ext).context("could not parse X509 extension")?),
         }
@@ -253,7 +264,6 @@ pub fn parse_certificate(cert: &[u8]) -> Result<template::Certificate> {
             .public_key()
             .context("the X509 does not have a valid public key!")?,
     )?;
-
     Ok(template::Certificate {
         serial_number: asn1int_to_bn("serial number", x509.serial_number())?,
         issuer: asn1name_to_name("issuer", x509.issuer_name())?,
@@ -262,14 +272,8 @@ pub fn parse_certificate(cert: &[u8]) -> Result<template::Certificate> {
             .context("cannot parse not_before time")?,
         not_after: asn1time_to_string(x509.not_after()).context("cannot parse not_after time")?,
         subject_public_key_info,
-        authority_key_identifier: asn1octets_to_vec(
-            x509.authority_key_id()
-                .context("the certificate has not authority key id")?,
-        ),
-        subject_key_identifier: asn1octets_to_vec(
-            x509.subject_key_id()
-                .context("the certificate has not subject key id")?,
-        ),
+        authority_key_identifier: auth_key_id,
+        subject_key_identifier: subj_key_id,
         basic_constraints,
         key_usage,
         subject_alt_name: get_subject_alt_name(&x509)?,
diff --git a/sw/host/ot_certs/src/x509/extension.rs b/sw/host/ot_certs/src/x509/extension.rs
index 1b98d2f94a2589..7e8ee20e3af458 100644
--- a/sw/host/ot_certs/src/x509/extension.rs
+++ b/sw/host/ot_certs/src/x509/extension.rs
@@ -266,6 +266,19 @@ impl<'a> asn1::SimpleAsn1Readable<'a> for KeyUsage {
     }
 }
 
+// From https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.1
+// AuthorityKeyIdentifier ::= SEQUENCE {
+//       keyIdentifier             [0] KeyIdentifier           OPTIONAL,
+//       authorityCertIssuer       [1] GeneralNames            OPTIONAL,
+//       authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL  }
+//
+// KeyIdentifier ::= OCTET STRING
+#[derive(asn1::Asn1Read)]
+struct AuthorityKeyIdentifier<'a> {
+    #[implicit(0)]
+    pub key_id: Option<&'a [u8]>,
+}
+
 /// Try to parse an X509 extension as a DICE TCB info extension.
 pub fn parse_dice_tcb_info_extension(ext: &[u8]) -> Result<DiceTcbInfoExtension> {
     asn1::parse_single::<DiceTcbInfo>(ext)
@@ -300,6 +313,23 @@ pub fn parse_basic_constraints(ext: &X509ExtensionRef) -> Result<BasicConstraint
         .to_basic_constraints()
 }
 
+pub fn parse_authority_key_id(ext: &X509ExtensionRef) -> Result<Vec<u8>> {
+    let auth = asn1::parse_single::<AuthorityKeyIdentifier>(ext.data.as_slice())?;
+    Ok(auth
+        .key_id
+        .context("authority key identifier extension is empty")?
+        .to_vec())
+}
+
+pub fn parse_subject_key_id(ext: &X509ExtensionRef) -> Result<Vec<u8>> {
+    // From https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.2
+    // SubjectKeyIdentifier ::= KeyIdentifier
+    //
+    //  KeyIdentifier ::= OCTET STRING
+    let subj = asn1::parse_single::<&[u8]>(ext.data.as_slice())?;
+    Ok(subj.to_vec())
+}
+
 /// Try to parse an X509 extension.
 pub fn parse_extension(ext: &X509ExtensionRef) -> Result<CertificateExtension> {
     let dice_oid =