diff --git a/AWSIoT/AWSIoTDataManager.h b/AWSIoT/AWSIoTDataManager.h index 87a6aef5263..95fbb801759 100644 --- a/AWSIoT/AWSIoTDataManager.h +++ b/AWSIoT/AWSIoTDataManager.h @@ -16,6 +16,7 @@ #import "AWSIoTDataService.h" #import "AWSIoTService.h" #import "AWSIoTMQTTTypes.h" +#import "AWSIoTKeychain.h" NS_ASSUME_NONNULL_BEGIN @@ -471,6 +472,23 @@ DEPRECATED_MSG_ATTRIBUTE("Use `updateUserMetaData` for updating the user meta da certificateId:(NSString *)certificateId statusCallback:(void (^)(AWSIoTMQTTStatus status))callback; +/** + Initialises the MQTT session and connects to AWS IoT using certificate-based mutual authentication + + @param clientId The Client Identifier identifies the Client to the Server. + @param cleanSession specifies if the server should discard previous session information. + @param certificateId contains the ID of the certificate to use in the connection; must be in the keychain + @param keyAlgorithmType The algorithm of the key pair. + @param callback When new mqtt session status is received callback will be called with new connection status.\ + + @return true if initialise finished with success + */ +- (BOOL)connectWithClientId:(NSString *)clientId + cleanSession:(BOOL)cleanSession + certificateId:(NSString *)certificateId + keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType + statusCallback:(void (^)(AWSIoTMQTTStatus status))callback; + /** Initialises the MQTT session and connects to AWS IoT on port 443 using certificate-based mutual authentication and ALPN (Application Layer Protocol Negotiation) @@ -492,6 +510,25 @@ DEPRECATED_MSG_ATTRIBUTE("Use `updateUserMetaData` for updating the user meta da statusCallback:(void (^)(AWSIoTMQTTStatus status))callback API_AVAILABLE(ios(11), macosx(10.13)); +/** + Initialises the MQTT session and connects to AWS IoT on port 443 using certificate-based mutual authentication + and ALPN (Application Layer Protocol Negotiation) + + @param clientId The Client Identifier identifies the Client to the Server. + @param cleanSession specifies if the server should discard previous session information. + @param certificateId contains the ID of the certificate to use in the connection; must be in the keychain. + @param keyAlgorithmType The algorithm of the key pair. + @param callback When new mqtt session status is received callback will be called with new connection status. + + @return true if initialise finished with success + */ +- (BOOL)connectUsingALPNWithClientId:(NSString *)clientId + cleanSession:(BOOL)cleanSession + certificateId:(NSString *)certificateId + keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType + statusCallback:(void (^)(AWSIoTMQTTStatus status))callback +API_AVAILABLE(ios(11), macosx(10.13)); + /** Initialises the MQTT session and connects to AWS IoT using WebSocket/SigV4 authentication. IAM credentials are taken from the current service configuration. diff --git a/AWSIoT/AWSIoTDataManager.m b/AWSIoT/AWSIoTDataManager.m index 913f1a1b0d6..d7c7f67f9ec 100644 --- a/AWSIoT/AWSIoTDataManager.m +++ b/AWSIoT/AWSIoTDataManager.m @@ -423,6 +423,20 @@ - (BOOL)connectUsingALPNWithClientId:(NSString *)clientId port:443]; } +- (BOOL)connectUsingALPNWithClientId:(NSString *)clientId + cleanSession:(BOOL)cleanSession + certificateId:(NSString *)certificateId + keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType + statusCallback:(void (^)(AWSIoTMQTTStatus status))callback +{ + return [self connectWithClientId:clientId + cleanSession:cleanSession + certificateId:certificateId + keyAlgorithmType:keyAlgorithmType + statusCallback:callback + port:443]; +} + - (BOOL)connectWithClientId:(NSString*)clientId cleanSession:(BOOL)cleanSession certificateId:(NSString *)certificateId @@ -435,6 +449,20 @@ - (BOOL)connectWithClientId:(NSString*)clientId port:8883]; } +- (BOOL)connectWithClientId:(NSString*)clientId + cleanSession:(BOOL)cleanSession + certificateId:(NSString *)certificateId + keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType + statusCallback:(void (^)(AWSIoTMQTTStatus status))callback +{ + return [self connectWithClientId:clientId + cleanSession:cleanSession + certificateId:certificateId + keyAlgorithmType:keyAlgorithmType + statusCallback:callback + port:8883]; +} + - (BOOL)connectWithClientId:(NSString*)clientId cleanSession:(BOOL)cleanSession certificateId:(NSString *)certificateId @@ -481,6 +509,54 @@ - (BOOL)connectWithClientId:(NSString*)clientId statusCallback:callback]; } +- (BOOL)connectWithClientId:(NSString*)clientId + cleanSession:(BOOL)cleanSession + certificateId:(NSString *)certificateId + keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType + statusCallback:(void (^)(AWSIoTMQTTStatus status))callback + port:(UInt32)port +{ + AWSDDLogDebug(@"<<%@>>In connectWithClientID", [NSThread currentThread]); + AWSDDLogInfo(@"hostName: %@", self.IoTData.configuration.endpoint.hostName); + AWSDDLogInfo(@"URL: %@", self.IoTData.configuration.endpoint.URL); + + if (clientId == nil || [clientId isEqualToString: @""]) { + return false; + } + + if (certificateId == nil || [certificateId isEqualToString:@""]) { + return false; + } + + if (_userDidIssueConnect) { + //User has already connected. Can't connect multiple times, return No. + return NO; + } + + _userDidIssueConnect = YES; + _userDidIssueDisconnect = NO; + + [self.mqttClient setBaseReconnectTime:self.mqttConfiguration.baseReconnectTimeInterval]; + [self.mqttClient setMinimumConnectionTime:self.mqttConfiguration.minimumConnectionTimeInterval]; + [self.mqttClient setMaximumReconnectTime:self.mqttConfiguration.maximumReconnectTimeInterval]; + [self.mqttClient setAutoResubscribe:self.mqttConfiguration.autoResubscribe]; + [self.mqttClient setPublishRetryThrottle:self.mqttConfiguration.publishRetryThrottle]; + [self.mqttClient setAutoResubscribe:self.mqttConfiguration.autoResubscribe]; + + return [self.mqttClient connectWithClientId:clientId + toHost:self.IoTData.configuration.endpoint.hostName + port:port + cleanSession:cleanSession + certificateId:certificateId + keyAlgorithmType:keyAlgorithmType + keepAlive:self.mqttConfiguration.keepAliveTimeInterval + willTopic:self.mqttConfiguration.lastWillAndTestament.topic + willMsg:[self.mqttConfiguration.lastWillAndTestament.message dataUsingEncoding:NSUTF8StringEncoding] + willQoS:self.mqttConfiguration.lastWillAndTestament.qos + willRetainFlag:self.mqttConfiguration.lastWillAndTestament.willRetain + statusCallback:callback]; +} + - (BOOL)connectUsingWebSocketWithClientId:(NSString *)clientId cleanSession:(BOOL)cleanSession statusCallback:(void (^)(AWSIoTMQTTStatus status))callback; diff --git a/AWSIoT/AWSIoTManager.h b/AWSIoT/AWSIoTManager.h index ba3eaae0e46..fa31924bb96 100644 --- a/AWSIoT/AWSIoTManager.h +++ b/AWSIoT/AWSIoTManager.h @@ -15,6 +15,7 @@ #import "AWSIoTService.h" #import "AWSIoTKeyChainTypes.h" +#import "AWSIoTKeychain.h" //CreateCertificateWithResponse @interface AWSIoTCreateCertificateResponse : AWSModel @@ -208,6 +209,23 @@ */ + (BOOL)importIdentityFromPKCS12Data:(NSData *)pkcs12Data passPhrase:(NSString *)passPhrase certificateId:(NSString *)certificateId; +/** + * Imports a complete identity from PKCS#12 data into the keychain, validating against a specific algorithm type. + * + * This function extracts the certificate, public key, and private key, then validates their internal consistency and + * matches them against the expected algorithm type provided by the caller. + * Supports RSA, EC, ECPrimeRandom key algorithms. + * + * @param pkcs12Data The identity data in PKCS#12 format. + * @param passPhrase The password for the PKCS#12 data. + * @param certificateId A unique identifier used to construct the final tags for the keychain items. + * @param keyAlgorithmType The expected algorithm of the key pair. The function will fail if the actual + * key type in the data does not match this parameter. + * + * @return `YES` if the entire identity was validated and imported successfully, `NO` otherwise. + */ ++ (BOOL)importIdentityFromPKCS12Data:(NSData *)pkcs12Data passPhrase:(NSString *)passPhrase certificateId:(NSString *)certificateId keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + /** * Validates the certificate with the given identifier of certificate. * @@ -217,6 +235,20 @@ */ + (BOOL)isValidCertificate:(NSString *)certificateId; +/** + * Check if a valid certificate identity exists for a given certificateId and algorithm type. + * + * This function constructs the appropriate private key tag and certificate + * label based on the provided algorithm type and certificate ID, then calls a more detailed + * validation method to confirm the identity's presence and integrity in the keychain. + * + * @param certificateId The unique identifier used to construct the full keychain tags. + * @param keyAlgorithmType The algorithm of the key pair associated with the certificate. + * + * @return `YES` if a valid identity is found, `NO` otherwise. + */ ++ (BOOL)isValidCertificate:(NSString *)certificateId keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + /** * Deletes keys and certificate * @@ -226,6 +258,20 @@ + (BOOL)deleteCertificateWithCertificateId:(NSString*)certificateId NS_SWIFT_NAME(deleteCertificate(certificateId:)); +/** + * Deletes a complete identity (certificate and key pair) from the keychain. + * + * This function constructs the necessary unique tags based on the certificate ID and algorithm type, + * and then attempts to remove both the certificate and its associated asymmetric key pair. + * + * @param certificateId The unique identifier for the identity to be deleted. + * @param keyAlgorithmType The algorithm of the key pair to be deleted. + * + * @return `YES` if all components were successfully deleted (or were already not present), + * `NO` if a critical deletion error occurred for any component. + */ ++ (BOOL)deleteCertificateWithCertificateIdAndKeyAlgorithmType:(NSString*)certificateId keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType NS_SWIFT_NAME(deleteCertificate(certificateId:keyAlgorithmType:)); + + (void)setKeyChainAccessibility:(AWSIoTKeyChainAccessibility)accessibility; + (NSString *)certTagWithCertificateId:(NSString *)certificateId; diff --git a/AWSIoT/AWSIoTManager.m b/AWSIoT/AWSIoTManager.m index 8b0d89b021a..c5000048c02 100644 --- a/AWSIoT/AWSIoTManager.m +++ b/AWSIoT/AWSIoTManager.m @@ -143,7 +143,7 @@ - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)configuration { + (NSString *)certTagWithCertificateId:(NSString *)certificateId { // tagCertificateEnabled property defaults to legacy behavior - return [AWSIoTKeychain.certTag stringByAppendingString: AWSIoTManager.tagCertificateEnabled ? certificateId : @""]; + return [AWSIoTKeychain.rsaCertTag stringByAppendingString: AWSIoTManager.tagCertificateEnabled ? certificateId : @""]; } - (void)createKeysAndCertificateFromCsr:(NSDictionary *)csrDictionary callback:(void (^)(AWSIoTCreateCertificateResponse *mainResponse))callback { @@ -159,8 +159,8 @@ - (void)createKeysAndCertificateFromCsr:(NSDictionary *)c } NSString *uuid = [[NSUUID UUID] UUIDString]; - NSString *publicTag = [AWSIoTKeychain.publicKeyTag stringByAppendingString:uuid]; - NSString *privateTag = [AWSIoTKeychain.privateKeyTag stringByAppendingString:uuid]; + NSString *publicTag = [AWSIoTKeychain.rsaPublicKeyTag stringByAppendingString:uuid]; + NSString *privateTag = [AWSIoTKeychain.rsaPrivateKeyTag stringByAppendingString:uuid]; [AWSIoTKeychain generateKeyPairWithPublicTag:publicTag privateTag:privateTag]; AWSIoTCSR *csr = [[AWSIoTCSR alloc] initWithCommonName: commonName countryName:countryName organizationName: organizationName organizationalUnitName: organizationalUnitName ]; @@ -198,8 +198,8 @@ - (void)createKeysAndCertificateFromCsr:(NSDictionary *)c AWSDDLogInfo(@"certificatePem: %@", certificatePem); if (certificatePem != nil && certificateArn != nil && certificateId != nil) { - NSString *newPublicTag = [AWSIoTKeychain.publicKeyTag stringByAppendingString:certificateId]; - NSString *newPrivateTag = [AWSIoTKeychain.privateKeyTag stringByAppendingString:certificateId]; + NSString *newPublicTag = [AWSIoTKeychain.rsaPublicKeyTag stringByAppendingString:certificateId]; + NSString *newPrivateTag = [AWSIoTKeychain.rsaPrivateKeyTag stringByAppendingString:certificateId]; // tagCertificateEnabled property defaults to legacy behavior NSString *newCertTag = [AWSIoTManager certTagWithCertificateId:certificateId]; @@ -208,7 +208,7 @@ - (void)createKeysAndCertificateFromCsr:(NSDictionary *)c SecKeyRef privateKeyRef = [AWSIoTKeychain getPrivateKeyRef:privateTag]; SecIdentityRef identityRef = nil; - if ([AWSIoTKeychain deleteAsymmetricKeysWithPublicTag:publicTag privateTag:privateTag] && + if ([AWSIoTKeychain deleteAsymmetricKeysWithPublicTag:publicTag privateTag:privateTag keyAlgorithmType:KeyAlgorithmTypeRSA] && [AWSIoTKeychain addPrivateKeyRef:privateKeyRef tag:newPrivateTag] && [AWSIoTKeychain addPublicKeyRef:publicKeyRef tag:newPublicTag] && [AWSIoTKeychain addCertificateToKeychain:certificatePem tag:newCertTag] && @@ -263,8 +263,8 @@ + (BOOL)importIdentityFromPKCS12Data:(NSData *)pkcs12Data passPhrase:(NSString * return NO; } - NSString *publicTag = [AWSIoTKeychain.publicKeyTag stringByAppendingString:certificateId]; - NSString *privateTag = [AWSIoTKeychain.privateKeyTag stringByAppendingString:certificateId]; + NSString *publicTag = [AWSIoTKeychain.rsaPublicKeyTag stringByAppendingString:certificateId]; + NSString *privateTag = [AWSIoTKeychain.rsaPrivateKeyTag stringByAppendingString:certificateId]; NSString *certTag = [AWSIoTManager certTagWithCertificateId:certificateId]; if (![AWSIoTKeychain addPrivateKeyRef:privateKey tag:privateTag]) { @@ -291,6 +291,101 @@ + (BOOL)importIdentityFromPKCS12Data:(NSData *)pkcs12Data passPhrase:(NSString * return YES; } ++ (BOOL)importIdentityFromPKCS12Data:(NSData *)pkcs12Data passPhrase:(NSString *)passPhrase certificateId:(NSString *)certificateId keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + __block SecKeyRef privateKey = NULL; + __block SecKeyRef publicKey = NULL; + __block SecCertificateRef certRef = NULL; + + void (^cleanup)(void) = ^void { + if (certRef) { + CFRelease(certRef); + } + if (privateKey) { + CFRelease(privateKey); + } + if (publicKey) { + CFRelease(publicKey); + } + }; + + if (keyAlgorithmType == KeyAlgorithmTypeUnknown) { + AWSDDLogError(@"keyAlgorithmType value is not valid"); + return NO; + } + + [AWSIoTManager readPk12:pkcs12Data passPhrase:passPhrase certRef:&certRef privateKeyRef:&privateKey publicKeyRef:&publicKey]; + + if (!certRef || !privateKey || !publicKey) { + cleanup(); + AWSDDLogError(@"Unable to extract PKCS12 data. Ensure the passPhrase is correct."); + return NO; + } + + KeyAlgorithmType privateKeyType = [AWSIoTKeychain getKeyAlgorithmTypeFromKeyRef:privateKey]; + KeyAlgorithmType publicKeyType = [AWSIoTKeychain getKeyAlgorithmTypeFromKeyRef:publicKey]; + + if (privateKeyType == KeyAlgorithmTypeUnknown || publicKeyType == KeyAlgorithmTypeUnknown) { + cleanup(); + AWSDDLogError(@"Could not determine key algorithm from the PKCS12 data."); + return NO; + } + if (privateKeyType != publicKeyType) { + cleanup(); + AWSDDLogError(@"Mismatched key algorithms in the PKCS12 data."); + return NO; + } + if (privateKeyType != keyAlgorithmType) { + cleanup(); + AWSDDLogError(@"The provided keyAlgorithmType does not match the actual key type found in the PKCS12 data."); + return NO; + } + + NSString *publicTagPrefix = [AWSIoTKeychain getPublicKeyTagFromKeyAlgorithmType:keyAlgorithmType]; + if (!publicTagPrefix) { + AWSDDLogError(@"Unable to get public key tag prefix"); + return NO; + } + NSString *privateTagPrefix = [AWSIoTKeychain getPrivateKeyTagFromKeyAlgorithmType:keyAlgorithmType]; + if (!privateTagPrefix) { + AWSDDLogError(@"Unable to get private key tag prefix"); + return NO; + } + NSString *certTagPrefix = [AWSIoTKeychain getCertificateTagFromKeyAlgorithmType:keyAlgorithmType]; + if (!certTagPrefix) { + AWSDDLogError(@"Unable to get certificate tag prefix"); + return NO; + } + + NSString *publicTag = [publicTagPrefix stringByAppendingString:certificateId]; + NSString *privateTag = [privateTagPrefix stringByAppendingString:certificateId]; + NSString *certTag = [certTagPrefix stringByAppendingString:certificateId]; + + if (![AWSIoTKeychain addPrivateKeyRef:privateKey tag:privateTag]) { + cleanup(); + AWSDDLogError(@"Unable to add private key"); + return NO; + } + + if (![AWSIoTKeychain addPublicKeyRef:publicKey tag:publicTag]) { + // can use privateKeyType since it is the same as publicKeyType + [AWSIoTKeychain deleteAsymmetricKeysWithPublicTag:publicTag privateTag:privateTag keyAlgorithmType:privateKeyType]; + cleanup(); + AWSDDLogError(@"Unable to add public key"); + return NO; + } + + if (![AWSIoTKeychain addCertificateRef:certRef tag:certTag]) { + // can use privateKeyType since it is the same as publicKeyType + [AWSIoTKeychain deleteAsymmetricKeysWithPublicTag:publicTag privateTag:privateTag keyAlgorithmType:privateKeyType]; + cleanup(); + AWSDDLogError(@"Unable to add certificate"); + return NO; + } + + cleanup(); + return YES; +} + // // Helper method to get certificate, public key, and private key references to import into the keychain. // @@ -388,25 +483,74 @@ + (BOOL)readPk12:(NSData *)pk12Data passPhrase:(NSString *)passPhrase certRef:(S } + (BOOL)deleteCertificate { - return [AWSIoTKeychain removeCertificateWithTag:AWSIoTKeychain.certTag]; + return [AWSIoTKeychain removeCertificateWithTag:AWSIoTKeychain.rsaCertTag]; } + (BOOL)deleteCertificateWithCertificateId:(NSString*)certificateId { NSString *certTag = [AWSIoTManager certTagWithCertificateId:certificateId]; - NSString *publicTag = [AWSIoTKeychain.publicKeyTag stringByAppendingString:certificateId]; - NSString *privateTag = [AWSIoTKeychain.privateKeyTag stringByAppendingString:certificateId]; + NSString *publicTag = [AWSIoTKeychain.rsaPublicKeyTag stringByAppendingString:certificateId]; + NSString *privateTag = [AWSIoTKeychain.rsaPrivateKeyTag stringByAppendingString:certificateId]; + + return [AWSIoTKeychain removeCertificateWithTag:certTag] && [AWSIoTKeychain + deleteAsymmetricKeysWithPublicTag:publicTag + privateTag:privateTag + keyAlgorithmType:KeyAlgorithmTypeRSA]; +} ++ (BOOL)deleteCertificateWithCertificateIdAndKeyAlgorithmType:(NSString *)certificateId keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + NSString *certTagPrefix = [AWSIoTKeychain getCertificateTagFromKeyAlgorithmType:keyAlgorithmType]; + if (!certTagPrefix) { + AWSDDLogError(@"Unable to get certificate tag prefix"); + return NO; + } + NSString *publicTagPrefix = [AWSIoTKeychain getPublicKeyTagFromKeyAlgorithmType:keyAlgorithmType]; + if (!publicTagPrefix) { + AWSDDLogError(@"Unable to get public key tag prefix"); + return NO; + } + NSString *privateTagPrefix = [AWSIoTKeychain getPrivateKeyTagFromKeyAlgorithmType:keyAlgorithmType]; + if (!privateTagPrefix) { + AWSDDLogError(@"Unable to get private key tag prefix"); + return NO; + } + NSString *certTag = [certTagPrefix stringByAppendingString:certificateId]; + NSString *publicTag = [publicTagPrefix stringByAppendingString:certificateId]; + NSString *privateTag = [privateTagPrefix stringByAppendingString:certificateId]; + return [AWSIoTKeychain removeCertificateWithTag:certTag] && [AWSIoTKeychain deleteAsymmetricKeysWithPublicTag:publicTag - privateTag:privateTag]; + privateTag:privateTag + keyAlgorithmType:keyAlgorithmType]; } + (BOOL)isValidCertificate:(NSString *)certificateId { - NSString *tag = [NSString stringWithFormat:@"%@%@", [AWSIoTKeychain privateKeyTag], certificateId]; + NSString *tag = [NSString stringWithFormat:@"%@%@", [AWSIoTKeychain rsaPrivateKeyTag], certificateId]; NSString *certLabel = [AWSIoTManager certTagWithCertificateId:certificateId]; return [AWSIoTKeychain isValidCertificate:tag certificateLabel:certLabel]; } ++ (BOOL)isValidCertificate:(NSString *)certificateId keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + if (keyAlgorithmType == KeyAlgorithmTypeUnknown) { + AWSDDLogError(@"keyAlgorithmType value is not valid"); + return NO; + } + + NSString *privateTagPrefix = [AWSIoTKeychain getPrivateKeyTagFromKeyAlgorithmType:keyAlgorithmType]; + if (!privateTagPrefix) { + AWSDDLogError(@"Unable to get private key tag prefix"); + return NO; + } + NSString *certTagPrefix = [AWSIoTKeychain getCertificateTagFromKeyAlgorithmType:keyAlgorithmType]; + if (!certTagPrefix) { + AWSDDLogError(@"Unable to get certificate tag prefix"); + return NO; + } + + NSString *tag = [NSString stringWithFormat:@"%@%@", privateTagPrefix, certificateId]; + NSString *certLabel = [NSString stringWithFormat:@"%@%@", certTagPrefix, certificateId]; + return [AWSIoTKeychain isValidCertificate:tag certificateLabel:certLabel keyAlgorithmType:keyAlgorithmType]; +} + + (void)setKeyChainAccessibility:(AWSIoTKeyChainAccessibility)accessibility { [AWSIoTKeychain setKeyChainAccessibility:accessibility]; } diff --git a/AWSIoT/Internal/AWSIoTCSR.m b/AWSIoT/Internal/AWSIoTCSR.m index 815047bc816..32351e94930 100644 --- a/AWSIoT/Internal/AWSIoTCSR.m +++ b/AWSIoT/Internal/AWSIoTCSR.m @@ -60,8 +60,8 @@ - (NSData*)generateCSRForCertificate:(NSString*)certificateId { return nil; } - NSString *publicTag = [AWSIoTKeychain.publicKeyTag stringByAppendingString:certificateId]; - NSString *privateTag = [AWSIoTKeychain.privateKeyTag stringByAppendingString:certificateId]; + NSString *publicTag = [AWSIoTKeychain.rsaPublicKeyTag stringByAppendingString:certificateId]; + NSString *privateTag = [AWSIoTKeychain.rsaPrivateKeyTag stringByAppendingString:certificateId]; _publicKeyBits = [AWSIoTKeychain getPublicKeyBits:publicTag]; if (!_publicKeyBits) { diff --git a/AWSIoT/Internal/AWSIoTKeychain.h b/AWSIoT/Internal/AWSIoTKeychain.h index d6bb9eb06ff..07c638f4b50 100644 --- a/AWSIoT/Internal/AWSIoTKeychain.h +++ b/AWSIoT/Internal/AWSIoTKeychain.h @@ -26,11 +26,29 @@ FOUNDATION_EXPORT NSString *const AWSIoTKeychainEndPrivateKeyTag; FOUNDATION_EXPORT NSString *const AWSIoTKeychainStartCertKeyTag; FOUNDATION_EXPORT NSString *const AWSIoTKeychainEndCertKeyTag; +/** + * Represents common cryptographic key algorithms. + * This enum provides a high-level abstraction for the low-level constants + * (e.g., kSecAttrKeyTypeRSA) used by the Security framework. + */ +typedef NS_ENUM(NSInteger, KeyAlgorithmType) { + KeyAlgorithmTypeUnknown, + KeyAlgorithmTypeRSA, + KeyAlgorithmTypeEC, + KeyAlgorithmTypeECPrimeRandom +}; + @interface AWSIoTKeychain : NSObject -+ (NSString *)publicKeyTag; -+ (NSString *)privateKeyTag; -+ (NSString *)certTag; ++ (NSString *)rsaPublicKeyTag; ++ (NSString *)ecPublicKeyTag; ++ (NSString *)ecPrimeRandomPublicKeyTag; ++ (NSString *)rsaPrivateKeyTag; ++ (NSString *)ecPrivateKeyTag; ++ (NSString *)ecPrimeRandomPrivateKeyTag; ++ (NSString *)rsaCertTag; ++ (NSString *)ecCertTag; ++ (NSString *)ecPrimeRandomCertTag; + (NSString *)base64Encode:(NSData *)data; + (NSData *)base64Decode:(NSString *)str; @@ -40,7 +58,44 @@ FOUNDATION_EXPORT NSString *const AWSIoTKeychainEndCertKeyTag; + (BOOL)generateKeyPairWithPublicTag:(NSString *)publicTag privateTag:(NSString *)privateTag; + (BOOL)deleteAsymmetricKeysWithPublicTag:(NSString *)publicTag privateTag:(NSString *)privateTag; + +/** + * Deletes an asymmetric key pair from the keychain for a specific algorithm. + * This function explicitly requires the algorithm type to build precise queries for deleting + * both the public and private keys associated with the provided tags. It attempts to delete + * both keys even if one is not found. + * + * @param publicTag The unique application tag for the public key. + * @param privateTag The unique application tag for the private key. + * @param keyAlgorithmType The algorithm of the key pair to be deleted. + * + * @return `YES` if both keys were either successfully deleted or were already not present. + * Returns `NO` if a critical, unexpected error occurred during either deletion operation. + */ ++ (BOOL)deleteAsymmetricKeysWithPublicTag:(NSString *)publicTag privateTag:(NSString *)privateTag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + + (BOOL)isValidCertificate:(NSString*)privateKeyTag certificateLabel:(NSString*)certificateLabel; + +/** + * Checks if a valid certificate can be extracted from a keychain identity specified by its composite tags. + * This function performs a validation check by first searching for a cryptographic identity + * (`SecIdentityRef`) using a specific private key tag, certificate label, and algorithm type. + * If an identity is found, it then attempts to copy the certificate (`SecCertificateRef`) from that + * identity to confirm its structural integrity. + * + * Note: This function does not validate the certificate's expiration date or trust chain; + * it only confirms that an identity exists and is well-formed enough for a certificate to be + * extracted from it. + * + * @param privateKeyTag The application tag of the private key associated with the identity. + * @param certificateLabel The label of the certificate associated with the identity. + * @param keyAlgorithmType The algorithm type of the key pair. + * + * @return `YES` if an identity is found and a certificate can be successfully extracted from it. + * Returns `NO` if the identity is not found or if the certificate extraction fails. + */ ++ (BOOL)isValidCertificate:(NSString*)privateKeyTag certificateLabel:(NSString*)certificateLabel keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + + (BOOL)addCertificateToKeychain:(NSString *)cert; + (BOOL)addCertificateToKeychain:(NSString*)cert tag:(NSString*)tag; + (BOOL)addCertificateFromPemFile:(NSString*)fileName withTag:(NSString*)tag; @@ -52,16 +107,226 @@ FOUNDATION_EXPORT NSString *const AWSIoTKeychainEndCertKeyTag; + (BOOL)removeCertificate; + (SecKeyRef)getPublicKeyRef:(NSString *)tag; + +/** + * Retrieves a public key reference (SecKeyRef) from the keychain using its tag and algorithm type. + * + * This function builds a query to find a `kSecClassKey` item that is a public key + * and matches a specific application tag and algorithm type. + * The caller is responsible for releasing the returned `SecKeyRef` object by calling `CFRelease()` + * to prevent a memory leak. + * + * @param tag The unique application tag used to store the key. + * @param keyAlgorithmType The algorithm type of the key. + * + * @return A `SecKeyRef` on success, or `NULL` if the key is not found or an error occurs. + */ ++ (SecKeyRef)getPublicKeyRef:(NSString *)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + + (NSData *)getPublicKeyBits:(NSString *)tag; + +/** + * Retrieves the raw data (bits) of a public key from the keychain. + * This function finds a public key matching a specific + * application tag and algorithm type, and returns its raw data representation. + * + * @param tag The unique application tag used to store the key. + * @param keyAlgorithmType The algorithm type of the key. + * + * @return `NSData` object containing the key's data on success, or `NULL` if the key is + * not found or an error occurs. + */ ++ (NSData *)getPublicKeyBits:(NSString *)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + + (SecKeyRef)getPrivateKeyRef:(NSString *)tag; + +/** + * Retrieves a private key reference (SecKeyRef) from the keychain using its tag and algorithm type. + * This function builds a precise query to find a `kSecClassKey` item that is a private key + * and matches a specific application tag and algorithm type. + * The caller is responsible for releasing the returned `SecKeyRef` object by calling `CFRelease()` + * to prevent a memory leak. + * + * @param tag The unique application tag used to store the key. + * @param keyAlgorithmType The algorithm type of the key. + * + * @return A `SecKeyRef` on success, or `NULL` if the key is not found or an error occurs. + */ ++ (SecKeyRef)getPrivateKeyRef:(NSString *)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + + (NSData *)getPrivateKeyBits:(NSString *)tag; + +/** + * Retrieves the raw data (bits) of a private key from the keychain. + * This function finds a private key matching a specific + * application tag and algorithm type, and returns its raw data representation. + * + * @param tag The unique application tag used to store the key. + * @param keyAlgorithmType The algorithm type of the key. + * + * @return `NSData` object containing the key's data on success, or `NULL` if the key is + * not found or an error occurs. + */ ++ (NSData *)getPrivateKeyBits:(NSString *)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + + (SecIdentityRef)getIdentityRef:(NSString*)privateKeyTag certificateLabel:(NSString *)certificateLabel; + +/** + * Retrieves a complete cryptographic identity (SecIdentityRef) from the keychain. + * This function builds a query to find a `kSecClassIdentity` item that matches a specific + * private key tag, certificate label, and algorithm type. + * The caller is responsible for releasing the returned `SecIdentityRef` object by calling `CFRelease()` + * to prevent a memory leak. + * + * @param privateKeyTag The application tag of the private key associated with the identity. + * @param certificateLabel The label of the certificate associated with the identity. + * @param keyAlgorithmType The algorithm type of the key pair. + * + * @return A `SecIdentityRef` on success, or `NULL` if the identity is not found or an error occurs. + */ ++ (SecIdentityRef)getIdentityRef:(NSString*)privateKeyTag certificateLabel:(NSString *)certificateLabel keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + + (BOOL)addPublicKeyRef:(SecKeyRef)pubkeyRef tag:(NSString *)tag; + +/** + * Adds a public key reference to the keychain with a specific tag and algorithm type. + * + * @param pubkeyRef A reference to the public key object to be stored. + * @param tag The unique application tag for the key. + * @param keyAlgorithmType The algorithm type of the key. + * + * @return `YES` if the key was successfully stored in the keychain, `NO` otherwise. + */ ++ (BOOL)addPublicKeyRef:(SecKeyRef)pubkeyRef tag:(NSString *)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + + (BOOL)addPublicKey:(NSData *)pubkey tag:(NSString *)tag; + +/** + * Adds public key data to the keychain with a specific tag and algorithm type. + * + * @param pubkey The raw `NSData` of the public key to be stored. + * @param tag The unique application tag for the key. + * @param keyAlgorithmType The algorithm type of the key. + * + * @return `YES` if the key was successfully stored in the keychain, `NO` otherwise. + */ ++ (BOOL)addPublicKey:(NSData *)pubkey tag:(NSString *)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + + (BOOL)addPrivateKeyRef:(SecKeyRef)privkeyRef tag:(NSString *)tag; + +/** + * Adds a private key reference to the keychain with a specific tag and algorithm type. + * + * @param privkeyRef A reference to the private key object to be stored. + * @param tag The unique application tag for the key. + * @param keyAlgorithmType The algorithm type of the key. + * @return `YES` if the key was successfully stored, `NO` otherwise. + */ ++ (BOOL)addPrivateKeyRef:(SecKeyRef)privkeyRef tag:(NSString *)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + + (BOOL)addPrivateKey:(NSData *)privkey tag:(NSString *)tag; + +/** + * Adds private key data to the keychain with a specific tag and algorithm type. + * + * @param privkey The raw `NSData` of the private key to be stored. + * @param tag The unique application tag for the key. + * @param keyAlgorithmType The algorithm type of the key. + * @return `YES` if the key was successfully stored, `NO` otherwise. + */ ++ (BOOL)addPrivateKey:(NSData *)privkey tag:(NSString *)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + + (BOOL)deletePrivateKeyWithTag:(NSString*)tag; +/** + * Deletes a specific private key from the keychain using its tag and algorithm type. + * This function finds and removes a private key from the keychain + * that matches the specified application tag and algorithm. + * + * @param tag The unique application tag for the private key. + * @param keyAlgorithmType The algorithm type of the key to be deleted. + * + * @return `YES` if the key was successfully deleted or was already not present. Returns `NO` + * if a critical, unexpected error occurred during the deletion operation. + */ ++ (BOOL)deletePrivateKeyWithTag:(NSString*)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + + (void)setKeyChainAccessibility:(AWSIoTKeyChainAccessibility)accessibility; +/** + * Converts a KeyAlgorithmType enum value to its corresponding Security framework constant. + * + * @param keyAlgorithmType The enum value representing the algorithm. + * @return The corresponding kSecAttrKeyType constant (e.g., kSecAttrKeyTypeRSA) on success, + * or `NULL` if the type is KeyAlgorithmTypeUnknown or otherwise unhandled. + */ ++ (CFStringRef)getKeyTypeFromKeyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + +/** + * Determines the key algorithm type by inspecting the provided keychain tag string. + * + * This utility function parses a tag string to check for the presence of specific, + * predefined identifiers like ".RSAPublicTag." or ".ECPrivateTag.". It is designed + * to work with tags that follow the naming convention established by methods like + * `rsaPublicKeyTag` and `ecPrivateKeyTag`. + * + * @param keyTag The keychain tag `NSString` to inspect. + * + * @return The corresponding `KeyAlgorithmType` enum value (`KeyAlgorithmTypeRSA` or + * `KeyAlgorithmTypeEC`). Returns `KeyAlgorithmTypeUnknown` if the tag is `nil`, + * empty, or does not match a known algorithm pattern. + */ ++ (KeyAlgorithmType)getKeyAlgorithmTypeFromTag:(NSString *)keyTag; + +/** + * Inspects a SecKeyRef and returns its algorithm type as a high-level enum. + * It interacts with the Security framework to read the key's attributes and maps the low-level + * keychain constant to the application's high-level `KeyAlgorithmType` enum. + * + * @param keyRef A reference to the key object (public or private) to be inspected. + * + * @return The corresponding `KeyAlgorithmType` enum value (e.g., `KeyAlgorithmTypeRSA`). + * Returns `KeyAlgorithmTypeUnknown` if the key reference is NULL or its type cannot be determined. + */ ++ (KeyAlgorithmType)getKeyAlgorithmTypeFromKeyRef:(SecKeyRef)keyRef; + +/** + * A factory method that returns the appropriate certificate tag string for a given algorithm type. + * This utility function acts as a dispatcher, calling the correct specific tag-generation + * method (e.g., `rsaCertTag` or `ecCertTag`) based on the provided + * `KeyAlgorithmType` enum. + * + * @param keyAlgorithmType The enum value representing the algorithm associated with the certificate. + * + * @return An `NSString` containing the fully-formed certificate tag. Returns `nil` if the + * algorithm type is `KeyAlgorithmTypeUnknown` or another unhandled value. + */ ++ (NSString *)getCertificateTagFromKeyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + +/** + * A factory method that returns the appropriate public key tag string for a given algorithm type. + * This utility function acts as a dispatcher, calling the correct specific tag-generation + * method (e.g., `rsaPublicKeyTag` or `ecPublicKeyTag`) based on the provided + * `KeyAlgorithmType` enum. + * + * @param keyAlgorithmType The enum value representing the algorithm. + * + * @return An `NSString` containing the fully-formed public key tag. Returns `nil` if the + * algorithm type is `KeyAlgorithmTypeUnknown` or another unhandled value. + */ ++ (NSString *)getPublicKeyTagFromKeyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + +/** + * A factory method that returns the appropriate private key tag string for a given algorithm type. + * This utility function acts as a dispatcher, calling the correct specific tag-generation + * method (e.g., `rsaPublicKeyTag` or `ecPublicKeyTag`) based on the provided + * `KeyAlgorithmType` enum. + * + * @param keyAlgorithmType The enum value representing the algorithm. + * + * @return An `NSString` containing the fully-formed public key tag. Returns `nil` if the + * algorithm type is `KeyAlgorithmTypeUnknown` or another unhandled value. + */ ++ (NSString *)getPrivateKeyTagFromKeyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType; + @end diff --git a/AWSIoT/Internal/AWSIoTKeychain.m b/AWSIoT/Internal/AWSIoTKeychain.m index a8871fb470d..f75e0f647c5 100644 --- a/AWSIoT/Internal/AWSIoTKeychain.m +++ b/AWSIoT/Internal/AWSIoTKeychain.m @@ -26,18 +26,42 @@ @implementation AWSIoTKeychain -+ (NSString*)publicKeyTag { ++ (NSString*)rsaPublicKeyTag { return [NSString stringWithFormat:@"%@.RSAPublicTag.",[[NSBundle bundleForClass:[self class]] bundleIdentifier]]; } -+ (NSString*)privateKeyTag { ++ (NSString *)ecPublicKeyTag { + return [NSString stringWithFormat:@"%@.ECPublicTag.",[[NSBundle bundleForClass:[self class]] bundleIdentifier]]; +} + ++ (NSString *)ecPrimeRandomPublicKeyTag { + return [NSString stringWithFormat:@"%@.ECPrimeRandomPublicTag.",[[NSBundle bundleForClass:[self class]] bundleIdentifier]]; +} + ++ (NSString*)rsaPrivateKeyTag { return [NSString stringWithFormat:@"%@.RSAPrivateTag.",[[NSBundle bundleForClass:[self class]] bundleIdentifier]]; } -+ (NSString*)certTag { ++ (NSString*)ecPrivateKeyTag { + return [NSString stringWithFormat:@"%@.ECPrivateTag.",[[NSBundle bundleForClass:[self class]] bundleIdentifier]]; +} + ++ (NSString*)ecPrimeRandomPrivateKeyTag { + return [NSString stringWithFormat:@"%@.ECPrimeRandomPrivateTag.",[[NSBundle bundleForClass:[self class]] bundleIdentifier]]; +} + ++ (NSString*)rsaCertTag { return [NSString stringWithFormat:@"%@.RSACertTag.",[[NSBundle bundleForClass:[self class]] bundleIdentifier]]; } ++ (NSString*)ecCertTag { + return [NSString stringWithFormat:@"%@.ECCertTag.",[[NSBundle bundleForClass:[self class]] bundleIdentifier]]; +} + ++ (NSString *)ecPrimeRandomCertTag { + return [NSString stringWithFormat:@"%@.ECPrimeRandomCertTag.",[[NSBundle bundleForClass:[self class]] bundleIdentifier]]; +} + + (NSString*)base64Encode:(NSData*)data { return [data base64EncodedStringWithOptions:kNilOptions]; } @@ -131,6 +155,57 @@ + (BOOL)deleteAsymmetricKeysWithPublicTag:(NSString*)publicTag privateTag:(NSStr return status; } ++ (BOOL)deleteAsymmetricKeysWithPublicTag:(NSString*)publicTag privateTag:(NSString*)privateTag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + + BOOL status = YES; + + if (keyAlgorithmType == KeyAlgorithmTypeUnknown) { + AWSDDLogError(@"Cannot not delete keys of unknown algorithm type"); + return NO; + } + CFStringRef keyType = [self getKeyTypeFromKeyAlgorithmType:keyAlgorithmType]; + if (keyType == NULL) { + AWSDDLogError(@"Could not determine a valid kSecAttrKeyType"); + return NO; + } + + OSStatus sanityCheck = noErr; + NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init]; + NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init]; + + [queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; + [queryPublicKey setObject:publicTag forKey:(id)kSecAttrApplicationTag]; + [queryPublicKey setObject:(__bridge id)keyType forKey:(id)kSecAttrKeyType]; + [queryPublicKey setObject:(id)kSecAttrKeyClassPublic forKey:(id)kSecAttrKeyClass]; + + [queryPrivateKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; + [queryPrivateKey setObject:privateTag forKey:(id)kSecAttrApplicationTag]; + [queryPrivateKey setObject:(__bridge id)keyType forKey:(id)kSecAttrKeyType]; + [queryPrivateKey setObject:(id)kSecAttrKeyClassPrivate forKey:(id)kSecAttrKeyClass]; + + sanityCheck = SecItemDelete((CFDictionaryRef)queryPrivateKey); + if (sanityCheck != noErr) { + if (sanityCheck == errSecItemNotFound) { + AWSDDLogError(@"Error removing private key errSecItemNotFound"); + } else { + AWSDDLogError(@"Error removing private key, OSStatus == %d.", (int)sanityCheck); + status = NO; + } + } + + sanityCheck = SecItemDelete((CFDictionaryRef)queryPublicKey); + if (sanityCheck != noErr) { + if (sanityCheck == errSecItemNotFound) { + AWSDDLogError(@"Error removing public key: errSecItemNotFound"); + } else { + AWSDDLogError(@"Error removing public key, OSStatus == %d.", (int)sanityCheck); + status = NO; + } + } + + return status; +} + + (BOOL)isValidCertificate:(NSString*)privateKeyTag certificateLabel:(NSString*)certificateLabel { SecIdentityRef identityRef = [AWSIoTKeychain getIdentityRef:privateKeyTag certificateLabel:certificateLabel]; @@ -147,6 +222,25 @@ + (BOOL)isValidCertificate:(NSString*)privateKeyTag certificateLabel:(NSString*) return NO; } ++ (BOOL)isValidCertificate:(NSString*)privateKeyTag certificateLabel:(NSString*)certificateLabel keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + + SecIdentityRef identityRef = [AWSIoTKeychain getIdentityRef:privateKeyTag certificateLabel:certificateLabel keyAlgorithmType:keyAlgorithmType]; + if (identityRef) { + SecCertificateRef cert = NULL; + OSStatus status = SecIdentityCopyCertificate(identityRef, &cert); + CFRelease(identityRef); + if (status == noErr) { + if (cert) { + CFRelease(cert); + } + return YES; + } else { + AWSDDLogError(@"SecIdentityCopyCertificate failed [%d]", (int)status); + } + } + return NO; +} + + (NSData *)certToDer:(NSString *)cert { if ([cert rangeOfString:AWSIoTKeychainStartCertKeyTag].location != NSNotFound) { @@ -160,7 +254,7 @@ + (NSData *)certToDer:(NSString *)cert { } + (BOOL)addCertificateToKeychain:(NSString*)cert { - return [AWSIoTKeychain addCertificateToKeychain:cert tag:[AWSIoTKeychain certTag]]; + return [AWSIoTKeychain addCertificateToKeychain:cert tag:[AWSIoTKeychain rsaCertTag]]; } + (BOOL)addCertificateToKeychain:(NSString*)cert tag:(NSString*)tag { @@ -196,7 +290,7 @@ + (BOOL)addCertificateFromPemFile:(NSString*)fileName withTag:(NSString*)tag { } + (BOOL)addCertificate:(NSData *)cert { - return [AWSIoTKeychain addCertificate:cert withTag:[AWSIoTKeychain certTag]]; + return [AWSIoTKeychain addCertificate:cert withTag:[AWSIoTKeychain rsaCertTag]]; } + (BOOL)addCertificate:(NSData*)cert withTag:(NSString*)tag { @@ -212,7 +306,7 @@ + (BOOL)addCertificate:(NSData*)cert withTag:(NSString*)tag { } + (BOOL)addCertificateRef:(SecCertificateRef)certRef { - return [AWSIoTKeychain addCertificateRef:certRef tag:[AWSIoTKeychain certTag]]; + return [AWSIoTKeychain addCertificateRef:certRef tag:[AWSIoTKeychain rsaCertTag]]; } + (BOOL)addCertificateRef:(SecCertificateRef)certRef tag:(NSString*)tag { @@ -235,7 +329,7 @@ + (BOOL)removeCertificate { NSMutableDictionary * queryCertificate = [[NSMutableDictionary alloc] init]; [queryCertificate setObject:(id)kSecClassCertificate forKey:(id)kSecClass]; - [queryCertificate setObject:[AWSIoTKeychain certTag] forKey:(id)kSecAttrLabel]; + [queryCertificate setObject:[AWSIoTKeychain rsaCertTag] forKey:(id)kSecAttrLabel]; OSStatus sanityCheck = SecItemDelete((CFDictionaryRef)queryCertificate); if (sanityCheck != noErr) { @@ -290,6 +384,33 @@ + (SecKeyRef)getPublicKeyRef:(NSString*)tag { return publicKeyReference; } ++ (SecKeyRef)getPublicKeyRef:(NSString*)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + OSStatus sanityCheck = noErr; + SecKeyRef publicKeyReference = NULL; + + CFStringRef keyType = [self getKeyTypeFromKeyAlgorithmType:keyAlgorithmType]; + if (keyType == NULL) { + AWSDDLogError(@"Could not determine a valid kSecAttrKeyType"); + return NULL; + } + + NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init]; + + [queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; + [queryPublicKey setObject:(id)kSecAttrKeyClassPublic forKey:(id)kSecAttrKeyClass]; + [queryPublicKey setObject:tag forKey:(id)kSecAttrApplicationTag]; + [queryPublicKey setObject:(__bridge id)keyType forKey:(id)kSecAttrKeyType]; + [queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef]; + + sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyReference); + + if (sanityCheck != noErr) { + AWSDDLogError(@"getPublicKeyRef error: %d",(int)sanityCheck); + } + + return publicKeyReference; +} + + (NSData *)getPublicKeyBits:(NSString*)tag { OSStatus sanityCheck = noErr; CFTypeRef publicKeyRef = NULL; @@ -311,6 +432,46 @@ + (NSData *)getPublicKeyBits:(NSString*)tag { return (__bridge_transfer NSData *)publicKeyRef; } +/** + * Retrieves the raw data (bits) of a public key from the keychain. + * + * This function finds a public key matching a specific + * application tag and algorithm type, and returns its raw data representation. + * + * @param tag The unique application tag used to store the key. + * @param keyAlgorithmType The algorithm type of the key. + * + * @return An `NSData` object containing the key's data on success, or `NULL` if the key is + * not found or an error occurs. + */ ++ (NSData *)getPublicKeyBits:(NSString*)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + OSStatus sanityCheck = noErr; + CFTypeRef publicKeyRef = NULL; + + CFStringRef keyType = [self getKeyTypeFromKeyAlgorithmType:keyAlgorithmType]; + if (keyType == NULL) { + AWSDDLogError(@"Could not determine a valid kSecAttrKeyType"); + return NULL; + } + + NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init]; + + [queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; + [queryPublicKey setObject:(id)kSecAttrKeyClassPublic forKey:(id)kSecAttrKeyClass]; + [queryPublicKey setObject:tag forKey:(id)kSecAttrApplicationTag]; + [queryPublicKey setObject:(__bridge id)keyType forKey:(id)kSecAttrKeyType]; + [queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData]; + + sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPublicKey, &publicKeyRef); + + if (sanityCheck != noErr){ + AWSDDLogError(@"getPublicKeyBits error: %d",(int)sanityCheck); + publicKeyRef = NULL; + } + + return (__bridge_transfer NSData *)publicKeyRef; +} + + (SecKeyRef)getPrivateKeyRef:(NSString*)tag { OSStatus sanityCheck = noErr; SecKeyRef privateKeyReference = NULL; @@ -332,6 +493,34 @@ + (SecKeyRef)getPrivateKeyRef:(NSString*)tag { return privateKeyReference; } ++ (SecKeyRef)getPrivateKeyRef:(NSString*)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + OSStatus sanityCheck = noErr; + SecKeyRef privateKeyReference = NULL; + + CFStringRef keyType = [self getKeyTypeFromKeyAlgorithmType:keyAlgorithmType]; + if (keyType == NULL) { + AWSDDLogError(@"Could not determine a valid kSecAttrKeyType"); + return NULL; + } + + NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init]; + + [queryPrivateKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; + [queryPrivateKey setObject:(id)kSecAttrKeyClassPrivate forKey:(id)kSecAttrKeyClass]; + [queryPrivateKey setObject:tag forKey:(id)kSecAttrApplicationTag]; + [queryPrivateKey setObject:(__bridge id)keyType forKey:(id)kSecAttrKeyType]; + [queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef]; + + sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKeyReference); + + if (sanityCheck != noErr) { + AWSDDLogError(@"getPrivateKeyRef error: %d",(int)sanityCheck); + privateKeyReference = NULL; + } + + return privateKeyReference; +} + + (NSData *)getPrivateKeyBits:(NSString*)tag { OSStatus sanityCheck = noErr; CFTypeRef privateKeyBits = NULL; @@ -353,6 +542,34 @@ + (NSData *)getPrivateKeyBits:(NSString*)tag { return (__bridge_transfer NSData *)privateKeyBits; } ++ (NSData *)getPrivateKeyBits:(NSString*)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + OSStatus sanityCheck = noErr; + CFTypeRef privateKeyBits = NULL; + + CFStringRef keyType = [self getKeyTypeFromKeyAlgorithmType:keyAlgorithmType]; + if (keyType == NULL) { + AWSDDLogError(@"Could not determine a valid kSecAttrKeyType"); + return NULL; + } + + NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init]; + + [queryPrivateKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; + [queryPrivateKey setObject:(id)kSecAttrKeyClassPrivate forKey:(id)kSecAttrKeyClass]; + [queryPrivateKey setObject:tag forKey:(id)kSecAttrApplicationTag]; + [queryPrivateKey setObject:(__bridge id)keyType forKey:(id)kSecAttrKeyType]; + [queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData]; + + sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPrivateKey, &privateKeyBits); + + if (sanityCheck != noErr){ + AWSDDLogError(@"getPrivateKeyBits error: %d",(int)sanityCheck); + privateKeyBits = NULL; + } + + return (__bridge_transfer NSData *)privateKeyBits; +} + + (SecIdentityRef)getIdentityRef:(NSString*)privateKeyTag certificateLabel:(NSString *)certificateLabel { OSStatus sanityCheck = noErr; SecIdentityRef identityRef = NULL; @@ -374,6 +591,33 @@ + (SecIdentityRef)getIdentityRef:(NSString*)privateKeyTag certificateLabel:(NSSt return identityRef; } ++ (SecIdentityRef)getIdentityRef:(NSString*)privateKeyTag certificateLabel:(NSString *)certificateLabel keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + OSStatus sanityCheck = noErr; + SecIdentityRef identityRef = NULL; + + CFStringRef keyType = [self getKeyTypeFromKeyAlgorithmType:keyAlgorithmType]; + if (keyType == NULL) { + AWSDDLogError(@"Could not determine a valid kSecAttrKeyType"); + return NULL; + } + + NSMutableDictionary * queryIdentityRef = [[NSMutableDictionary alloc] init]; + + [queryIdentityRef setObject:(id)kSecClassIdentity forKey:(id)kSecClass]; + [queryIdentityRef setObject:privateKeyTag forKey:(id)kSecAttrApplicationTag]; + [queryIdentityRef setObject:certificateLabel forKey:(id)kSecAttrLabel]; + [queryIdentityRef setObject:(__bridge id)keyType forKey:(id)kSecAttrKeyType]; + [queryIdentityRef setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef]; + + sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryIdentityRef, (CFTypeRef *)&identityRef); + if (sanityCheck != noErr){ + AWSDDLogError(@"getIdentityRef error: %d",(int)sanityCheck); + return nil; + } + + return identityRef; +} + + (BOOL)addPublicKeyRef:(SecKeyRef)pubkeyRef tag:(NSString*)tag { OSStatus sanityCheck = noErr; @@ -397,6 +641,34 @@ + (BOOL)addPublicKeyRef:(SecKeyRef)pubkeyRef tag:(NSString*)tag { return YES; } ++ (BOOL)addPublicKeyRef:(SecKeyRef)pubkeyRef tag:(NSString*)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + + OSStatus sanityCheck = noErr; + NSMutableDictionary * publicKeyAttr = [[NSMutableDictionary alloc] init]; + + CFStringRef keyType = [self getKeyTypeFromKeyAlgorithmType:keyAlgorithmType]; + if (keyType == NULL) { + AWSDDLogError(@"Could not determine a valid kSecAttrKeyType"); + return NO; + } + + [publicKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass]; + [publicKeyAttr setObject:(__bridge id)keyType forKey:(id)kSecAttrKeyType]; + [publicKeyAttr setObject:tag forKey:(id)kSecAttrApplicationTag]; + [publicKeyAttr setObject:(__bridge id _Nonnull)(pubkeyRef) forKey:(id)kSecValueRef]; + [publicKeyAttr setObject:(id)kSecAttrKeyClassPublic forKey:(id)kSecAttrKeyClass]; + [publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnPersistentRef]; + [publicKeyAttr setObject:(__bridge id)[AWSIoTKeychain accessibilityType] forKey:(id)kSecAttrAccessible]; + + sanityCheck = SecItemAdd((CFDictionaryRef) publicKeyAttr, nil); + if ((sanityCheck != noErr) && (sanityCheck != errSecDuplicateItem)){ + AWSDDLogError(@"addPublicKeyRef error: %d",(int)sanityCheck); + return NO; + } + + return YES; +} + + (BOOL)addPublicKey:(NSData*)pubkey tag:(NSString*)tag { OSStatus sanityCheck = noErr; @@ -421,6 +693,34 @@ + (BOOL)addPublicKey:(NSData*)pubkey tag:(NSString*)tag { return YES; } ++ (BOOL)addPublicKey:(NSData*)pubkey tag:(NSString*)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + + OSStatus sanityCheck = noErr; + + CFStringRef keyType = [self getKeyTypeFromKeyAlgorithmType:keyAlgorithmType]; + if (keyType == NULL) { + AWSDDLogError(@"Could not determine a valid kSecAttrKeyType"); + return NO; + } + + NSMutableDictionary * publicKeyAttr = [[NSMutableDictionary alloc] init]; + + [publicKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass]; + [publicKeyAttr setObject:(__bridge id)keyType forKey:(id)kSecAttrKeyType]; + [publicKeyAttr setObject:tag forKey:(id)kSecAttrApplicationTag]; + [publicKeyAttr setObject:pubkey forKey:(id)kSecValueData]; + [publicKeyAttr setObject:(id)kSecAttrKeyClassPublic forKey:(id)kSecAttrKeyClass]; + [publicKeyAttr setObject:(__bridge id)[AWSIoTKeychain accessibilityType] forKey:(id)kSecAttrAccessible]; + + sanityCheck = SecItemAdd((CFDictionaryRef) publicKeyAttr, nil); + if ((sanityCheck != noErr) && (sanityCheck != errSecDuplicateItem)){ + AWSDDLogError(@"addPublicKey error: %d",(int)sanityCheck); + return NO; + } + + return YES; +} + + (BOOL)addPrivateKeyRef:(SecKeyRef)privkeyRef tag:(NSString*)tag { OSStatus sanityCheck = noErr; @@ -443,6 +743,34 @@ + (BOOL)addPrivateKeyRef:(SecKeyRef)privkeyRef tag:(NSString*)tag { return YES; } ++ (BOOL)addPrivateKeyRef:(SecKeyRef)privkeyRef tag:(NSString*)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + + OSStatus sanityCheck = noErr; + NSMutableDictionary * privateKeyAttr = [[NSMutableDictionary alloc] init]; + + CFStringRef keyType = [self getKeyTypeFromKeyAlgorithmType:keyAlgorithmType]; + if (keyType == NULL) { + AWSDDLogError(@"Could not determine a valid kSecAttrKeyType"); + return NO; + } + + [privateKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass]; + [privateKeyAttr setObject:(__bridge id)keyType forKey:(id)kSecAttrKeyType]; + [privateKeyAttr setObject:tag forKey:(id)kSecAttrApplicationTag]; + [privateKeyAttr setObject:(__bridge id _Nonnull)(privkeyRef) forKey:(id)kSecValueRef]; + [privateKeyAttr setObject:(id)kSecAttrKeyClassPrivate forKey:(id)kSecAttrKeyClass]; + [privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnPersistentRef]; + [privateKeyAttr setObject:(__bridge id)[AWSIoTKeychain accessibilityType] forKey:(id)kSecAttrAccessible]; + + sanityCheck = SecItemAdd((CFDictionaryRef) privateKeyAttr, nil); + if ((sanityCheck != noErr) && (sanityCheck != errSecDuplicateItem)){ + AWSDDLogError(@"addPrivateKeyRef error: %d",(int)sanityCheck); + return NO; + } + + return YES; +} + + (BOOL)addPrivateKey:(NSData*)privkey tag:(NSString*)tag { OSStatus sanityCheck = noErr; @@ -467,6 +795,34 @@ + (BOOL)addPrivateKey:(NSData*)privkey tag:(NSString*)tag { return YES; } ++ (BOOL)addPrivateKey:(NSData*)privkey tag:(NSString*)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + + OSStatus sanityCheck = noErr; + + CFStringRef keyType = [self getKeyTypeFromKeyAlgorithmType:keyAlgorithmType]; + if (keyType == NULL) { + AWSDDLogError(@"Could not determine a valid kSecAttrKeyType"); + return NO; + } + + NSMutableDictionary * privateKeyAttr = [[NSMutableDictionary alloc] init]; + + [privateKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass]; + [privateKeyAttr setObject:(__bridge id)keyType forKey:(id)kSecAttrKeyType]; + [privateKeyAttr setObject:tag forKey:(id)kSecAttrApplicationTag]; + [privateKeyAttr setObject:privkey forKey:(id)kSecValueData]; + [privateKeyAttr setObject:(id)kSecAttrKeyClassPrivate forKey:(id)kSecAttrKeyClass]; + [privateKeyAttr setObject:(__bridge id)[AWSIoTKeychain accessibilityType] forKey:(id)kSecAttrAccessible]; + + sanityCheck = SecItemAdd((CFDictionaryRef) privateKeyAttr, nil); + if ((sanityCheck != noErr) && (sanityCheck != errSecDuplicateItem)){ + AWSDDLogError(@"addPrivateKey error: %d",(int)sanityCheck); + return NO; + } + + return YES; +} + + (BOOL)addPrivateKeyFromPemFile:(NSString*)fileName withTag:(NSString*)tag { if ([fileName hasSuffix:@".pem"]) { @@ -518,10 +874,175 @@ + (BOOL)deletePrivateKeyWithTag:(NSString*)tag { return YES; } ++ (BOOL)deletePrivateKeyWithTag:(NSString*)tag keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + + CFStringRef keyType = [self getKeyTypeFromKeyAlgorithmType:keyAlgorithmType]; + if (keyType == NULL) { + AWSDDLogError(@"Could not determine a valid kSecAttrKeyType"); + return NO; + } + + NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init]; + + [queryPrivateKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; + [queryPrivateKey setObject:(id)kSecAttrKeyClassPrivate forKey:(id)kSecAttrKeyClass]; + [queryPrivateKey setObject:tag forKey:(id)kSecAttrApplicationTag]; + [queryPrivateKey setObject:(__bridge id)keyType forKey:(id)kSecAttrKeyType]; + + OSStatus sanityCheck = SecItemDelete((CFDictionaryRef)queryPrivateKey); + if (sanityCheck != noErr) { + if (sanityCheck == errSecItemNotFound) { + AWSDDLogError(@"Error removing private key: errSecItemNotFound"); + } else { + AWSDDLogError(@"Error removing private key, OSStatus == %d.", (int)sanityCheck); + return NO; + } + } + + return YES; +} + + (void)setKeyChainAccessibility:(AWSIoTKeyChainAccessibility)accessibility { _accessibility = accessibility; } ++ (KeyAlgorithmType)getKeyAlgorithmTypeFromTag:(NSString *)keyTag { + if (!keyTag || keyTag.length == 0) { + return KeyAlgorithmTypeUnknown; + } + + if ([keyTag containsString:[AWSIoTKeychain rsaPublicKeyTag]] || [keyTag containsString:[AWSIoTKeychain rsaPrivateKeyTag]]) { + return KeyAlgorithmTypeRSA; + } + + else if ([keyTag containsString:[AWSIoTKeychain ecPublicKeyTag]] || [keyTag containsString:[AWSIoTKeychain ecPrivateKeyTag]]) { + return KeyAlgorithmTypeEC; + } + + else if ([keyTag containsString:[AWSIoTKeychain ecPrimeRandomPublicKeyTag]] || [keyTag containsString:[AWSIoTKeychain ecPrimeRandomPrivateKeyTag]]) { + return KeyAlgorithmTypeECPrimeRandom; + } + + return KeyAlgorithmTypeUnknown; +} + ++ (CFStringRef)getKeyTypeFromKeyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + switch (keyAlgorithmType) { + case KeyAlgorithmTypeRSA: + return kSecAttrKeyTypeRSA; + case KeyAlgorithmTypeEC: + return kSecAttrKeyTypeEC; + case KeyAlgorithmTypeECPrimeRandom: + return kSecAttrKeyTypeECSECPrimeRandom; + case KeyAlgorithmTypeUnknown: + return NULL; + default: + AWSDDLogError(@"Unhandled KeyAlgorithmType"); + return NULL; + } +} + +/** + * A convenience method that determines the Security framework key type constant directly from a keychain tag string. + * + * This function encapsulates the two-step process of first identifying the high-level + * algorithm type (e.g., RSA/EC) from the tag's format, and then converting that + * type into the specific CFStringRef constant (e.g., kSecAttrKeyTypeRSA) required + * by the Security framework for keychain queries. + * + * @param keyTag The keychain tag string to inspect (e.g., from `rsaPublicKeyTag` or `ecPrivateKeyTag`). + * + * @return The corresponding kSecAttrKeyType constant (e.g., kSecAttrKeyTypeRSA), + * or `NULL` if the tag does not match a known algorithm pattern. + */ ++ (CFStringRef)getKeyTypeFromKeyTag:(NSString *)keyTag { + + KeyAlgorithmType keyAlgorithmType = [self getKeyAlgorithmTypeFromTag:keyTag]; + + CFStringRef keyType = [self getKeyTypeFromKeyAlgorithmType:keyAlgorithmType]; + if (keyType == NULL) { + AWSDDLogError(@"Could not determine a valid kSecAttrKeyType from the key tag: '%@'", keyTag); + return NULL; + } + return keyType; +} + ++ (KeyAlgorithmType)getKeyAlgorithmTypeFromKeyRef:(SecKeyRef)keyRef { + if (keyRef == NULL) { + return KeyAlgorithmTypeUnknown; + } + + CFDictionaryRef attributes = SecKeyCopyAttributes(keyRef); + if (attributes == NULL) { + AWSDDLogError(@"Could not read attributes from the provided SecKeyRef."); + return KeyAlgorithmTypeUnknown; + } + + KeyAlgorithmType algorithmType = KeyAlgorithmTypeUnknown; + + CFStringRef keyType = CFDictionaryGetValue(attributes, kSecAttrKeyType); + if (keyType != NULL) { + if (CFEqual(keyType, kSecAttrKeyTypeRSA)) { + algorithmType = KeyAlgorithmTypeRSA; + } else if (CFEqual(keyType, kSecAttrKeyTypeEC)) { + algorithmType = KeyAlgorithmTypeEC; + } else if (CFEqual(keyType, kSecAttrKeyTypeECSECPrimeRandom)) { + algorithmType = KeyAlgorithmTypeECPrimeRandom; + } + } + CFRelease(attributes); + + return algorithmType; +} + ++ (NSString *)getCertificateTagFromKeyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + switch (keyAlgorithmType) { + case KeyAlgorithmTypeRSA: + return [self rsaCertTag]; + case KeyAlgorithmTypeEC: + return [self ecCertTag]; + case KeyAlgorithmTypeECPrimeRandom: + return [self ecPrimeRandomCertTag]; + case KeyAlgorithmTypeUnknown: + return NULL; + default: + AWSDDLogError(@"Unhandled KeyAlgorithmType"); + return NULL; + } +} + ++ (NSString *)getPublicKeyTagFromKeyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + switch (keyAlgorithmType) { + case KeyAlgorithmTypeRSA: + return [self rsaPublicKeyTag]; + case KeyAlgorithmTypeEC: + return [self ecPublicKeyTag]; + case KeyAlgorithmTypeECPrimeRandom: + return [self ecPrimeRandomPublicKeyTag]; + case KeyAlgorithmTypeUnknown: + return NULL; + default: + AWSDDLogError(@"Unhandled KeyAlgorithmType"); + return NULL; + } +} + ++ (NSString *)getPrivateKeyTagFromKeyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType { + switch (keyAlgorithmType) { + case KeyAlgorithmTypeRSA: + return [self rsaPrivateKeyTag]; + case KeyAlgorithmTypeEC: + return [self ecPrivateKeyTag]; + case KeyAlgorithmTypeECPrimeRandom: + return [self ecPrimeRandomPrivateKeyTag]; + case KeyAlgorithmTypeUnknown: + return NULL; + default: + AWSDDLogError(@"Unhandled KeyAlgorithmType"); + return NULL; + } +} + // The following keys are deprecated, but they still need to be supported: // - AWSIoTKeyChainAccessibilityAlways, kSecAttrAccessibleAlways, // - AWSIoTKeyChainAccessibilityAlwaysThisDeviceOnly, kSecAttrAccessibleAlwaysThisDeviceOnly diff --git a/AWSIoT/Internal/AWSIoTMQTTClient.h b/AWSIoT/Internal/AWSIoTMQTTClient.h index 6ebe3349c7f..0d848217cf5 100644 --- a/AWSIoT/Internal/AWSIoTMQTTClient.h +++ b/AWSIoT/Internal/AWSIoTMQTTClient.h @@ -18,6 +18,7 @@ #import "AWSIoTDataManager.h" #import "AWSSRWebSocket.h" #import "AWSIoTMQTTTypes.h" +#import "AWSIoTKeychain.h" @interface AWSIoTMQTTTopicModel : NSObject @property (nonatomic, strong) NSString *topic; @@ -125,6 +126,19 @@ willRetainFlag:(BOOL)willRetainFlag statusCallback:(void (^)(AWSIoTMQTTStatus status))callback; +- (BOOL)connectWithClientId:(NSString *)clientId + toHost:(NSString *)host + port:(UInt32)port + cleanSession:(BOOL)cleanSession + certificateId:(NSString *)certificateId + keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType + keepAlive:(UInt16)theKeepAliveInterval + willTopic:(NSString*)willTopic + willMsg:(NSData*)willMsg + willQoS:(UInt8)willQoS + willRetainFlag:(BOOL)willRetainFlag + statusCallback:(void (^)(AWSIoTMQTTStatus status))callback; + - (BOOL)connectWithClientId:(NSString *)clientId cleanSession:(BOOL)cleanSession configuration:(AWSServiceConfiguration *)configuration diff --git a/AWSIoT/Internal/AWSIoTMQTTClient.m b/AWSIoT/Internal/AWSIoTMQTTClient.m index 8cce64a6446..cbf95d8f495 100644 --- a/AWSIoT/Internal/AWSIoTMQTTClient.m +++ b/AWSIoT/Internal/AWSIoTMQTTClient.m @@ -267,7 +267,7 @@ - (BOOL)connectWithClientId:(NSString*)clientId self.userDidIssueConnect = YES; self.session = nil; - NSString *privateKeyTag = [NSString stringWithFormat:@"%@%@",[AWSIoTKeychain privateKeyTag], certificateId]; + NSString *privateKeyTag = [NSString stringWithFormat:@"%@%@",[AWSIoTKeychain rsaPrivateKeyTag], certificateId]; NSString *certificateLabel = [AWSIoTManager certTagWithCertificateId:certificateId]; SecIdentityRef identityRef = [AWSIoTKeychain getIdentityRef:privateKeyTag certificateLabel:certificateLabel]; @@ -291,6 +291,64 @@ - (BOOL)connectWithClientId:(NSString*)clientId return [self connectWithCert]; } +- (BOOL)connectWithClientId:(NSString*)clientId + toHost:(NSString*)host + port:(UInt32)port + cleanSession:(BOOL)cleanSession + certificateId:(NSString*)certificateId + keyAlgorithmType:(KeyAlgorithmType)keyAlgorithmType + keepAlive:(UInt16)theKeepAliveInterval + willTopic:(NSString*)willTopic + willMsg:(NSData*)willMsg + willQoS:(UInt8)willQoS + willRetainFlag:(BOOL)willRetainFlag + statusCallback:(void (^)(AWSIoTMQTTStatus status))callback { + + if (self.userDidIssueConnect ) { + //Issuing connect multiple times. Not allowed. + return NO; + } + //Intialize connection state + self.userDidIssueDisconnect = NO; + self.userDidIssueConnect = YES; + self.session = nil; + + NSString *privateTagPrefix = [AWSIoTKeychain getPrivateKeyTagFromKeyAlgorithmType:keyAlgorithmType]; + if (!privateTagPrefix) { + AWSDDLogError(@"Unable to get private key tag prefix"); + return NO; + } + + NSString *certTagPrefix = [AWSIoTKeychain getCertificateTagFromKeyAlgorithmType:keyAlgorithmType]; + if (!certTagPrefix) { + AWSDDLogError(@"Unable to get certificate tag prefix"); + return NO; + } + + NSString *privateKeyTag = [privateTagPrefix stringByAppendingString:certificateId]; + NSString *certificateLabel = [certTagPrefix stringByAppendingString:certificateId]; + + SecIdentityRef identityRef = [AWSIoTKeychain getIdentityRef:privateKeyTag certificateLabel:certificateLabel keyAlgorithmType:keyAlgorithmType]; + if (identityRef == NULL) { + AWSDDLogError(@"Could not find SecIdentityRef"); + return NO; + }; + self.mqttStatus = AWSIoTMQTTStatusConnecting; + self.clientCerts = [[NSArray alloc] initWithObjects:(__bridge_transfer id)identityRef, nil]; + self.host = host; + self.port = port; + self.cleanSession = cleanSession; + self.connectStatusCallback = callback; + self.clientId = clientId; + self.keepAliveInterval = theKeepAliveInterval; + self.lastWillAndTestamentTopic = willTopic; + self.lastWillAndTestamentMessage = willMsg; + self.lastWillAndTestamentQoS = willQoS; + self.lastWillAndTestamentRetainFlag = willRetainFlag; + + return [self connectWithCert]; +} + - (BOOL) connectWithCert { self.mqttStatus = AWSIoTMQTTStatusConnecting;