├── .DS_Store ├── .gitattributes ├── Classes ├── .DS_Store ├── ECDHAlgorithmSwift.swift └── Tool │ ├── Constants.swift │ ├── DataTool.swift │ ├── EcdhCharacterExtension.swift │ ├── EcdhDataExtension.swift │ └── EcdhStringExtension.swift ├── ECDHAlgorithmSwift.podspec ├── LICENSE └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Json031/ECDHAlgorithmSwift/f6afffdee0deb38d519fe68fcf695520a26e62f8/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-vendored 2 | *.swift linguist-vendored=false 3 | -------------------------------------------------------------------------------- /Classes/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Json031/ECDHAlgorithmSwift/f6afffdee0deb38d519fe68fcf695520a26e62f8/Classes/.DS_Store -------------------------------------------------------------------------------- /Classes/ECDHAlgorithmSwift.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ECDHAlgorithmSwift.swift 3 | // 4 | // Created by MorganChen on 2025/3/26. 5 | // 6 | 7 | import ECDHAlgorithmiOS 8 | 9 | class ECDHAlgorithmSwift: NSObject { 10 | 11 | private var sharedKey: Data? 12 | private var gmellipticCurveCrypto: GMEllipticCurveCrypto? 13 | 14 | private var gmellipticCurve: GMEllipticCurve = defaultGMEllipticCurve { 15 | didSet { 16 | self.gmellipticCurveCrypto = GMEllipticCurveCrypto.generateKeyPair(for: self.gmellipticCurve) 17 | } 18 | } 19 | 20 | //If false, the public key is 65 bytes, with the first byte being 04. If true, the public key is 32 bytes 21 | private var compressedPublicKey: Bool = false { 22 | didSet { 23 | self.gmellipticCurveCrypto?.compressedPublicKey = self.compressedPublicKey 24 | } 25 | } 26 | 27 | static let shared = ECDHAlgorithmSwift() 28 | 29 | public func resetGMEllipticCurveCrypto() { 30 | self.gmellipticCurveCrypto = nil 31 | } 32 | 33 | 34 | /// generate a key pair (64 bytes for public key and 32 bytes for private key) 35 | /// - Parameters: 36 | /// - gmellipticCurve: GMEllipticCurveSecp256r1 37 | /// - compressedPublicKey: If false, the public key is 65 bytes, with the first byte being 04. If true, the public key is 32 bytes 38 | public func generateKeys(gmellipticCurve: GMEllipticCurve, compressedPublicKey: Bool) { 39 | self.gmellipticCurve = gmellipticCurve; 40 | self.compressedPublicKey = compressedPublicKey; 41 | } 42 | 43 | /// Obtain the public key and send it to the other party, the other party can get the share key with this public key 44 | public func getPublicKey() -> Data { 45 | if (self.gmellipticCurveCrypto == nil) { 46 | self.generateKeys(gmellipticCurve: defaultGMEllipticCurve, compressedPublicKey: defaultCompressedPublicKey) 47 | } 48 | return DataTool.clipData(data: self.gmellipticCurveCrypto!.publicKey, start: 1, endNotInclude: self.gmellipticCurveCrypto!.publicKey.count) 49 | } 50 | 51 | 52 | 53 | /// After sending one's own public key to the other party, the other party (server or device) returns the processing of the public key 54 | /// Wait for the other party to notify and send its public key, call configThirdPublicKey to generate a share key by combining the other party's public key with its own private key 55 | /// - Parameter otherPKStr: the other party public key 56 | public func generateSharedKeyWithOtherPK(otherPKStr: String) { 57 | if (self.gmellipticCurveCrypto == nil) { 58 | generateKeys(gmellipticCurve: defaultGMEllipticCurve, compressedPublicKey: defaultCompressedPublicKey); 59 | } 60 | // attach a 0x04 byte to the first byte of the received public key, call the compressPublicKey method to compress it into a 32-bit public key, and then pass it to the sharedSecretForPublicKey method to obtain the shared key 61 | var pData = Data.data(byte: CurveCryptoECDHKeyCompressType.notCompress.rawValue) 62 | guard let pkData: Data = otherPKStr.hexStrToData() else { 63 | return 64 | } 65 | pData.append(pkData) 66 | 67 | let compressPublicKey = self.gmellipticCurveCrypto?.compressPublicKey(pData) 68 | //Generate a share key by combining the other party's public key with their own private key 69 | self.sharedKey = self.gmellipticCurveCrypto?.sharedSecret(forPublicKey: compressPublicKey) 70 | //send sharedKey to device or server for verification 71 | } 72 | 73 | /// A shared key generated from the public key of another party (server or device) of 64 bytes and its own private key of 32 bytes 74 | public func getSharedKey() -> Data? { 75 | if (self.gmellipticCurveCrypto == nil) { 76 | generateKeys(gmellipticCurve: defaultGMEllipticCurve, compressedPublicKey: defaultCompressedPublicKey); 77 | } 78 | return self.sharedKey; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Classes/Tool/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // 4 | // Created by MorganChen on 2025/3/26. 5 | // 6 | 7 | import ECDHAlgorithmiOS 8 | 9 | //Default Settings 10 | let defaultGMEllipticCurve: GMEllipticCurve = GMEllipticCurveSecp256r1 11 | let defaultCompressedPublicKey: Bool = true 12 | 13 | enum CurveCryptoECDHKeyCompressType: UInt8 { 14 | case notCompress = 0x04 15 | } 16 | -------------------------------------------------------------------------------- /Classes/Tool/DataTool.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataTool.swift 3 | // 4 | // Created by MorganChen on 2025/3/26. 5 | // 6 | 7 | class DataTool: NSObject { 8 | 9 | class func clipData(data: Data, start: Int, endNotInclude: Int) -> Data { 10 | if start < 0 { 11 | return data 12 | } 13 | if data.count < endNotInclude { 14 | return data 15 | } 16 | return data.subdata(in: start.. 0x238C 12 | } 13 | 14 | /// Checks if the scalars will be merged into an emoji 15 | var combinedIntoEmoji: Bool { unicodeScalars.count > 1 && unicodeScalars.first?.properties.isEmoji ?? false } 16 | 17 | var isEmojiChar: Bool { containEmoji || combinedIntoEmoji } 18 | } 19 | -------------------------------------------------------------------------------- /Classes/Tool/EcdhDataExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EcdhDataExtension.swift 3 | // 4 | // Created by MorganChen on 2025/3/26. 5 | // 6 | 7 | extension Data { 8 | 9 | static func data(byte: UInt8) -> Data { 10 | return Data(bytes: [byte], count: 1) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Classes/Tool/EcdhStringExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EcdhStringExtension.swift 3 | // 4 | // Created by MorganChen on 2025/3/26. 5 | // 6 | extension String { 7 | 8 | /// hexStrToData 9 | /// - Returns: Data 10 | func hexStrToData() -> Data? { 11 | let data = NSMutableData(capacity: self.count) 12 | let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive) 13 | regex.enumerateMatches(in: self, options: [], range: NSMakeRange(0, self.count)) { match, flags, stop in 14 | let byteString = (self as NSString).substring(with: match!.range) 15 | var num = UInt8(byteString, radix: 16) 16 | data?.append(&num, length: 1) 17 | } 18 | return data as Data? 19 | } 20 | 21 | 22 | } 23 | 24 | -------------------------------------------------------------------------------- /ECDHAlgorithmSwift.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint ECDHAlgorithmSwift.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |spec| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | spec.name = "ECDHAlgorithmSwift" 19 | spec.version = "1.0.23" 20 | spec.summary = "A key exchange protocol algorithm based on elliptic curve cryptography for Swift." 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | spec.description = <<-DESC 28 | 29 | A key exchange protocol algorithm based on elliptic curve cryptography for iOS. 30 | 31 | The process of exchanging symmetric keys using ECDH asymmetric encryption method: 32 | 1 Connect the device, obtain CBService, obtain CBCharacteristic, subscribe to CBCharacteristic Notify: peripheral.setNotifyValue(true, for: characteristic) 33 | 2 Call method generateKeys to generate a key pair (64 bytes for public key and 32 bytes for private key) 34 | 3 Call the sendPublicKey method to convert step 2 Send the public key to the other party 35 | 4 Wait for the other party to notify and send its public key, call configThirdPublicKey to generate a share key from the other party's public key and its own private key, and the other party also uses step 3 The received public key and its private key generate a share key, and the two share keys are the same 36 | Send the share key to the backend server, and if the backend server verifies that both are the same, it indicates successful authentication 37 | 38 | DESC 39 | 40 | spec.homepage = "https://github.com/Json031/ECDHAlgorithmSwift" 41 | # spec.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 42 | 43 | 44 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 45 | # 46 | # Licensing your code is important. See https://choosealicense.com for more info. 47 | # CocoaPods will detect a license file if there is a named LICENSE* 48 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 49 | # 50 | 51 | # spec.license = "MIT (example)" 52 | spec.license = { :type => "MIT", :file => "LICENSE" } 53 | 54 | 55 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 56 | # 57 | # Specify the authors of the library, with email addresses. Email addresses 58 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 59 | # accepts just a name if you'd rather not provide an email address. 60 | # 61 | # Specify a social_media_url where others can refer to, for example a twitter 62 | # profile URL. 63 | # 64 | 65 | spec.author = { "MorganChen" => "cjfmail@foxmail.com" } 66 | # Or just: spec.author = "Rate" 67 | # spec.authors = { "Rate" => "cjfmail@foxmail.com" } 68 | # spec.social_media_url = "https://twitter.com/Rate" 69 | 70 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 71 | # 72 | # If this Pod runs only on iOS or OS X, then specify the platform and 73 | # the deployment target. You can optionally include the target after the platform. 74 | # 75 | 76 | # spec.platform = :ios 77 | spec.platform = :ios, "12.0" 78 | 79 | # When using multiple platforms 80 | # spec.ios.deployment_target = "5.0" 81 | # spec.osx.deployment_target = "10.7" 82 | # spec.watchos.deployment_target = "2.0" 83 | # spec.tvos.deployment_target = "9.0" 84 | 85 | 86 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 87 | # 88 | # Specify the location from where the source should be retrieved. 89 | # Supports git, hg, bzr, svn and HTTP. 90 | # 91 | 92 | spec.source = { :git => "https://github.com/Json031/ECDHAlgorithmSwift.git", :tag => "#{spec.version}" } 93 | 94 | 95 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 96 | # 97 | # CocoaPods is smart about how it includes source code. For source files 98 | # giving a folder will include any swift, h, m, mm, c & cpp files. 99 | # For header files it will include any header in the folder. 100 | # Not including the public_header_files will make all headers public. 101 | # 102 | 103 | spec.source_files = "Classes", "Classes/**/*.{h,m,swift}" 104 | spec.exclude_files = "Classes/Exclude" 105 | 106 | 107 | 108 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 109 | # 110 | # A list of resources included with the Pod. These are copied into the 111 | # target bundle with a build phase script. Anything else will be cleaned. 112 | # You can preserve files from being cleaned, please don't preserve 113 | # non-essential files like tests, examples and documentation. 114 | # 115 | 116 | # spec.resource = "icon.png" 117 | # spec.resources = "Resources/*.png" 118 | 119 | # spec.preserve_paths = "FilesToSave", "MoreFilesToSave" 120 | 121 | 122 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 123 | # 124 | # Link your library with frameworks, or libraries. Libraries do not include 125 | # the lib prefix of their name. 126 | # 127 | 128 | # spec.framework = "SomeFramework" 129 | # spec.frameworks = "SomeFramework", "AnotherFramework" 130 | 131 | # spec.library = "iconv" 132 | # spec.libraries = "iconv", "xml2" 133 | 134 | spec.swift_versions = '5.0' 135 | 136 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 137 | # 138 | # If your library depends on compiler flags you can set them in the xcconfig hash 139 | # where they will only apply to your library. If you depend on other Podspecs 140 | # you can include multiple dependencies to ensure it works. 141 | 142 | # spec.requires_arc = true 143 | 144 | # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 145 | spec.dependency "ECDHAlgorithmiOS" 146 | 147 | 148 | end 149 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2025 Morgan Chen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Click here to go to the Objective-C version

2 | 3 | # ECDHAlgorithmSwift 4 | [![CocoaPods](https://img.shields.io/cocoapods/v/ECDHAlgorithmSwift.svg)](https://cocoapods.org/pods/ECDHAlgorithmSwift) 5 | ![Swift 5](https://img.shields.io/badge/Swift-5.0-orange.svg) 6 | [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/Json031/ECDHAlgorithmSwift/blob/main/LICENSE) 7 |
8 | 一种基于椭圆曲线密码学的Swift密钥交换协议算法开源项目。 9 |
An open-source project for Swift key exchange protocol algorithm based on elliptic curve cryptography. 10 | 11 | # Installation 安装: 12 | 13 | ## CocoaPods 14 | The [ECDHAlgorithmSwift SDK for iOS](https://github.com/Json031/ECDHAlgorithmSwift) is available through [CocoaPods](http://cocoapods.org). If CocoaPods is not installed, install it using the following command. Note that Ruby will also be installed, as it is a dependency of Cocoapods. 15 | ```bash 16 | brew install cocoapods 17 | pod setup 18 | ``` 19 | 20 | ```bash 21 | $iOSVersion = '11.0' 22 | 23 | platform :ios, $iOSVersion 24 | use_frameworks! 25 | 26 | target 'YourProjectName' do 27 | pod 'ECDHAlgorithmSwift' # Full version with all features 28 | end 29 | ``` 30 | 31 | ## 手动安装 manual install 32 | 将Classes文件夹拽入项目中,OC项目还需要桥接 33 |
Drag the Classes folder into the project, OC project still needs bridging 34 | 35 | # ECDH算法 36 | 37 | ECDH非对称加密方式交换对称密钥流程: 38 |
1️⃣调用方法generateKeys生成密钥对(公钥64字节和私钥32字节) 39 |
2️⃣通过蓝牙或http方式,将步骤1️⃣生成的公钥发给对方 40 |
3️⃣等待对方蓝牙Notify等方式发送它的公钥过来,调用configThirdPublicKey将对方的公钥和自己的私钥生成share key,对方也通过步骤2️⃣接收到的公钥与其私钥生成share key,根据椭圆曲线点乘的交换性原理这两个share key是一样的 41 |
4️⃣将共享密钥发送到后端服务器,如果后端服务器验证两者相同,则表示身份验证成功; 42 |
可用于双方身份验证及绑定关联; 43 |
还可以作为后续通信过程的数据对称加密算法的密钥,基于椭圆曲线离散对数问题的困难性,使得攻击者难以从公开的通信信息中获取共享密钥,确保了通信数据安全性; 44 | 45 | The process of exchanging symmetric keys using ECDH asymmetric encryption method: 46 |
1 Call method generateKeys to generate a key pair (64 bytes for public key and 32 bytes for private key). 47 |
2 Call the sendPublicKey method to convert step 2 Send the public key to the other party. 48 |
3 Wait for the other party to notify and send its public key, call configThirdPublicKey to generate a share key from the other party's public key and its own private key, and the other party also uses step 3 The received public key and its private key generate a share key, and the two share keys are the same. 49 |
4 Send the share key to the backend server, and if the backend server verifies that both are the same, it indicates successful authentication. 50 |
Can be used for mutual authentication and binding association; 51 |
It can also serve as a key for data symmetric encryption algorithms in subsequent communication processes to ensure the security of communication data; 52 | 53 | # ECDH asymmetric encryption example: 54 | ✳️Param: set compressedPublicKey = false 55 | 56 | 1️⃣GMEllipticCurveCrypto1 generateKeyPair: 57 |
publicKey1: "d4b78cec17668f06ae96943d71049c7f75a620cb50b6facff9bdb09a174f7a808c22f0e51f1b2578e9fd7682be17fb8e07deb6517b68880273baee7fc4d6efdd" 58 |
privateKey1: "jCfYOOEE+t2BHvUHjp1O0RObXhND7JLV9BaHGR1XDZE=" 59 | 60 | 2️⃣GMEllipticCurveCrypto2 generateKeyPair: 61 |
publicKey2: "79cff9b55e086234c43f5c64a775eb20f39c7dc11bf3b2962677d6019c42af5cf57d6d5007fa7ccc94bddec7b1b8fdbf68e50642de88b7223e40007602290e50" 62 |
privateKey2: "ISfGAyQrHKX4ELRoZLls3TqBXVf7yqoahEgj7RMX0Us=" 63 | 64 | 3️⃣generateSharedKeyWithOtherPK 65 |
sharedKey1 = GMEllipticCurveCrypto1.sharedSecret(forPublicKey: publicKey2) 66 |
sharedKey2 = GMEllipticCurveCrypto2.sharedSecret(forPublicKey: publicKey1) 67 | 68 | ✅Result: sharedKey1 should equal to sharedKey2 69 |
sharedKey1: "2fd727d984828a28ab6a521f53dd2d06c67fbb80104aef8c1369a9e352094424" 70 |
sharedKey2: "2fd727d984828a28ab6a521f53dd2d06c67fbb80104aef8c1369a9e352094424" 71 | 72 | # Troubleshooting 73 | 74 |
75 | Missing sharedKey,Please obtain the public key from a third party first, and then pass it to the sharedScreetForPublicKey method of GMEllipticCurveCrypto to generate the share key 76 | 77 | Need to obtain the public key from a third party first, then go to generateSharedKeyWithOtherPK. 78 | 79 |
80 | 81 |
82 | 83 | # License 84 | This library is licensed under the [MIT License](https://github.com/Json031/ECDHAlgorithmSwift/blob/main/LICENSE). 85 | --------------------------------------------------------------------------------