SecKey (PCKS1) to Base64 (PCKS8) from p12 - swift

I have looked at other answers on StackOverflow and I didn't find what I was looking for (IOS11). I have a SecKey (privateKey) that when I print:
SecKeyRef algorithm id: 1, key type: RSAPrivateKey, version: 4, block size: 2048 bits, addr: 0x1d0223f60
I have tried to convert it to Data and from there to Base64
let password = "1234"
let p12data = NSData(contentsOfFile: urls[0].path)!
var importResult: CFArray? = nil
let err = SecPKCS12Import(p12data as NSData,[kSecImportExportPassphrase as String: password] as NSDictionary,&importResult )
//GET IDENTITY
let identityDictionaries = importResult as! [[String:Any]]
var privateKey: SecKey?
//GET SECKEY
SecIdentityCopyPrivateKey(identityDictionaries[0][kSecImportItemIdentity as String] as! SecIdentity, &privateKey);
print(privateKey)
//Return data in PCKS1
let dataPrivate = SecKeyCopyExternalRepresentation(privateKey!, nil)
let b64Key:Data = dataPrivate as! Data
print(b64Key.base64EncodedString(options: .lineLength64Characters))
Apple documentation says that SecKeyCopyExternalRepresentation return PCKS1 data (https://developer.apple.com/documentation/security/1643698-seckeycopyexternalrepresentation) but I need PCKS8.
The result is a base64 from PCKS1 but I have to send it to a JAVA server to be processed and expected format is base64 from PCKS8.
is there a way to convert from PCKS1 to PCKS8 and then to base64?
I have found this article: https://blog.wingsofhermes.org/?p=42 that more or less is what I want but its in objective-c and I have not been able to convert it to swift

Related

Problem with Steam RSA Login implementation in Swift 5.0

So when logging in to steam, I need my password encrypted with RSA public key they provided. But when I encrypt with SecKeyEncryptedData and sent it for authentication, it says my password is incorrect. I think maybe it's a problem with the format of encryption but I cannot figure it out. Please help me with that.
static func encrypt(string: String, mod: String, exp: String) -> String? {
let keyString = self.rsaPublicKeyder(mod: mod, exp: exp)
guard let data = Data(base64Encoded: keyString) else { return nil }
var attributes: CFDictionary {
return [kSecAttrKeyType : kSecAttrKeyTypeRSA,
kSecAttrKeyClass : kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits : 2048,
kSecReturnPersistentRef : kCFBooleanTrue!] as CFDictionary
}
var error: Unmanaged<CFError>? = nil
guard let secKey = SecKeyCreateWithData(data as CFData, attributes, &error) else {
print(error.debugDescription)
return nil
}
guard let result = SecKeyCreateEncryptedData(secKey, SecKeyAlgorithm.rsaEncryptionPKCS1, string.data(using: .utf8)! as CFData, &error) else{
print(error.debugDescription)
return nil
}
return (result as Data).base64EncodedString()
}
}
The self.rsaPublicKeyder function's role is to convert the mod and exp format to PKCS#8 DER format and it can import with SecKeyCreateWithData. And I tried the result Der in cyberchef which seems no problem.
I tried using BigInt library to encrypt myself, but still failed. How????
class RSA{
static func encrypt(string: String, mod: String, exp: String) -> String
{
let secret = pkcs1pad2(data: string.data(using: .utf8)!, keySize: mod.count / 2)!
return secret.power(BigUInt(exp, radix: 16)!, modulus: BigUInt(mod, radix: 16)!).serialize().base64EncodedString()
}
static func pkcs1pad2(data: Data, keySize: Int) -> BigUInt?{
if (keySize < data.count + 11){
return nil;
}
var rndData: [UInt8] = [UInt8](repeating: 0, count: keySize - 3 - data.count)
let status = SecRandomCopyBytes(kSecRandomDefault, rndData.count, &rndData)
for i in 0..<rndData.count{
if rndData[i] == 0{
rndData[i] = UInt8(i+1)
}
}
guard status == errSecSuccess else{
return nil
}
return BigUInt(Data([0x00, 0x02]) + Data(rndData) + Data([0x00]) + data)
}
}
I have cost a bunch of time on this problem and I still don't know which part of my code is doing wrong. I have put all codes on Github. If you can help me with it I'll be so grateful.
https://github.com/MTAwsl/iAuth/tree/dev
I tried to debug myself using Fiddler and I found the answer.
Firstly, when a base64 string is transferred through HTTP GET method, it needs to be encoded by "%" encoding. So, String.addingPercentEncoding should be called to encode the string with proper encoding. But in CharacterSet.urlHostAllowed set, it did not include the "+" character so when the server decoding the data from base64, it treats "+" as space, which is definitely not what we wanted. I add the extension to String module and it solved the problem. Besides, yeah, both the BigInt and SecKey method works. There's nothing about the RSA encryption.
extension String{
var encoded: String? {
var urlB64Encoded: CharacterSet = .urlHostAllowed
urlB64Encoded.remove(charactersIn: "+")
return self.addingPercentEncoding(withAllowedCharacters: urlB64Encoded)
}
}

How to generate RSA private Key in Swift?

I have encrypted a text with a public key generated using modulus and exponent, I followed that link : https://meniny.cn/posts/RSA_public_key_with_modulus_and_exponent/
I want to decrypt that text with a private key generated using another modulus and exponent.I didn't find a function in Swift or Objective-C that solve this issue.
So I use a Java code to get the private key as String and generate also a pem file.
Here is my code :
let PRIVATE_KEY = "MIGxAgEAMA0GCSqGSIb3DQEBAQUABIGcMIGZAgEAAkBXIKDI5NbyZd/d5tO6djSv\rt8GDc7soyNaqSqZq/w9A/zxiZTA0uwnvYv9E+OXKS9yjPCqpu9d1ELzxQxU9KRFD\rAgEAAkAYEBbX5PvIboJpkrqfIM5kSWfUmj3ygaVn2r4jhtX7qS8+0v09fwifoeMP\r5TgmB2B8+47n8+MQ55/cKbMs2QpBAgEAAgEAAgEAAgEAAgEA\r"
let data = PRIVATE_KEY.data(using: String.Encoding.utf8)!
let priv = data.base64EncodedString()
let keyData = Data(base64Encoded: priv)!
let dict = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
kSecAttrKeySizeInBits : NSNumber(value : 128),
] as [CFString : Any]
let key = SecKeyCreateWithData(keyData as NSData, dict as NSDictionary, nil)
let blockSize = SecKeyGetBlockSize(key!)
var encrypted = [UInt8](repeating: 0, count: blockSize)
var encSize = blockSize
let status = SecKeyDecrypt(key!,
SecPadding.PKCS1, x,
x.count, &encrypted,
&encSize)
let decData = NSData(bytes: &encrypted, length: encrypted.count)
let decString = decData.base64EncodedString(options: NSData.Base64EncodingOptions())
I always get nil for key value.
I tried to find a solution but I am unable to get the suitable private key to decrypt correctly my text.
Any help please?

HMAC SHA256 in Swift 4

I have a string and a key, which i want to generate an HMAC SHA256 from it. Although i'm using 2 libs
IDZSwiftCommonCrypto and CryptoSwift
and this answer
Nothing really worked for me. My source of truth are those 2 websites
https://myeasywww.appspot.com/utility/free/online/Crypt_Decrypt-MD5-AES-HMAC-SHA-DES-RABBIT/en?command=UTILITY&ID=2
and
https://www.freeformatter.com/hmac-generator.html#ad-output
Which they always generate the correct hash key for my case.
Any idea in what can work here? Some code samples
For IDZSwiftCommonCrypto
func getHMacSHA256(forMessage message: String, key: String) -> String? {
let hMacVal = HMAC(algorithm: HMAC.Algorithm.sha256, key: key).update(string: message)?.final()
if let encryptedData = hMacVal {
let decData = NSData(bytes: encryptedData, length: Int(encryptedData.count))
let base64String = decData.base64EncodedString(options: .lineLength64Characters)
print("base64String: \(base64String)")
return base64String
} else {
return nil
}
}
And for CryptoSwift
let password: Array<UInt8> = Array(payload.utf8)
let salt: Array<UInt8> = Array("somekey".utf8)
let signedBody = try? HKDF(password: password, salt: salt, variant: .sha256).calculate()
But nothing really works like the sources of truth.Any idea?
If you target iOS 13.0+ or macOS 10.15+, use Apple's CryptoKit
import CryptoKit
let secretString = "my-secret"
let key = SymmetricKey(data: Data(secretString.utf8))
let string = "An apple a day keeps anyone away, if you throw it hard enough"
let signature = HMAC<SHA256>.authenticationCode(for: Data(string.utf8), using: key)
print(Data(signature).map { String(format: "%02hhx", $0) }.joined()) // 1c161b971ab68e7acdb0b45cca7ae92d574613b77fca4bc7d5c4effab89dab67
I've been using this:
import Foundation
enum CryptoAlgorithm {
case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
var HMACAlgorithm: CCHmacAlgorithm {
var result: Int = 0
switch self {
case .MD5: result = kCCHmacAlgMD5
case .SHA1: result = kCCHmacAlgSHA1
case .SHA224: result = kCCHmacAlgSHA224
case .SHA256: result = kCCHmacAlgSHA256
case .SHA384: result = kCCHmacAlgSHA384
case .SHA512: result = kCCHmacAlgSHA512
}
return CCHmacAlgorithm(result)
}
var digestLength: Int {
var result: Int32 = 0
switch self {
case .MD5: result = CC_MD5_DIGEST_LENGTH
case .SHA1: result = CC_SHA1_DIGEST_LENGTH
case .SHA224: result = CC_SHA224_DIGEST_LENGTH
case .SHA256: result = CC_SHA256_DIGEST_LENGTH
case .SHA384: result = CC_SHA384_DIGEST_LENGTH
case .SHA512: result = CC_SHA512_DIGEST_LENGTH
}
return Int(result)
}
}
extension String {
func hmac(algorithm: CryptoAlgorithm, key: String) -> String {
let str = self.cString(using: String.Encoding.utf8)
let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8))
let digestLen = algorithm.digestLength
let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
let keyStr = key.cString(using: String.Encoding.utf8)
let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8))
CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)
let digest = stringFromResult(result: result, length: digestLen)
result.deallocate(capacity: digestLen)
return digest
}
private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
let hash = NSMutableString()
for i in 0..<length {
hash.appendFormat("%02x", result[i])
}
return String(hash).lowercased()
}
}
You'll need to add #import <CommonCrypto/CommonHMAC.h> to your Objective-C bridging header.
Source: #thevalyreangroup on this github thread
You're doing it wrong with CryptoSwift.
For future readers, here's how to do it:
let result = try! HMAC(key: key, variant: .sha256).authenticate(message.bytes)
Swift 4.2 solution for HMAC encryption
Not so long ago I had the same problem, so I wrote simple framework for use in Swift on all platforms - iOS macOS and tvOS
It's called EasyCrypt and you can find it here:
https://github.com/lukszar/EasyCrypt
This framework let you encrypt message with your key, using HMAC algorithms.
Usage is simple, like following:
let crypto = EasyCrypt(secret: "mySecretKey", algorithm: .sha256)
let result = crypto.hash("This is very secret text to encrypt")
let otherResult = crypto.hash("This is another secret text to encrypt")
print("result: ", result)
print("otherResult: ", otherResult)
You can fast install using Carthage. Inside project you can find Playground for demo usage with instructions.

How to cast decrypted UInt8 to String?

I am using CryptoSwift to encrypt data. I am learning how to use it however I cannot get past the first basic tutorial. I am unable to convert the encrypted data back to a String - which kind of defeats the purpose of encrypting it in the first place if I cannot legibly decrypt the data
Code:
let string = "Hi. This is Atlas"
let input: [UInt8] = Array(string.utf8)
print(input)
let key: [UInt8] = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
let iv: [UInt8] = AES.randomIV(AES.blockSize)
do {
let encryptedBytes: [UInt8] = try AES(key: key, iv: iv, blockMode: .CBC).encrypt(input, padding: PKCS7())
print(encryptedBytes)
let decrypted: [UInt8] = try AES(key: key, iv: iv, blockMode: .CBC).decrypt(encryptedBytes, padding: PKCS7())
print(decrypted) // << need to convert this array of byted to a string (should be equal to original input)
} catch {
} catch {
}
Thank you for the help
You'll want Foundation to decode the UTF8 for you since there's no way to generate a String.UTF8View directly. So convert to NSData first.
let decrypted: [UInt8] = [0x48, 0x65, 0x6c, 0x6c, 0x6f]
let data = NSData(bytes: decrypted, length: decrypted.count)
let str = String(data: data, encoding: NSUTF8StringEncoding)
If you want to do it without Foundation, you can, but it's a little work. You have to manage the decoding yourself.
extension String {
init?(utf8Bytes: [UInt8]) {
var decoder = UTF8()
var g = utf8Bytes.generate()
var characters: [Character] = []
LOOP:
while true {
let result = decoder.decode(&g)
switch result {
case .Result(let scalar): characters.append(Character(scalar))
case .EmptyInput: break LOOP
case .Error: return nil
}
}
self.init(characters)
}
}
let unicode = String(utf8Bytes: bytes)
(I'm very surprised that this isn't built into Swift stdlib since it's so common and can be quickly built out of other parts of Swift stdlib. Often when that's the case, there's a reason that I'm just not aware of yet, so there may be some subtle problem with my approach here.)
let stringDecrypted = String(decrypted.map { Character(UnicodeScalar($0)) })
So it maps each UInt8 to UnicodeScalar and then to Character. After that it uses String's initializer to create String from array of Characters.

How do I export a public key <SecKey> that was generated using SecKeyGeneratePair to be used on a server?

I generated a keeper using SecKeyGeneratePair.
var publicKeyPtr, privateKeyPtr: Unmanaged<SecKey>?
let publicKeyParameters: [String: AnyObject] = [
kSecAttrIsPermanent: true,
kSecAttrApplicationTag: "com.example.site.public"
]
let privateKeyParameters: [String: AnyObject] = [
kSecAttrIsPermanent: true,
kSecAttrApplicationTag: "com.example.site.private"
]
let parameters: [String: AnyObject] = [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits: 2048,
kSecPublicKeyAttrs.takeUnretainedValue() as String: publicKeyParameters,
kSecPrivateKeyAttrs.takeUnretainedValue() as String: privateKeyParameters
]
let result = SecKeyGeneratePair(parameters, &publicKeyPtr, &privateKeyPtr)
let publicKey = publicKeyPtr!.takeRetainedValue()
let privateKey = privateKeyPtr!.takeRetainedValue()
let blockSize = SecKeyGetBlockSize(publicKey)
If I print out the publicKey I can see the modulus, which I'm pretty sure is what I need:
publicKey: <SecKeyRef algorithm id: 1, key type: RSAPublicKey, version: 3, block size: 2048 bits, exponent: {hex: 10001, decimal: 65537}, modulus: B2A7BD90C909F8084AD5B34040ABDAF7D1A6AFBADB35F3B6AB5CDDAB473449B0F175DEA32A7476F339D98F4AB3716AA2C1476D4009A80574B984DDFA1EF1A2550E48C46791CEFBFC39EF281049AA74E4C734C3B2A7B3F621B8A41F8B6689C4978696690D4EF9FFF0F90DB85C8ECBCF721FB7652AD7B337880A09D97EA736008C3ADBB72223F18C522C0C0889B05122561042D8637D1CBEF8F9F5AE88CDC43E411AA217E2A81C2D812B46D01C3BDC2799DFF3EAD46BB092A566E18EE94F63C4690ECE806B993FDDAC3159BE2098C2428F24969C109E221D8F066BEE3530848DE328D888B4C7E701435EACB116F97BB77B9379EF818B4D280890262EE678B92705, addr: 0x144841a00>
But I cannot figure out how to export the key so I can send it to my server for use there.
From my understanding. A SecKey is stored in Keychain and is a pointer to it, the block size is the length of the key in the memory. So in theory I can extract it as NSData and then convert it to something my server can read. In theory I think that will work, I've hit a wall trying to do that in practice. All help will be greatly appreciated.
SecItemCopyMatching is for you:
var dataPtr:Unmanaged<AnyObject>?
let query: [String:AnyObject] = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: "com.example.site.public",
kSecReturnData: kCFBooleanTrue
]
let qResult = SecItemCopyMatching(query, &dataPtr)
// error handling with `qResult` ...
let publicKeyData = dataPtr!.takeRetainedValue() as NSData
// convert to Base64 string
let base64PublicKey = publicKeyData.base64EncodedStringWithOptions(nil)
Swift 4:
var dataPtr:CFTypeRef?
let query: [String: Any] = [
kSecClass as String: kSecClassKey,
kSecAttrApplicationTag as String: "com.example.site.public",
kSecReturnData as String: true
]
let qResult = SecItemCopyMatching(query as CFDictionary, &dataPtr)
// error handling with `qResult` ...
let data = dataPtr as! Data
let base64PublicKey = data.base64EncodedString()
Note that the size of the data is 270, not the same as block size of the key. See this question on the crypto.stackexchange.com.

Categories