mitzi 43 Posted October 17, 2019 (edited) Hi, can you please add this function to your components? It is tested and works and allows to load PFX/P12 certificates from memory. It is based on your LoadFromP12File function. procedure TX509Base.LoadFromP12Buffer(ABuffer: Pointer; ABufferSize: Cardinal; IncludePKey, IncludeInters: TCertReadOpt; const Password: String); var FileBio : PBIO; P12 : PPKCS12; Cert : PX509; PKey : PEVP_PKEY; Ca: PSTACK_OF_X509; PW: PAnsiChar; I: integer; begin InitializeSsl; FileBio := f_BIO_new_mem_buf(ABuffer,ABufferSize); if not Assigned(FileBio) then RaiseLastOpenSslError(EX509Exception, TRUE, 'Error reading PKCS12 certificates from buffer'); try P12 := f_d2i_PKCS12_bio(FileBio, Nil); if not Assigned(P12) then RaiseLastOpenSslError(EX509Exception, TRUE, 'Error reading PKCS12 certificates from buffer'); try Cert := Nil; Pkey := Nil; Ca := Nil; PW := Nil; if Length(Password) > 0 then PW := PAnsiChar(PasswordConvert(Password)); { V8.55 } if f_PKCS12_parse(P12, PW, Pkey, Cert, Ca) = 0 then begin if Ics_Ssl_ERR_GET_REASON(f_ERR_peek_error) = 113 then { PKCS12_R_MAC_VERIFY_FAILURE } raise EX509Exception.Create('Error PKCS12 Certificate password invalid for buffer') else RaiseLastOpenSslError(EX509Exception, TRUE, 'Error parsing PKCS12 certificates from buffer'); end; if (IncludePKey > croNo) then begin { V8.50 don't ignore croYes } if Assigned(PKey) then begin if (f_X509_check_private_key(Cert, PKey) < 1) then raise EX509Exception.Create('Certificate and private key do not match'); SetX509(Cert); SetPrivateKey(PKey); f_EVP_PKEY_free(PKey); end else begin if IncludePKey = croYes then { V8.50 require private key so error } raise EX509Exception.Create('Error reading private key from buffer'); end; end else SetX509(Cert); f_X509_free(Cert); FreeAndNilX509Inters; { intermediate certificates are optional, no error if none found } if (IncludeInters > croNo) and Assigned(Ca) then begin { V8.50 don't ignore croYes } FX509Inters := f_OPENSSL_sk_new_null; for I := 0 to f_OPENSSL_sk_num(Ca) - 1 do f_OPENSSL_sk_insert(FX509Inters, PAnsiChar(f_X509_dup (PX509(f_OPENSSL_sk_value(Ca, I)))), I); f_OPENSSL_sk_free(Ca); end; finally f_PKCS12_free(p12); end; finally f_bio_free(FileBio); end; end; Edited October 17, 2019 by mitzi Share this post Link to post
Angus Robertson 577 Posted October 17, 2019 Thanks, added to my master, will be in SVN in a couple of days with other minor SSL improvements. Are you working with the Windows certificate store by any chance, by pending list includes putting certificates (Let's Encrypt) into the store. Angus Share this post Link to post
Angus Robertson 577 Posted October 17, 2019 ICS has code to read certificates from the Windows certificate store, but not to add them. Not needed for ICS applications generally, but for servers using SChannel. Angus Share this post Link to post
mitzi 43 Posted October 17, 2019 (edited) What kind of certificates? PEM,DER,PFX? If you are able to get certificate context then adding to windows store is simple: (it uses WinCrypt API functions) function AddCertContextToStore(ACertContext: PCCERT_CONTEXT; const AStoreName: string = 'MY'): boolean; // other store names are e.g CA or ROOT var cs: HCERTSTORE; begin cs:=CertOpenStore(CERT_STORE_PROV_SYSTEM,0,0,CERT_SYSTEM_STORE_CURRENT_USER,PChar(AStoreName)); try Result:=Assigned(cs) and CertAddCertificateContextToStore(cs,ACertContext,CERT_STORE_ADD_REPLACE_EXISTING,nil); finally CertCloseStore(cs,0); end; end; But obtaining the certificate contexts from different file(certificate) types is more complicated. Following function gets first certificate context from PEM,P7C,P7B,CER,CRT,DER certificate files, PFX/P12 and combined PEM files are more difficult. function GetCertContext(const AFilename: string; out ACertContext: PCCERT_CONTEXT): boolean; var cs: HCERTSTORE; begin Result:=False; ACertContext:=nil; cs:=CertOpenStore(CERT_STORE_PROV_FILENAME,X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,0,CERT_STORE_OPEN_EXISTING_FLAG or CERT_STORE_READONLY_FLAG,PChar(AFilename)); if Assigned(cs) then begin ACertContext:=CertEnumCertificatesInStore(cs,nil); Result:=Assigned(ACertContext); CertCloseStore(cs,0); end; end; Edited October 17, 2019 by mitzi Share this post Link to post
Angus Robertson 577 Posted October 17, 2019 Only concerned about internal DER ASN_ENCODING format which ICS already supports, not file formats. OverbyteIcsPemtool1.pas already has code for opening a store, getting certificate contexts and converting them to X509 and PEM, need to move that into one of the library units, create a PCCERT_CONTEXT and add it to the correct store. Angus Share this post Link to post
mitzi 43 Posted October 17, 2019 (edited) Another question: How to convert TX509Base.SubjectOneLine to readable normalized form? I get e.g. /C=CZ/postalCode=00000/ST=\x00L\x00i\x00b\x00e\x00r\x00e\x00c\x00k\x00\xFD/L=SomeCity/street=SomeStreet/O=SomeName/OU=IT/CN=SomeName but need C=CZ, PostalCode=00000, S=Liberecký, L=SomeCity, STREET=SomeStreet, O=SomeName, OU=IT, CN=SomeName Edited October 17, 2019 by mitzi Share this post Link to post
Angus Robertson 577 Posted October 17, 2019 (edited) All the common subject and issuer fields are available as properties, ie SubjectCName, SubjectOName, IssuerCName, SubjectCOName, SubAltNameDNS, etc, or you can use GetNameEntryByNid with the NID literal for obscure ones like STREET, not even sure that exists... Look at the function ListCertDetail in OverbyteIcsPemtool1.pas which creates a string detailing most certificate fields. Angus Edited October 17, 2019 by Angus Robertson Share this post Link to post
mitzi 43 Posted October 17, 2019 Yes but when i want to compare Subject of one certificate with subject of another one, then I need whole Subject line in right order and filled fields. I don't know what fields are filled in certificate so I can't successfully compose whole subject line for comparison. So it would be nice to have SubjectOneLine (and IssuerOneLine) in mentioned form. I get this info in correct form from another certificate with no problem via WinCrypt API and need to compare it with data returned by ICS in TX509Base. Share this post Link to post
Angus Robertson 577 Posted October 17, 2019 OpenSSL does have newer functions to format certificate information, but most users want single fields, not a list of cryptic fields. Angus Share this post Link to post