Heimdall 2.0.1

Heimdall 2.0.1

测试已测试
Lang语言 SwiftSwift
许可证 MIT
发布最后发布2018年9月
SPM支持 SPM

Henri NormakJory Stiefel 维护。



Heimdall 2.0.1

Heimdall Helmet

Build Status CocoaPods compatible Carthage compatible

Heimdall

在北欧神话中,Heimdall 是 Bifröst 的守护者,Bifröst 是连接人类居住地 Midgard 和众神之域 Asgard 的彩虹桥。

在 iOS 中,Heimdall 作为安全框架和 UI 之间的守护者,提供了围绕加密、解密、签名和验证消息的 C API 的优秀包装。

此外,Heimdall 还帮助维护 Keychain 中的公钥-私钥 RSA 密钥对,允许同时创建和删除密钥对。

要求

Heimdall 需要 Swift 3 并仅与 Xcode 8 及以上版本兼容。

安装

CocoaPods

Heimdall可以作为CocoaPod使用,只需在Podfile中添加以下行

pod 'Heimdall', '~> 1.1.0'

此外,请确保您的Podfile包含以下行,这是支持Swift框架所必需的

use_frameworks!

Carthage

只需在Cartfile中包含以下行

github "henrinormak/Heimdall"

请注意,Heimdall在Carthage构建目录中生成两个框架 - Heimdall.frameworkCommonCrypto.framework,您只需将 Heimdall.framework 包含/嵌入到您的项目中。

子项目

由于Heimdall使用了CommonCrypto并将其包装在伪模块中,因此使用Heimdall最简单的方法是将整个项目作为子项目包含到您的工作空间中。

要执行此操作,请将位于Heimdall文件夹中的Heimdall.xcodeproj包含到您的Xcode工作空间中。然后指定Heimdall目标作为您主应用程序目标的目标依赖项

Target Dependency selection in Xcode

最后,确保在Xcode中Heimdall列在嵌入的可执行文件部分

Embedded Binaries under application target settings

直接

虽然不建议这样做,但您也可以直接添加Heimdall,只需在您的项目中包含Heimdall.swift即可。

由于Heimdall使用了CommonCrypto,您还需要为以下脚本添加构建阶段,在编译Heimdall.swift之前必须执行此脚本

modulesDirectory=$DERIVED_FILES_DIR/modules
modulesMap=$modulesDirectory/module.modulemap
modulesMapTemp=$modulesDirectory/module.modulemap.tmp

mkdir -p "$modulesDirectory"

cat > "$modulesMapTemp" << MAP
module CommonCrypto [system] {
    header "$SDKROOT/usr/include/CommonCrypto/CommonCrypto.h"
    export *
}
MAP

diff "$modulesMapTemp" "$modulesMap" >/dev/null 2>/dev/null
if [[ $? != 0 ]] ; then
    mv "$modulesMapTemp" "$modulesMap"
else
    rm "$modulesMapTemp"
fi

此外,将以下路径($(DERIVED_DATA_DIR)/modules)添加到构建设置中的包含路径(SWIFT_INCLUDE_PATHS)

用法

使用Heimdall很简单,对于公钥私钥对,只需创建一个实例,它可以用于加密/解密、签名/验证。

使用此方法,您可以将数据本地加密以存储在磁盘或数据库中,而无需将所有内容都放入Keychain中。

if let heimdall = Heimdall(tagPrefix: "com.example") {
    let testString = "This is a test string"

    // Encryption/Decryption
    if let encryptedString = heimdall.encrypt(testString) {
        println(encryptedString) // "cQzaQCQLhAWqkDyPoHnPrpsVh..."

        if let decryptedString = heimdall.decrypt(encryptedString) {
            println(decryptedString) // "This is a test string"
        }
    }

    // Signatures/Verification
    if let signature = heimdall.sign(testString) {
        println(signature) // "fMVOFj6SQ7h+cZTEXZxkpgaDsMrki..."
        var verified = heimdall.verify(testString, signatureBase64: signature)
        println(verified) // True

        // If someone meddles with the message and the signature becomes invalid
        verified = heimdall.verify(testString + "injected false message",
                                    signatureBase64: signature)
        println(verified) // False
    }
}

关于加密/解密注解

由于RSA对可以被加密的消息长度设有限制,Heimdall通过结合AES和RSA来加密任意长度的消息。具体实现方式如下

  1. 生成一个随机AES密钥,长度基于RSA密钥对的尺寸(可以是128、192或256位)*
  2. 使用这个AES密钥加密消息
  3. 将密钥使用RSA密钥对的公钥部分来加密(并填充到正确的块大小)*
  4. 构建负载,包含加密后的密钥,然后是加密后的消息。在解密过程中,总是假设第一个块是RSA加密的AES密钥,这就是为什么Heimdall只能解密由其他Heimdall实例或其他与Heimdall逻辑兼容的代码加密的消息*

复杂场景

一个更复杂的用例是,在多个可以在不同主机上运行的Heimdall实例之间交换加密消息。

第一步是与另一方分享你的公钥

let localHeimdall = Heimdall(tagPrefix: "com.example")
if let heimdall = localHeimdall, publicKeyData = heimdall.publicKeyDataX509() {

    var publicKeyString = publicKeyData.base64EncodedString()

    // If you want to make this string URL safe,
    // you have to remember to do the reverse on the other side later
    publicKeyString = publicKeyString.replacingOccurrences(of: "/", with: "_")
    publicKeyString = publicKeyString.replacingOccurrences(of: "+", with: "-")

    println(publicKeyString) // Something along the lines of "MIGfMA0GCSqGSIb3DQEBAQUAA..."

    // Data transmission of public key to the other party
}

第二步,作为接收方(想要发送加密消息的方),你接收到提取出的公钥并创建一个匹配的Heimdall实例

// On other party, assuming keyData contains the received public key data
if let partnerHeimdall = Heimdall(publicTag: "com.example.partner", publicKeyData: keyData) {
    // Transmit some message to the partner
    let message = "This is a secret message to my partner"
    let encryptedMessage = partnerHeimdall.encrypt(message)

    // Transmit the encryptedMessage back to the origin of the public key
}

最后,收到加密消息后,发送公钥的方可以使用原始Heimdall实例来解密它

// Initial host receives encryptedMessage
if let heimdall = localHeimdall {
    if let decryptedMessage = heimdall.decrypt(encryptedMessage) {
        println(decryptedMessage) // "This is a secret message to my partner"
    }
}

所有主机上的工作流程应进行镜像,提取它们的公钥并与所有其他方分享。公钥可以用来构建只能加密消息和验证签名的特殊Heimdall实例

贡献和当前工作

对代码库的贡献非常受欢迎,有关需要哪些想法,请查看开放问题。此外,关于以下主题的建议也欢迎

  • 安全性,与Keychain交互,确保结果得到安全保存等。
  • 测试,添加测试也可能提高安全性
  • 增加额外的可配置性,可能允许使用非永久密钥
  • 错误处理,当前大多数API在出现错误时简单返回nil,这应该更改并实施适当的错误报告
  • 减少Heimdall实例公开API中可选数目。

联系

如有任何疑问,请随时与我联系。如果发现问题,请在GitHub上创建一个问题。

Henri Normak

许可

The MIT License (MIT)

Copyright (c) 2015 Henri Normak

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.