Skip to content

Commit c745403

Browse files
Merge pull request #11 from arduino/feat/APP-481-mqtt-csrwithec-ios
chore(phone-as-device): app-481 get private key from Keychain
2 parents 76e3261 + ae056b5 commit c745403

File tree

1 file changed

+87
-79
lines changed

1 file changed

+87
-79
lines changed

ios/MqttClient.swift

+87-79
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,38 @@ class MqttClient : RCTEventEmitter {
6955
self.hasListeners = false
7056
}
7157

58+
func loadPrivateKeyFromKeychain(keyTag: String, reject: RCTPromiseRejectBlock, 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+
} else {
80+
reject("LOAD_KEY_ERROR", "Key does not exist", nil)
81+
}
82+
}
83+
7284
@objc(setIdentity:resolve:reject:)
7385
func setIdentity(params: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void
7486
{
7587
let caCertPem: String = RCTConvert.nsString(params["caCertPem"])
7688
let certPem: String = RCTConvert.nsString(params["certPem"])
77-
let keyPem: String = RCTConvert.nsString(params["keyPem"])
89+
let keyTag: String = RCTConvert.nsString(params["keyTag"])
7890
let keyStoreOptions = RCTConvert.nsDictionary(params["keyStoreOptions"])
7991
let caCertLabel: String = RCTConvert.nsString(keyStoreOptions?["caCertLabel"]) ?? Self.DEFAULT_CA_CERT_LABEL
8092
let certLabel: String = RCTConvert.nsString(keyStoreOptions?["certLabel"]) ?? Self.DEFAULT_CERT_LABEL
@@ -87,75 +99,71 @@ class MqttClient : RCTEventEmitter {
8799
reject("RANGE_ERROR", "invalid certificate", nil)
88100
return
89101
}
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)
102+
103+
let block: SecKeyPerformBlock = { privateKey in
104+
do {
105+
// adds the private key to the keychain
106+
let addKeyAttrs: [String: Any] = [
107+
kSecClass as String: kSecClassKey,
108+
kSecValueRef as String: privateKey,
109+
kSecAttrLabel as String: "Private key that signed an MQTT client certificate",
110+
kSecAttrApplicationTag as String: keyApplicationTag
111+
]
112+
let err = SecItemAdd(addKeyAttrs as CFDictionary, nil)
113+
guard err == errSecSuccess || err == errSecDuplicateItem else {
114+
reject("INVALID_IDENTITY", "failed to add the private key to the keychain: \(err)", nil)
115+
return
116+
}
117+
}
118+
catch let error {
119+
reject("RANGE_ERROR", error.localizedDescription, nil)
120+
}
121+
// adds the certificate to the keychain
122+
let addCertAttrs: [String: Any] = [
123+
kSecClass as String: kSecClassCertificate,
124+
kSecValueRef as String: cert,
125+
kSecAttrLabel as String: certLabel
126+
]
127+
var err = SecItemAdd(addCertAttrs as CFDictionary, nil)
128+
guard err == errSecSuccess || err == errSecDuplicateItem else {
129+
reject("INVALID_IDENTITY", "failed to add the certificate to the keychain: \(err)", nil)
97130
return
98131
}
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
132+
// adds the root certificate to the keychain
133+
// TODO: root certificate may be stored in other place,
134+
// because it is public information.
135+
let addCaCertAttrs: [String: Any] = [
136+
kSecClass as String: kSecClassCertificate,
137+
kSecValueRef as String: caCert,
138+
kSecAttrLabel as String: caCertLabel
106139
]
107-
let err = SecItemAdd(addKeyAttrs as CFDictionary, nil)
140+
err = SecItemAdd(addCaCertAttrs as CFDictionary, nil)
108141
guard err == errSecSuccess || err == errSecDuplicateItem else {
109-
reject("INVALID_IDENTITY", "failed to add the private key to the keychain: \(err)", nil)
142+
reject("INVALID_IDENTITY", "failed to add the root certificate to the keychain: \(err)", nil)
110143
return
111144
}
145+
// obtains the identity
146+
let queryIdentityAttrs: [String: Any] = [
147+
kSecClass as String: kSecClassIdentity,
148+
kSecAttrApplicationTag as String: keyApplicationTag,
149+
kSecReturnRef as String: true
150+
]
151+
var identity: CFTypeRef?
152+
err = SecItemCopyMatching(queryIdentityAttrs as CFDictionary, &identity)
153+
guard err == errSecSuccess else {
154+
reject("INVALID_IDENTITY", "failed to query the keychain for the identity: \(err)", nil)
155+
return
156+
}
157+
guard CFGetTypeID(identity) == SecIdentityGetTypeID() else {
158+
reject("INVALID_IDENTITY", "failed to query the keychain for the identity: type ID mismatch", nil)
159+
return
160+
}
161+
// remembers the identity and the CA certificate
162+
self.certArray = [identity!, caCert] as CFArray
163+
resolve(nil)
112164
}
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)
165+
166+
self.loadPrivateKeyFromKeychain(keyTag: keyTag, reject: reject, block: block)
159167
}
160168

161169
@objc(loadIdentity:resolve:reject:)

0 commit comments

Comments
 (0)