Swift3之后:
参考github链接:
https://github.com/TakeScoop/SwiftyRSA
Swift3之前:
copy 下面三个文件代码到项目中,然后在桥文件 ...-Bridging-Header.h 中加上
#import "NSData+SHA.h" 最后在需要的地方用就好了。
如publickey加密:
try SwiftyRSA.encryptString(param as String, publicKeyPEM: MyClass.Constants.RSA_Public_Key as String)
1.NSData+SHA.h
#import <Foundation/Foundation.h>
@interface NSData (NSData_SwiftyRSASHA)
- (nonnull NSData*) SwiftyRSASHA1;
- (nonnull NSData*) SwiftyRSASHA224;
- (nonnull NSData*) SwiftyRSASHA256;
- (nonnull NSData*) SwiftyRSASHA384;
- (nonnull NSData*) SwiftyRSASHA512;
@end
2.NSData+SHA.m
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonCrypto.h>
@implementation NSData (NSData_SwiftyRSASHA)
- (nonnull NSData*) SwiftyRSASHA1 {
unsigned int outputLength = CC_SHA1_DIGEST_LENGTH;
unsigned char output[outputLength];
CC_SHA1(self.bytes, (unsigned int) self.length, output);
return [NSData dataWithBytes:output length:outputLength];
}
- (nonnull NSData*) SwiftyRSASHA224 {
unsigned int outputLength = CC_SHA224_DIGEST_LENGTH;
unsigned char output[outputLength];
CC_SHA224(self.bytes, (unsigned int) self.length, output);
return [NSData dataWithBytes:output length:outputLength];
}
- (nonnull NSData*) SwiftyRSASHA256 {
unsigned int outputLength = CC_SHA256_DIGEST_LENGTH;
unsigned char output[outputLength];
CC_SHA256(self.bytes, (unsigned int) self.length, output);
return [NSData dataWithBytes:output length:outputLength];
}
- (nonnull NSData*) SwiftyRSASHA384 {
unsigned int outputLength = CC_SHA384_DIGEST_LENGTH;
unsigned char output[outputLength];
CC_SHA384(self.bytes, (unsigned int) self.length, output);
return [NSData dataWithBytes:output length:outputLength];
}
- (nonnull NSData*) SwiftyRSASHA512 {
unsigned int outputLength = CC_SHA512_DIGEST_LENGTH;
unsigned char output[outputLength];
CC_SHA512(self.bytes, (unsigned int) self.length, output);
return [NSData dataWithBytes:output length:outputLength];
}
@end
3.SwiftyRSA.swift :
import Foundation
import Security
public class SwiftyRSAError: NSError {
init(message: String) {
super.init(domain: "com.takescoop.SwiftyRSA", code: 500, userInfo: [
NSLocalizedDescriptionKey: message
])
}
@available(*, unavailable)
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
/**
Represents the result of a signature verification
*/
@objc
public class VerificationResult: NSObject, BooleanType {
/// `true` if the signature was verified
public let boolValue: Bool
init(_ boolValue: Bool) {
self.boolValue = boolValue
}
}
@objc
public class SwiftyRSA: NSObject {
@objc public enum DigestType: Int {
case SHA1
case SHA224
case SHA256
case SHA384
case SHA512
}
private var keyTags: [NSData] = []
private static let defaultPadding: SecPadding = .PKCS1
private static var defaultDigest: DigestType = .SHA1
// MARK: - Public Shorthands
public class func encryptString(str: String, publicKeyPEM: String, padding: SecPadding = defaultPadding) throws -> String {
let rsa = SwiftyRSA()
let key = try rsa.publicKeyFromPEMString(publicKeyPEM)
return try rsa.encryptString(str, publicKey: key, padding: padding)
}
public class func encryptString(str: String, publicKeyDER: NSData, padding: SecPadding = defaultPadding) throws -> String {
let rsa = SwiftyRSA()
let key = try rsa.publicKeyFromDERData(publicKeyDER)
return try rsa.encryptString(str, publicKey: key, padding: padding)
}
public class func decryptString(str: String, privateKeyPEM: String, padding: SecPadding = defaultPadding) throws -> String {
let rsa = SwiftyRSA()
let key = try rsa.privateKeyFromPEMString(privateKeyPEM)
return try rsa.decryptString(str, privateKey: key, padding: padding)
}
public class func encryptData(data: NSData, publicKeyPEM: String, padding: SecPadding = defaultPadding) throws -> NSData {
let rsa = SwiftyRSA()
let key = try rsa.publicKeyFromPEMString(publicKeyPEM)
return try rsa.encryptData(data, publicKey: key, padding: padding)
}
public class func encryptData(data: NSData, publicKeyDER: NSData, padding: SecPadding = defaultPadding) throws -> NSData {
let rsa = SwiftyRSA()
let key = try rsa.publicKeyFromDERData(publicKeyDER)
return try rsa.encryptData(data, publicKey: key, padding: padding)
}
public class func decryptData(data: NSData, privateKeyPEM: String, padding: SecPadding = defaultPadding) throws -> NSData {
let rsa = SwiftyRSA()
let key = try rsa.privateKeyFromPEMString(privateKeyPEM)
return try rsa.decryptData(data, privateKey: key, padding: padding)
}
/**
Sign a `String` using a private key. The supplied string will be hashed using the specified
hashing function and the resulting digest will be signed.
- parameter str: The `String` to be signed.
- parameter privateKeyPEM: A `String` containing the private key for the signing operation in PEM format
- parameter digestMethod: The `DigestType` that indicates the hashing function
- returns: Base64 encoded signature for the hash of the string.
- throws: `SwiftyRSAError` if there is an error in the signing process
*/
public class func signString(str: String, privateKeyPEM: String, digestMethod: DigestType = defaultDigest) throws -> String {
let rsa = SwiftyRSA()
let key = try rsa.privateKeyFromPEMString(privateKeyPEM)
return try rsa.signString(str, privateKey: key, digestMethod: digestMethod)
}
/**
Sign an `NSData` block using a private key. The supplied data will be hashed using the specified
hashing function and the resulting digest will be signed.
- parameter data: The `NSData` to be signed.
- parameter privateKeyPEM: A `String` containing the private key for the signing operation in PEM format
- parameter digestMethod: The `DigestType` that indicates the hashing function
- returns: The signature for the hash of the string.
- throws: `SwiftyRSAError` if there is an error in the signing process
*/
public class func signData(data: NSData, privateKeyPEM: String, digestMethod: DigestType = defaultDigest) throws -> NSData {
let rsa = SwiftyRSA()
let key = try rsa.privateKeyFromPEMString(privateKeyPEM)
return try rsa.signData(data, privateKey: key, digestMethod: digestMethod)
}
/**
Verify a signature using a public key. The supplied `String` will be hashed and the
resulting digest will be verified against the supplied signature.
- parameter str: The `String` to be verified. This string will be hashed.
- parameter signature: The BASE64 string representation of the signature to be verified.
- parameter publicKeyPEM: A `String` containing the public key for the signing operation in PEM format
- parameter digestMethod: The `DigestType` that indicates the hashing function
- returns: A `VerificationResult` that indicates whether the signature was valid or not
- throws: `SwiftyRSAError` if there is an error in the verification process
*/
public class func verifySignatureString(str: String, signature: String, publicKeyPEM: String, digestMethod: DigestType = defaultDigest) throws -> VerificationResult {
let rsa = SwiftyRSA()
let key = try rsa.publicKeyFromPEMString(publicKeyPEM)
return try rsa.verifySignatureString(str, signature: signature, publicKey: key, digestMethod: digestMethod)
}
/**
Verify a signature using a public key. The supplied `NSData` will be hashed and the
resulting digest will be verified against the supplied signature.
- parameter data: The `NSData` to be verified. This data will be hashed
- parameter signature: The signature to be verified.
- parameter publicKeyPEM: A `String` containing the public key for the signing operation in PEM format
- parameter digestMethod: The `DigestType` that indicates the hashing function
- returns: A `VerificationResult` that indicates whether the signature was valid or not
- throws: `SwiftyRSAError` if there is an error in the verification process
*/
public class func verifySignatureData(data: NSData, signature: NSData, publicKeyPEM: String, digestMethod: DigestType = defaultDigest) throws -> VerificationResult {
let rsa = SwiftyRSA()
let key = try rsa.publicKeyFromPEMString(publicKeyPEM)
return try rsa.verifySignatureData(data, signatureData: signature, publicKey: key, digestMethod: digestMethod)
}
/**
Verify a signature using a public key. The supplied `String` will be hashed and the
resulting digest will be verified against the supplied signature.
- parameter str: The `String` to be verified. This string will be hashed
- parameter signature: The BASE64 string representation of the signature to be verified.
- parameter publicKeyDER: The public key for the signing operation in DER format
- parameter digestMethod: The `DigestType` that indicates the hashing function
- returns: A `VerificationResult` that indicates whether the signature was valid or not
- throws: `SwiftyRSAError` if there is an error in the verification process
*/
public class func verifySignatureString(str: String, signature: String, publicKeyDER: NSData, digestMethod: DigestType = defaultDigest) throws -> VerificationResult {
let rsa = SwiftyRSA()
let key = try rsa.publicKeyFromDERData(publicKeyDER)
return try rsa.verifySignatureString(str, signature: signature, publicKey: key, digestMethod: digestMethod)
}
/**
Verify a signature using a public key. The supplied `NSData` will be hashed and the
resulting digest will be verified against the supplied signature.
- parameter data: The `NSData` to be verified. This data will be hashed
- parameter signature: The signature to be verified.
- parameter publicKeyDER: The public key for the signing operation in DER format
- parameter digestMethod: The `DigestType` that indicates the hashing function
- returns: A `VerificationResult` that indicates whether the signature was valid or not
- throws: `SwiftyRSAError` if there is an error in the verification process
*/
public class func verifySignatureData(data: NSData, signature: NSData, publicKeyDER: NSData, digestMethod: DigestType = defaultDigest) throws -> VerificationResult {
let rsa = SwiftyRSA()
let key = try rsa.publicKeyFromDERData(publicKeyDER)
return try rsa.verifySignatureData(data, signatureData: signature, publicKey: key, digestMethod: digestMethod)
}
// MARK: - Public Advanced Methods
public override init() {
super.init()
}
public func publicKeyFromDERData(keyData: NSData) throws -> SecKeyRef {
return try addKey(keyData, isPublic: true)
}
public func publicKeyFromPEMString(key: String) throws -> SecKeyRef {
let data = try dataFromPEMKey(key)
return try addKey(data, isPublic: true)
}
public func privateKeyFromPEMString(key: String) throws -> SecKeyRef {
let data = try dataFromPEMKey(key)
return try addKey(data, isPublic: false)
}
// Encrypts data with a RSA key
public func encryptData(data: NSData, publicKey: SecKeyRef, padding: SecPadding) throws -> NSData {
let blockSize = SecKeyGetBlockSize(publicKey)
let maxChunkSize = blockSize - 11
var decryptedDataAsArray = [UInt8](count: data.length / sizeof(UInt8), repeatedValue: 0)
data.getBytes(&decryptedDataAsArray, length: data.length)
var encryptedData = [UInt8](count: 0, repeatedValue: 0)
var idx = 0
while (idx < decryptedDataAsArray.count) {
let idxEnd = min(idx + maxChunkSize, decryptedDataAsArray.count)
let chunkData = [UInt8](decryptedDataAsArray[idx..<idxEnd])
var encryptedDataBuffer = [UInt8](count: blockSize, repeatedValue: 0)
var encryptedDataLength = blockSize
let status = SecKeyEncrypt(publicKey, padding, chunkData, chunkData.count, &encryptedDataBuffer, &encryptedDataLength)
guard status == noErr else {
throw SwiftyRSAError(message: "Couldn't encrypt chunk at index \(idx)")
}
encryptedData += encryptedDataBuffer
idx += maxChunkSize
}
return NSData(bytes: encryptedData, length: encryptedData.count)
}
// Decrypt an encrypted data with a RSA key
public func decryptData(encryptedData: NSData, privateKey: SecKeyRef, padding: SecPadding) throws -> NSData {
let blockSize = SecKeyGetBlockSize(privateKey)
var encryptedDataAsArray = [UInt8](count: encryptedData.length / sizeof(UInt8), repeatedValue: 0)
encryptedData.getBytes(&encryptedDataAsArray, length: encryptedData.length)
var decryptedData = [UInt8](count: 0, repeatedValue: 0)
var idx = 0
while (idx < encryptedDataAsArray.count) {
let idxEnd = min(idx + blockSize, encryptedDataAsArray.count)
let chunkData = [UInt8](encryptedDataAsArray[idx..<idxEnd])
var decryptedDataBuffer = [UInt8](count: blockSize, repeatedValue: 0)
var decryptedDataLength = blockSize
let status = SecKeyDecrypt(privateKey, padding, chunkData, idxEnd-idx, &decryptedDataBuffer, &decryptedDataLength)
guard status == noErr else {
throw SwiftyRSAError(message: "Couldn't decrypt chunk at index \(idx)")
}
decryptedData += [UInt8](decryptedDataBuffer[0..<decryptedDataLength])
idx += blockSize
}
return NSData(bytes: decryptedData, length: decryptedData.count)
}
public func encryptString(str: String, publicKey: SecKeyRef, padding: SecPadding = defaultPadding) throws -> String {
guard let data = str.dataUsingEncoding(NSUTF8StringEncoding) else {
throw SwiftyRSAError(message: "Couldn't get UT8 data from provided string")
}
let encryptedData = try encryptData(data, publicKey: publicKey, padding: padding)
return encryptedData.base64EncodedStringWithOptions([])
}
public func decryptString(str: String, privateKey: SecKeyRef, padding: SecPadding = defaultPadding) throws -> String {
guard let data = NSData(base64EncodedString: str, options: []) else {
throw SwiftyRSAError(message: "Couldn't decode base 64 encoded string")
}
let decryptedData = try decryptData(data, privateKey: privateKey, padding: padding)
guard let decryptedString = NSString(data: decryptedData, encoding: NSUTF8StringEncoding) else {
throw SwiftyRSAError(message: "Couldn't convert decrypted data to UTF8 string")
}
return decryptedString as String
}
// Mark: - Digital signatures
// Sign data with an RSA key
/**
Sign a `String` using a private key. The supplied string will be hashed using the specified
hashing method and the resulting hash will be signed.
- parameter str: The `String` to be signed.
- parameter privateKey: A `SecKeyRef` for the private key
- parameter digestMethod: The `DigestType` that indicates the hashing function
- returns: Base64 encoded signature for the hash of the string.
- throws: `SwiftyRSAError` if there is an error in the signing process
*/
public func signString(str: String, privateKey: SecKeyRef, digestMethod: DigestType = defaultDigest) throws -> String {
guard let data = str.dataUsingEncoding(NSUTF8StringEncoding) else {
throw SwiftyRSAError(message: "Couldn't get UTF8 data from provided string")
}
let signature = try signData(data, privateKey: privateKey, digestMethod:digestMethod)
return signature.base64EncodedStringWithOptions([])
}
/**
Sign an `NSData` block using a private key. The supplied data will be hashed using the specified
hashing method and the resulting digest will be signed.
- parameter data: The `NSData` to be signed.
- parameter privateKey: A `SecKeyRef` for the private key
- parameter digestMethod: The `DigestType` that indicates the hashing function
- returns: The signature for the hash of the string.
- throws: `SwiftyRSAError` if there is an error in the signing process
*/
public func signData(data: NSData, privateKey: SecKeyRef, digestMethod: DigestType = defaultDigest) throws -> NSData {
let (digest, padding) = self.digestForData(data, digestMethod: digestMethod)
return try signDigest(digest, privateKey: privateKey, padding: padding)
}
/**
Sign an `NSData` block using a private key. The supplied data must represent an SHA1 digest.
- parameter digest: The `NSData` containing the SHA1 digest to be signed.
- parameter privateKey: A `SecKeyRef` for the private key
- returns: The signature for the SHA1 digest.
- throws: `SwiftyRSAError` if there is an error in the signing process
*/
@available(*, deprecated = 0.31, message = "Use signDigest() with digestMethod = .SHA1")
public func signSHA1Digest(digest: NSData, privateKey: SecKeyRef) throws -> NSData {
return try self.signDigest(digest, privateKey: privateKey, padding: .PKCS1SHA1)
}
/**
Sign an `NSData` block using a private key. The supplied data must represent a digest of the indicated type.
- parameter digest: The `NSData` containing the SHA1 digest to be signed.
- parameter privateKey: A `SecKeyRef` for the private key
- parameter digestMethod: The digest type contained in `digest`
- returns: The signature for the SHA1 digest.
- throws: `SwiftyRSAError` if there is an error in the signing process
*/
public func signDigest(digest: NSData, privateKey: SecKeyRef, digestMethod: DigestType = defaultDigest) throws -> NSData {
let (_,padding) = self.digestForData(digest, digestMethod: digestMethod)
return try self.signDigest(digest, privateKey: privateKey, padding: padding)
}
// Verify data with an RSA key
/**
Verify a signature using a public key. The supplied `String` will be hashed using the specified
hasing function and resulting digest will be verified against the supplied signature.
- parameter str: The `String` to be verified. This string will be hashed
- parameter signature: The BASE64 string representation of the signature to be verified.
- parameter publicKey: A `SecKeyRef` for the public key
- parameter digestMethod: The `DigestType` that indicates the hashing function
- returns: A `VerificationResult` that indicates whether the signature was valid or not
- throws: `SwiftyRSAError` if there is an error in the verification process
*/
public func verifySignatureString(str: String, signature: String, publicKey: SecKeyRef, digestMethod: DigestType = defaultDigest) throws -> VerificationResult {
guard let data = str.dataUsingEncoding(NSUTF8StringEncoding) else {
throw SwiftyRSAError(message: "Couldn't get UTF8 data from provided string")
}
guard let signatureData = NSData(base64EncodedString: signature, options: []) else {
throw SwiftyRSAError(message: "Couldn't get signature data from provided base64 string")
}
return try verifySignatureData(data, signatureData: signatureData, publicKey: publicKey, digestMethod: digestMethod)
}
/**
Verify a signature using a public key. The supplied `NSData` will be hashed and the
resulting digest will be verified against the supplied signature.
- parameter data: The `NSData` to be verified. This string will be hashed
- parameter signatureData: The of the signature data to be verified.
- parameter publicKey: A `SecKeyRef` for the public key
- parameter digestMethod: The `DigestType` that indicates the hashing function
- returns: A `VerificationResult` that indicates whether the signature was valid or not
- throws: `SwiftyRSAError` if there is an error in the verification process
*/
public func verifySignatureData(data: NSData, signatureData: NSData, publicKey: SecKeyRef, digestMethod: DigestType = defaultDigest) throws -> VerificationResult {
let (digest, padding) = self.digestForData(data, digestMethod: digestMethod)
return try verifySignatureData(digest, signature: signatureData, publicKey: publicKey, padding: padding)
}
/**
Verify a signature using a public key. The supplied `NSData` represents an SHA1 digest to be verified against the supplied signature.
- parameter SHA1Data: The `NSData` containing the SHA1 digest to be verified.
- parameter signature: The `NSData` containing the signature to be verified.
- parameter publicKey: A `SecKeyRef` for the public key
- returns: A `VerificationResult` that indicates whether the signature was valid or not
- throws: `SwiftyRSAError` if there is an error in the verification process
*/
@available(*, deprecated = 0.31, message = "Use verifySignature() with digestMethod = .SHA1")
public func verifySHA1SignatureData(SHA1Data: NSData, signature: NSData, publicKey: SecKeyRef) throws -> VerificationResult {
return try self.verifySignatureData(SHA1Data, signature: signature, publicKey: publicKey, padding: .PKCS1SHA1)
}
/**
Verify a signature using a public key. The supplied `NSData` represents a digest to be verified against the supplied signature.
- parameter digestData: The `NSData` containing the digest to be verified.
- parameter signature: The `NSData` containing the signature to be verified.
- parameter publicKey: A `SecKeyRef` for the public key
- parameter digestMethod: The method used to create the digest in the `digest` parameter
- returns: A `VerificationResult` that indicates whether the signature was valid or not
- throws: `SwiftyRSAError` if there is an error in the verification process
*/
public func verifySignatureData(digestData: NSData, signature: NSData, publicKey: SecKeyRef, digestMethod: DigestType = defaultDigest) throws -> VerificationResult {
let (_,padding) = self.digestForData(digestData, digestMethod: digestMethod)
return try self.verifySignatureData(digestData, signature: signature, publicKey: publicKey, padding: padding)
}
// MARK: - Private
private func addKey(keyData: NSData, isPublic: Bool) throws -> SecKeyRef {
var keyData = keyData
// Strip key header if necessary
if isPublic {
try keyData = stripPublicKeyHeader(keyData)
}
let tag = NSUUID().UUIDString
let tagData = NSData(bytes: tag, length: tag.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
removeKeyWithTagData(tagData)
// Add persistent version of the key to system keychain
let persistKey = UnsafeMutablePointer<AnyObject?>(nil)
let keyClass = isPublic ? kSecAttrKeyClassPublic : kSecAttrKeyClassPrivate
// Add persistent version of the key to system keychain
let keyDict = NSMutableDictionary()
keyDict.setObject(kSecClassKey, forKey: kSecClass as! NSCopying)
keyDict.setObject(tagData, forKey: kSecAttrApplicationTag as! NSCopying)
keyDict.setObject(kSecAttrKeyTypeRSA, forKey: kSecAttrKeyType as! NSCopying)
keyDict.setObject(keyData, forKey: kSecValueData as! NSCopying)
keyDict.setObject(keyClass, forKey: kSecAttrKeyClass as! NSCopying)
keyDict.setObject(NSNumber(bool: true), forKey: kSecReturnPersistentRef as! NSCopying)
keyDict.setObject(kSecAttrAccessibleWhenUnlocked, forKey: kSecAttrAccessible as! NSCopying)
var secStatus = SecItemAdd(keyDict as CFDictionary, persistKey)
if secStatus != noErr && secStatus != errSecDuplicateItem {
throw SwiftyRSAError(message: "Provided key couldn't be added to the keychain")
}
keyTags.append(tagData)
// Now fetch the SecKeyRef version of the key
var keyRef: AnyObject? = nil
keyDict.removeObjectForKey(kSecValueData)
keyDict.removeObjectForKey(kSecReturnPersistentRef)
keyDict.setObject(NSNumber(bool: true), forKey: kSecReturnRef as! NSCopying)
keyDict.setObject(kSecAttrKeyTypeRSA, forKey: kSecAttrKeyType as! NSCopying)
secStatus = SecItemCopyMatching(keyDict as CFDictionaryRef, &keyRef)
guard let unwrappedKeyRef = keyRef else {
throw SwiftyRSAError(message: "Couldn't get key reference from the keychain")
}
return unwrappedKeyRef as! SecKeyRef
}
private func dataFromPEMKey(key: String) throws -> NSData {
let lines = key.componentsSeparatedByString("\n").filter { line in
return !line.hasPrefix("-----BEGIN") && !line.hasPrefix("-----END")
}
guard lines.count != 0 else {
throw SwiftyRSAError(message: "Couldn't get data from PEM key: no data available after stripping headers")
}
// Decode base64 key
let base64EncodedKey = lines.joinWithSeparator("")
let keyData = NSData(base64EncodedString: base64EncodedKey, options: .IgnoreUnknownCharacters)
guard let unwrappedKeyData = keyData where unwrappedKeyData.length != 0 else {
throw SwiftyRSAError(message: "Couldn't decode PEM key data (base64)")
}
return unwrappedKeyData
}
/**
This method strips the x509 from a provided ASN.1 DER public key.
If the key doesn't contain a header, the DER data is returned as is.
Supported formats are:
Headerless:
SEQUENCE
INTEGER (1024 or 2048 bit) -- modulo
INTEGER -- public exponent
With x509 header:
SEQUENCE
SEQUENCE
OBJECT IDENTIFIER 1.2.840.113549.1.1.1
NULL
BIT STRING
SEQUENCE
INTEGER (1024 or 2048 bit) -- modulo
INTEGER -- public exponent
Example of headerless key:
https://lapo.it/asn1js/#3082010A0282010100C1A0DFA367FBC2A5FD6ED5A071E02A4B0617E19C6B5AD11BB61192E78D212F10A7620084A3CED660894134D4E475BAD7786FA1D40878683FD1B7A1AD9C0542B7A666457A270159DAC40CE25B2EAE7CCD807D31AE725CA394F90FBB5C5BA500545B99C545A9FE08EFF00A5F23457633E1DB84ED5E908EF748A90F8DFCCAFF319CB0334705EA012AF15AA090D17A9330159C9AFC9275C610BB9B7C61317876DC7386C723885C100F774C19830F475AD1E9A9925F9CA9A69CE0181A214DF2EB75FD13E6A546B8C8ED699E33A8521242B7E42711066AEC22D25DD45D56F94D3170D6F2C25164D2DACED31C73963BA885ADCB706F40866B8266433ED5161DC50E4B3B0203010001
Example of key with X509 header (notice the additional ASN.1 sequence):
https://lapo.it/asn1js/#30819F300D06092A864886F70D010101050003818D0030818902818100D0674615A252ED3D75D2A3073A0A8A445F3188FD3BEB8BA8584F7299E391BDEC3427F287327414174997D147DD8CA62647427D73C9DA5504E0A3EED5274A1D50A1237D688486FADB8B82061675ABFA5E55B624095DB8790C6DBCAE83D6A8588C9A6635D7CF257ED1EDE18F04217D37908FD0CBB86B2C58D5F762E6207FF7B92D0203010001
*/
private func stripPublicKeyHeader(keyData: NSData) throws -> NSData {
let count = keyData.length / sizeof(CUnsignedChar)
guard count > 0 else {
throw SwiftyRSAError(message: "Provided public key is empty")
}
var byteArray = [UInt8](count: count, repeatedValue: 0)
keyData.getBytes(&byteArray, length: keyData.length)
var index = 0
guard byteArray[index] == 0x30 else {
throw SwiftyRSAError(message: "Provided key doesn't have a valid ASN.1 structure (first byte should be 0x30 == SEQUENCE)")
}
index += 1
if byteArray[index] > 0x80 {
index += Int(byteArray[index]) - 0x80 + 1
}
else {
index += 1
}
// If current byte marks an integer (0x02), it means the key doesn't have a X509 header and just
// contains its modulo & public exponent. In this case, we can just return the provided DER data as is.
if Int(byteArray[index]) == 0x02 {
return keyData
}
// Now that we've excluded the possibility of headerless key, we're looking for a valid X509 header sequence.
// It should look like this:
// 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00
guard Int(byteArray[index]) == 0x30 else {
throw SwiftyRSAError(message: "Provided key doesn't have a valid X509 header")
}
index += 15
if byteArray[index] != 0x03 {
throw SwiftyRSAError(message: "Invalid byte at index \(index - 1) (\(byteArray[index - 1])) for public key header")
}
index += 1
if byteArray[index] > 0x80 {
index += Int(byteArray[index]) - 0x80 + 1
}
else {
index += 1
}
guard byteArray[index] == 0 else {
throw SwiftyRSAError(message: "Invalid byte at index \(index - 1) (\(byteArray[index - 1])) for public key header")
}
index += 1
let strippedKeyBytes = [UInt8](byteArray[index...keyData.length - 1])
let data = NSData(bytes: strippedKeyBytes, length: keyData.length - index)
return data
}
private func removeKeyWithTagData(tagData: NSData) {
let publicKey = NSMutableDictionary()
publicKey.setObject(kSecClassKey, forKey: kSecClass as! NSCopying)
publicKey.setObject(kSecAttrKeyTypeRSA, forKey: kSecAttrKeyType as! NSCopying)
publicKey.setObject(tagData, forKey: kSecAttrApplicationTag as! NSCopying)
SecItemDelete(publicKey as CFDictionaryRef)
}
private func signDigest(digest: NSData, privateKey: SecKeyRef, padding: SecPadding) throws -> NSData {
let blockSize = SecKeyGetBlockSize(privateKey)
let maxChunkSize = blockSize - 11
guard (digest.length / sizeof(UInt8) <= maxChunkSize) else {
throw SwiftyRSAError(message: "data length exceeds \(maxChunkSize)")
}
var signDataAsArray = [UInt8](count: digest.length / sizeof(UInt8), repeatedValue: 0)
digest.getBytes(&signDataAsArray, length: digest.length)
var signatureData = [UInt8](count: blockSize, repeatedValue: 0)
var signatureDataLength = blockSize
let status = SecKeyRawSign(privateKey, padding, signDataAsArray, signDataAsArray.count, &signatureData, &signatureDataLength)
guard status == noErr else {
throw SwiftyRSAError(message: "Couldn't sign data \(status)")
}
return NSData(bytes: signatureData, length: signatureData.count)
}
private func verifySignatureData(SHAData: NSData, signature: NSData, publicKey: SecKeyRef, padding: SecPadding) throws -> VerificationResult {
var verifyDataAsArray = [UInt8](count: SHAData.length / sizeof(UInt8), repeatedValue: 0)
SHAData.getBytes(&verifyDataAsArray, length: SHAData.length)
var signatureDataAsArray = [UInt8](count: signature.length / sizeof(UInt8), repeatedValue: 0)
signature.getBytes(&signatureDataAsArray, length: signature.length)
let status = SecKeyRawVerify(publicKey, padding, verifyDataAsArray, verifyDataAsArray.count, signatureDataAsArray, signatureDataAsArray.count)
if (status == errSecSuccess) {
return VerificationResult(true)
} else if (status == -9809) {
return VerificationResult(false)
} else {
throw SwiftyRSAError(message: "Couldn't verify signature - \(status)")
}
}
//20161102
private func digestForData(data: NSData, digestMethod: DigestType) -> (digest:NSData, padding:SecPadding) {
var digest: NSData
var padding: SecPadding
switch digestMethod {
case .SHA1:
digest = data.SwiftyRSASHA1()
padding = .PKCS1SHA1
case .SHA224:
digest = data.SwiftyRSASHA224()
padding = .PKCS1SHA224
case .SHA256:
digest = data.SwiftyRSASHA256()
padding = .PKCS1SHA256
case .SHA384:
digest = data.SwiftyRSASHA384()
padding = .PKCS1SHA384
case .SHA512:
digest = data.SwiftyRSASHA512()
padding = .PKCS1SHA512
}
return (digest,padding)
}
deinit {
for tagData in keyTags {
removeKeyWithTagData(tagData)
}
}
}