We are trying to implement SSL pinning in our Xamarin.ios app.
POC : As a proof of concept, we calculated public key hash of https://www.google.co.uk . We used openssl s_client -servername www.google.co.uk -connect www.google.co.uk:443 | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64 command in terminal to get hash of public key (SHA256) .
We pinned this hash of public key for https://www.google.co.uk in our app inside our app and tried comparing it at runtime.
Then using NSURLSession we tried fetching data of https://www.google.co.uk .
Inside DidReceiveChallenge we calculate hash of public key using following code :
SecKey key = challenge.ProtectionSpace.ServerSecTrust.GetKey();
NSError error = new NSError();
NSData keyData = key.GetExternalRepresentation(out error);
string str = sha256(keyData);
That hash calculating function body is as below :
public string sha256(NSData data)
{
using (SHA256 mySHA256 = SHA256.Create())
{
byte[] bytes = data.ToArray();
byte[] hashvalue = mySHA256.ComputeHash(bytes);
NSData d = NSData.FromArray(hashvalue);
return d.GetBase64EncodedString(NSDataBase64EncodingOptions.SixtyFourCharacterLineLength);
}
}
But the hash returned by sha256 function written by us above does not match with the hash calculated using terminal command.
Are we doing something wrong in hash calculation.? We have also tried public key api, SecKey key = challenge.ProtectionSpace.ServerSecTrust.GetPublicKey();
We referred this article for this approach : https://medium.com/flawless-app-stories/ssl-pinning-254fa8ca2109 this article which uses Swift code for hash calculation.
Related
I have a small application running on an ESP32 dev board (I use the Arduino IDE together with the shipped mbedtls) that issues and verifies JWT tokens. I have at first used RSA signatures successfully, but now wanted to go for shorter signatures and thus attempted to use ECDSA.
The application itself can issue tokens and verify them as well, but if I attempt to verify the tokens outside of my application - for instance with JOSE or the Debugger- I get verification failures and I cant quite wrap my head around why this is happening.
This is an example token (this token does practically not contain information):
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NzI0MTMxNjgsImV4cCI6MTY3MjQxNjc2OH0.MEUCIAjwEDXI424qjrAkSzZ_ydcVLOSAvfQ8YVddYvzDzMvQAiEAkVy4d-hZ01KpcMNKhPHk8E_SDYiB4JKwhm-Kc-Z81rI
This is the corresponding public key (this key is not used anywhere besides the purpose of presenting the issue here):
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUGnNIOhPhZZSOg4A4BqAFtGO13W4BGDQpQ0ieTvLU9/CXrY7W77o7pNx7tvugeIoYJxS0NjmxvT4TMpo4Z8P7A==
-----END PUBLIC KEY-----
As far as I understand, JWT tokens can be issued and verified using ECDSA. The so called "ES256" method is supposed to use prime256v1 in combination with SHA256, so I generated my key material with the following commands:
openssl ecparam -name prime256v1 -genkey -noout -out ecc-private.pem
openssl ec -in ecc-private.pem -pubout -out ecc-public.pem
For the signing part, the private key is loaded as follows, where ecc_priv is a String containing the PEM representation of the key:
//get the key
byte *keybuffer = (byte*)malloc((ecc_priv.length()+1)*sizeof(byte));
ecc_priv.getBytes(keybuffer, ecc_priv.length() + 1);
mbedtls_pk_context pk_context;
mbedtls_pk_init(&pk_context);
int rc = mbedtls_pk_parse_key(&pk_context, keybuffer, ecc_priv.length() + 1, NULL, 0);
if (rc != 0){
printf("Failed to mbedtls_pk_parse_key: %d (-0x%x): %s\n", rc, -rc, mbedtlsError(rc));
return -1;
}
free(keybuffer);
Since this worked for me with RSA keys, I just replaced the keys and kept all other code to sign the actual message. As far as I understand, this should be possible with mbedtls_pk methods:
//mbedtls context
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_entropy_init(&entropy);
const char* pers="some entropy";
mbedtls_ctr_drbg_seed(
&ctr_drbg,
mbedtls_entropy_func,
&entropy,
(const unsigned char*)pers,
strlen(pers));
//get the header and payload bytes
byte *headerAndPayloadbytes = (byte*)malloc((headerAndPayload.length()+1)*sizeof(byte));
headerAndPayload.getBytes(headerAndPayloadbytes, headerAndPayload.length() + 1);
//prepare digest
uint8_t digest[32];
rc = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), headerAndPayloadbytes, headerAndPayload.length(), digest);
if (rc != 0) {
printf("Failed to mbedtls_md: %d (-0x%x): %s\n", rc, -rc, mbedtlsError(rc));
return -1;
}
free(headerAndPayloadbytes);
//prepare output
byte *oBuf = (byte*)malloc(5000*sizeof(byte));
size_t retSize;
//sign digest
rc = mbedtls_pk_sign(&pk_context, MBEDTLS_MD_SHA256, digest, sizeof(digest), oBuf, &retSize, mbedtls_ctr_drbg_random, &ctr_drbg);
if (rc != 0) {
printf("Failed to mbedtls_pk_sign: %d (-0x%x): %s\n", rc, -rc, mbedtlsError(rc));
return -1;
}
//encode signature to base64
unsigned int osize = encode_base64_length(retSize);
byte *output = (byte*)malloc((osize+1)*sizeof(byte));
encode_base64(oBuf, retSize, output);
String sig = String((char*)output);
free(output);
//base64 URL specific
sig.replace('+','-');
sig.replace('/','_');
sig.replace("=","");
String completejwt = headerAndPayload + "." + sig;
//free resources
mbedtls_ctr_drbg_free( &ctr_drbg );
mbedtls_entropy_free( &entropy );
mbedtls_pk_free(&pk_context);
free(oBuf);
My expectation was that I can simply replace the RSA keys with the ECDSA (prime256v1) keys and keep everything else as is, but the resulting token are not verifiable outside of my application. Again I want to emphasize that inside my application I can definitely verify the token and that the code worked perfectly fine with RSA keys, even outside my application. There must be something Im missing here, Im sure about that.
Any help or directions to research are highly appreciated.
EDIT: Here is a minimal compilable example (Arduino sketch)
Your ECDSA signature is a DER-encoded ASN.1 structure, rather than a simple r || s concatenation as proposed by IEEE-P1363 which is what the JOSE specification mandates.
I was given a SSH private key in this format, although I'm not familiar with any SSH clients that accept it as is.
{
"crv": "P-256",
"d": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"kid": "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx",
"kty": "EC",
"x": "xxxxxxxxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxx",
"y": "xxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxx"
}
How do I convert it to a .ppk file that PuTTY could read? I've tried just taking the d value that I understand to be the private key and using it to make a .pem formatted key, with both the BEGIN PRIVATE KEY and BEGIN EC PRIVATE KEY formats - starting with a BEGIN PRIVATE KEY file and using openssl ec -in test.pem -text to convert to the BEGIN EC PRIVATE KEY format, and PuTTYgen would not accept either file.
I've also tried to just manually fill out a .ppk file, which also so far hasn't worked. I'm guessing I don't have the right Private-MAC value because everything else seemed pretty straightforward...
PuTTY-User-Key-File-3: ecdsa-sha2-nistp256
Encryption: none
Comment: ecdsa-key-20221204
Public-Lines: 3
publickeyxxxxxxx
xxxxxxxx
xxxxxxx
Private-Lines: 1
privatexxxxxxxxxxxxxxxxx
Private-MAC: hmac-sha256(blank key, 'ecdsa-sha2-nistp256' + 'none' + 'ecdsa-key-20221204' + base64decode(publickeyxxxxxxxxxxxxxxxxxxxxxxxxxxxx) + base64decode(privatexxxxxxxxxxxxxxxxx))
What is the easiest way to convert this key into either a PPK or some other format that I can use with PuTTY?
I am using the PKCS11Interop library to Encrypt and Decrypt data with the parameters below.
CK_RSA_PKCS_OAEP_PARAMS p = new CK_RSA_PKCS_OAEP_PARAMS();
p.HashAlg = (uint)CKM.CKM_SHA256;
p.Mgf = (uint)CKG.CKG_MGF1_SHA1;
p.Source = (uint)CKZ.CKZ_DATA_SPECIFIED;
p.SourceData = IntPtr.Zero;
p.SourceDataLen = 0;
CK_MECHANISM mech = CkmUtils.CreateMechanism(CKM.CKM_RSA_PKCS_OAEP, p);
The error I get is CKR_MECHANISM_PARAM_INVALID when attempting to Encrypt/Decrypt.
But when I use CKG_MGF1_SHA256 for MGF1 then both encryption and decryption works.
Am I missing something or mismatched hashes aren't supported by HSM boxes?
I am using a smart card that is signing a SHA-1 hash of a document, and compute a 256 bytes digital signature.
I am using the code posted on this question - iText signing PDF using external signature with smart card.
My problem is that I get the error:" The document has been altered or corrupted since the signature was applied".
I am using a GUI to create the hash and then send the signed 256 bytes that is computed on the card to the signing functions .
Here is my code:
hash creating code of filepath pdf document:
SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
SHA256 sha2 = SHA256.Create();
//sha2.ComputeHash
byte[] pdfBytes = System.IO.File.ReadAllBytes(filePath);
byte[] hash = null;
hash= sha1.ComputeHash(pdfBytes);
the above code is used in one of the GUI functions to create the hash of the document
namespace EIDSmartCardSign
{
class PdfSignature
{
private string outputPdfPath;
private string certPath;
byte[] messageDigest;
private string inputPdfPath;
public PdfSignature(byte[] messageDigest, string inputPdfPath,string outputPdfPath)
{
this.messageDigest = messageDigest;
this.outputPdfPath = outputPdfPath;
this.inputPdfPath = inputPdfPath;
}
public void setCertPath(string certPath)
{
this.certPath = certPath;
}
public void signPdf()
{
X509Certificate2 cert = new X509Certificate2();
cert.Import(certPath); // .cer file certificate obtained from smart card
X509CertificateParser certParse = new Org.BouncyCastle.X509.X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[1] ;
chain[0] = certParse.ReadCertificate(cert.RawData);
X509Certificate2[] certs;
PdfReader reader = new PdfReader(inputPdfPath);
FileStream fout = new FileStream(outputPdfPath,FileMode.Create);
PdfStamper stamper = PdfStamper.CreateSignature(reader, fout, '\0',null,true);
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.SignatureCreator = "Me";
appearance.Reason = "Testing iText";
appearance.Location = "On my Laptop";
iTextSharp.text.Rectangle rec = new iTextSharp.text.Rectangle(50, 50, 250, 100);
appearance.SetVisibleSignature(rec, 1, "Signature");
IExternalSignature extSignature= new MyExternalSignature("SHA-1",this.messageDigest);
MakeSignature.SignDetached(appearance, extSignature, chain, null, null, null, 0, CryptoStandard.CMS);
//MakeSignature.
}
}
}
Your hash creating function
SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
SHA256 sha2 = SHA256.Create();
//sha2.ComputeHash
byte[] pdfBytes = System.IO.File.ReadAllBytes(filePath);
byte[] hash = null;
hash = sha1.ComputeHash(pdfBytes);
calculates the wrong hash value.
Have a look at this answer on Information Security Stack Exchange, in particular the sketch
shows that to get the document bytes to sign you do not take the original PDF but instead have to prepare it for integrating the signature container (add signature field, field value with some space reserved for the signature container, and field visualization) and then hash all the bytes except the space reserved for the signature container.
Furthermore, even this naked hash is not the data to sign. Instead a set of attributes is built, one of them containing the document hash calculated as above, other ones containing references to the signer certificate etc., and these attributes are to be signed.
Thus, instead do what you already claimed to be doing:
I am using the code posted on this question - iText signing PDF using external signature with smart card.
In particular the code there does not sign the hash of the whole file but instead uses the data the method Sign of the IExternalSignature implementation receives as parameter which is constructed as explained above.
More details
In a comment the OP said
The card I am working with expects a 20 bytes hash.
20 bytes would be typical for a naked hash generated by either SHA1 or RIPEMD-160. According to your question text, I assume the former algorithm is used. (This by the way indicates that the context does not require a high security level as SHA1 effectively is already phased out or in the process of being phased out for such use cases.)
What steps are needed to further create this hash After hashing the contents of the pdf?
Simply do as in the IExternalSignature implementation in the question you referenced:
public virtual byte[] Sign(byte[] message) {
byte[] hash = null;
using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
{
hash = sha1.ComputeHash(message);
}
byte[] sig = MySC.GetSignature(hash);
return sig;
}
(Obviously chances are that your smart card signing routine is not called MySC.GetSignature and you have to replace that call accordingly...)
As your card appears to expect a naked hash value in contrast to the card of the OP of the referenced question, this should work for you.
Where can I find examples of creating the aformentioned integrated signature container?
In the examples to the iText white paper Digital Signatures for PDF Documents.
After the signature process, I have 256 bytes signed data, 3 .cer certificates exported from the card.
256 bytes signed data sounds like a naked signature generated using RSA or RSASSA-PSS with a 2048 bit key size.
That been said, you need the signer certificate before signing: In most relevant profiles the signed attributes have to contain a reference to the signer certificate. In the code in the question you referenced that signer certificate is handled here
public void StartTest(){
X509Certificate2 cert = new X509Certificate2();
cert.Import("cert.cer"); // certificate obtained from smart card
X509CertificateParser certParse = new Org.BouncyCastle.X509.X509CertificateParser();
Org.BouncyCastle.X509.X509Certificate[] chain = new Org.BouncyCastle.X509.X509Certificate[] { certParse.ReadCertificate(cert.RawData) };
[...]
MyMakeSignature.SignDetached(appearance, externalSignature, chain, null, null, tsc, 0, CryptoStandard.CADES);
In particular you have to identify the correct signer certificate among those three certificate your card returns; otherwise you might have the same issue as the OP in the referenced question.
How do I create the Contents object when I have all of this data?
Considering what you said about your use case, chances are good that you really merely have to use the code posted of the question iText signing PDF using external signature with smart card with minor adaptions.
I need to integrate my iPhone app with a system, and they require to encrypt data by a given public key, there are 3 files in 3 different format .xml .der and .pem, I have researched and found some articles about getting SecKeyRef from DER/PEM, but they are always return nil. Below is my code:
NSString *pkFilePath = [[NSBundle mainBundle] pathForResource:#"PKFile" ofType:#"der"];
NSData *pkData = [NSData dataWithContentsOfFile:pkFilePath];
SecCertificateRef cert;
cert = SecCertificateCreateWithData(NULL, (CFDataRef) pkData);
assert(cert != NULL);
OSStatus err;
if (cert != NULL) {
err = SecItemAdd(
(CFDictionaryRef) [NSDictionary dictionaryWithObjectsAndKeys:
(id) kSecClassCertificate, kSecClass,
(id) cert, kSecValueRef,
nil
],
NULL
);
if ( (err == errSecSuccess) || (err == errSecDuplicateItem) ) {
CFArrayRef certs = CFArrayCreate(kCFAllocatorDefault, (const void **) &cert, 1, NULL);
SecPolicyRef policy = SecPolicyCreateBasicX509();
SecTrustRef trust;
SecTrustCreateWithCertificates(certs, policy, &trust);
SecTrustResultType trustResult;
SecTrustEvaluate(trust, &trustResult);
if (certs) {
CFRelease(certs);
}
if (trust) {
CFRelease(trust);
}
return SecTrustCopyPublicKey(trust);
}
}
return NULL;
Problem happens at SecCertificateCreateWithData, it always return nil even through read file is ok.
Anybody has done this please help me, thanks!
EDIT: The cert file was MD5 signature.
I struggled a lot with the same problem and finally found a solution. My problem was that I needed to use both an external private and public key for encrypting/decrypting data in an iOS app and didn't want to use the keychain.
It turns out you also need a signed certificate for the iOS security library to be able to read the key data and of course the files have to be in the correct format.
The procedure is basically as follows:
Say you have a private key in PEM format (with the -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY----- markers): rsaPrivate.pem
//Create a certificate signing request with the private key
openssl req -new -key rsaPrivate.pem -out rsaCertReq.csr
//Create a self-signed certificate with the private key and signing request
openssl x509 -req -days 3650 -in rsaCertReq.csr -signkey rsaPrivate.pem -out rsaCert.crt
//Convert the certificate to DER format: the certificate contains the public key
openssl x509 -outform der -in rsaCert.crt -out rsaCert.der
//Export the private key and certificate to p12 file
openssl pkcs12 -export -out rsaPrivate.p12 -inkey rsaPrivate.pem -in rsaCert.crt
Now you have two files which are compatible with the iOS security framework: rsaCert.der (public key) and rsaPrivate.p12 (private key). The code below reads in the public key assuming the file is added to your bundle:
- (SecKeyRef)getPublicKeyRef {
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:#"rsaCert" ofType:#"der"];
NSData *certData = [NSData dataWithContentsOfFile:resourcePath];
SecCertificateRef cert = SecCertificateCreateWithData(NULL, (CFDataRef)certData);
SecKeyRef key = NULL;
SecTrustRef trust = NULL;
SecPolicyRef policy = NULL;
if (cert != NULL) {
policy = SecPolicyCreateBasicX509();
if (policy) {
if (SecTrustCreateWithCertificates((CFTypeRef)cert, policy, &trust) == noErr) {
SecTrustResultType result;
OSStatus res = SecTrustEvaluate(trust, &result);
//Check the result of the trust evaluation rather than the result of the API invocation.
if (result == kSecTrustResultProceed || result == kSecTrustResultUnspecified) {
key = SecTrustCopyPublicKey(trust);
}
}
}
}
if (policy) CFRelease(policy);
if (trust) CFRelease(trust);
if (cert) CFRelease(cert);
return key;
}
To read in the private key use the following code:
SecKeyRef getPrivateKeyRef() {
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:#"rsaPrivate" ofType:#"p12"];
NSData *p12Data = [NSData dataWithContentsOfFile:resourcePath];
NSMutableDictionary * options = [[NSMutableDictionary alloc] init];
SecKeyRef privateKeyRef = NULL;
//change to the actual password you used here
[options setObject:#"password_for_the_key" forKey:(id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import((CFDataRef) p12Data,
(CFDictionaryRef)options, &items);
if (securityError == noErr && CFArrayGetCount(items) > 0) {
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identityApp =
(SecIdentityRef)CFDictionaryGetValue(identityDict,
kSecImportItemIdentity);
securityError = SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);
if (securityError != noErr) {
privateKeyRef = NULL;
}
}
[options release];
CFRelease(items);
return privateKeyRef;
}
Starting with iOS 10, it is actually possible to import PEM private keys w/o converting them to PKCS#12 (which is a very universal container format for everything related to cryptography) and thus also w/o using OpenSSL on command line or statically linking apps with it. On macOS it's even possible since 10.7 using a different function than the ones mentioned here (but so far it doesn't exist for iOS). Exactly the way described below will also work on macOS 10.12 and later, though.
To import a certificate, it's enough to just strip the
-----BEGIN CERTIFICATE-----
and
-----END CERTIFICATE-----
lines, then run base64 decoding over the data left, the result is a certificate in standard DER format, which can just be fed to SecCertificateCreateWithData() to get a SecCertificateRef. This has always been working, also prior to iOS 10.
To import a private key, a little bit of extra work may be required. If the private key is wrapped with
-----BEGIN RSA PRIVATE KEY-----
then it is very easy. Again, the first and last line needs to be stripped, the remaining data needs to be base64 decoded and the result is a RSA key in PKCS#1 format. This format can only hold RSA keys and it is directly readable, just feed the decoded data into SecKeyCreateWithData() to obtain a SecKeyRef. The attributes dictionary just need the following key/value pairs:
kSecAttrKeyType: kSecAttrKeyTypeRSA
kSecAttrKeyClass: kSecAttrKeyClassPrivate
kSecAttrKeySizeInBits: CFNumberRef with then number of bits in the key (e.g. 1024, 2048, etc.) If not known, this information can actually be read from the raw key data, which is ASN.1 data (it's a bit beyond the scope of this answer, but I will provide some helpful links below about how to parse that format). This value is maybe optional! In my tests it was actually not necessary to set this value; if absent, the API determined the value on its own and it was always set correctly later on.
In case the private key is wrapped by -----BEGIN PRIVATE KEY-----, then the base64 encoded data is not in PKCS#1 format but in PKCS#8 format, however, this is a just a more generic container that can also hold non-RSA keys but for RSA keys the inner data of that container is equal to PKCS#1, so one could say for RSA keys PKCS#8 is PKCS#1 with an extra header and all you need to do is stripping that extra header. Just strip the first 26 bytes of the base64 decoded data and you have PKCS#1 again. Yes, it's really that simple.
To learn more about PKCS#x formats in PEM encodings, have a look at this site. To learn more about ASN.1 format, here's a good site for that. And if you need a simple, yet powerful and interactive online ASN.1 parser to play around with different formats, one that can directly read PEM data, as well as ASN.1 in base64 and hexdump, try this site.
Very important: When adding a private key to keychain, that you created as above, please be aware that such a private key doesn't contain a public key hash, yet a public key hash is important for they keychain API to form an identity (SecIdentityRef), as using the public key hash is how the API finds the correct private key that belongs to an imported certificate (a SecIdentityRef is just a SecKeyRef of a private key and a SecCertificateRef of a cert forming a combined object and it's the public key hash, that binds them together). So when you plan to add the private key to keychain, be sure to set a public key hash manually, otherwise you won't ever be able to get an identity for it and without that you cannot use keychain API for tasks like signing or decrypting data. The public key hash must be stored in an attribute named kSecAttrApplicationLabel (stupid name, I know, but it's really not a label and nothing the user can ever see, check out the documentation). E.g.:
OSStatus error = SecItemAdd(
(__bridge CFDictionaryRef)#{
(__bridge NSString *)kSecClass:
(__bridge NSString *)kSecClassKey,
(__bridge NSString *)kSecAttrApplicationLabel:
hashOfPublicKey, // hashOfPublicKey is NSData *
#if TARGET_OS_IPHONE
(__bridge NSString *)kSecValueRef:
(__bridge id)privateKeyToAdd, // privateKeyToAdd is SecKeyRef
#else
(__bridge NSString *)kSecUseItemList:
#[(__bridge id)privateKeyToAdd], // privateKeyToAdd is SecKeyRef
// #[ ... ] wraps it into a NSArray object,
// as kSecUseItemList expects an array of items
#endif
},
&outReference // Can also be NULL,
// otherwise reference to added keychain entry
// that must be released with CFRelease()
);
After hours of effort researching online with the help of this post, I finally get it working perfectly. Here is the notes with working Swift code of the most current version. I hope it can help someone!
Received a certificate in the base64 encoded string sandwiched between header and tail like this (PEM format):
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----
strip out the header and the tail, such as
// remove the header string
let offset = ("-----BEGIN CERTIFICATE-----").characters.count
let index = certStr.index(cerStr.startIndex, offsetBy: offset+1)
cerStr = cerStr.substring(from: index)
// remove the tail string
let tailWord = "-----END CERTIFICATE-----"
if let lowerBound = cerStr.range(of: tailWord)?.lowerBound {
cerStr = cerStr.substring(to: lowerBound)
}
decode base64 string to NSData:
let data = NSData(base64Encoded: cerStr,
options:NSData.Base64DecodingOptions.ignoreUnknownCharacters)!
Convert it from NSdata format to SecCertificate:
let cert = SecCertificateCreateWithData(kCFAllocatorDefault, data)
Now, this cert can be used to compare with the certificate received from the urlSession trust:
certificateFromUrl = SecTrustGetCertificateAtIndex(...)
if cert == certificate {
}