为 Perfect 提供摘要、密码和编码支持。
将此项目作为依赖项添加到您的 Package.swift 文件中。
.package(url: "https://github.com/PerfectlySoft/Perfect-Crypto.git", from: "4.0.0")
请确保已安装 libssl-dev。此软件包需要 OpenSSL 1.0.2+。在 Ubuntu 14 或某些 Debian 发行版上,您需要更新 OpenSSL 才能构建此软件包。
sudo apt-get install openssl libssl-dev
此软件包封装了 OpenSSL 提供的一些功能,并在其之上添加了一个 Swift 层。主要特点是
let testStr = "Hello, world!"
guard let hexBytes = testStr.encode(.hex) else {
return
}
String(validatingUTF8: hexBytes) == "48656c6c6f2c20776f726c6421"
guard let unHex = hexBytes.decode(.hex) else {
return
}
String(validatingUTF8: unHex) == testStr
let testStr = "Hello, world!"
guard let baseBytes = testStr.encode(.base64) else {
return
}
String(validatingUTF8: baseBytes) == "SGVsbG8sIHdvcmxkIQ=="
guard let unBase = baseBytes.decode(.base64) else {
return
}
String(validatingUTF8: unBase) == testStr
let testStr = "Hello, world!"
let testAnswer = "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3"
guard let enc = testStr.digest(.sha256)?.encode(.hex) else {
return
}
String(validatingUTF8: enc) == testAnswer
以下代码片段将 HMAC-SHA1 签名,编码为 base64,然后解码并验证数据字符串。根据您的需求替换 .sha1 或 .base64 的用法。
let password = "this is a good pw"
let data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
if let signed = data.sign(.sha1, key: HMACKey(password))?.encode(.base64),
let base64Str = String(validatingUTF8: signed),
let reRawData = base64Str.decode(.base64) {
let verifyResult = data.verify(.sha1, signature: reRawData, key: HMACKey(password))
XCTAssert(verifyResult)
} else {
XCTAssert(false, "Failed signing")
}
public extension String {
/// Construct a string from a UTF8 character pointer.
/// Character data does not need to be null terminated.
/// The buffer's count indicates how many characters are to be converted.
/// Returns nil if the data is invalid.
init?(validatingUTF8 ptr: UnsafeRawBufferPointer?)
/// Construct a string from a UTF8 character array.
/// The array's count indicates how many characters are to be converted.
/// Returns nil if the data is invalid.
init?(validatingUTF8 a: [UInt8])
}
public extension String {
/// Decode the String into an array of bytes using the indicated encoding.
/// The string's UTF8 characters are decoded.
func decode(_ encoding: Encoding) -> [UInt8]?
/// Encode the String into an array of bytes using the indicated encoding.
/// The string's UTF8 characters are decoded.
func encode(_ encoding: Encoding) -> [UInt8]?
/// Perform the digest algorithm on the String's UTF8 bytes
func digest(_ digest: Digest) -> [UInt8]?
/// Sign the String data into an array of bytes using the indicated algorithm and key.
func sign(_ digest: Digest, key: Key) -> [UInt8]?
/// Verify the signature against the String data.
/// Returns true if the signature is verified. Returns false otherwise.
func verify(_ digest: Digest, signature: [UInt8], key: Key) -> Bool
/// Encrypt this buffer using the indicated cipher, password, and salt.
/// The string's UTF8 characters are encoded.
/// Resulting data is in PEM encoded CMS format.
func encrypt(_ cipher: Cipher,
password: String,
salt: String,
keyIterations: Int = 2048,
keyDigest: Digest = .md5) -> String?
/// Decrypt this PEM encoded CMS buffer using the indicated password and salt.
/// Resulting decrypted data must be valid UTF-8 characters or the operation will fail.
func decrypt(_ cipher: Cipher,
password: String,
salt: String,
keyIterations: Int = 2048,
keyDigest: Digest = .md5) -> String?
}
public protocol Octal {}
extension UInt8: Octal {}
public extension Array where Element: Octal {
/// Encode the Array into An array of bytes using the indicated encoding.
func encode(_ encoding: Encoding) -> [UInt8]?
/// Decode the Array into an array of bytes using the indicated encoding.
func decode(_ encoding: Encoding) -> [UInt8]?
/// Digest the Array data into an array of bytes using the indicated algorithm.
func digest(_ digest: Digest) -> [UInt8]?
/// Sign the Array data into an array of bytes using the indicated algorithm and key.
func sign(_ digest: Digest, key: Key) -> [UInt8]?
/// Verify the array against the signature.
/// Returns true if the signature is verified. Returns false otherwise.
func verify(_ digest: Digest, signature: [UInt8], key: Key) -> Bool
/// Decrypt this buffer using the indicated cipher, key an iv (initialization vector).
func encrypt(_ cipher: Cipher, key: [UInt8], iv: [UInt8]) -> [UInt8]?
/// Decrypt this buffer using the indicated cipher, key an iv (initialization vector).
func decrypt(_ cipher: Cipher, key: [UInt8], iv: [UInt8]) -> [UInt8]?
/// Encrypt this buffer using the indicated cipher, password, and salt.
/// Resulting data is PEM encoded CMS format.
func encrypt(_ cipher: Cipher,
password: [UInt8],
salt: [UInt8],
keyIterations: Int = 2048,
keyDigest: Digest = .md5) -> [UInt8]?
/// Decrypt this PEM encoded CMS buffer using the indicated password and salt.
func decrypt(_ cipher: Cipher,
password: [UInt8],
salt: [UInt8],
keyIterations: Int = 2048,
keyDigest: Digest = .md5) -> [UInt8]?
}
public extension UnsafeRawBufferPointer {
/// Encode the buffer using the indicated encoding.
/// The return value must be deallocated by the caller.
func encode(_ encoding: Encoding) -> UnsafeMutableRawBufferPointer?
/// Decode the buffer using the indicated encoding.
/// The return value must be deallocated by the caller.
func decode(_ encoding: Encoding) -> UnsafeMutableRawBufferPointer?
/// Digest the buffer using the indicated algorithm.
/// The return value must be deallocated by the caller.
func digest(_ digest: Digest) -> UnsafeMutableRawBufferPointer?
/// Sign the buffer using the indicated algorithm and key.
/// The return value must be deallocated by the caller.
func sign(_ digest: Digest, key: Key) -> UnsafeMutableRawBufferPointer?
/// Verify the signature against the buffer.
/// Returns true if the signature is verified. Returns false otherwise.
func verify(_ digest: Digest, signature: UnsafeRawBufferPointer, key: Key) -> Bool
/// Encrypt this buffer using the indicated cipher, key and iv (initialization vector).
/// Returns a newly allocated buffer which must be freed by the caller.
func encrypt(_ cipher: Cipher, key: UnsafeRawBufferPointer, iv: UnsafeRawBufferPointer) -> UnsafeMutableRawBufferPointer?
/// Decrypt this buffer using the indicated cipher, key and iv (initialization vector).
/// Returns a newly allocated buffer which must be freed by the caller.
func decrypt(_ cipher: Cipher, key: UnsafeRawBufferPointer, iv: UnsafeRawBufferPointer) -> UnsafeMutableRawBufferPointer?
/// Encrypt this buffer to PEM encoded CMS format using the indicated cipher, password, and salt.
/// Returns a newly allocated buffer which must be freed by the caller.
func encrypt(_ cipher: Cipher,
password: UnsafeRawBufferPointer,
salt: UnsafeRawBufferPointer,
keyIterations: Int = 2048,
keyDigest: Digest = .md5) -> UnsafeMutableRawBufferPointer?
/// Decrypt this PEM encoded CMS buffer using the indicated password and salt.
/// Returns a newly allocated buffer which must be freed by the caller.
func decrypt(_ cipher: Cipher,
password: UnsafeRawBufferPointer,
salt: UnsafeRawBufferPointer,
keyIterations: Int = 2048,
keyDigest: Digest = .md5) -> UnsafeMutableRawBufferPointer?
}
public extension UnsafeRawBufferPointer {
/// Allocate memory for `size` bytes with word alignment from the encryption library's
/// random number generator.
///
/// - Postcondition: The memory is allocated and initialized to random bits.
static func allocateRandom(count size: Int) -> UnsafeRawBufferPointer?
}
public extension FixedWidthInteger {
/// get a random integer, i.e., signed or unsigned int8/16/32/64
public static var random: Self
}
public extension Float {
/// get a random float
public static var random: Float
}
public extension Double {
/// get a random double
public static var random: Double
}
此加密包提供了一种创建新 JWT 令牌和验证现有令牌的方法。
JSON Web Token (JWT) 是一种开放标准 (RFC 7519),它定义了一种紧凑且自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。此信息可以被验证并信任,因为它已进行数字签名。 JWT 可以使用密钥(使用 HMAC 算法)或使用 RSA 的公钥/私钥对进行签名。来源:JWT。
新的 JWT 令牌通过 JWTCreator
对象创建。
/// Creates and signs new JWT tokens.
public struct JWTCreator {
/// Creates a new JWT token given a payload.
/// The payload can then be signed to generate a JWT token string.
public init?(payload: [String:Any])
/// Sign and return a new JWT token string using an HMAC key.
/// Additional headers can be optionally provided.
/// Throws a JWT.Error.signingError if there is a problem generating the token string.
public func sign(alg: JWT.Alg, key: String, headers: [String:Any] = [:]) throws -> String
/// Sign and return a new JWT token string using the given key.
/// Additional headers can be optionally provided.
/// The key type must be compatible with the indicated `algo`.
/// Throws a JWT.Error.signingError if there is a problem generating the token string.
public func sign(alg: JWT.Alg, key: Key, headers: [String:Any] = [:]) throws -> String
}
现有的 JWT 令牌可以通过 JWTVerifier
对象进行验证。
/// Accepts a JWT token string and verifies its structural validity and signature.
public struct JWTVerifier {
/// The headers obtained from the token.
public var header: [String:Any]
/// The payload carried by the token.
public var payload: [String:Any]
/// Create a JWTVerifier given a source string in the "aaaa.bbbb.cccc" format.
/// Returns nil if the given string is not a valid JWT.
/// *Does not perform verification in this step.* Call `verify` with your key to validate.
/// If verification succeeds then the `.headers` and `.payload` properties can be safely accessed.
public init?(_ jwt: String)
/// Verify the token based on the indicated algorithm and HMAC key.
/// Throws a JWT.Error.verificationError if any aspect of the token is incongruent.
/// Returns without any error if the token was able to be verified.
/// The parameter `algo` must match the token's "alg" header.
public func verify(algo: JWT.Alg, key: String) throws
/// Verify the token based on the indicated algorithm and key.
/// Throws a JWT.Error.verificationError if any aspect of the token is incongruent.
/// Returns without any error if the token was able to be verified.
/// The parameter `algo` must match the token's "alg" header.
/// The key type must be compatible with the indicated `algo`.
public func verify(algo: JWT.Alg, key: Key) throws
}
以下示例将使用 "HS256" alg 方案创建然后验证令牌。
let name = "John Doe"
let tstPayload = ["sub": "1234567890", "name": name, "admin": true] as [String : Any]
let secret = "secret"
guard let jwt1 = JWTCreator(payload: tstPayload) else {
return // fatal error
}
let token = try jwt1.sign(alg: .hs256, key: secret)
guard let jwt = JWTVerifier(token) else {
return // fatal error
}
try jwt.verify(algo: .hs256, key: HMACKey(secret))
let fndName = jwt.payload["name"] as? String
// name == fndName!
重要的是要注意,JWTVerifier 将验证令牌在加密方面是否健全,但它 **不** 验证有效负载声明,例如 iss(uer) 或 exp(iration)。您可以从有效负载字典中获取这些信息,并根据您的应用程序的需求进行验证。
/// Available encoding methods.
public enum Encoding {
case base64
case hex
}
/// Available digest methods.
public enum Digest {
case md4
case md5
case sha
case sha1
case dss
case dss1
case ecdsa
case sha224
case sha256
case sha384
case sha512
case ripemd160
case whirlpool
case custom(String)
}
/// Available ciphers.
public enum Cipher {
case des_ecb
case des_ede
case des_ede3
case des_ede_ecb
case des_ede3_ecb
case des_cfb64
case des_cfb1
case des_cfb8
case des_ede_cfb64
case des_ede3_cfb1
case des_ede3_cfb8
case des_ofb
case des_ede_ofb
case des_ede3_ofb
case des_cbc
case des_ede_cbc
case des_ede3_cbc
case desx_cbc
case des_ede3_wrap
case rc4
case rc4_40
case rc4_hmac_md5
case rc2_ecb
case rc2_cbc
case rc2_40_cbc
case rc2_64_cbc
case rc2_cfb64
case rc2_ofb
case bf_ecb
case bf_cbc
case bf_cfb64
case bf_ofb
case cast5_ecb
case cast5_cbc
case cast5_cfb64
case cast5_ofb
case aes_128_ecb
case aes_128_cbc
case aes_128_cfb1
case aes_128_cfb8
case aes_128_cfb128
case aes_128_ofb
case aes_128_ctr
case aes_128_ccm
case aes_128_gcm
case aes_128_xts
case aes_128_wrap
case aes_192_ecb
case aes_192_cbc
case aes_192_cfb1
case aes_192_cfb8
case aes_192_cfb128
case aes_192_ofb
case aes_192_ctr
case aes_192_ccm
case aes_192_gcm
case aes_192_wrap
case aes_256_ecb
case aes_256_cbc
case aes_256_cfb1
case aes_256_cfb8
case aes_256_cfb128
case aes_256_ofb
case aes_256_ctr
case aes_256_ccm
case aes_256_gcm
case aes_256_xts
case aes_256_wrap
case aes_128_cbc_hmac_sha1
case aes_256_cbc_hmac_sha1
case aes_128_cbc_hmac_sha256
case aes_256_cbc_hmac_sha256
case camellia_128_ecb
case camellia_128_cbc
case camellia_128_cfb1
case camellia_128_cfb8
case camellia_128_cfb128
case camellia_128_ofb
case camellia_192_ecb
case camellia_192_cbc
case camellia_192_cfb1
case camellia_192_cfb8
case camellia_192_cfb128
case camellia_192_ofb
case camellia_256_ecb
case camellia_256_cbc
case camellia_256_cfb1
case camellia_256_cfb8
case camellia_256_cfb128
case camellia_256_ofb
case seed_ecb
case seed_cbc
case seed_cfb128
case seed_ofb
case custom(String)
}
有关更多文档,请访问 perfect.org。