How to create SecKey from pem file in Swift - swift

I'm trying to create a SecKey using SecKeyCreateWithData. I'm loading the private key from a .pem file, and I create the key pair like this.
openssl genrsa -des3 -out WM_IO_my_rsa_key_pair 2048
Here is the code, but I get a SecKeyCreate init(RSAPublicKey) failed: -50 error.
import UIKit
import Security
func createKey(pem: String) -> SecKey? {
let attributes: [NSObject : NSObject] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
kSecAttrKeySizeInBits: NSNumber(value: 2048)
]
let privateKey = pem
.replacingOccurrences(of: "-----BEGIN PRIVATE KEY-----", with: "")
.replacingOccurrences(of: "-----END PRIVATE KEY-----", with: "")
.split(separator: "\n").joined()
guard let privateKeyData = Data(base64Encoded: privateKey, options: .ignoreUnknownCharacters) else {
return nil
}
var error: Unmanaged<CFError>?
return SecKeyCreateWithData(privateKeyData as CFData, attributes as CFDictionary, &error)
}
let privateKey = """
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCpySPOXAAsi1jN
vxDrNfinFOUZlLk38P8BF2ukJWU7OOAXol6TG8fFrn8O+UVW4jjPTZjLT9G6385N
iervmwopS5CtHS2p0ptLm1ExigZAuS/LTaIkw3yugr4b+5roEwL9nFW0Qfd4o/ds
3lKPtdwtO0rfqTfuZ6CqcQP0sTJcky7nk3mIyz6jZOZE2QLL/oF/oUKsi5Wt5uJp
uluZ+gt087Ftm1oGKR8lZt5bucbD7WZnmlckNyUh7O973mqqDZ6zTwrgNuxIBmtr
afNetsgZv6/We56IWenKayWW1xA9icPhmViJtzPU3Gu8HW/FMovyRZAMFD1Yzf+L
eiSGC5x9AgMBAAECggEAGOrN+HEEDYV9yOQrhXlsPokQfkqXTCBwLCbVw0dBrglQ
yecoXgqLrPVI7Fek13VnxPSsYdoa+4Pw8Ow2sGdefGT8nItVskCYTeZDajPJayJC
Y2HHVAHkgG/rmou4VirEG5gf885ilS/rFe0izLhx0amL+XsKHFDVAW6ImBaRP4iU
82fkZtLlh4DLi6w/oNu3ePZkMeTl4aAkJH8gRy2Mzsyl0DNi5+mH0du12BVSca58
iAXEhufIweTvDqb3lx0CR5+0+dbk/MeIvoklDCkPAlK9IOXr9m0UYjWXNYTADLi8
PmQf8k7pf74ma/2bFuT8LakIA6z3HEwLO7iKm4VVOQKBgQDYVG7AJWJL3DDMCpz3
csP+fKccr72Q3VKCZ5uq0jC/Wp4jfNip/b76Rt3xb8nqzgIDntu+B6Vzz6HPB5d4
j8510lNZrRzIQj0unxOx7pJHIGHhGv2dJ00IqVa5aNL8aCNhxXhgR3vIojyoo8R9
nc4WAQu4ID+xOCsqtvNBm6T1jwKBgQDI67Obqjqt1IDWV5/7dJundWEtfGBdHYBd
iuVxoyqOKAeasaWXuUxJgpzy/ukIAuI5WtzR8nP/US8MIUkRj7eXDi3E2tgpkUwZ
IT9StEdXM8UznpyXfQX9oMmlHC8MZRwLJkldcbGMjsfXsVcAMbe+HBmXs+izsxqe
icsEPPe/MwKBgQDU2O5HPAElJrcUa3TZux/AayF5hih8OmcOS6bMQhcYj658uD/t
se6QDd4dyaHf12X/7fPDW36dHjPUoWGVi9jV8GV2HG/vUc0k6/vS3CsstF3ZwFa8
o4iV9xePQeYl1sjJUCQKhwrx8z6/prKT7gpxeAHx3jkMw3klg/CAIwYBXQKBgFIc
OT+Rlv95S9nM352k7wPFrZwoKz2Ck/YmkFQbiYWlCE6I8RKLcIjOLxQDZvqWKxuj
bYEDY7Jg3ChJ5hGeOTorWjsL8LE0JvRSKQ3EjS8vAhVxaa9jMbKQJjgqx3N6Uraf
w/XDQ/scUsFsQRDcQKoZ07+yj5P4yxUlXOlzfRAPAoGBALri0o6PhjRRPwKzE3gO
ClUJ1KTEhA55GOPs5ZDPxDckYigSC+R8nLLxeueFiFkIDrar85tiO9J4P0j7tovB
Ivu6+QWQso3U3RC2Deb3uWagKHOt9m+DB8LdKsLpY/aPUXzPKKXJ4BWVIYExm418
FUzB7PCpc0Cdjg6A7Ca2cP0U
-----END PRIVATE KEY-----
"""
createKey(pem: privateKey)

The pem key is base64 encoded, so you need to decode it first:
guard let privateKeyData = Data(base64Encoded: privateKey, options: .ignoreUnknownCharacters) else {
...

Related

How do I parse an x509 certificate and extract its key's signature in Swift?

I'm trying to generate an RSA pair key, then add an X.509 certificate to the header of Public Key. For this, I'm using library SwiftyRSA
The problem is that when I add the X.509 certificate to the header I cannot parse the Public Key. When I print the Public Key after that, it still shows the same as it was without the certificate.
But when I print bytes it shows different bytes, without the certificate it shows 270 bytes, with the certificate it shows 294bytes. This means it's adding the certificate but it's not parsing it with the certificate.
After reading deeper in SwiftyRSA library, it says:
Warning : Storing (with SwiftyRSA's methods) or creating a PublicKey
instance will automatically strip the header from the key. For more
info, see Under the hood above.
If worth mentioning even the case that is still Opened there.
I'm still looking for a solution but cannot find any. The code that I tried to implement:
if let password = passwordTextField.text {
//Generate RSA
guard let keyPair = try? SwiftyRSA.generateRSAKeyPair(sizeInBits: 2048),
let privateKeyPem = try? keyPair.privateKey.pemString(),
let publicKeyPem = try? keyPair.publicKey.pemString()
else {
return
}
/// Generate Certificate in format X.509 from Public Key
let publicKey = try! PublicKey(data: keyPair.publicKey.data())
let publicKeyData = try! keyPair.publicKey.data()
let publicKey_with_X509_header = try! SwiftyRSA.prependX509KeyHeader(keyData: publicKeyData)
let publicKey509 = try! PublicKey(data: publicKey_with_X509_header)
print(try! publicKey.pemString()) // Without Certificate
print(try! publicKey509.pemString()) // With Certificate
// These two print results are completely the same, but it should be different.
// Encrypt private key
let salt = String.random(length: 32)
let aesKey = Array(String((password + salt).prefix(32)).utf8)
let iv = [UInt8](String(salt.prefix(16)).utf8)
guard let aes = try? AES(key: aesKey, blockMode: CBC(iv: iv), padding: .pkcs7),
let inputData = privateKeyPem.data(using: .utf8),
let encryptedBytes = try? aes.encrypt(inputData.bytes)
else {
return
}
let encryptedData = NSData(bytes: encryptedBytes, length: encryptedBytes.count)
let base64String = encryptedData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
// Print keys and salt
print(try! publicKey509.pemString())
print(base64String)
print(salt)
}
How can I parse the Public Key with an x509 certificate?
I would appreciate any contribution.
As mentioned above, you won't be able to create x509 certificate with SwiftyRSA. You can actually do this with Security framework.
First, you need to create an extension of Data, which will be used to add the certificate to the public key.
extension Data {
public func dataByPrependingX509Header() -> Data {
let result = NSMutableData()
let encodingLength: Int = (self.count + 1).encodedOctets().count
let OID: [CUnsignedChar] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00]
var builder: [CUnsignedChar] = []
// ASN.1 SEQUENCE
builder.append(0x30)
// Overall size, made of OID + bitstring encoding + actual key
let size = OID.count + 2 + encodingLength + self.count
let encodedSize = size.encodedOctets()
builder.append(contentsOf: encodedSize)
result.append(builder, length: builder.count)
result.append(OID, length: OID.count)
builder.removeAll(keepingCapacity: false)
builder.append(0x03)
builder.append(contentsOf: (self.count + 1).encodedOctets())
builder.append(0x00)
result.append(builder, length: builder.count)
// Actual key bytes
result.append(self)
return result as Data
}
}
Then, make sure you import Security and, you change your code to this:
// Generate keys
if let password = passwordTextField.text {
//Generate RSA
let attributes: [String: Any] = [
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeySizeInBits): 4096, // you can change the size to 2048, but 4096 has higher security.
String(kSecPrivateKeyAttrs): [
String(kSecAttrIsPermanent): true,
String(kSecAttrApplicationTag): "com.example.keys.mykey"]
]
var error: Unmanaged<CFError>?
// Private Key
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
fatalError("Error: \(error.debugDescription)")
}
let privateKeyData = SecKeyCopyExternalRepresentation(privateKey, &error)
let privData = privateKeyData as Data?
let privateKeyString = privData!.base64EncodedString()
print("Private Key")
print(privateKeyString)
// You'll probably need to structure Private and public key to go in new line every time after 76 character
let structuredPrivateKeyString = privateKeyString.inserting(separator: "\n", every: 76)
let privateKeyFinal = "-----BEGIN RSA PRIVATE KEY-----\n" + structuredPrivateKeyString + "\n\n-----END RSA PRIVATE KEY-----"
print(privateKeyFinal)
// Public Key
let publicKey = SecKeyCopyPublicKey(privateKey) // generate public key from private key
let publicKeyData = SecKeyCopyExternalRepresentation(publicKey!, &error)
let pubData = publicKeyData as Data? // We need to turn it into data so we can add the certificate or print it as string
print("Public Key")
print(pubData!.base64EncodedString())
// Add x509 certificate to public key
let publicKeyX509 = SecKeyCopyPublicKey(privateKey)
let publicKeyDataX509 = SecKeyCopyExternalRepresentation(publicKeyX509!, &error)
let pubData1 = publicKeyDataX509 as Data?
let x509Data = pubData1!.dataByPrependingX509Header() // Here we add the certificate (Function that we created previosly in Data extension).
var publicKeyX509String = x509Data.base64EncodedString()
print("Public Key x509")
print(publicKeyX509String)
let structuredPublicKeyX509String = publicKeyX509String.inserting(separator: "\n", every: 76)
let publicKeyFinal = "-----BEGIN RSA PUBLIC KEY-----\n\(structuredPublicKeyX509String)\n\n-----END RSA PUBLIC KEY-----"
print("Public Key for the API")
print(publicKeyFinal)
// Encrypt private key
let salt = String.random(length: 32)
let aesKey = Array(String((password + salt).prefix(32)).utf8)
let iv = [UInt8](String(salt.prefix(16)).utf8)
guard let aes = try? AES(key: aesKey, blockMode: CBC(iv: iv), padding: .pkcs7),
let inputData = privateKeyFinal.data(using: .utf8),
let encryptedBytes = try? aes.encrypt(inputData.bytes)
else {
setLoadingView(visible: false)
return
}
let encryptedData = NSData(bytes: encryptedBytes, length: encryptedBytes.count)
let base64String = encryptedData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
// Print keys and salt
print(publicKeyFinal)
print(base64String)
print(salt)
}
Note: I added a comment on each line that I thought is important, so you will now what's happening.

How can I generate an ed25519 key pair in OpenSSH format with swift?

I tried to generate the keys with different approaches, get the raw data but I can't find any library that transform these bytes into OpenSSH compatible public and private keys.
Approach 1:
let privateKeyParams: [String: Any] = [
kSecAttrIsPermanent as String: false,
]
let parameters: [String: Any] = [
String(kSecClass): kSecClassKey,
String(kSecAttrKeyType): kSecAttrKeyTypeECSECPrimeRandom,
String(kSecAttrKeySizeInBits): 256,
kSecPrivateKeyAttrs as String: privateKeyParams
]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(parameters as CFDictionary, &error) else {
throw error!.takeRetainedValue() as Error
}
guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
throw KeyGeneratorError(description: "Failed to generate the public key")
}
guard let privateKeyData = SecKeyCopyExternalRepresentation(privateKey, nil) as? Data else {
throw KeyGeneratorError(description: "Failed to extract data from key")
}
guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, nil) as? Data else {
throw KeyGeneratorError(description: "Failed to extract data from key")
}
Approach 2 (using CryptoKit):
let privateKey = Curve25519.KeyAgreement.PrivateKey()
let publicKey = privateKey.publicKey
let privateKeyData = privateKey.rawRepresentation
let publicKeyData = publicKey.rawRepresentation
Approach 3 (using SwCrypt library):
let (privateKey, publicKey) = try SwCrypt.CC.EC.generateKeyPair(256)
I always get an object of type "Data" but I cannot transform this data into an OpenSSH string of this type:
Private Key:
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAZ9c1i+q
9gMY51eotxWjeRAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIPAWeGw39CgOruIW
i0N/yji4+K/r/VPdwgg6BQD3e1Y5AAAAoJ9WV+HMYQx4bPyjTprzDQfLmcl3BiCC6hSe93
c29g2jd8zMSr4fVH0ECby6iaLxlwYbvRz+gdIqpI8MpxEJ+XBzWYNWM8r9tD7wS4rHICXg
rjXnXfq6OUfiq+RzYFrn6mLYE3PwA//IsNyrnk9tHk9y4E0dj1JlYHQ4Y5LBOg3DsosTNo
qTVa9+7XrCuSxDMaXqVMgSMbBo0H8bolX/4uM=
-----END OPENSSH PRIVATE KEY-----
Public Key:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPAWeGw39CgOruIWi0N/yji4+K/r/VPdwgg6BQD3e1Y5

How to Generate RSA Public key using exponent and modulus in SWIFT?

I am trying to encrypt the AES key with RSAEncryption, I have created a Function for both, AES Encryption will first encrypt the DATA and then call the RSAEncryption to encrypt the AES Encryption Key itself and then send it to the server for response.
Below is what i tried, right now i am just checking if the public key that i am creating on runtime to encrypt matches with the public key that it should be, when i run the program, i print the pem representation of public key corresponding to the modulus and exponent, the prefix of the RSA public key is missing 32 characters from it.
CODE:
import Foundation
import Alamofire
import SwiftyRSA
class Encryption {
private let rsaModulus: String = ""
private let rsaExponent: String = ""
public func rsaEncryption(password: String, data: Data, modulus: String, exponent: String) -> String? {
if let modData = Data(base64Encoded: modulus),
let expData = Data(base64Encoded: exponent),
let keyData = Rsa.generatePublicKey(withModulus: modData, exponent: expData) {
let keyDict:[NSObject:NSObject] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits: NSNumber(value: 2048),
kSecReturnPersistentRef: true as NSObject
]
let publicKey = SecKeyCreateWithData(keyData as CFData, keyDict as CFDictionary, nil)
let pub = try! PublicKey(reference: publicKey!)
let pem = try! pub.pemString()
let clear = try! ClearMessage(string: "Clear Text", using: .utf8)
let encrypted = try! clear.encrypted(with: pub, padding: .PKCS1)
print("Encrypted AES Key: \(encrypted.base64String)")
print(separator: "\n")
print(pem) //Print pem to check public key generated
}
return "NO"
}
}
Result Output :
Encrypted AES Key: /.../
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAvvJwQMvjUI7DDnC2PYwGNGJrkq6acdkjIW1+WznI68FyfVWb15Gz
hiJ7IVVbPo1Rphkbr0Gs8vrkunwBxVIb1wCjiiwqdoR7EUvUHrk5WkNcSoNEu2l0
VnRVuFe+XTKrnQsgfRy2TzyW3eG2kOkQBHWwJCQT6pFOwLXhY4JwSBzdhPIUk7HM
20ntqmamMnKMEmEC2N+qOfBx2hKVv2s7bwGBI8NZoIdt6dbhFXgv5NWN+U9Mx3kd
mXpjz7CaGgxfbBhTy6SfJQzN0Exfv4VGxOHRDkO0Mmu/d2VIeT4Q8lM53YZWuyCm
/pZ4XCgi9Z/2ZmQmCszSOc2/495JQcV9ZQIDAQAB
-----END RSA PUBLIC KEY-----
Actual Output of public key should be :
-----BEGIN PUBLIC KEY-----
**MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A**MIIBCgKCAQEAvvJwQMvjUI7DDnC2PYwG
NGJrkq6acdkjIW1+WznI68FyfVWb15GzhiJ7IVVbPo1Rphkbr0Gs8vrkunwBxVIb
1wCjiiwqdoR7EUvUHrk5WkNcSoNEu2l0VnRVuFe+XTKrnQsgfRy2TzyW3eG2kOkQ
BHWwJCQT6pFOwLXhY4JwSBzdhPIUk7HM20ntqmamMnKMEmEC2N+qOfBx2hKVv2s7
bwGBI8NZoIdt6dbhFXgv5NWN+U9Mx3kdmXpjz7CaGgxfbBhTy6SfJQzN0Exfv4VG
xOHRDkO0Mmu/d2VIeT4Q8lM53YZWuyCm/pZ4XCgi9Z/2ZmQmCszSOc2/495JQcV9
ZQIDAQAB
-----END PUBLIC KEY-----
Characters between stars are missing from the result output.

SecKeyRawSign returning error -1, "generic error"?

I am working on an iOS application that needs to generate a key pair on the device, store the private key in the Secure Enclave, and then access it later to use for signing (does not need to be exported, ever). When I sign, I always hash the data using SHA256, following a couple of stack Overflow answers, and it seems to be working when I print the results. However, after obtaining a valid private key reference from the KeyChain, hashing the data to be signed, and specifying that it is a SHA256 hash, SecKeyRawSign still returns -1. This is just listed as a 'generic error', and my setup seems like it should be valid. Some insight on what's going wrong would be greatly appreciated. Here are my methods to generate and sign:
private func genKeyPair() -> (privateAlias: String, publicKey: NSData)? {
// Generate a keyhandle, which will be returned as an alias for the private key
let numBytes = Int(keyHandleLength)
var randomBytes = [UInt8](count: numBytes, repeatedValue: 0)
SecRandomCopyBytes(kSecRandomDefault, numBytes, &randomBytes)
let data = NSData(bytes: &randomBytes, length: numBytes)
let alias = data.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
let access = SecAccessControlCreateWithFlags(nil, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, .TouchIDCurrentSet, nil)!
// Key pair parameters
var keyParams: [String:AnyObject] = [
kSecAttrKeyType as String: kSecAttrKeyTypeEC,
kSecAttrKeySizeInBits as String: 256
]
// Private key parameters
keyParams[kSecPrivateKeyAttrs as String] = [
kSecAttrIsPermanent as String: true,
kSecAttrLabel as String: alias,
kSecAttrApplicationTag as String: applicationTag,
kSecAttrAccessControl as String: access
]
// Public key parameters
keyParams[kSecPublicKeyAttrs as String] = [
kSecAttrIsPermanent as String: true,
kSecAttrLabel as String: alias + "-pub",
kSecAttrApplicationTag as String: applicationTag
]
var pubKeyRef, privKeyRef: SecKey?
var err = SecKeyGeneratePair(keyParams, &pubKeyRef, &privKeyRef)
guard let _ = pubKeyRef where err == errSecSuccess else {
print("Error while generating key pair: \(err).")
return nil
}
// Export the public key for application use
let query = [
kSecClass as String: kSecClassKey,
kSecAttrLabel as String: alias + "-pub",
kSecAttrKeyType as String: kSecAttrKeyTypeEC,
kSecReturnData as String: true
]
var pubKeyOpt: AnyObject?
err = SecItemCopyMatching(query, &pubKeyOpt)
if let pubKey = pubKeyOpt as? NSData where err == errSecSuccess {
print("Successfully retrieved public key!")
return (alias, pubKey)
} else {
print("Error retrieving public key: \(err).")
return nil
}
}
private func sign(bytes data: NSData, usingKeyWithAlias alias: String) -> NSData? {
let query = [
kSecClass as String: kSecClassKey,
kSecAttrLabel as String: alias,
kSecAttrApplicationTag as String: applicationTag,
kSecAttrKeyType as String: kSecAttrKeyTypeEC,
kSecReturnRef as String: true
]
var privateKey: AnyObject?
var error = SecItemCopyMatching(query, &privateKey)
guard error == errSecSuccess else {
print("Could not obtain reference to private key with alias \"\(alias)\", error: \(error).")
return nil
}
print("\nData: \(data)")
print("Length: \(data.length)")
let hashedData = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))!
CC_SHA256(data.bytes, CC_LONG(data.length), UnsafeMutablePointer(hashedData.mutableBytes))
print("\nHashed data: \(hashedData)")
print("Length: \(hashedData.length)")
var signedHashLength = SecKeyGetBlockSize(privateKey as! SecKeyRef)
let signedHash = NSMutableData(length: signedHashLength)!
error = SecKeyRawSign(privateKey as! SecKeyRef, .PKCS1SHA256, UnsafePointer<UInt8>(hashedData.mutableBytes), hashedData.length, UnsafeMutablePointer<UInt8>(signedHash.mutableBytes), &signedHashLength)
print("\nSigned hash: \(signedHash)")
print("Length: \(signedHashLength)\n")
guard error == errSecSuccess else {
print("Failed to sign data, error: \(error).")
return nil
}
return signedHash
}

Error: "Block size and Initialization Vector must be the same length!"

I am trying to Encrypt some data and I keep getting this error,
Meanwhile my iv and key are at the same size but contain 29 values...This is my code. When I try a key with 16 values it works but I got to work with this other key too and I don't know how to solve it. Can somebody help me?
class AESHelper {
var key: String
var iv : String
let BLOCK_SIZE = AES.blockSize
init(key: String, iv: String){
self.key = key
self.iv = iv
}
func encrypt(stringToEncrypt: String) -> String {
let messageData = stringToEncrypt.dataUsingEncoding(NSUTF8StringEncoding)
let byteArray = messageData!.arrayOfBytes()
let encryptedBytes = try! AES(key: self.key, iv: self.iv, blockMode: .CBC).encrypt(byteArray, padding: PKCS7())
let toBase64 = NSData(bytes: encryptedBytes).base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
return toBase64
}