mirror of
https://github.com/saymrwulf/cryptography.git
synced 2026-05-14 20:37:55 +00:00
feat(admissions): implement parsing of admissions extension (#11903)
* feat: implement parsing of admissions extension Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> * chore: add tests for admissions extension parsing Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> * chore: use cryptography result return type Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> * chore: apply fixes done by cargo fmt and clippy Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> * add gematik company name and the gmbh abbreviations to known words Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> * fix: regenerate the synthetic certificate with additional admission covering the case of naming authority with no data Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> * fix: parse none for profession_oids if profession_oids is none Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> * chore: apply formatting to changes in rust codebase Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> * refactor: switch return type of parse_profession_infos from PyObject to Bound<PyAny> Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com> * refactor: switch return type of parse_naming_authority from PyObject to Bound<PyAny> Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com> * refactor: switch return type of parse_admissions from PyObject to Bound<PyAny> Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com> * chore: remove gematik certs from repo Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com> * chore: remove gematik certs from this pr Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com> * chore: extend parser tests with an additional synthetic certificate to complete rust coverage Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com> * chore: add description for the additional certificate without authority Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com> * use into_bound(py) as shortcut, refrain from using to_object() in all added functions Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com> * add better description for the admissions synthetic cert Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com> * adjust description to avoid using misspelled words Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com> --------- Signed-off-by: oleg.hoefling <oleg.hoefling@gmail.com> Signed-off-by: Oleg Hoefling <oleg.hoefling@gmail.com>
This commit is contained in:
parent
78e89e4975
commit
fef127093b
6 changed files with 316 additions and 5 deletions
|
|
@ -546,6 +546,16 @@ Custom X.509 Vectors
|
|||
This is an invalid certificate per :rfc:`5280` 4.2.1.12.
|
||||
* ``malformed-san.pem`` - A certificate with a malformed SAN.
|
||||
* ``malformed-ian.pem`` - A certificate with a malformed IAN.
|
||||
* ``admissions_extension_optional_data_not_provided.pem`` -
|
||||
A certificate containing the ``Admissions`` extension with multiple admissions,
|
||||
signed by ``x509/custom/ca/rsa_ca.pem`` CA. The admissions in this certificate
|
||||
are prepared using synthetic data to verify the possible corner cases are handled
|
||||
by the parser correctly (an admission missing naming authority or admission
|
||||
authority, a profession info missing naming authority or profession OIDs
|
||||
or the registration number etc).
|
||||
* ``admissions_extension_authority_not_provided.pem`` - A certificate containing
|
||||
the ``Admissions`` extension with no admissions and no admission authority,
|
||||
signed by ``x509/custom/ca/rsa_ca.pem`` CA.
|
||||
|
||||
Custom X.509 Request Vectors
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
|||
|
|
@ -263,6 +263,12 @@ pub static CERTIFICATE_VERSION_V1: LazyPyImport =
|
|||
LazyPyImport::new("cryptography.x509", &["Version", "v1"]);
|
||||
pub static CERTIFICATE_VERSION_V3: LazyPyImport =
|
||||
LazyPyImport::new("cryptography.x509", &["Version", "v3"]);
|
||||
pub static ADMISSION: LazyPyImport = LazyPyImport::new("cryptography.x509", &["Admission"]);
|
||||
pub static NAMING_AUTHORITY: LazyPyImport =
|
||||
LazyPyImport::new("cryptography.x509", &["NamingAuthority"]);
|
||||
pub static PROFESSION_INFO: LazyPyImport =
|
||||
LazyPyImport::new("cryptography.x509", &["ProfessionInfo"]);
|
||||
pub static ADMISSIONS: LazyPyImport = LazyPyImport::new("cryptography.x509", &["Admissions"]);
|
||||
|
||||
pub static CRL_REASON_FLAGS: LazyPyImport =
|
||||
LazyPyImport::new("cryptography.x509.extensions", &["_CRLREASONFLAGS"]);
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ use std::hash::{Hash, Hasher};
|
|||
use cryptography_x509::certificate::Certificate as RawCertificate;
|
||||
use cryptography_x509::common::{AlgorithmParameters, Asn1ReadableOrWritable};
|
||||
use cryptography_x509::extensions::{
|
||||
AuthorityKeyIdentifier, BasicConstraints, DisplayText, DistributionPoint,
|
||||
DistributionPointName, DuplicateExtensionsError, ExtendedKeyUsage, IssuerAlternativeName,
|
||||
KeyUsage, MSCertificateTemplate, NameConstraints, PolicyConstraints, PolicyInformation,
|
||||
PolicyQualifierInfo, Qualifier, RawExtensions, SequenceOfAccessDescriptions,
|
||||
SequenceOfSubtrees, UserNotice,
|
||||
Admission, Admissions, AuthorityKeyIdentifier, BasicConstraints, DisplayText,
|
||||
DistributionPoint, DistributionPointName, DuplicateExtensionsError, ExtendedKeyUsage,
|
||||
IssuerAlternativeName, KeyUsage, MSCertificateTemplate, NameConstraints, NamingAuthority,
|
||||
PolicyConstraints, PolicyInformation, PolicyQualifierInfo, ProfessionInfo, Qualifier,
|
||||
RawExtensions, SequenceOfAccessDescriptions, SequenceOfSubtrees, UserNotice,
|
||||
};
|
||||
use cryptography_x509::extensions::{Extension, SubjectAlternativeName};
|
||||
use cryptography_x509::{common, oid};
|
||||
|
|
@ -731,6 +731,100 @@ pub(crate) fn parse_access_descriptions(
|
|||
Ok(ads.to_object(py))
|
||||
}
|
||||
|
||||
fn parse_naming_authority<'p>(
|
||||
py: pyo3::Python<'p>,
|
||||
authority: NamingAuthority<'p>,
|
||||
) -> CryptographyResult<pyo3::Bound<'p, pyo3::PyAny>> {
|
||||
let py_id = match &authority.id {
|
||||
Some(data) => oid_to_py_oid(py, data)?,
|
||||
None => py.None().into_bound(py),
|
||||
};
|
||||
let py_url = match authority.url {
|
||||
Some(data) => pyo3::types::PyString::new_bound(py, data.as_str()).into_any(),
|
||||
None => py.None().into_bound(py),
|
||||
};
|
||||
let py_text = match authority.text {
|
||||
Some(data) => parse_display_text(py, data)?,
|
||||
None => py.None(),
|
||||
};
|
||||
|
||||
Ok(types::NAMING_AUTHORITY
|
||||
.get(py)?
|
||||
.call1((py_id, py_url, py_text))?)
|
||||
}
|
||||
|
||||
fn parse_profession_infos<'a>(
|
||||
py: pyo3::Python<'a>,
|
||||
profession_infos: &asn1::SequenceOf<'a, ProfessionInfo<'a>>,
|
||||
) -> CryptographyResult<pyo3::Bound<'a, pyo3::PyAny>> {
|
||||
let py_infos = pyo3::types::PyList::empty_bound(py);
|
||||
for info in profession_infos.clone() {
|
||||
let py_naming_authority = match info.naming_authority {
|
||||
Some(data) => parse_naming_authority(py, data)?,
|
||||
None => py.None().into_bound(py),
|
||||
};
|
||||
let py_profession_items = pyo3::types::PyList::empty_bound(py);
|
||||
for item in info.profession_items.unwrap_read().clone() {
|
||||
let py_item = parse_display_text(py, item)?;
|
||||
py_profession_items.append(py_item)?;
|
||||
}
|
||||
let py_profession_oids = match info.profession_oids {
|
||||
Some(oids) => {
|
||||
let py_oids = pyo3::types::PyList::empty_bound(py);
|
||||
for oid in oids.unwrap_read().clone() {
|
||||
let py_oid = oid_to_py_oid(py, &oid)?;
|
||||
py_oids.append(py_oid)?;
|
||||
}
|
||||
py_oids.into_any()
|
||||
}
|
||||
None => py.None().into_bound(py),
|
||||
};
|
||||
let py_registration_number = match info.registration_number {
|
||||
Some(data) => pyo3::types::PyString::new_bound(py, data.as_str()).into_any(),
|
||||
None => py.None().into_bound(py),
|
||||
};
|
||||
let py_add_profession_info = match info.add_profession_info {
|
||||
Some(data) => pyo3::types::PyBytes::new_bound(py, data).into_any(),
|
||||
None => py.None().into_bound(py),
|
||||
};
|
||||
let py_info = types::PROFESSION_INFO.get(py)?.call1((
|
||||
py_naming_authority,
|
||||
py_profession_items,
|
||||
py_profession_oids,
|
||||
py_registration_number,
|
||||
py_add_profession_info,
|
||||
))?;
|
||||
py_infos.append(py_info)?;
|
||||
}
|
||||
Ok(py_infos.into_any())
|
||||
}
|
||||
|
||||
fn parse_admissions<'a>(
|
||||
py: pyo3::Python<'a>,
|
||||
admissions: &asn1::SequenceOf<'a, Admission<'a>>,
|
||||
) -> CryptographyResult<pyo3::Bound<'a, pyo3::PyAny>> {
|
||||
let py_admissions = pyo3::types::PyList::empty_bound(py);
|
||||
for admission in admissions.clone() {
|
||||
let py_admission_authority = match admission.admission_authority {
|
||||
Some(authority) => x509::parse_general_name(py, authority)?,
|
||||
None => py.None(),
|
||||
};
|
||||
let py_naming_authority = match admission.naming_authority {
|
||||
Some(data) => parse_naming_authority(py, data)?,
|
||||
None => py.None().into_bound(py),
|
||||
};
|
||||
let py_infos = parse_profession_infos(py, admission.profession_infos.unwrap_read())?;
|
||||
|
||||
let py_entry = types::ADMISSION.get(py)?.call1((
|
||||
py_admission_authority,
|
||||
py_naming_authority,
|
||||
py_infos,
|
||||
))?;
|
||||
py_admissions.append(py_entry)?;
|
||||
}
|
||||
Ok(py_admissions.into_any())
|
||||
}
|
||||
|
||||
pub fn parse_cert_ext<'p>(
|
||||
py: pyo3::Python<'p>,
|
||||
ext: &Extension<'_>,
|
||||
|
|
@ -869,6 +963,20 @@ pub fn parse_cert_ext<'p>(
|
|||
ms_cert_tpl.minor_version,
|
||||
))?))
|
||||
}
|
||||
oid::ADMISSIONS_OID => {
|
||||
let admissions = ext.value::<Admissions<'_>>()?;
|
||||
let admission_authority = match admissions.admission_authority {
|
||||
Some(authority) => x509::parse_general_name(py, authority)?,
|
||||
None => py.None(),
|
||||
};
|
||||
let py_admissions =
|
||||
parse_admissions(py, admissions.contents_of_admissions.unwrap_read())?;
|
||||
Ok(Some(
|
||||
types::ADMISSIONS
|
||||
.get(py)?
|
||||
.call1((admission_authority, py_admissions))?,
|
||||
))
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1861,6 +1861,138 @@ class TestRSACertificate:
|
|||
with pytest.raises(TypeError):
|
||||
cert.verify_directly_issued_by(leaf)
|
||||
|
||||
def test_admissions_extension(self, backend):
|
||||
cert = _load_cert(
|
||||
os.path.join(
|
||||
"x509",
|
||||
"custom",
|
||||
"admissions_extension_optional_data_not_provided.pem",
|
||||
),
|
||||
x509.load_pem_x509_certificate,
|
||||
)
|
||||
ext = cert.extensions.get_extension_for_class(x509.Admissions)
|
||||
assert ext.value == x509.Admissions(
|
||||
authority=x509.DirectoryName(
|
||||
value=x509.Name(
|
||||
[
|
||||
x509.NameAttribute(
|
||||
oid=x509.NameOID.COUNTRY_NAME, value="DE"
|
||||
),
|
||||
x509.NameAttribute(
|
||||
oid=x509.NameOID.ORGANIZATION_NAME,
|
||||
value="Elektronisches Gesundheitsberuferegister",
|
||||
),
|
||||
]
|
||||
)
|
||||
),
|
||||
admissions=[
|
||||
x509.Admission(
|
||||
admission_authority=x509.RegisteredID(
|
||||
value=x509.NameOID.ORGANIZATION_NAME
|
||||
),
|
||||
naming_authority=x509.NamingAuthority(
|
||||
id=x509.ObjectIdentifier("1.2.276.0.76.4.223"),
|
||||
url="",
|
||||
text="Betriebsstätte GKV-Spitzenverband",
|
||||
),
|
||||
profession_infos=[
|
||||
x509.ProfessionInfo(
|
||||
naming_authority=x509.NamingAuthority(
|
||||
id=x509.ObjectIdentifier("1.2.276.0.76.4.225"),
|
||||
url="https://example.com",
|
||||
text=(
|
||||
"Betriebsstätte Deutscher "
|
||||
"Apothekerverband"
|
||||
),
|
||||
),
|
||||
profession_items=["Ã\x84rztin/Arzt", ""],
|
||||
profession_oids=[
|
||||
x509.ObjectIdentifier("1.2.276.0.76.4.30"),
|
||||
x509.ObjectIdentifier("1.2.276.0.76.4.31"),
|
||||
],
|
||||
registration_number="9-999/99999999",
|
||||
add_profession_info=(
|
||||
b'\x16"additional profession info example'
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
x509.Admission(
|
||||
admission_authority=x509.OtherName(
|
||||
type_id=x509.NameOID.COUNTRY_NAME,
|
||||
value=b"\x04\x04\x13\x02DE",
|
||||
),
|
||||
naming_authority=None,
|
||||
profession_infos=[
|
||||
x509.ProfessionInfo(
|
||||
naming_authority=x509.NamingAuthority(
|
||||
id=x509.ObjectIdentifier("1.2.276.0.76.4.227"),
|
||||
url=None,
|
||||
text=(
|
||||
"Betriebsstätte der Deutsche Krankenhaus "
|
||||
"TrustCenter und Informationsverarbeitung "
|
||||
"GmbH"
|
||||
),
|
||||
),
|
||||
profession_items=["Krankenhaus"],
|
||||
profession_oids=[
|
||||
x509.ObjectIdentifier("1.2.276.0.76.4.53"),
|
||||
x509.ObjectIdentifier("1.2.276.0.76.4.246"),
|
||||
],
|
||||
registration_number="9.9.9-99999999",
|
||||
add_profession_info=None,
|
||||
),
|
||||
x509.ProfessionInfo(
|
||||
naming_authority=None,
|
||||
profession_items=[
|
||||
"Krankenhaus",
|
||||
"Betriebsstätte Geburtshilfe",
|
||||
],
|
||||
profession_oids=[
|
||||
x509.ObjectIdentifier("1.2.276.0.76.4.53")
|
||||
],
|
||||
registration_number="",
|
||||
add_profession_info=None,
|
||||
),
|
||||
],
|
||||
),
|
||||
x509.Admission(
|
||||
admission_authority=None,
|
||||
naming_authority=None,
|
||||
profession_infos=[
|
||||
x509.ProfessionInfo(
|
||||
naming_authority=None,
|
||||
profession_items=[],
|
||||
profession_oids=None,
|
||||
registration_number=None,
|
||||
add_profession_info=None,
|
||||
)
|
||||
],
|
||||
),
|
||||
x509.Admission(
|
||||
admission_authority=None,
|
||||
naming_authority=x509.NamingAuthority(None, None, None),
|
||||
profession_infos=[],
|
||||
),
|
||||
x509.Admission(
|
||||
admission_authority=None,
|
||||
naming_authority=None,
|
||||
profession_infos=[],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
cert = _load_cert(
|
||||
os.path.join(
|
||||
"x509",
|
||||
"custom",
|
||||
"admissions_extension_authority_not_provided.pem",
|
||||
),
|
||||
x509.load_pem_x509_certificate,
|
||||
)
|
||||
ext = cert.extensions.get_extension_for_class(x509.Admissions)
|
||||
assert ext.value == x509.Admissions(authority=None, admissions=[])
|
||||
|
||||
|
||||
class TestRSACertificateRequest:
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDiTCCAy+gAwIBAgIUDuURI/KxJjJlnU/YDGmX0V0DyNQwCgYIKoZIzj0EAwIw
|
||||
JzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeSBDQTAeFw0yNDEx
|
||||
MDkxMzI4MjVaFw0yNDEyMDkxMzI4MjVaMCkxCzAJBgNVBAYTAlVTMRowGAYDVQQD
|
||||
DBFjcnlwdG9ncmFwaHkgdGVzdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
|
||||
ggIBANBIheRc1HT4MzV5GvUbDk9CFU6DTomRApNqRmizriRqm6OY4Ht3d71BXog6
|
||||
/IBkqAnZ4/XJQ40G4sVDb52k11oPvfJ/F5pc+6UqPBL+QGzYGkJoubAqXFpI6ow0
|
||||
qayFNQLv0T9o4yh0QQOoGvgCmv91qmitLrZNXu4U9S76G+DiGST+QyMkMxj+VsGR
|
||||
sRRBufV1urcnvFWjU6Q2+cr2cp0mMAG96NTyIskYiJ8vL03Wz4DX4klO4X47fPmD
|
||||
nU/OMn4SbvMZ896j1L0J04S+uVThTkxQWcFcqXhX5qM8kzcjJUmybFlbf150j3Wi
|
||||
ucW48K/j7fJ0x9q3iUo4Gva0coScglJWcgo/BBCwFDw8NVba7npxSRMiaS3qTv0d
|
||||
EFcRnvByc+7hyGxxlWdTE9tHisUI1eZVk9P9ziqNOZKscY8ZX1+/C4M9X69Y7A8I
|
||||
74F5dO27IRycEgOrSo2z1NhfSwbqJr9a2TBtRsFinn8rjKBIzNn0E5p9jO1Wjxtk
|
||||
cjHfXXpLN8FFMvoYI9l/K+ZWDm9sboaF8jrgozSc004AFemAH79mmCGVRKXn1vDA
|
||||
o4DLC6p3NiBFYQcYbW9V+beGD6srsF6xJtuY/UwtPROLWSzuCCrZ/4BlmpNsR0eh
|
||||
IFFvzEKjX6rR2yp3YKlguDbMBMKMpfSGxAFwcZ7OiaxR20UHAgMBAAGjbDBqMA0G
|
||||
BSskCAMDBAQwAjAAMB0GA1UdDgQWBBTWrADzmGKoPZIVNf6QvnOYMOtMhDA6BgNV
|
||||
HSMEMzAxoSukKTAnMQswCQYDVQQGEwJVUzEYMBYGA1UEAwwPY3J5cHRvZ3JhcGh5
|
||||
IENBggIDCTAKBggqhkjOPQQDAgNIADBFAiAnRuoEuL/8c/B3Cb89FOSMlV/sX1QW
|
||||
MXM8X69xVWxyjAIhAIuZ8HI2TUtuTOGascFW46AjkPfwCggknB7kkq86QOn3
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIF1zCCBXygAwIBAgIUckdGKz+upx7gGI/r6y1UvvQQFKowCgYIKoZIzj0EAwIw
|
||||
JzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD2NyeXB0b2dyYXBoeSBDQTAeFw0yNDEx
|
||||
MDkxMzI0NTlaFw0yNDEyMDkxMzI0NTlaMCkxCzAJBgNVBAYTAlVTMRowGAYDVQQD
|
||||
DBFjcnlwdG9ncmFwaHkgdGVzdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
|
||||
ggIBANBIheRc1HT4MzV5GvUbDk9CFU6DTomRApNqRmizriRqm6OY4Ht3d71BXog6
|
||||
/IBkqAnZ4/XJQ40G4sVDb52k11oPvfJ/F5pc+6UqPBL+QGzYGkJoubAqXFpI6ow0
|
||||
qayFNQLv0T9o4yh0QQOoGvgCmv91qmitLrZNXu4U9S76G+DiGST+QyMkMxj+VsGR
|
||||
sRRBufV1urcnvFWjU6Q2+cr2cp0mMAG96NTyIskYiJ8vL03Wz4DX4klO4X47fPmD
|
||||
nU/OMn4SbvMZ896j1L0J04S+uVThTkxQWcFcqXhX5qM8kzcjJUmybFlbf150j3Wi
|
||||
ucW48K/j7fJ0x9q3iUo4Gva0coScglJWcgo/BBCwFDw8NVba7npxSRMiaS3qTv0d
|
||||
EFcRnvByc+7hyGxxlWdTE9tHisUI1eZVk9P9ziqNOZKscY8ZX1+/C4M9X69Y7A8I
|
||||
74F5dO27IRycEgOrSo2z1NhfSwbqJr9a2TBtRsFinn8rjKBIzNn0E5p9jO1Wjxtk
|
||||
cjHfXXpLN8FFMvoYI9l/K+ZWDm9sboaF8jrgozSc004AFemAH79mmCGVRKXn1vDA
|
||||
o4DLC6p3NiBFYQcYbW9V+beGD6srsF6xJtuY/UwtPROLWSzuCCrZ/4BlmpNsR0eh
|
||||
IFFvzEKjX6rR2yp3YKlguDbMBMKMpfSGxAFwcZ7OiaxR20UHAgMBAAGjggK3MIIC
|
||||
szCCAlQGBSskCAMDBIICSTCCAkWkQjBAMQswCQYDVQQGEwJERTExMC8GA1UECgwo
|
||||
RWxla3Ryb25pc2NoZXMgR2VzdW5kaGVpdHNiZXJ1ZmVyZWdpc3RlcjCCAf0wgfKg
|
||||
BYgDVQQKoTQwMgYIKoIUAEwEgV8WAAwkQmV0cmllYnNzdMODwqR0dGUgR0tWLVNw
|
||||
aXR6ZW52ZXJiYW5kMIGyMIGvoE8wTQYIKoIUAEwEgWEWE2h0dHBzOi8vZXhhbXBs
|
||||
ZS5jb20MLEJldHJpZWJzc3TDg8KkdHRlIERldXRzY2hlciBBcG90aGVrZXJ2ZXJi
|
||||
YW5kMBIMDsODwoRyenRpbi9Bcnp0DAAwEgYHKoIUAEwEHgYHKoIUAEwEHxMOOS05
|
||||
OTkvOTk5OTk5OTkEJBYiYWRkaXRpb25hbCBwcm9mZXNzaW9uIGluZm8gZXhhbXBs
|
||||
ZTCB8aAPoA0GA1UEBqAGBAQTAkRFMIHdMIGcoGYwZAYIKoIUAEwEgWMMWEJldHJp
|
||||
ZWJzc3TDg8KkdHRlIGRlciBEZXV0c2NoZSBLcmFua2VuaGF1cyBUcnVzdENlbnRl
|
||||
ciB1bmQgSW5mb3JtYXRpb25zdmVyYXJiZWl0dW5nIEdtYkgwDQwLS3Jhbmtlbmhh
|
||||
dXMwEwYHKoIUAEwENQYIKoIUAEwEgXYTDjkuOS45LTk5OTk5OTk5MDwwLQwLS3Jh
|
||||
bmtlbmhhdXMMHkJldHJpZWJzc3TDg8KkdHRlIEdlYnVydHNoaWxmZTAJBgcqghQA
|
||||
TAQ1EwAwBjAEMAIwADAGoQIwADAAMAIwADAdBgNVHQ4EFgQU1qwA85hiqD2SFTX+
|
||||
kL5zmDDrTIQwOgYDVR0jBDMwMaErpCkwJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMM
|
||||
D2NyeXB0b2dyYXBoeSBDQYICAwkwCgYIKoZIzj0EAwIDSQAwRgIhAMz8iUp3Tj0W
|
||||
3mMOPIyNyQ6ZwydHCX199oH5j0opH+4GAiEAyOF2Mw4H6xDOfsEa2NvnpO4mt8Pa
|
||||
y7msciyCxhMgUZY=
|
||||
-----END CERTIFICATE-----
|
||||
Loading…
Reference in a new issue