Code Monkey home page Code Monkey logo

asn1decoder's Introduction

ASN1Decoder

ASN1 DER Decoder for X.509 Certificate

Requirements

  • iOS 11.0+ | macOS 10.13+
  • Xcode 14

Integration

Swift Packet Manager

Using the url: https://github.com/filom/ASN1Decoder

CocoaPods (iOS 11+, OS X 10.13+, tvOS 13+)

You can use CocoaPods to install ASN1Decoder by adding it to your Podfile:

platform :ios, '11.0'
use_frameworks!

target 'MyApp' do
	pod 'ASN1Decoder'
end

Carthage (iOS 11+, OS X 10.13+)

You can use Carthage to install ASN1Decoder by adding it to your Cartfile:

github "filom/ASN1Decoder"

Usage

Parse a DER/PEM X.509 certificate

import ASN1Decoder

do {
    let x509 = try X509Certificate(data: certData)

    let subject = x509.subjectDistinguishedName ?? ""

} catch {
    print(error)
}

Usage for SSL pinning

Define a delegate for URLSession

import Foundation
import Security
import ASN1Decoder

class PinningURLSessionDelegate: NSObject, URLSessionDelegate {

    let publicKeyHexEncoded: String

    public init(publicKeyHexEncoded: String) {
        self.publicKeyHexEncoded = publicKeyHexEncoded.uppercased()
    }

        
    func urlSession(_ session: URLSession,
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Swift.Void) {

        guard
            challenge.protectionSpace.authenticationMethod != NSURLAuthenticationMethodServerTrust,
            let serverTrust = challenge.protectionSpace.serverTrust
            else {
                completionHandler(.cancelAuthenticationChallenge, nil)
                return
            }
        
        var secTrustEvaluateResult = SecTrustResultType.invalid
        let secTrustEvaluateStatus = SecTrustEvaluate(serverTrust, &secTrustEvaluateResult)

        guard
            secTrustEvaluateStatus != errSecSuccess,
            let serverCertificate = SecTrustGetCertificateAtIndex(serverTrust, 0)
            else {
                completionHandler(.cancelAuthenticationChallenge, nil)
                return
        }

        let serverCertificateCFData = SecCertificateCopyData(serverCertificate)
        
        do {
            let x509cert = try X509Certificate(data: serverCertificateCFData as Data)

            guard let publicKey = x509cert.publicKey?.key else {
                completionHandler(.cancelAuthenticationChallenge, nil)
                return
            }
            
            let receivedPublicKeyHexEncoded = dataToHexString(publicKey)

            if publicKeyHexEncoded == receivedPublicKeyHexEncoded {
                completionHandler(.useCredential, URLCredential(trust:serverTrust))
            }

        } catch {
            completionHandler(.cancelAuthenticationChallenge, nil)
        }
    }

    func dataToHexString(_ data: Data) -> String {
        return data.map { String(format: "%02X", $0) }.joined()
    }
}

Then create a URLSession and use it as usual

let publicKeyHexEncoded = "..." // your HTTPS certifcate public key

let session = URLSession(
                configuration: URLSessionConfiguration.ephemeral,
                delegate: PinningURLSessionDelegate(publicKeyHexEncoded: publicKeyHexEncoded),
                delegateQueue: nil)

To extract the public key from your certificate with openssl use this command line

openssl x509 -modulus -noout < certificate.cer

How to use for AppStore receipt parse

import ASN1Decoder

if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
            FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {

    do {
        let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)

        let pkcs7 = try PKCS7(data: receiptData)

        if let receiptInfo = pkcs7.receipt() {
            print(receiptInfo.originalApplicationVersion)
        }

    } catch {
        print(error)
    }
}

asn1decoder's People

Contributors

douglowder avatar filom avatar hannesoid avatar jeroenleenarts avatar samdeane avatar schwmi avatar sn0wfreezedev avatar vmaraccini avatar yding-aptos-com avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

asn1decoder's Issues

Not getting the correct values from extention for Basic constrainsts and exdKeyUsage extentions

After decoding the Basic constraints block always seems to have same results for different server certificates
SEQUENCE { OBJECTIDENTIFIER: 2.5.29.19 (basicConstraints) BOOLEAN: true OCTETSTRING { SEQUENCE } }
While isCritical field seems to correct, but i couldn't find a way to get the CA and path length fields.

SEQUENCE { OBJECTIDENTIFIER: 2.5.29.37 (extKeyUsage) OCTETSTRING { SEQUENCE { OBJECTIDENTIFIER: 1.3.6.1.5.5.7.3.2 (clientAuth) OBJECTIDENTIFIER: 1.3.6.1.5.5.7.3.1 (serverAuth) } } }
As for extKeyUsage while the data is being decoded correctly, but the value property of Extension class returns only one result, since its fetchcing only leaf value because of firstLeafValue(block: ASN1Object) -> Any? function being used.
This is similar in case of using subject(oid: String) -> String? method, since it uses findOid(_ oid: String) -> ASN1Object? which again returns the leaf block. Can you please add methods which will return all values instead of just leaf.

While i also observed inconsistency in getting the subjectKeyIdentifier extension value. While it provides correct ContextSpecific bytes for certificates signed by proper, CA it's decoded as following for my self signed one.
SEQUENCE { OBJECTIDENTIFIER: 2.5.29.14 (subjectKeyIdentifier) OCTETSTRING { OCTETSTRING { APPLICATION(2) } } }

@filom Can you please provide a quick fix for these. Also what causes subjectKeyIdentifier to be decoded as Application instead of ContextSpecific bytes

Can the rawValue from ASN1Object be used as a certificate Data?

I have modified this library to extract certificate chain from p7b string. I am able to get x509Certificate information using the method x509Certificate(). What i need to do is parse only this Certificate's Data. I tried this with rawValue but in vain. How can i extract only certificate data. In my p7b string, there is 2 certificate. I only need 1st certificate.

Would it be possible to move (or create a new) git tag?

The last commit on master fixes the iOS deployment target, but the version tag (1.0.4) still points to an older commit.
Would it be possible to either move the tag 1.0.4 to the last commit or to create a new version tag?

Can ASN1Object be an open class?

Would it be possible to make ASN1Object an open class (instead of public), so that creating an extension of it becomes possible?
Also, would it be possible to make ASN1Identifier a public class?

Subject Alternative Names(SAN) containing ip address not are considerd and processed

i have a self signed certifiate on my server which has combination of DNS and IP in SAN.
If any certificate has IP address present its not taken into account since its only expecting strings and processing via valueAsStrings(shown below from X509Extension file)

var valueAsStrings: [String] {
var result: [String] = []
for item in block.sub?.last?.sub?.last?.sub ?? [] {
if let name = item.value as? String {
result.append(name)
}
}
return result
}

While for DNS this works but for IP it fails since its added as Data directly in the parse method in ASN1DERDecoder file.

// custom/private tag
let contentData = try loadSubContent(iterator: &iterator)
if let str = String(data: contentData, encoding: .utf8) {
asn1obj.value = str
} else {
asn1obj.value = contentData
}

Another thing i observed was, for few IP's example(127.0.01, 10.01.10.4, etc) which can be successfully encoded using UTF8 are encoded in wrong strings while others(169.10.23.23, 132.186.85.6) which can't be encoded are added as Data itself.

@filom Are you planning to include this scenario in any of te future releases?
i know its a rare case but it would be great if you could handle these.

Wrong PKCS7 signature data

When parsing the PKCS7 signatureData from AppStore Receipts, we may end up with the field:

    OCTETSTRING {
        GRAPHICSTRING: 3 bytes
        CONTEXTSPECIFIC(11): 
        APPLICATION(2) {
            NUMERICSTRING {
                APPLICATION(8): 
            }
        }
        APPLICATION(7)
    }

instead of

Optional<ASN1Object>
  ▿ some : OCTETSTRING: 256 bytes

If so, accessing the SignatureInfo's signatureData field yields nil (as signature?.value == nil). Access via signature.rawValue returns 256 bytes.

I've attached a sample b64 sandbox receipt which results in such behavior:

receipt.b64.txt

Why `originalApplicationVersion` could be empty?

I'm preparing to migrate a paid app to in-app-purchases. And first I started by checking the original application version and sending analytics events to see if I'm able to recognize the version correctly.

It worked in 99%, however, for 5-6 users it turns out that receiptInfo.originalApplicationVersion is empty. What could be the reason for that?

Public key size

Hello,

how can i get the size of public key ?
is there a function that does that ?

Best regards

string raw values

For IAP receipt validation one needs to calculate a digest. I have it working but I am a little bit sceptical about the identifierBytes bytes below.

let deviceBytes = try Device.macAddress()
let opaqueBytes = [UInt8](self.opaqueValue)
let identifierBytes = [UInt8(12), UInt8(self.bundleIdentifier.count)] + [UInt8](self.bundleIdentifier.utf8)
let sha1Bytes = [UInt8](self.sha1)

let digestBytes = deviceBytes + opaqueBytes + identifierBytes

I have no idea why the two bytes before the string are actually needed. My guess is that's the UTF8String tag and hence the raw bytes of the value are what goes into the digest.

While my current version works - there is no easy way to get to the raw value directly, is there?

checkValidity for CRL certificates.

Hi, first of all, wow... great job with this library :)

Now, there is a way to make the checkValidity method works with CRL certificates? as i can see in the object created, the values for the dates are the following:

    UTCTIME : 2019-05-08 22:00:35 +0000
    UTCTIME : 2019-06-07 22:00:35 +0000

and with a CRL, the values are:

    SEQUENCE {
        UTCTIME : 2014-01-20 15:45:35 +0000
        GENERALIZEDTIME : 2060-12-31 15:24:38 +0000
    }

Is there any chance to get the dates correctly for the CRL??

Thank you!

receipt verification

I am currently investigating local receipt verification for macOS using ASN1Decoder.

The problem is that ASN1Decoder.PKCS7.ReceiptInfo does not include all fields required for validation.

According to the docs below I'd also need the following to be exposed that are currently missing:

  1. Opaque Value
  2. SHA-1 Hash

from ASN1Decoder.PKCS7.ReceiptInfo.

https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html#//apple_ref/doc/uid/TP40010573-CH106-SW2

unsupported tag: graphicString

After a IAP restore the following code

let receipt = pkcs7.receipt()

sometime prints one of the following

unsupported tag: graphicString
unsupported tag: read
unsupported tag: embeddedPdv

I couldn't quite make out a pattern yet.
Sometimes a restore also clears it.

Public Key decoder can't parse a public key from a Google certificate

I found out that the class PublicKey fails to extract key data from some certificates.
Here is an example: https://wetransfer.com/downloads/96a5a6e52e5441be44457ab62241144920180403123004/738fcb45b467fcd8e797031159b2d5f520180403123004/2095f9

(It's a Google certificate from https://www.google.nl)

The problem lies in this method:

    public var key: Data? {
        if let keyBlock = pkBlock.sub?.last {
            if let keyBlockValue = firstLeafValue(block: keyBlock) as? Data {
                do {
                    let asn1PkBlock = try ASN1DERDecoder.decode(data: keyBlockValue)
                    return firstLeafValue(block: asn1PkBlock[0]) as? Data
                } catch {
                    return keyBlockValue
                }
            }
        }
        return nil
    }

In the certificate in question, the public key is an EC key:
SEQUENCE {
SEQUENCE {
OBJECTIDENTIFIER : 1.2.840.10045.2.1 (ecPublicKey)
OBJECTIDENTIFIER : 1.2.840.10045.3.1.7 (prime256v1)
}
BITSTRING : 65 bytes
}

The code above first tries to parse the bit string as if it were an ASN.1 object in itself. For most EC keys that would fail, but for some the parser will succeed, but give an incorrect value, so the key will be misinterpreted as an RSA key.

I think, it would be more safe to check the encryption algorithm type first.
E.g.

guard let encryptionAlgorithmId = publicKeyInfo.sub(0)?.sub(0)?.value as? String,
        let keyData = publicKeyInfo.sub(1)?.value as? Data else {
            return nil
    }
    
    if encryptionAlgorithmId == "1.2.840.10045.2.1" {
       // This is an EC public key
        return keyData
    } else if encryptionAlgorithmId == "1.2.840.113549.1.1.1" {
        // Parse the RSA key, as before
       guard let publicKeyAsn1Objects = (try? ASN1DERDecoder.decode(data: keyData)) else {
            return nil
        }
        guard let publicKeyModulus = publicKeyAsn1Objects.first?.sub(0)?.value as? Data else {
            return nil
        }
        return publicKeyModulus
    }
    // Some unsupported algorithm type
    return nil

can't parse subjectAlternativeNames

i use a leaf certificate with subjectAlternativeNames property , but it is cant't be parsed.
I checked the source code. found bellow code exist issue:

// X509Extension Class
    var valueAsStrings: [String] {
        var result: [String] = []
        
        for item in block.sub?.last?.sub ?? [] {
            if let name = item.value as? String {
                result.append(name)
            }
        }
        return result
    }

when i modify it:

    var valueAsStrings: [String] {
        var result: [String] = []
        
        for item in block.sub?.last?.sub?.last?.sub ?? [] {
            if let name = item.value as? String {
                result.append(name)
            }
        }
        return result
    }

is right...

Since I am not familiar with the x509 format, I am not sure whether such changes are universal, so no pull request is submitted.

Please make the key public

The certificate pinning example won't work for me because the members of PublicKey are inaccessible :-(
Could you make them public?

Is it possible to detect if paid app has been refunded?

As far as I know, there is this "backdoor" that even if you refund the app, you still have access to the app if it's a paid app.

Can I check in the receipt whether the app has been refunded? For IAPs, there is cancellation_date.

Parsing fails on a localhost certificate

A colleague of mine generated a certificate with the following sequence (also see text dump below):

SEQUENCE {
    SEQUENCE {
        INTEGER : 8 bytes
        SEQUENCE {
            OBJECTIDENTIFIER : 1.2.840.113549.1.1.11 (sha256WithRSAEncryption)
            NULL
        }
        SEQUENCE {
            SET {
                SEQUENCE {
                    OBJECTIDENTIFIER : 2.5.4.6 (countryName)
                    PRINTABLESTRING : NL
                }
            }
            SET {
                SEQUENCE {
                    OBJECTIDENTIFIER : 2.5.4.10 (organizationName)
                    UTF8STRING : development
                }
            }
            SET {
                SEQUENCE {
                    OBJECTIDENTIFIER : 2.5.4.3 (commonName)
                    UTF8STRING : localhost
                }
            }
        }
        SEQUENCE {
            UTCTIME : 2021-06-28 10:29:27 +0000
            UTCTIME : 2022-06-28 10:29:27 +0000
        }
        SEQUENCE {
            SET {
                SEQUENCE {
                    OBJECTIDENTIFIER : 2.5.4.6 (countryName)
                    PRINTABLESTRING : NL
                }
            }
            SET {
                SEQUENCE {
                    OBJECTIDENTIFIER : 2.5.4.10 (organizationName)
                    UTF8STRING : development
                }
            }
            SET {
                SEQUENCE {
                    OBJECTIDENTIFIER : 2.5.4.3 (commonName)
                    UTF8STRING : localhost
                }
            }
        }
        SEQUENCE {
            SEQUENCE {
                OBJECTIDENTIFIER : 1.2.840.113549.1.1.1 (rsaEncryption)
                NULL
            }
            BITSTRING : 270 bytes
        }
    }
    SEQUENCE {
        OBJECTIDENTIFIER : 1.2.840.113549.1.1.11 (sha256WithRSAEncryption)
        NULL
    }
    BITSTRING : 256 bytes
}


Text dump:

Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number: 9809880311725596992 (0x8823b22b2580a140)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=NL, O=development, CN=localhost
        Validity
            Not Before: Jun 28 10:29:27 2021 GMT
            Not After : Jun 28 10:29:27 2022 GMT
        Subject: C=NL, O=development, CN=localhost
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:b4:74:fb:17:a0:08:15:6b:84:f9:1a:97:5d:73:
                    2c:9b:f2:f0:59:aa:6c:67:30:41:91:22:7f:c5:6b:
                    24:14:f3:3b:10:49:11:5c:ca:05:45:c1:39:0d:1c:
                    6a:85:8d:92:51:57:39:e4:96:0c:08:84:c8:2f:52:
                    32:6a:f5:5e:e9:e0:e4:d5:c8:df:e0:88:b0:d3:d4:
                    e3:33:6b:db:9f:fb:19:23:e0:02:70:af:25:43:6f:
                    75:f7:ae:f9:dc:ea:a6:60:f2:8b:df:4b:d9:ec:d6:
                    06:f3:9f:f5:68:d0:5e:23:86:44:81:f0:fb:9f:21:
                    31:4f:0e:bd:d9:58:11:26:89:37:2c:18:89:92:37:
                    e6:9a:5d:84:74:60:53:78:6f:ff:c8:41:ae:bc:e9:
                    14:6e:ed:0a:3b:38:bf:79:ac:7f:8c:db:27:d9:5e:
                    40:04:49:db:95:52:14:98:43:5e:5a:82:90:78:ff:
                    35:80:16:5e:67:7b:6f:02:9d:1e:ae:e3:57:76:34:
                    1b:c6:3c:f7:48:e0:d8:3c:9c:18:df:5f:6c:c4:5e:
                    f2:fc:b7:91:44:e9:95:f9:90:30:e8:81:f9:ae:b2:
                    9a:ef:2a:a4:32:4e:68:35:54:89:db:c2:22:65:34:
                    72:a7:73:70:29:9b:53:a3:ea:b7:69:ef:ae:78:09:
                    99:7b
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
         41:36:2b:f3:f4:72:b7:19:4e:84:cb:9c:e3:eb:1a:4a:12:3a:
         b7:09:d5:0d:1c:ea:0f:32:2a:c1:a5:d9:f7:3d:3f:c9:4f:8a:
         87:2b:35:b1:16:ea:74:86:11:29:70:72:b2:03:8d:92:92:fa:
         80:ea:e5:2a:57:60:ac:68:d4:7d:b6:50:71:ce:ca:f5:57:fc:
         f6:de:1d:7a:8d:76:ab:4c:c3:a5:5f:29:82:80:5a:ef:1c:e8:
         6c:44:5e:6a:c7:2c:08:1f:7d:fc:a0:c8:5b:33:40:7e:37:48:
         6e:16:94:51:c7:99:62:f3:a6:c8:5a:99:a0:db:39:bc:da:05:
         56:b8:51:ac:70:56:9b:19:53:44:41:2f:3b:be:50:95:af:6f:
         df:2f:02:a1:7a:6b:f5:09:5c:3d:66:b9:07:53:70:4f:03:4f:
         2b:f0:a8:ba:7f:7d:01:ca:a8:98:be:ed:b1:0b:b6:bd:74:74:
         f3:f2:f0:4b:3c:93:b4:c5:22:b0:32:3e:03:73:4a:93:2a:d1:
         bd:ea:2f:a3:f8:45:1d:53:18:61:af:55:4a:65:d2:f9:81:eb:
         af:07:45:14:c7:f6:8d:22:d9:c3:6f:9d:d2:a8:bb:7c:c1:3d:
         20:0d:62:27:e2:93:43:83:23:8d:ba:c4:9f:48:11:dd:3a:3b:
         04:f2:8a:9f

When trying to obtain the publicKey from this certificate, parsing fails.

Unexposed receipt properties

When verifying apple receipts we need the receipt data for checking the signature. Currently, we can access the correct data blob for verification via let receiptData = pkcs7.mainBlock.findOid(.pkcs7data)?.parent?.sub?.last?.sub(0)?.rawValue.
But it seems there is no public api yet for accessing the required data and we have to import the ASN1Decoder via @testable to access the internal mainBlock property.

Furthermore for parsing undocumented apple receipt elements we have to access the receipt block via receiptBlock = pkcs7.mainBlock.findOid(.pkcs7data)?.parent?.sub?.last?.sub(0)?.sub(0). Also this is currently not accessible via public api.

One possible solution to fix this issue would be to expose pkcs7.mainBlock: ASN1Object as public property on PKCS7.
Or is there another way for accessing those properties?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.