在Rust中从WebAuthn Auth Data提取公钥并验证时遭遇ASN.1编码错误的问题咨询
在Rust中从WebAuthn Auth Data提取公钥并验证时遭遇ASN.1编码错误的问题咨询
我现在卡在从WebAuthn API的Auth Data里提取公钥的环节了,目前用coset、openssl和ciborium这几个Rust crate来处理COSE密钥和公钥转换,但在验证签名的时候遇到了ASN.1编码相关的错误,想请大家帮忙看看问题出在哪。
我的实现代码
首先是从COSE密钥中提取公钥的函数:
pub(super) fn extract_public_key_from_cose(&self, cose_key_value: ciborium::value::Value) -> Option<Vec<u8>> { if let ciborium::value::Value::Map(cose_key_map) = cose_key_value { let mut cose_key_builder: CoseKeyBuilder = CoseKeyBuilder::new(); for (key, value) in cose_key_map { if let ciborium::value::Value::Integer(param) = key { if param == Integer::from(1) { if let ciborium::value::Value::Integer(kty) = value { cose_key_builder = cose_key_builder.kty(KeyType::Assigned(iana::KeyType::from_i64(kty.try_into().ok()?)?)); } } else if param == Integer::from(3) { if let ciborium::value::Value::Integer(alg) = value { cose_key_builder = cose_key_builder.algorithm(iana::Algorithm::from_i64(alg.try_into().ok()?)?); } } else if param == Integer::from(-2) { if let ciborium::value::Value::Bytes(x) = value { cose_key_builder = cose_key_builder.param(-2, ciborium::value::Value::Bytes(x)); } } else if param == Integer::from(-3) { if let ciborium::value::Value::Bytes(y) = value { cose_key_builder = cose_key_builder.param(-3, ciborium::value::Value::Bytes(y)); } } } } let cose_key: CoseKey = cose_key_builder.build(); let cbor_key: Vec<u8> = cose_key.to_cbor_value().unwrap().to_vec().unwrap(); return Some(cbor_key); } None }
然后是从Auth Data中提取COSE密钥并调用上面函数的逻辑:
pub(super) fn extract_public_key(&self, auth_data: &[u8]) -> Option<Vec<u8>> { // The length of the authenticator data is at least 37 bytes if auth_data.len() < 37 { return None; } // The attestedCredentialData starts at byte 37 let attested_credential_data = &auth_data[37..]; // The AAGUID is 16 bytes, and the credential ID length is 2 bytes let aaguid_length = 16; let credential_id_length = 2; // Ensure there is enough data for the AAGUID and credential ID length if attested_credential_data.len() < aaguid_length + credential_id_length { return None; } // Extract the credential ID length let credential_id_length = u16::from_be_bytes([ attested_credential_data[aaguid_length], attested_credential_data[aaguid_length + 1], ]) as usize; // Ensure there is enough data for the credential ID and public key let public_key_start = aaguid_length + 2 + credential_id_length; if attested_credential_data.len() <= public_key_start { return None; } // Extract the public key in COSE format let public_key_cose = &attested_credential_data[public_key_start..]; // Manually deserialize the COSE key let mut cursor = Cursor::new(public_key_cose); let cose_key_value: ciborium::Value = match ciborium::from_reader(&mut cursor) { Ok(value) => value, Err(e) => { println!("Failed to deserialize public_key_cose: {:?}", e); return None; } }; // Extract the public key from the COSE key self.extract_public_key_from_cose(cose_key_value) }
最后是核心的签名验证逻辑:
// Extract the public key from authData let public_key = if let ciborium::Value::Bytes(ref auth_data_bytes) = auth_data { self.extract_public_key(auth_data_bytes) } else { return Err("Invalid authData format".into()); }; let auth_data_bytes = if let ciborium::Value::Bytes(auth_data_bytes) = auth_data { auth_data_bytes.clone() } else { println!("Invalid authData format"); return Err("Invalid authData format".into()); }; let mut signature_base = Vec::new(); signature_base.extend_from_slice(&auth_data_bytes); signature_base.extend_from_slice(client_data_json); // Verify the signature using the public key return self.verify_signature_with_public_key(sig, &signature_base, &public_key.unwrap(), alg); // --- 验证函数内部 --- let public_key = openssl::pkey::PKey::public_key_from_der(public_key)?; let message_digest = match alg { -7 => openssl::hash::MessageDigest::sha256(), -35 => openssl::hash::MessageDigest::sha384(), -36 => openssl::hash::MessageDigest::sha512(), _ => return Err("Unsupported algorithm".into()), }; let mut verifier = openssl::sign::Verifier::new(message_digest, &public_key)?; verifier.update(data)?; Ok(verifier.verify(&sig)?)
遇到的错误
当执行到openssl::pkey::PKey::public_key_from_der(public_key)?这一行时,抛出了ASN.1编码错误:
error:068000A8:asn1 encoding routines:asn1_check_tlen:wrong tag:crypto/asn1/tasn_dec.c:1194:, error:0688010A:asn1 encoding routines:asn1_item_embed_d2i:nested asn1 error:crypto/asn1/tasn_dec.c:349:Type=X509_PUBKEY
我的疑问
是不是我的实现哪里出错了?特别是extract_public_key_from_cose这个函数,它返回的是COSE密钥的CBOR序列化字节,但openssl::pkey::PKey::public_key_from_der需要的是DER格式的公钥,是不是这里的格式转换逻辑错了?应该怎么正确把COSE密钥转换成openssl能识别的DER格式公钥?
备注:内容来源于stack exchange,提问作者notnuyy
相关产品推荐
相关产品推荐

