I want to create mock CA and set QCStatements extension, but I can't find any information about setting it using OpenSSL. I was looking about I found some old topics where people looking for this information, but no one answer. Is this possible to set QCStatements using openSSL?

3

Best Answer


It is possible with pure openssl. You have to create configuration for the signing request with all required qcStatements information and then sing the certificate copying the requested extension.

Sample configuration file

[req]distinguished_name = req_distinguished_namereq_extensions = qcStatements[req_distinguished_name][qcStatements]1.3.6.1.5.5.7.1.3=ASN1:SEQUENCE:qcStatement[qcStatement]etsiQcsCompliance=SEQUENCE:etsiQcsComplianceqcs-QcPDS=SEQUENCE:qcs-QcPDSid-qc-statement=SEQUENCE:id-qc-statementqcs-QcType=SEQUENCE:qcs-QcType[etsiQcsCompliance]statementId=OID:0.4.0.1862.1.1[qcs-QcPDS]statementId=OID:0.4.0.1862.1.5QcPDS-List=SEQUENCE:QcPDS-List[QcPDS-List]QcPDS1=SEQUENCE:QcPDS1[QcPDS1]url=IA5STRING:https://example.org/pkidisclosuredescription=PRINTABLESTRING:example[id-qc-statement]statementId=OID:0.4.0.19495.2statementInfo=SEQUENCE:id-qc-statement-Info[id-qc-statement-Info]rolesOfPSP=SEQUENCE:rolesOfPSPnCAName=UTF8String:Dummy Financial Supervision AuthoritynCAId=UTF8String:XX-DFSA[rolesOfPSP]PSP_AI=SEQUENCE:PSP_AIPSP_AS=SEQUENCE:PSP_ASPSP_PI=SEQUENCE:PSP_PIPSP_IC=SEQUENCE:PSP_IC[PSP_AI]roleOfPspOid=OID:0.4.0.19495.1.3roleOfPspName=UTF8String:PSP_AI[PSP_AS]roleOfPspOid=OID:0.4.0.19495.1.1roleOfPspName=UTF8String:PSP_AS[PSP_PI]roleOfPspOid=OID:0.4.0.19495.1.2roleOfPspName=UTF8String:PSP_PI[PSP_IC]roleOfPspOid=OID:0.4.0.19495.1.4roleOfPspName=UTF8String:PSP_IC[qcs-QcType]statementId=OID:0.4.0.1862.1.6statementInfo=SEQUENCE:qcs-QcType-Info[qcs-QcType-Info]qct-esign=OID:0.4.0.1862.1.6.1qct-eseal=OID:0.4.0.1862.1.6.2qct-web=OID:0.4.0.1862.1.6.3

You have to apply configuration to signing request

openssl req -new -key dummy.key -out dummy.csr -subj /C=XX/CN=dummy -config qcstatements.conf

And then sign with option to copy extensions from request inside CA configuration file.

copy_extensions = copy

If someone will encounters same problem there is a solution I found. I created certs with qcStatement using open source application ejbca app. There is a qcStatement section in this app: enter image description here

Specil thanks to AGrzes - You really got me on the right track.

It took me a lot of searching to get through all of these related standards, so I thought I would help and post here to save someone the trouble.

When I am doing ASN.1 work, I'm usually perusing https://censys.io/certificates to check if my code matches existing certificates. Always saves me time. Take note of how many certificates your query results in- people make mistakes, so you might be headed down the wrong track.

Anyways, my openssl-RFC3739.cnf:

#Qualified Certificate Profiles / Payment Services Directive / ETSI / eIDAS#TODO: implement: biometricInfo: typeOfBiometricData = OID:(finger/palm/retina/voice OID under my_pen OID arc)#TEST: \export OPENSSL_CONF=openssl-RFC3739.cnf; #use this conf \openssl asn1parse -genstr OID:1.3.6.1.5.5.7.1.3; #check OID support \openssl req -x509 -nodes -newkey ec -pkeyopt ec_paramgen_curve:secp384r1 -keyout key.pem -out cert.pem -batch; #generate public/prive keys \openssl asn1parse -in cert.pem | sed -n '/qcStatements/{n;s/.*://p}' | xxd -r -p | openssl asn1parse -inform DER -i; #show qcStatements \openssl asn1parse -in cert.pem | sed -n '/Biometric Info/{n;s/.*://p}' | xxd -r -p | openssl asn1parse -inform DER -i; #shox biometricInfo \openssl asn1parse -in cert.pem | sed -n '/X509v3 Subject Directory Attributes/{n;s/.*://p}' | xxd -r -p | openssl asn1parse -inform DER -i; #show subjectDirectoryAttributes \openssl asn1parse -in cert.pem | sed -n '/cabfOrganizationIdentifier/{n;s/.*://p}' | xxd -r -p | openssl asn1parse -inform DER -i; #show cabfOrganizationIdentifier \#https://censys.io/certificates?q=parsed.unknown_extensions.id%3A+1.3.6.1.5.5.7.1.3 #example certs using this OID#ETSI EN 319 412-1 V1.4.2 (2020-07) - https://www.etsi.org/deliver/etsi_en/319400_319499/31941201/01.04.02_20/en_31941201v010402a.pdf#ETSI EN 319 412-2 V2.2.1 (2020-07) - https://www.etsi.org/deliver/etsi_en/319400_319499/31941202/02.02.01_60/en_31941202v020201p.pdf#ETSI EN 319 412-3 V1.2.1 (2020-07) - https://www.etsi.org/deliver/etsi_en/319400_319499/31941203/01.02.01_60/en_31941203v010201p.pdf#ETSI EN 319 412-4 V1.1.1 (2016-02) - https://www.etsi.org/deliver/etsi_en/319400_319499/31941204/01.01.01_60/en_31941204v010101p.pdf#ETSI EN 319 412-5 V2.3.1 (2020-04) - https://www.etsi.org/deliver/etsi_en/319400_319499/31941205/02.03.01_60/en_31941205v020301p.pdf#ETSI EN 319 411-1 V1.2.0 (2017-08) - https://www.etsi.org/deliver/etsi_EN/319400_319499/31941101/01.02.00_20/en_31941101v010200a.pdf#ETSI TS 119 495 V1.4.1 (2019-11) - https://www.etsi.org/deliver/etsi_ts/119400_119499/119495/01.04.01_60/ts_119495v010401p.pdf#ETSI TS 101 456 V1.4.3 (2007-05) - https://www.etsi.org/deliver/etsi_ts/101400_101499/101456/01.04.03_60/ts_101456v010403p.pdf#VARIABLES - DefaultsSSL_url_0 = example.comSSL_url_1 = secure.example.comSSL_organizationName_0 = Example Company LimitedSSL_organizationName_1 = Công Ty Trách Nhiệm Hữu Hạn ExampleSSL_countryName = VN#SSL_countryName_1 = CASSL_stateOrProvinceName = Hà NộiSSL_nCA = EXAMPLESSL_nCAId = 12345678SSL_currency = VNDopenssl_conf = RFC3739_init#oid_section = RFC3739_OIDs[RFC3739_init]oid_section = RFC3739_OIDs[RFC3739_OIDs]#biometricInfo = 1.3.6.1.5.5.7.1.2 #id-pe-biometricInfo#qcStatements = 1.3.6.1.5.5.7.1.3 #id-pe-qcStatements#id-qcs = 1.3.6.1.5.5.7.11 #qcs#id-qcs-pkixQCSyntax-v1 = 1.3.6.1.5.5.7.11.1 #qcs-pkixQCSyntax-v1id-qcs-pkixQCSyntax-v2 = 1.3.6.1.5.5.7.11.2 #qcs-pkixQCSyntax-v2cabfOrganizationIdentifier = 2.23.140.3.1 #cabforganization_identifier#qualified-certificate-policies - ETSI TS 101 456#qualified-certificate-policies = 0.4.0.1456 #id-etsi-qualified-certificate-policiesqcp-public-with-sscd = 0.4.0.1456.1qcp-public = 0.4.0.1456.2#id-etsi-qcStatements - ETSI EN 319 412-5#qc-profile = 0.4.0.1862 #etsi-qc-profile eIDAS PSD2#id-mod-qc-profile-2 = 0.4.0.1862.0.2#id-etsi-qcStatements = 0.4.0.1862.1 #etsi-qcStatementsqcs-qcCompliance = 0.4.0.1862.1.1 #qcComplianceqcs-qcLimitValue = 0.4.0.1862.1.2 #qcLimitValueqcs-qcRetentionPeriod = 0.4.0.1862.1.3 #qcRetentionPeriod - int yearsqcs-QcSSCD = 0.4.0.1862.1.4 #qcSSCD - Secure Signature Creation Device (SSCD)qcs-qcPDS = 0.4.0.1862.1.5 #qcPDS - urlqcs-qcCClegislation = 0.4.0.1862.1.7 #qcCClegislationqcs-qcType = 0.4.0.1862.1.6 #qcTypeqct-eSign = 0.4.0.1862.1.6.1qct-eSeal = 0.4.0.1862.1.6.2qct-web = 0.4.0.1862.1.6.3#other-certificate-policies - ETSI EN 319 411-1#other-certificate-policies = 0.4.0.2042.1 #id-etsi-other-certificate-policiesncp = 0.4.0.2042.1.1 #normalized-certificate-policyncpplus = 0.4.0.2042.1.2 #normalized-secure-certificate-policylcp = 0.4.0.2042.1.3 #lightweight-certificate-policyevcp = 0.4.0.2042.1.4 #extended-validation-certificate-policyevcpplus = 0.4.0.2042.1.5 #extended-validation-secure-certificate-policydvcp = 0.4.0.2042.1.6 #domain-validation-certificate-policyovcp = 0.4.0.2042.1.7 #organization-validation-certificate-policyivcp = 0.4.0.2042.1.8 #individual-validation-certificate-policy#id-etsi-psd2 - ETSI TS 119 495#etsi-psd2 = 0.4.0.19495#etsi-psd2-roles = 0.4.0.19495.1id-psd2-role-psp-as = 0.4.0.19495.1.1 #psp-account-servicingid-psd2-role-psp-pi = 0.4.0.19495.1.2 #psp-payment-initiationid-psd2-role-psp-ai = 0.4.0.19495.1.3 #psp-account-informationid-psd2-role-psp-ic = 0.4.0.19495.1.4 #psp-issuing-card-payment-instructionid-etsi-psd2-qcStatement = 0.4.0.19495.2 #etsi-psd2-qcStatement, qcPDS2, qcs-qcPDS2#etsi-psd2-policy = 0.4.0.19495.3 #psd2-policyqcp-web-psd2 = 0.4.0.19495.3.1 #qcp-w-psd2#qcs-policy-identifiers - ETSI EN 319 412-2#id-etsi-qcs-policy-identifiers = 0.4.0.194112.1 #qcs-policy-identifiersqcp-natural = 0.4.0.194112.1.0 #qcp-l - natural personqcp-legal = 0.4.0.194112.1.1 #qcp-n - legal personqcp-natural-qscd = 0.4.0.194112.1.2 #qcp-n-qscd - natural person seal creation deviceqcp-legal-qscd = 0.4.0.194112.1.3 #qcp-l-qscd - legal person seal creation deviceqcp-web = 0.4.0.194112.1.4 #qcp-w - website#qcs-semantics-identifiers - ETSI EN 319 412-1#id-etsi-qcs-semantics-identifiers = 0.4.0.194121.1 #qcs-semantics-identifiersid-etsi-qcs-SemanticsId-Natural = 0.4.0.194121.1.1 #qcs-SemanticsId-Natural, eSignid-etsi-qcs-SemanticsId-Legal = 0.4.0.194121.1.2 #qcs-SemanticsId-Legal, eSealid-etsi-qcs-SemanticsId-eIDASNatural = 0.4.0.194121.1.3 #qcs-SemanticsId-eIDASNatural, eSignid-etsi-qcs-SemanticsId-eIDASLegal = 0.4.0.194121.1.4 #qcs-SemanticsId-eIDASLegal, eSeal[req]distinguished_name = RFC3739_DNsx509_extensions = RFC3739_ext[RFC3739_DNs]description = RFC3739description_default = RFC3739[RFC3739_ext]certificatePolicies = qcp-web,qcp-legal,qcp-legal-qscd,evcp,evcpplus,qcp-web-psd2,qcp-publicqcStatements = ASN1:SEQ:qcStatementssubjectDirectoryAttributes = ASN1:SEQ:personalDataAttributes #id-pdabiometricInfo = ASN1:SEQ:biometricSyntaxcabfOrganizationIdentifier = ASN1:SEQ:cabfOrganizationIdentifier[cabfOrganizationIdentifier]scheme = PRINTABLE:PSDcountry = PRINTABLE:${ENV::SSL_countryName}state = IMP:0,UTF8:${ENV::SSL_stateOrProvinceName}nCAId = UTF8:${ENV::SSL_nCA}-${ENV::SSL_nCAId}[biometricSyntax]0.biometricData = SEQ:biometricData-01.biometricData = SEQ:biometricData-1[biometricData-0]typeOfBiometricData = INT:0 #picture INT:0, handwritten-signature INT:1, biometricDataOidhashAlgorithm = SEQWRAP,OID:SHA256biometricDataHash = OCT:38CF6A36FDE77FC193E224834AE353CEA31302D9B681FB39C11622D63A5ADB99sourceDataUri = IA5:https://${ENV::SSL_url_1}/ca/user/${biometricDataHash}.jpg #optional[biometricData-1]typeOfBiometricData = INT:1hashAlgorithm = SEQWRAP,OID:SHA256biometricDataHash = OCT:38CF6A36FDE77FC193E224834AE353CEA31302D9B681FB39C11622D63A5ADB99sourceDataUri = IA5:https://${ENV::SSL_url_1}/ca/user/${biometricDataHash}.jpg #optional[personalDataAttributes]dateOfBirth = SEQ:pda-dateOfBirthplaceOfBirth = SEQ:pda-placeOfBirthgender = SEQ:pda-genderCountryOfCitizenship = SEQ:pda-countryOfCitizenship #can be specified multiple times0.CountryOfResidence = SEQ:pda-countryOfResidence-0 #can be specified multiple times1.CountryOfResidence = SEQ:pda-countryOfResidence-1[pda-dateOfBirth]oid = OID:id-pda-dateOfBirthvalue = SETWRAP,GENTIME:19870716000000Z #GeneralizedTime - YYYYMMDDHHMMSS.SZ[pda-placeOfBirth]oid = OID:id-pda-placeOfBirthvalue = SETWRAP,UTF8:my hospital, address, province, CA #Should be DirectoryString = choice (3) = utf8[pda-gender]oid = OID:id-pda-gendervalue = SETWRAP,PRINTABLE:M #PrintableString - M,m,F,f[pda-countryOfCitizenship]oid = OID:id-pda-countryOfCitizenshipvalue = SETWRAP,PRINTABLE:CA #PrintableString - ISO 3166 Country Code - 2 digits[pda-countryOfResidence-0]oid = OID:id-pda-countryOfResidencevalue = SETWRAP,PRINTABLE:${ENV::SSL_countryName} #PrintableString - ISO 3166 Country Code - 2 digits[pda-countryOfResidence-1]oid = OID:id-pda-countryOfResidencevalue = SETWRAP,PRINTABLE:CA #PrintableString - ISO 3166 Country Code - 2 digits[qcStatements]qcpkixQCSyntax = SEQ:qcs-qcpkixQCSyntax #SEQWRAP,OID:id-qcs-pkixQCSyntax-v2 #id-qcs-pkixQCSyntax-v1qcCompliance = SEQWRAP,OID:qcs-qcComplianceqcLimitValue = SEQ:qcs-qcLimitValueqcRetentionPeriod = SEQ:qcs-qcRetentionPeriodqcSSCD = SEQWRAP,OID:qcs-QcSSCDqcPDS = SEQ:qcs-qcPDSqcType = SEQ:qcs-qcTypeqcCClegislation = SEQ:qcs-qcCClegislationqcPDS2 = SEQ:qcs-qcPDS2[qcs-qcpkixQCSyntax]statementId = OID:id-qcs-pkixQCSyntax-v2 #id-qcs-pkixQCSyntax-v1statementInfo = SEQWRAP,OID:id-etsi-qcs-SemanticsId-Legal #can have multiple[qcs-qcLimitValue]statementId = OID:qcs-qcLimitValuemonetaryValue = SEQ:qcLimitValue[qcLimitValue]limitCurrency = PRINTABLE:${ENV::SSL_currency} #or INT:(0-999) ISO 4217limitAmount = INT:1000000limitExponent = INT:10 #value = amount * 10^exponent[qcs-qcRetentionPeriod]statementId = OID:qcs-qcRetentionPeriodretentionYears = INT:15[qcs-qcPDS]statementId = OID:qcs-qcPDSqcPDS = SEQ:qcPDS[qcPDS]0.PDSLocation = SEQ:PDSLocation-01.PDSLocation = SEQ:PDSLocation-12.PDSLocation = SEQ:PDSLocation-23.PDSLocation = SEQ:PDSLocation-3[PDSLocation-0]url = IA5:https://${ENV::SSL_url_0}/ca/pdslang = PRINTABLE:en[PDSLocation-1]url = IA5:https://${ENV::SSL_url_0}/ca/pdslang = PRINTABLE:vi[PDSLocation-2]url = IA5:https://${ENV::SSL_url_1}/ca/pdslang = PRINTABLE:en[PDSLocation-3]url = IA5:https://${ENV::SSL_url_1}/ca/pdslang = PRINTABLE:vi[qcs-qcType]statementId = OID:qcs-qcTypetypeName = SEQ:qcType[qcType]0.qcType = OID:qct-eSign1.qcType = OID:qct-eSeal2.qcType = OID:qct-web[qcs-qcCClegislation]statementId = OID:qcs-qcCClegislationcountryName = SEQ:qcCClegislation[qcCClegislation]0.countryName = PRINTABLE:${ENV::SSL_countryName}1.countryName = PRINTABLE:CA[qcs-qcPDS2]statementId = OID:id-etsi-psd2-qcStatementstatementInfo = SEQ:qcPDS2[qcPDS2]rolesOfPSP = SEQ:PSProlesnCAName = UTF8:${ENV::SSL_organizationName_0} / ${ENV::SSL_organizationName_1}nCAId = UTF8:PSD${ENV::SSL_countryName}-${ENV::SSL_nCA}-${ENV::SSL_nCAId} #VN-TWOEIGHT-1234567890[PSProles]0.PSProle = SEQ:PSProle-01.PSProle = SEQ:PSProle-12.PSProle = SEQ:PSProle-23.PSProle = SEQ:PSProle-3[PSProle-0]roleOfPspOid = OID:id-psd2-role-psp-asroleOfPspName = UTF8:PSP_AS[PSProle-1]roleOfPspOid = OID:id-psd2-role-psp-piroleOfPspName = UTF8:PSP_PI[PSProle-2]roleOfPspOid = OID:id-psd2-role-psp-airoleOfPspName = UTF8:PSP_AI[PSProle-3]roleOfPspOid = OID:id-psd2-role-psp-icroleOfPspName = UTF8:PSP_IC

I'll keep an eye open for comments and edit if anything is incorrect.