Skip to content

Commit 9f791d5

Browse files
chore(phone-as-device): app-481 get private key from Keychain
1 parent 76e3261 commit 9f791d5

File tree

1 file changed

+85
-79
lines changed

1 file changed

+85
-79
lines changed

ios/MqttClient.swift

Lines changed: 85 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import CocoaMQTT
22
import os
33
import Security
44

5+
typealias SecKeyPerformBlock = (SecKey) -> ()
6+
57
func loadX509Certificate(fromPem: String) -> SecCertificate? {
68
let pemContents = fromPem
79
.replacingOccurrences(of: "-----BEGIN CERTIFICATE-----", with: "")
@@ -13,22 +15,6 @@ func loadX509Certificate(fromPem: String) -> SecCertificate? {
1315
return SecCertificateCreateWithData(nil, data)
1416
}
1517

16-
func loadPrivateKey(fromPem: String) -> SecKey? {
17-
let pemContents = fromPem
18-
.replacingOccurrences(of: "-----BEGIN RSA PRIVATE KEY-----", with: "")
19-
.replacingOccurrences(of: "-----END RSA PRIVATE KEY-----", with: "")
20-
guard let data = NSData.init(base64Encoded: pemContents, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) else
21-
{
22-
return nil
23-
}
24-
let options: [String: Any] = [
25-
kSecAttrType as String: kSecAttrKeyTypeRSA,
26-
kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
27-
kSecAttrKeySizeInBits as String: 2048 // supposes that the private key has 2048 bits
28-
]
29-
return SecKeyCreateWithData(data, options as CFDictionary, nil)
30-
}
31-
3218
@objc(MqttClient)
3319
class MqttClient : RCTEventEmitter {
3420
static let DEFAULT_KEY_APPLICATION_TAG = "com.github.emoto-kc-ak.react-native-mqtt-client"
@@ -69,12 +55,36 @@ class MqttClient : RCTEventEmitter {
6955
self.hasListeners = false
7056
}
7157

58+
func loadPrivateKeyFromKeychain(keyTag: String, block: SecKeyPerformBlock){
59+
var query: [String: AnyObject] = [
60+
String(kSecClass) : kSecClassKey,
61+
String(kSecAttrApplicationTag): keyTag as AnyObject,
62+
String(kSecReturnRef) : true as AnyObject
63+
]
64+
65+
if #available(iOS 10, *) {
66+
query[String(kSecAttrKeyType)] = kSecAttrKeyTypeECSECPrimeRandom
67+
} else {
68+
// Fallback on earlier versions
69+
query[String(kSecAttrKeyType)] = kSecAttrKeyTypeEC
70+
}
71+
72+
var result : AnyObject?
73+
74+
let status = SecItemCopyMatching(query as CFDictionary, &result)
75+
76+
if status == errSecSuccess {
77+
print("\(keyTag) Key existed!")
78+
block((result as! SecKey?)!)
79+
}
80+
}
81+
7282
@objc(setIdentity:resolve:reject:)
7383
func setIdentity(params: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void
7484
{
7585
let caCertPem: String = RCTConvert.nsString(params["caCertPem"])
7686
let certPem: String = RCTConvert.nsString(params["certPem"])
77-
let keyPem: String = RCTConvert.nsString(params["keyPem"])
87+
let keyTag: String = RCTConvert.nsString(params["keyTag"])
7888
let keyStoreOptions = RCTConvert.nsDictionary(params["keyStoreOptions"])
7989
let caCertLabel: String = RCTConvert.nsString(keyStoreOptions?["caCertLabel"]) ?? Self.DEFAULT_CA_CERT_LABEL
8090
let certLabel: String = RCTConvert.nsString(keyStoreOptions?["certLabel"]) ?? Self.DEFAULT_CERT_LABEL
@@ -87,75 +97,71 @@ class MqttClient : RCTEventEmitter {
8797
reject("RANGE_ERROR", "invalid certificate", nil)
8898
return
8999
}
90-
//guard let key = loadPrivateKey(fromPem: keyPem) else {
91-
// reject("RANGE_ERROR", "invalid private key", nil)
92-
// return
93-
//}
94-
do {
95-
guard let key = try ECPrivateKey(key: keyPem).nativeKey else {
96-
reject("RANGE_ERROR", "invalid private key", nil)
100+
101+
let block: SecKeyPerformBlock = { privateKey in
102+
do {
103+
// adds the private key to the keychain
104+
let addKeyAttrs: [String: Any] = [
105+
kSecClass as String: kSecClassKey,
106+
kSecValueRef as String: privateKey,
107+
kSecAttrLabel as String: "Private key that signed an MQTT client certificate",
108+
kSecAttrApplicationTag as String: keyApplicationTag
109+
]
110+
let err = SecItemAdd(addKeyAttrs as CFDictionary, nil)
111+
guard err == errSecSuccess || err == errSecDuplicateItem else {
112+
reject("INVALID_IDENTITY", "failed to add the private key to the keychain: \(err)", nil)
113+
return
114+
}
115+
}
116+
catch let error {
117+
reject("RANGE_ERROR", error.localizedDescription, nil)
118+
}
119+
// adds the certificate to the keychain
120+
let addCertAttrs: [String: Any] = [
121+
kSecClass as String: kSecClassCertificate,
122+
kSecValueRef as String: cert,
123+
kSecAttrLabel as String: certLabel
124+
]
125+
var err = SecItemAdd(addCertAttrs as CFDictionary, nil)
126+
guard err == errSecSuccess || err == errSecDuplicateItem else {
127+
reject("INVALID_IDENTITY", "failed to add the certificate to the keychain: \(err)", nil)
97128
return
98129
}
99-
100-
// adds the private key to the keychain
101-
let addKeyAttrs: [String: Any] = [
102-
kSecClass as String: kSecClassKey,
103-
kSecValueRef as String: key,
104-
kSecAttrLabel as String: "Private key that signed an MQTT client certificate",
105-
kSecAttrApplicationTag as String: keyApplicationTag
130+
// adds the root certificate to the keychain
131+
// TODO: root certificate may be stored in other place,
132+
// because it is public information.
133+
let addCaCertAttrs: [String: Any] = [
134+
kSecClass as String: kSecClassCertificate,
135+
kSecValueRef as String: caCert,
136+
kSecAttrLabel as String: caCertLabel
106137
]
107-
let err = SecItemAdd(addKeyAttrs as CFDictionary, nil)
138+
err = SecItemAdd(addCaCertAttrs as CFDictionary, nil)
108139
guard err == errSecSuccess || err == errSecDuplicateItem else {
109-
reject("INVALID_IDENTITY", "failed to add the private key to the keychain: \(err)", nil)
140+
reject("INVALID_IDENTITY", "failed to add the root certificate to the keychain: \(err)", nil)
110141
return
111142
}
143+
// obtains the identity
144+
let queryIdentityAttrs: [String: Any] = [
145+
kSecClass as String: kSecClassIdentity,
146+
kSecAttrApplicationTag as String: keyApplicationTag,
147+
kSecReturnRef as String: true
148+
]
149+
var identity: CFTypeRef?
150+
err = SecItemCopyMatching(queryIdentityAttrs as CFDictionary, &identity)
151+
guard err == errSecSuccess else {
152+
reject("INVALID_IDENTITY", "failed to query the keychain for the identity: \(err)", nil)
153+
return
154+
}
155+
guard CFGetTypeID(identity) == SecIdentityGetTypeID() else {
156+
reject("INVALID_IDENTITY", "failed to query the keychain for the identity: type ID mismatch", nil)
157+
return
158+
}
159+
// remembers the identity and the CA certificate
160+
self.certArray = [identity!, caCert] as CFArray
161+
resolve(nil)
112162
}
113-
catch let error {
114-
reject("RANGE_ERROR", error.localizedDescription, nil)
115-
}
116-
// adds the certificate to the keychain
117-
let addCertAttrs: [String: Any] = [
118-
kSecClass as String: kSecClassCertificate,
119-
kSecValueRef as String: cert,
120-
kSecAttrLabel as String: certLabel
121-
]
122-
var err = SecItemAdd(addCertAttrs as CFDictionary, nil)
123-
guard err == errSecSuccess || err == errSecDuplicateItem else {
124-
reject("INVALID_IDENTITY", "failed to add the certificate to the keychain: \(err)", nil)
125-
return
126-
}
127-
// adds the root certificate to the keychain
128-
// TODO: root certificate may be stored in other place,
129-
// because it is public information.
130-
let addCaCertAttrs: [String: Any] = [
131-
kSecClass as String: kSecClassCertificate,
132-
kSecValueRef as String: caCert,
133-
kSecAttrLabel as String: caCertLabel
134-
]
135-
err = SecItemAdd(addCaCertAttrs as CFDictionary, nil)
136-
guard err == errSecSuccess || err == errSecDuplicateItem else {
137-
reject("INVALID_IDENTITY", "failed to add the root certificate to the keychain: \(err)", nil)
138-
return
139-
}
140-
// obtains the identity
141-
let queryIdentityAttrs: [String: Any] = [
142-
kSecClass as String: kSecClassIdentity,
143-
kSecAttrApplicationTag as String: keyApplicationTag,
144-
kSecReturnRef as String: true
145-
]
146-
var identity: CFTypeRef?
147-
err = SecItemCopyMatching(queryIdentityAttrs as CFDictionary, &identity)
148-
guard err == errSecSuccess else {
149-
reject("INVALID_IDENTITY", "failed to query the keychain for the identity: \(err)", nil)
150-
return
151-
}
152-
guard CFGetTypeID(identity) == SecIdentityGetTypeID() else {
153-
reject("INVALID_IDENTITY", "failed to query the keychain for the identity: type ID mismatch", nil)
154-
return
155-
}
156-
// remembers the identity and the CA certificate
157-
self.certArray = [identity!, caCert] as CFArray
158-
resolve(nil)
163+
164+
self.loadPrivateKeyFromKeychain(keyTag: keyTag, block: block)
159165
}
160166

161167
@objc(loadIdentity:resolve:reject:)

0 commit comments

Comments
 (0)