How do I parse an x509 certificate and extract its key's signature in Swift? - 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.

Related

How to create SecKey from pem file in 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 {
...

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

Swift RSA Encryption Public Key to Java Server is failing

I am trying to create public base64 key from RSA Private key using Security framework. Here is snippet.
let tag = "com.example.keys.mykey"
public extension SecKey {
static func generateBase64Encoded2048BitRSAKey() throws -> (private: String, public: String) {
let type = kSecAttrKeyTypeRSA
let attributes: [String: Any] =
[kSecAttrKeyType as String: type,
kSecAttrKeySizeInBits as String: 2048
]
var error: Unmanaged<CFError>?
guard let key = SecKeyCreateRandomKey(attributes as CFDictionary, &error),
let data = SecKeyCopyExternalRepresentation(key, &error) as Data?,
let publicKey = SecKeyCopyPublicKey(key),
let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
throw error!.takeRetainedValue() as Error
}
return (private: data.base64EncodedString(), public: publicKeyData.base64EncodedString())
}
}
do {
let (pvtKey, pubKey) = try SecKey.generateBase64Encoded2048BitRSAKey()
print(pubKey)
} catch let error {
print(error)
}
This is the output
MIIBCgKCAQEA1ZafTYboquQbCTZMEb1IqHKIr8wiDjdn6e0toRajZCQo9W5zuTlEuctrjJJQ08HcOuK3BPFRaFTUP1RBFvnba/T2S1Mc6WVX81b0DmKS8aPJ83TvvQlH3bZjVqFzndXJHJatcXRkZKlbidNQYxV9OYFCRLwgR5PBoJ1P5tp8f8735vIADOBL/93nFywODSjAWLXcyG5tUyRlRGX7eDodL7jqVOFxVMB7K9UOJehPuJQiheykyPSbBSLE6raZbpCHlranTLdihWYFs2tYbxzNrVbXzgKIxDDjrhDLVFvo3beudKQcLQkSO+m2LJIDT91zAnxVQ075AIn80ZHh5kdyQQIDAQAB
But this public key is not getting accepted by our Java server. It is throwing exception for the same.
Here is java snippet
public static void main(String[] args) {
String pubKey = "MIIBCgKCAQEA1ZafTYboquQbCTZMEb1IqHKIr8wiDjdn6e0toRajZCQo9W5zuTlEuctrjJJQ08HcOuK3BPFRaFTUP1RBFvnba/T2S1Mc6WVX81b0DmKS8aPJ83TvvQlH3bZjVqFzndXJHJatcXRkZKlbidNQYxV9OYFCRLwgR5PBoJ1P5tp8f8735vIADOBL/93nFywODSjAWLXcyG5tUyRlRGX7eDodL7jqVOFxVMB7K9UOJehPuJQiheykyPSbBSLE6raZbpCHlranTLdihWYFs2tYbxzNrVbXzgKIxDDjrhDLVFvo3beudKQcLQkSO+m2LJIDT91zAnxVQ075AIn80ZHh5kdyQQIDAQAB";
PublicKey key = getPublic(pubKey);
}
public static PublicKey getPublic(String key) {
PublicKey pbKey = null;
try {
byte[] keyBytes = Base64.getDecoder().decode(key);
System.out.println(keyBytes.length);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
pbKey = factory.generatePublic(spec);
} catch (Exception e) {
e.printStackTrace();
}
return pbKey;
}
Here is the exception
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:205)
at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
at Main.getPublic(Main.java:40)
at Main.main(Main.java:28)
But the online PEM parser website - https://8gwifi.org/PemParserFunctions.jsp is accepting this public key, which is using bouncycastle library in the background to validate this base64 encoded public key.
The exception is thrown because the ASN.1 DER encoding of an RSA public key generated on iOS is represented with the RSAPublicKey type as defined by PKCS#1, while Java (and many other languages and tools) expect the DER encoding to be represented with the SubjectPublicKeyInfo type as defined by X.509. There are of course two sides where this problem can be solved. And if you choose to convert the DER encoding of the RSA public key at the iOS side, you could use this project I recently published on GitHub. The structure you may be interested in is RSAPublicKeyExporter, which uses the SimpleASN1Writer for converting the DER encoding. The code snippet below shows how to use it:
import RSAPublicKeyExporter
let publicKeyData = ... // Get external representation of RSA public key some how
let x509EncodedKeyData = RSAPublicKeyExporter().toSubjectPublicKeyInfo(publicKeyData)
The answer I posted here contains some information that may be useful in case the exported key is fetched from the keychain.
Thanks guys. Due to some issues with bouncycastle library, we did not used it in backend service. So in iOS, we are including ASN1 header.
struct ASN1 {
let type: UInt8
let length: Int
let data: Data
init?(type: UInt8, arbitraryData data: Data) {
guard data.count > 4 else {
return nil
}
var result = data
let byteArray = [UInt8](result)
for (_, v) in byteArray.enumerated() {
if v == type { // ASN1 SEQUENCE Type
break
}
result = Data(result.dropFirst())
}
guard result.count > 4 else {
return nil
}
guard
let first = result.advanced(by: 0).first, // advanced start from 7.0
let second = result.advanced(by: 1).first,
let third = result.advanced(by: 2).first,
let fourth = result.advanced(by: 3).first
else {
return nil
}
var length = 0
switch second {
case 0x82:
length = ((Int(third) << 8) | Int(fourth)) + 4
break
case 0x81:
length = Int(third) + 3
break
default:
length = Int(second) + 2
break
}
guard result.startIndex + length <= result.endIndex else { // startIndex, endIndex start from 7.0
return nil
}
result = result[result.startIndex..<result.startIndex + length]
self.data = result
self.length = length
self.type = first
}
var last: ASN1? {
get {
var result: Data?
var dataToFetch = self.data
while let fetched = ASN1(type: self.type, arbitraryData: dataToFetch) {
if let range = data.range(of: fetched.data) {
if range.upperBound == data.count {
result = fetched.data
dataToFetch = Data(fetched.data.dropFirst())
} else {
dataToFetch = Data(data.dropFirst(range.upperBound))
}
} else {
break
}
}
return ASN1(type: type, arbitraryData: result!)
}
}
static func wrap(type: UInt8, followingData: Data) -> Data {
var adjustedFollowingData = followingData
if type == 0x03 {
adjustedFollowingData = Data([0]) + followingData // add prefix 0
}
let lengthOfAdjustedFollowingData = adjustedFollowingData.count
let first: UInt8 = type
var bytes = [UInt8]()
if lengthOfAdjustedFollowingData <= 0x80 {
let second: UInt8 = UInt8(lengthOfAdjustedFollowingData)
bytes = [first, second]
} else if lengthOfAdjustedFollowingData > 0x80 && lengthOfAdjustedFollowingData <= 0xFF {
let second: UInt8 = UInt8(0x81)
let third: UInt8 = UInt8(lengthOfAdjustedFollowingData)
bytes = [first, second, third]
} else {
let second: UInt8 = UInt8(0x82)
let third: UInt8 = UInt8(lengthOfAdjustedFollowingData >> 8)
let fourth: UInt8 = UInt8(lengthOfAdjustedFollowingData & 0xFF)
bytes = [first, second, third, fourth]
}
return Data(bytes) + adjustedFollowingData
}
static func rsaOID() -> Data {
var bytes = [UInt8]()
bytes = [0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00]
return Data(bytes)
}
}
Then called this during generating public key of RSA in swift.
class func RSAPublicKeyBitsFromKey(_ secKey:SecKey) -> Data? {
var queryPublicKey:[String:AnyObject] = [:]
queryPublicKey[kSecClass as String] = kSecClassKey as NSString
queryPublicKey[kSecAttrKeyType as String] = kSecAttrKeyTypeRSA as NSString
if let publicKeyData = SwiftCrypto.publicKeyInData(queryPublicKey, secKey: secKey) {
let bitstringSequence = ASN1.wrap(type: 0x03, followingData: publicKeyData)
let oidData = ASN1.rsaOID()
let oidSequence = ASN1.wrap(type: 0x30, followingData: oidData)
let X509Sequence = ASN1.wrap(type: 0x30, followingData: oidSequence + bitstringSequence)
return X509Sequence
}
return nil
}
So, in this way, I had fixed this issue.

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
}

how to encrypt in swift like android cipher?

hi i am developing app in android and swift, encrypt in android using
public static String Encrypt(String text, String key)
throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] keyBytes= new byte[16];
byte[] b= key.getBytes("UTF-8");
int len= b.length;
if (len > keyBytes.length) len = keyBytes.length;
System.arraycopy(b, 0, keyBytes, 0, len);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(keyBytes);
cipher.init(Cipher.ENCRYPT_MODE,keySpec,ivSpec);
byte[] results = cipher.doFinal(text.getBytes("UTF-8"));
BASE64Encoder encoder;
encoder = new BASE64Encoder();
return encoder.encode(results);
}
and server side using RIJnadal lgorithm to get it but swift side i am using Cryptoswift library
let key = privateKey // length == 3
let iv = "0123456789" // lenght == 16
let s = string
let enc = try! s.aesEncrypt(key, iv: iv)
let dec = try! enc.aesDecrypt(key, iv: iv)
print(s) //string to encrypt
print("enc:\(enc)") //2r0+KirTTegQfF4wI8rws0LuV8h82rHyyYz7xBpXIpM=
print("dec:\(dec)") //string to encrypt
print("\(s == dec)") //true
it produce error Block size and Initialization Vector must be the same length!
how to fix
The difference between Rijndael and AES is that AES is a subset. AES has a block size of 128-bits (16-bytes) and key sizes of 128, 192 and 256 bits.
Here is some example Swift code using Common Crypto and the hardware encryption engine, note that using the hardware encryption engine is 500 to 1000 times faster than a pure code implementation:
Add Security.framework to the project
Add #import to the bridging header.
Swift 2.x:
func testCrypt(data data:[UInt8], keyData:[UInt8], ivData:[UInt8], operation:Int) -> [UInt8]? {
let cryptLength = size_t(data.count+kCCBlockSizeAES128)
var cryptData = [UInt8](count:cryptLength, repeatedValue:0)
let keyLength = size_t(kCCKeySizeAES128)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(CCOperation(operation),
algoritm,
options,
keyData, keyLength,
ivData,
data, data.count,
&cryptData, cryptLength,
&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.removeRange(numBytesEncrypted..<cryptData.count)
} else {
print("Error: \(cryptStatus)")
}
return cryptData;
}
Example from deprecated documentation section:
AES encryption in CBC mode with a random IV (Swift 3+)
The iv is prefixed to the encrypted data
aesCBC128Encrypt will create a random IV and prefixed to the encrypted code.
aesCBC128Decrypt will use the prefixed IV during decryption.
Inputs are the data and key are Data objects. If an encoded form such as Base64 if required convert to and/or from in the calling method.
The key should be exactly 128-bits (16-bytes), 192-bits (24-bytes) or 256-bits (32-bytes) in length. If another key size is used an error will be thrown.
PKCS#7 padding is set by default.
This example requires Common Crypto
It is necessary to have a bridging header to the project:
#import <CommonCrypto/CommonCrypto.h>
Add the Security.framework to the project.
This is example, not production code.
enum AESError: Error {
case KeyError((String, Int))
case IVError((String, Int))
case CryptorError((String, Int))
}
// The iv is prefixed to the encrypted data
func aesCBCEncrypt(data:Data, keyData:Data) throws -> Data {
let keyLength = keyData.count
let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
if (validKeyLengths.contains(keyLength) == false) {
throw AESError.KeyError(("Invalid key length", keyLength))
}
let ivSize = kCCBlockSizeAES128;
let cryptLength = size_t(ivSize + data.count + kCCBlockSizeAES128)
var cryptData = Data(count:cryptLength)
let status = cryptData.withUnsafeMutableBytes {ivBytes in
SecRandomCopyBytes(kSecRandomDefault, kCCBlockSizeAES128, ivBytes)
}
if (status != 0) {
throw AESError.IVError(("IV generation failed", Int(status)))
}
var numBytesEncrypted :size_t = 0
let options = CCOptions(kCCOptionPKCS7Padding)
let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
data.withUnsafeBytes {dataBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(kCCEncrypt),
CCAlgorithm(kCCAlgorithmAES),
options,
keyBytes, keyLength,
cryptBytes,
dataBytes, data.count,
cryptBytes+kCCBlockSizeAES128, cryptLength,
&numBytesEncrypted)
}
}
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.count = numBytesEncrypted + ivSize
}
else {
throw AESError.CryptorError(("Encryption failed", Int(cryptStatus)))
}
return cryptData;
}
// The iv is prefixed to the encrypted data
func aesCBCDecrypt(data:Data, keyData:Data) throws -> Data? {
let keyLength = keyData.count
let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
if (validKeyLengths.contains(keyLength) == false) {
throw AESError.KeyError(("Invalid key length", keyLength))
}
let ivSize = kCCBlockSizeAES128;
let clearLength = size_t(data.count - ivSize)
var clearData = Data(count:clearLength)
var numBytesDecrypted :size_t = 0
let options = CCOptions(kCCOptionPKCS7Padding)
let cryptStatus = clearData.withUnsafeMutableBytes {cryptBytes in
data.withUnsafeBytes {dataBytes in
keyData.withUnsafeBytes {keyBytes in
CCCrypt(CCOperation(kCCDecrypt),
CCAlgorithm(kCCAlgorithmAES128),
options,
keyBytes, keyLength,
dataBytes,
dataBytes+kCCBlockSizeAES128, clearLength,
cryptBytes, clearLength,
&numBytesDecrypted)
}
}
}
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
clearData.count = numBytesDecrypted
}
else {
throw AESError.CryptorError(("Decryption failed", Int(cryptStatus)))
}
return clearData;
}
Example usage:
let clearData = "clearData0123456".data(using:String.Encoding.utf8)!
let keyData = "keyData890123456".data(using:String.Encoding.utf8)!
print("clearData: \(clearData as NSData)")
print("keyData: \(keyData as NSData)")
var cryptData :Data?
do {
cryptData = try aesCBCEncrypt(data:clearData, keyData:keyData)
print("cryptData: \(cryptData! as NSData)")
}
catch (let status) {
print("Error aesCBCEncrypt: \(status)")
}
let decryptData :Data?
do {
let decryptData = try aesCBCDecrypt(data:cryptData!, keyData:keyData)
print("decryptData: \(decryptData! as NSData)")
}
catch (let status) {
print("Error aesCBCDecrypt: \(status)")
}
Example Output:
clearData: <636c6561 72446174 61303132 33343536>
keyData: <6b657944 61746138 39303132 33343536>
cryptData: <92c57393 f454d959 5a4d158f 6e1cd3e7 77986ee9 b2970f49 2bafcf1a 8ee9d51a bde49c31 d7780256 71837a61 60fa4be0>
decryptData: <636c6561 72446174 61303132 33343536>
Notes:
One typical problem with CBC mode example code is that it leaves the creation and sharing of the random IV to the user. This example includes generation of the IV, prefixed the encrypted data and uses the prefixed IV during decryption. This frees the casual user from the details that are necessary for CBC mode.
For security the encrypted data also should have authentication, this example code does not provide that in order to be small and allow better interoperability for other platforms.
Also missing is key derivation of the key from a password, it is suggested that PBKDF2 be used is text passwords are used as keying material.
For robust production ready multi-platform encryption code see RNCryptor.
Your IV has 10 bytes, not 16 (as you have in comment). Block size for AES is 16 bytes.