-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcscheck.swift
executable file
·151 lines (126 loc) · 5.35 KB
/
cscheck.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#!/usr/bin/swift
import Foundation
import Security
class StandardError: TextOutputStream {
func write(_ string: String) {
try! FileHandle.standardError.write(contentsOf: Data(string.utf8))
}
}
var stdErr = StandardError()
func getCodeSigningInfo(for appPath: String) -> [String: AnyObject]? {
var staticCode: SecStaticCode?
let status = SecStaticCodeCreateWithPath(URL(fileURLWithPath: appPath) as CFURL, [], &staticCode)
guard status == errSecSuccess, let code = staticCode else {
print("error checking \(appPath) [\(status)]", to: &stdErr)
return nil
}
var codeInfo: CFDictionary?
let infoStatus = SecCodeCopySigningInformation(code, SecCSFlags(rawValue: kSecCSSigningInformation), &codeInfo)
guard infoStatus == errSecSuccess, let info = codeInfo as? [String: AnyObject] else {
print("Failed to get signing information for \(appPath). Status: \(infoStatus)", to: &stdErr)
return nil
}
return info
}
func formatDeveloperName(_ name: String) -> String {
// Remove prefix if present
let trimmedName: Substring
if let range = name.range(of: "Developer ID Application: ") {
trimmedName = name[range.upperBound...]
} else {
trimmedName = Substring(name)
}
// Remove DevID since we already output it separately
if let idRange = trimmedName.range(of: " (") {
return String(trimmedName[..<idRange.lowerBound])
}
return String(trimmedName)
}
func printDeveloperCertificateInfo(for appPath: String, certificates: [SecCertificate], teamIdentifier: String?) {
if certificates.isEmpty {
print("No certificates found for \(appPath)", to: &stdErr)
return
}
print("App: \(appPath)")
// Extract SHA-256 Fingerprint
for certificate in certificates {
let values = SecCertificateCopyValues(certificate, nil, nil) as? [CFString: AnyObject]
if let fingerprintsDict = values?["Fingerprints" as CFString] as? [CFString: AnyObject],
let fingerprints = fingerprintsDict[kSecPropertyKeyValue] as? [[CFString: AnyObject]] {
for fingerprint in fingerprints {
if let label = fingerprint[kSecPropertyKeyLabel] as? String, label == "SHA-256",
let fingerprintData = fingerprint[kSecPropertyKeyValue] as? Data {
let fingerprintString = fingerprintData.map { String(format: "%02hhx", $0) }.joined()
print("SHA-256 Fingerprint: \(fingerprintString)")
}
}
} else {
print("Failed to get SHA-256 fingerprint for \(appPath)", to: &stdErr)
}
// Extract Developer ID and Name
var developerID: String? = teamIdentifier
var developerName: String?
if let subjectNameDict = values?["2.16.840.1.113741.2.1.1.1.8" as CFString] as? [CFString: AnyObject],
let subjectNameValue = subjectNameDict[kSecPropertyKeyValue] as? [[CFString: AnyObject]] {
for attribute in subjectNameValue {
if let label = attribute[kSecPropertyKeyLabel] as? String,
let value = attribute[kSecPropertyKeyValue] as? String {
if label == "0.9.2342.19200300.100.1.1" || label == "2.5.4.5" {
developerID = value
} else if label == "2.5.4.3" {
developerName = value
}
}
}
}
if developerID == nil {
if let idValue = values?["2.5.4.5" as CFString] as? [CFString: AnyObject],
let id = idValue[kSecPropertyKeyValue] as? String {
developerID = id
}
}
if developerName == nil {
if let nameValue = values?["2.5.4.3" as CFString] as? [CFString: AnyObject],
let name = nameValue[kSecPropertyKeyValue] as? String {
developerName = name
}
}
if let id = developerID {
print("Developer ID: \(id)")
} else {
print("Failed to get Developer ID for \(appPath)", to: &stdErr)
}
if let name = developerName {
print("Developer Name:", formatDeveloperName(name))
} else {
print("Failed to get Developer Name for \(appPath)", to: &stdErr)
}
// If we found the information, no need to continue checking further certificates
if developerID != nil && developerName != nil {
break
}
}
}
func main() {
guard CommandLine.arguments.count > 1 else {
print("Usage: cscheck <path> [<path>...]")
exit(0)
}
let appPaths = CommandLine.arguments.dropFirst()
for appPath in appPaths {
guard let codeSigningInfo = getCodeSigningInfo(for: appPath) else {
continue
}
// Extract team identifier if present
let teamIdentifier = codeSigningInfo[kSecCodeInfoTeamIdentifier as String] as? String
if let certificates = codeSigningInfo[kSecCodeInfoCertificates as String] as? [SecCertificate] {
printDeveloperCertificateInfo(for: appPath, certificates: certificates, teamIdentifier: teamIdentifier)
} else {
print("No certificates found in signing information for \(appPath)", to: &stdErr)
if CommandLine.arguments.count == 2 {
exit(1)
}
}
}
}
main()