Create certificate with QCStatements using OpenSSL

Viewed 1976

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 Answers

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_name
req_extensions = qcStatements

[req_distinguished_name]

[qcStatements]
1.3.6.1.5.5.7.1.3=ASN1:SEQUENCE:qcStatement

[qcStatement]
etsiQcsCompliance=SEQUENCE:etsiQcsCompliance
qcs-QcPDS=SEQUENCE:qcs-QcPDS
id-qc-statement=SEQUENCE:id-qc-statement
qcs-QcType=SEQUENCE:qcs-QcType
[etsiQcsCompliance]
statementId=OID:0.4.0.1862.1.1
[qcs-QcPDS]
statementId=OID:0.4.0.1862.1.5
QcPDS-List=SEQUENCE:QcPDS-List
[QcPDS-List]
QcPDS1=SEQUENCE:QcPDS1
[QcPDS1]
url=IA5STRING:https://example.org/pkidisclosure
description=PRINTABLESTRING:example

[id-qc-statement]
statementId=OID:0.4.0.19495.2
statementInfo=SEQUENCE:id-qc-statement-Info
[id-qc-statement-Info]
rolesOfPSP=SEQUENCE:rolesOfPSP
nCAName=UTF8String:Dummy Financial Supervision Authority
nCAId=UTF8String:XX-DFSA
[rolesOfPSP]
PSP_AI=SEQUENCE:PSP_AI
PSP_AS=SEQUENCE:PSP_AS
PSP_PI=SEQUENCE:PSP_PI
PSP_IC=SEQUENCE:PSP_IC
[PSP_AI]
roleOfPspOid=OID:0.4.0.19495.1.3
roleOfPspName=UTF8String:PSP_AI
[PSP_AS]
roleOfPspOid=OID:0.4.0.19495.1.1
roleOfPspName=UTF8String:PSP_AS
[PSP_PI]
roleOfPspOid=OID:0.4.0.19495.1.2
roleOfPspName=UTF8String:PSP_PI
[PSP_IC]
roleOfPspOid=OID:0.4.0.19495.1.4
roleOfPspName=UTF8String:PSP_IC
[qcs-QcType]
statementId=OID:0.4.0.1862.1.6
statementInfo=SEQUENCE:qcs-QcType-Info
[qcs-QcType-Info]
qct-esign=OID:0.4.0.1862.1.6.1
qct-eseal=OID:0.4.0.1862.1.6.2
qct-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 - Defaults
  SSL_url_0                            = example.com
  SSL_url_1                            = secure.example.com
  SSL_organizationName_0               = Example Company Limited
  SSL_organizationName_1               = Công Ty Trách Nhiệm Hữu Hạn Example
  SSL_countryName                      = VN
  #SSL_countryName_1                    = CA
  SSL_stateOrProvinceName              = Hà Nội
  SSL_nCA                              = EXAMPLE
  SSL_nCAId                            = 12345678
  SSL_currency                         = VND
openssl_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-v1
  id-qcs-pkixQCSyntax-v2               = 1.3.6.1.5.5.7.11.2 #qcs-pkixQCSyntax-v2
  cabfOrganizationIdentifier           = 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-policies
  qcp-public-with-sscd                 = 0.4.0.1456.1
  qcp-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-qcStatements
  qcs-qcCompliance                     = 0.4.0.1862.1.1     #qcCompliance
  qcs-qcLimitValue                     = 0.4.0.1862.1.2     #qcLimitValue
  qcs-qcRetentionPeriod                = 0.4.0.1862.1.3     #qcRetentionPeriod - int years
  qcs-QcSSCD                           = 0.4.0.1862.1.4     #qcSSCD            - Secure Signature Creation Device (SSCD)
  qcs-qcPDS                            = 0.4.0.1862.1.5     #qcPDS             - url
  qcs-qcCClegislation                  = 0.4.0.1862.1.7     #qcCClegislation
  qcs-qcType                           = 0.4.0.1862.1.6     #qcType
  qct-eSign                            = 0.4.0.1862.1.6.1
  qct-eSeal                            = 0.4.0.1862.1.6.2
  qct-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-policies
  ncp                                  = 0.4.0.2042.1.1     #normalized-certificate-policy
  ncpplus                              = 0.4.0.2042.1.2     #normalized-secure-certificate-policy
  lcp                                  = 0.4.0.2042.1.3     #lightweight-certificate-policy
  evcp                                 = 0.4.0.2042.1.4     #extended-validation-certificate-policy
  evcpplus                             = 0.4.0.2042.1.5     #extended-validation-secure-certificate-policy
  dvcp                                 = 0.4.0.2042.1.6     #domain-validation-certificate-policy
  ovcp                                 = 0.4.0.2042.1.7     #organization-validation-certificate-policy
  ivcp                                 = 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.1
  id-psd2-role-psp-as                  = 0.4.0.19495.1.1    #psp-account-servicing
  id-psd2-role-psp-pi                  = 0.4.0.19495.1.2    #psp-payment-initiation
  id-psd2-role-psp-ai                  = 0.4.0.19495.1.3    #psp-account-information
  id-psd2-role-psp-ic                  = 0.4.0.19495.1.4    #psp-issuing-card-payment-instruction
  id-etsi-psd2-qcStatement             = 0.4.0.19495.2      #etsi-psd2-qcStatement, qcPDS2, qcs-qcPDS2
  #etsi-psd2-policy                     = 0.4.0.19495.3      #psd2-policy
  qcp-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-identifiers
  qcp-natural                          = 0.4.0.194112.1.0   #qcp-l      - natural person
  qcp-legal                            = 0.4.0.194112.1.1   #qcp-n      - legal person
  qcp-natural-qscd                     = 0.4.0.194112.1.2   #qcp-n-qscd - natural person seal creation device
  qcp-legal-qscd                       = 0.4.0.194112.1.3   #qcp-l-qscd - legal person seal creation device
  qcp-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-identifiers
  id-etsi-qcs-SemanticsId-Natural      = 0.4.0.194121.1.1   #qcs-SemanticsId-Natural, eSign
  id-etsi-qcs-SemanticsId-Legal        = 0.4.0.194121.1.2   #qcs-SemanticsId-Legal, eSeal
  id-etsi-qcs-SemanticsId-eIDASNatural = 0.4.0.194121.1.3   #qcs-SemanticsId-eIDASNatural, eSign
  id-etsi-qcs-SemanticsId-eIDASLegal   = 0.4.0.194121.1.4   #qcs-SemanticsId-eIDASLegal, eSeal

[req]
  distinguished_name = RFC3739_DNs
  x509_extensions    = RFC3739_ext
[RFC3739_DNs]
  description         = RFC3739
  description_default = RFC3739

[RFC3739_ext]
  certificatePolicies               = qcp-web,qcp-legal,qcp-legal-qscd,evcp,evcpplus,qcp-web-psd2,qcp-public
  qcStatements                      = ASN1:SEQ:qcStatements
  subjectDirectoryAttributes        = ASN1:SEQ:personalDataAttributes #id-pda
  biometricInfo                     = ASN1:SEQ:biometricSyntax
  cabfOrganizationIdentifier        = ASN1:SEQ:cabfOrganizationIdentifier

[cabfOrganizationIdentifier]
  scheme  = PRINTABLE:PSD
  country = PRINTABLE:${ENV::SSL_countryName}
  state   = IMP:0,UTF8:${ENV::SSL_stateOrProvinceName}
  nCAId   = UTF8:${ENV::SSL_nCA}-${ENV::SSL_nCAId}

[biometricSyntax]
  0.biometricData = SEQ:biometricData-0
  1.biometricData = SEQ:biometricData-1
[biometricData-0]
  typeOfBiometricData = INT:0 #picture INT:0, handwritten-signature INT:1, biometricDataOid
  hashAlgorithm = SEQWRAP,OID:SHA256
  biometricDataHash = OCT:38CF6A36FDE77FC193E224834AE353CEA31302D9B681FB39C11622D63A5ADB99
  sourceDataUri = IA5:https://${ENV::SSL_url_1}/ca/user/${biometricDataHash}.jpg #optional
[biometricData-1]
  typeOfBiometricData = INT:1
  hashAlgorithm = SEQWRAP,OID:SHA256
  biometricDataHash = OCT:38CF6A36FDE77FC193E224834AE353CEA31302D9B681FB39C11622D63A5ADB99
  sourceDataUri = IA5:https://${ENV::SSL_url_1}/ca/user/${biometricDataHash}.jpg #optional

[personalDataAttributes]
  dateOfBirth          = SEQ:pda-dateOfBirth
  placeOfBirth         = SEQ:pda-placeOfBirth
  gender               = SEQ:pda-gender
  CountryOfCitizenship = SEQ:pda-countryOfCitizenship #can be specified multiple times
  0.CountryOfResidence = SEQ:pda-countryOfResidence-0 #can be specified multiple times
  1.CountryOfResidence = SEQ:pda-countryOfResidence-1
[pda-dateOfBirth]
  oid   = OID:id-pda-dateOfBirth
  value = SETWRAP,GENTIME:19870716000000Z #GeneralizedTime - YYYYMMDDHHMMSS.SZ
[pda-placeOfBirth]
  oid   = OID:id-pda-placeOfBirth
  value = SETWRAP,UTF8:my hospital, address, province, CA #Should be DirectoryString = choice (3) = utf8
[pda-gender]
  oid   = OID:id-pda-gender
  value = SETWRAP,PRINTABLE:M #PrintableString - M,m,F,f
[pda-countryOfCitizenship]
  oid   = OID:id-pda-countryOfCitizenship
  value = SETWRAP,PRINTABLE:CA #PrintableString - ISO 3166 Country Code - 2 digits
[pda-countryOfResidence-0]
  oid   = OID:id-pda-countryOfResidence
  value = SETWRAP,PRINTABLE:${ENV::SSL_countryName} #PrintableString - ISO 3166 Country Code - 2 digits
[pda-countryOfResidence-1]
  oid   = OID:id-pda-countryOfResidence
  value = SETWRAP,PRINTABLE:CA #PrintableString - ISO 3166 Country Code - 2 digits

[qcStatements]
  qcpkixQCSyntax    = SEQ:qcs-qcpkixQCSyntax #SEQWRAP,OID:id-qcs-pkixQCSyntax-v2 #id-qcs-pkixQCSyntax-v1
  qcCompliance      = SEQWRAP,OID:qcs-qcCompliance
  qcLimitValue      = SEQ:qcs-qcLimitValue
  qcRetentionPeriod = SEQ:qcs-qcRetentionPeriod
  qcSSCD            = SEQWRAP,OID:qcs-QcSSCD
  qcPDS             = SEQ:qcs-qcPDS
  qcType            = SEQ:qcs-qcType
  qcCClegislation   = SEQ:qcs-qcCClegislation
  qcPDS2            = SEQ:qcs-qcPDS2
[qcs-qcpkixQCSyntax]
  statementId       = OID:id-qcs-pkixQCSyntax-v2 #id-qcs-pkixQCSyntax-v1
  statementInfo     = SEQWRAP,OID:id-etsi-qcs-SemanticsId-Legal #can have multiple
[qcs-qcLimitValue]
  statementId       = OID:qcs-qcLimitValue
  monetaryValue     = SEQ:qcLimitValue
[qcLimitValue]
  limitCurrency     = PRINTABLE:${ENV::SSL_currency} #or INT:(0-999) ISO 4217
  limitAmount       = INT:1000000
  limitExponent     = INT:10 #value = amount * 10^exponent
[qcs-qcRetentionPeriod]
  statementId       = OID:qcs-qcRetentionPeriod
  retentionYears    = INT:15
[qcs-qcPDS]
  statementId       = OID:qcs-qcPDS
  qcPDS             = SEQ:qcPDS
[qcPDS]
  0.PDSLocation     = SEQ:PDSLocation-0
  1.PDSLocation     = SEQ:PDSLocation-1
  2.PDSLocation     = SEQ:PDSLocation-2
  3.PDSLocation     = SEQ:PDSLocation-3
[PDSLocation-0]
  url               = IA5:https://${ENV::SSL_url_0}/ca/pds
  lang              = PRINTABLE:en
[PDSLocation-1]
  url               = IA5:https://${ENV::SSL_url_0}/ca/pds
  lang              = PRINTABLE:vi
[PDSLocation-2]
  url               = IA5:https://${ENV::SSL_url_1}/ca/pds
  lang              = PRINTABLE:en
[PDSLocation-3]
  url               = IA5:https://${ENV::SSL_url_1}/ca/pds
  lang              = PRINTABLE:vi
[qcs-qcType]
  statementId       = OID:qcs-qcType
  typeName          = SEQ:qcType
[qcType]
  0.qcType          = OID:qct-eSign
  1.qcType          = OID:qct-eSeal
  2.qcType          = OID:qct-web
[qcs-qcCClegislation]
  statementId       = OID:qcs-qcCClegislation
  countryName       = SEQ:qcCClegislation
[qcCClegislation]
  0.countryName     = PRINTABLE:${ENV::SSL_countryName}
  1.countryName     = PRINTABLE:CA
[qcs-qcPDS2]
  statementId       = OID:id-etsi-psd2-qcStatement
  statementInfo     = SEQ:qcPDS2
[qcPDS2]
  rolesOfPSP        = SEQ:PSProles
  nCAName           = 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-0
  1.PSProle         = SEQ:PSProle-1
  2.PSProle         = SEQ:PSProle-2
  3.PSProle         = SEQ:PSProle-3
[PSProle-0]
  roleOfPspOid      = OID:id-psd2-role-psp-as
  roleOfPspName     = UTF8:PSP_AS
[PSProle-1]
  roleOfPspOid      = OID:id-psd2-role-psp-pi
  roleOfPspName     = UTF8:PSP_PI
[PSProle-2]
  roleOfPspOid      = OID:id-psd2-role-psp-ai
  roleOfPspName     = UTF8:PSP_AI
[PSProle-3]
  roleOfPspOid      = OID:id-psd2-role-psp-ic
  roleOfPspName     = UTF8:PSP_IC

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

Related