CryptographicException: ASN1 corrupted data in F# - f#

I want to hash a message with the RSA class using f#.
Currently, I managed to create these few lines using the default randomly generated key pair.
let rsa = RSA.Create()
let dataInBytes = System.Text.Encoding.UTF8.GetBytes ("Message to be hashed.")
let bytes = rsa.SignData(dataInBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1)
In order to use my own private/public key pair I want to use .ImportPkcs8PrivateKey(). My code is:
let privKey = System.IO.File.ReadAllText #"C:\Users\User\Documents\FSharp\privkey.pem";;
val privKey: string =
"-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASC"+[1671 chars]
let convertFromBase64ToBytes (input: string) =
let str = match input.Length % 4 with
| 1 -> input.Substring (1, input.Length - 1)
| 2 -> input + string "=="
| 3 -> input + string "="
| _ -> input
let strReplaced = str
.Replace("-", "+")
.Replace("_", "/")
System.Convert.FromBase64String strReplaced
let privKeyBytes = convertFromBase64ToBytes privKey
> rsa.ImportPkcs8PrivateKey (privKeyBytes);;
but it throws me the error:
System.Security.Cryptography.CryptographicException: ASN1 corrupted data.
---> System.Formats.Asn1.AsnContentException: The encoded length exceeds the maximum supported by this library (Int32.MaxValue).
at System.Formats.Asn1.AsnDecoder.ReadLength(ReadOnlySpan`1 source, AsnEncodingRules ruleSet, Int32& bytesConsumed)
at System.Formats.Asn1.AsnDecoder.ReadEncodedValue(ReadOnlySpan`1 source, AsnEncodingRules ruleSet, Int32& contentOffset, Int32& contentLength, Int32& bytesConsumed)
at System.Security.Cryptography.CngPkcs8.ImportPkcs8PrivateKey(ReadOnlySpan`1 source, Int32& bytesRead)
--- End of inner exception stack trace ---
at System.Security.Cryptography.CngPkcs8.ImportPkcs8PrivateKey(ReadOnlySpan`1 source, Int32& bytesRead)
at System.Security.Cryptography.RSAImplementation.RSACng.ImportPkcs8PrivateKey(ReadOnlySpan`1 source, Int32& bytesRead)
at <StartupCode$FSI_0012>.$FSI_0012.main#() in C:\Users\User\PowerShell\stdin:line 26
Stopped due to error
What am I missing?

ImportPkcs8PrivateKey() expects a DER encoded key in PKCS#8 format. The posted key has the correct format, but is PEM encoded. The conversion from PEM to DER consists of removing the header, footer, all line breaks, and Base64 decoding the rest:
...
let derB64 = privKey.Replace("-----BEGIN PRIVATE KEY-----", "").Replace("-----END PRIVATE KEY-----", "").Replace("\r\n", "")
let privKeyBytes = Convert.FromBase64String derB64
rsa.ImportPkcs8PrivateKey privKeyBytes
...
As of .NET 5, the ImportFromPem() method is available, which can directly import the PEM encoded key.
The convertFromBase64ToBytes() method converts Base64url encoded data to Base64 encoded data and then performs a Base64 decoding.
Since PEM encoded keys are Base64 encoded (and not Base64url encoded) this method is actually not necessary, instead Convert.FromBase64String() can be applied directly (if it is used anyway, it has no effect, because already Base64 encoded data is not changed).

The format that begins / ends with -----BEGIN XXX----- and ends with a similar line is called PEM encoding.
You are missing the fact that the base 64 also contains newlines and - etc. (so the length calculation fails). You should not have to pad with = symbols - those are mandatory for PEM. Similarly, PEM uses normal base 64, so there is no need to do anything with - or _ characters, as those should not be present (in the base 64, i.e. outside the header / footer lines obviously). In short, the base 64 (url) decoding fails on multiple levels.
Probably best to use PEM specific encoding / decoding routines. You can find those here if you're using a new runtime, or otherwise you can use the Bouncy Castle libraries.

Related

How to access bytes in public key generated by SecKeyCreateRandomKey()?

I am trying to send encrypted messages using AES-GCM between an iOS app and an ESP microcontroller. But before I do that, I need to perform a key exchange between the two devices so that they both have a shared secret. So I am looking into iOS methods for generating private/public key pairs and there are several. However there is one problem, the keys generated by iOS aren't readily compatible with anything outside of iOS (as far as I understand) which means I have to jump in and make some modifications to the keys. I noticed the keys that are generated are always abdc1234 where abcd never changes and 1234 changes every time a key is generated. So I am assuming that abcd is just something iOS uses and 1234 is the actual key. At least, that's what happens in Android. So to test my hypothesis, I'm trying to get the raw bytes of the public key that was generated in iOS so I can cut off the abcd and send the actual key 1234 to the ESP. The problem is I can't find how to access the bytes that the key contains. Here's what I tried so far...
let tag = "".data(using: .utf8)!
let attributes: [String: Any] = [
kSecAttrType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256,
kSecPrivateKeyAttrs as String: [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: tag
]
]
let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error)
let publicKey = SecKeyCopyPublicKey(privateKey!)
print("\(publicKey!)")
var address = Unmanaged.passUnretained(publicKey!).toOpaque()
do{
withUnsafeBytes(of: &address) { bytes in
for byte in bytes{
print(byte)
}
}
}
So the first print statement outputs the following
<SecKeyRef curve type: kSecECCurveSecp256r1, algorithm id: 3, key type: ECPublicKey, version: 4, block size: 256, bits, y: ..., x: ..., addr: 0x...>
Which so far so good, the key that I want is on the curve that I want and the key is the correct length and such.
But now the next print outputs the following
144
70
215
9
1
0
0
0
That's it. Clearly, the last 8 things to be printed are not the key, it's too short. So yeah, how do I extract the x and y value of the public key. I can see it printed so there must be a way to access it but yeah I have searched everywhere and no dice. My theory that I can "chop" off the "iOS" part of the generated key might not even be correct but the thing is I can't even test it without being able to send the bytes to the ESP. I hope there is an easier way to achieve the key exchange between iOS app and ESP but for now, this is the only way I know how. Oh yeah, I send the key bytes over bluetooth, I was able to connect the iOS app to the ESP via bluetooth and that's how I'm trying to make the key exchange. I know bluetooth is techncially encrypted but I just want to do the key exchange and then encrypt the bluetooth messages further by using AES-GCM. So please. If you know of a way to access the key bytes, please share!
UPDATE:
I use this code to generate key pair for ECDH key exchange:
let attributes: [String: Any] = [
kSecAttrType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256,
kSecPrivateKeysAttrs as String: [
kSecAttrIsPermanent as String: true
]
]
var error: Unmanaged<CFError>?
privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error)!
publicKey = SecKeyCopyPublicKey(privateKey!)!
let pubKeyExternRep = SecKeyCopyExternalRepresentation(publicKey!, &error)
print("\(publicKeyExternRep!)")
Which outputs the following:
{length = 65, bytes = 0x048820d8 0482e62f 7abac673 02d8a68e ... 6e0117684
ff455540 }
I am trying to get everything after the 0x04 in the bytes section to be in a character array so that I can send it over bluetooth in a sequence of packets. So it is imperative that I obtain everything after the 0x04, all 128 characters, and have it in a byte array. In the end, if I print the contents of the byte array, it should just say 8820d80482e62f ... 6e0117684ff455540.
I have tried the following:
//^^^ previous code block that generates the key pair ^^^
let length = CFDataGetLength(pubKeyExternRep!)
let range = CFRange.init(location: 0, length: length)
let uint8Pointer = UnsafeMutablePointer<UInt8>.allocate(capacity:length)
CFDataGetBytes(pubKeyExternRep!, range, uint8Pointer)
print("\(uint8Pointer.pointee)")
This outputs the following:
4
Which seems promising since it matches the first 4 of the key, the 0x04 but then I move up the pointer by switching location: 5 and it prints a random two digit number that doesn't match with anything useful, i.e. 68 or something like that.
I also tried this:
^^^ previous code block that generates the key pair ^^^
let keyData = pubKeyExternRep as Data?
let dataString = keyData?.base64EncodedString()
var byteArray: [UInt8] = []
byteArray = Array(dataString!.utf8)
print("\(byteArray)")
Now this gets me extremely close to what I need, it's a byte array and I can perform operations on it and concatenate stuff and such, if I print it outputs this:
[66, 79, 76, ..., 119, 81, 61]
The problem is not only do the members of the array not match any of the 128 bytes in the pubKeyExternRep but its also shorter than the 128 bytes I need. It's 64 bytes. This is actually something I been noticing for both methods I tried (the CFDataGetBytes method and the String to Array method), whatever it is I get, it's never 128 bytes, it's always 64.
I just need all 128 bytes after the 0x04 when I print(publicKeyExternRep!)
UPDATE 2:
Found solution, it's not the one that's marked but that did technically answer the question. Will post solution after work!
Just use SecKeyCopyExternalRepresentation(_:_:). Encoded format is not as what is commonly referred to as X9.63 by the way, it is a flat uncompressed public key point without the parameters of the named curve.

Wireshark Lua dissector utf16 string

I am writing a custom Wireshark Lua dissector. One field in the dissector is a UTF16 string. I tried to specify this field with
msg_f = ProtoField.string("mydissector.msg", "msg", base.UNICODE)
local getMsg = buffer(13) -- starting on byte 13
subtree:add_le(m.msg_f, getMsg)
However, this only adds the first character rather than the whole string. It also raises an Expert Info warning undecoded trailing/stray characters.
What is the correct way to parse a UTF16 string?
You haven't specified the range of bytes that comprises the string. This is typically determined by either an explicit length field or by a NULL-terminator. The exact method of determining the range is dependent upon the particular protocol and field in question.
An example of each type:
If there's a length field, say of 1 byte in length that precedes the string, then you can use something like:
local str_len = buffer(13, 1):le_uint()
subtree:add_le(m.msg_len_f, buffer(13))
if str_len > 0 then
subtree:add_le(m.msg_f, buffer(14, str_len))
end
And if the string is NULL-terminated, you can use something like:
local str = buffer(13):stringz()
local str_len = str:len()
subtree:add_le(m.msg_f, buffer(13, str_len + 1))
These are just pseudo-examples, so you'll need to apply whatever method, possibly none of these, to fit your data.
Refer to the Wireshark's Lua API Reference Manual for more details, or to the Wireshark LuaAPI wiki pages.
The solution I came up with is simply:
msg_f = ProtoField.string("mydissector.msg", "msg")
local getMsg = buffer(13) -- starting on byte 13
local msg = getMsg:le_ustring()
subtree:add(msg_f, getMsg, msg)

How to set a 24 byte length key to elixir/erlang block_encrypt/4 function using des_ede3 as mode of encryption

Am trying to send data to an external api that expects encrypted data using 3DES encryption but am having issues understanding how to pass my api key as the key field to erlangs des_ede3 cipher.
According to erlangs cipher docs des_ede3 expects 3 keys that are all 8 bytes in length. How can i pass my 24 byte api key as the key to elixir/erlang :cryptoblock_encrypt/4 function
*** how can i pass key to block_encrypt/4 ***
key = "123456789012345678901234"
data = "hello world! The world is yours"
block_size = 8
cipher = :crypto.block_encrypt(:des_ede3, [key, key, key], iv, pad(data, block_size))
How do i pass my 24 byte api key as the key to erlangs block_encrypt/4 in order for me to pass data to the external api.
Thanks
Use binary pattern matching:
<<k1 :: binary-size(8),
k2 :: binary-size(8),
k3 :: binary-size(8)>> = "123456789012345678901234"
#⇒ "123456789012345678901234"
{k1, k2, k3}
#⇒ {"12345678", "90123456", "78901234"}

Convert first two bytes of Lua string (in bigendian format) to unsigned short number

I want to have a lua function that takes a string argument. String has N+2 bytes of data. First two bytes has length in bigendian format, and rest N bytes contain data.
Say data is "abcd" So the string is 0x00 0x04 a b c d
In Lua function this string is an input argument to me.
How can I calculate length optimal way.
So far I have tried below code
function calculate_length(s)
len = string.len(s)
if(len >= 2) then
first_byte = s:byte(1);
second_byte = s:byte(2);
//len = ((first_byte & 0xFF) << 8) or (second_byte & 0xFF)
len = second_byte
else
len = 0
end
return len
end
See the commented line (how I would have done in C).
In Lua how do I achieve the commented line.
The number of data bytes in your string s is #s-2 (assuming even a string with no data has a length of two bytes, each with a value of 0). If you really need to use those header bytes, you could compute:
len = first_byte * 256 + second_byte
When it comes to strings in Lua, a byte is a byte as this excerpt about strings from the Reference Manual makes clear:
The type string represents immutable sequences of bytes. Lua is 8-bit clean: strings can contain any 8-bit value, including embedded zeros ('\0'). Lua is also encoding-agnostic; it makes no assumptions about the contents of a string.
This is important if using the string.* library:
The string library assumes one-byte character encodings.
If the internal representation in Lua of your number is important, the following excerpt from the Lua Reference Manual may be of interest:
The type number uses two internal representations, or two subtypes, one called integer and the other called float. Lua has explicit rules about when each representation is used, but it also converts between them automatically as needed.... Therefore, the programmer may choose to mostly ignore the difference between integers and floats or to assume complete control over the representation of each number. Standard Lua uses 64-bit integers and double-precision (64-bit) floats, but you can also compile Lua so that it uses 32-bit integers and/or single-precision (32-bit) floats.
In other words, the 2 byte "unsigned short" C data type does not exist in Lua. Integers are stored using the "long long" type (8 byte signed).
Lastly, as lhf pointed out in the comments, bitwise operations were added to Lua in version 5.3, and if lhf is the lhf, he should know ;-)

Encryption compatibility between Obj-C and VB.Net

There are about 100,000 different possible ways to encrypt a string. Using standards like AES, CBC, and PKCS7 make things easier - but there are still problems with IVs. Salts, Encoding, etc. (As noted on www.Crypto.Stackexchange.com)
For my iOS project (Obj-C), I'm using the FBEncryptor project which is an easy and well documented way to encrypt strings. However, I need code that will be able to decrypt that iOS AES string generated by FBEncryptor on a Windows platform - preferably VB.NET.
I have yet to find another project for VB.NET that is compatible with FBEncryptor.
Does anyone know of any VB.NET encryption projects that work with FBEncryptor? If not, then what do I need to do to make the Rijndael Managed library (provided by Microsoft) work with FBEncryptor?
My VB.NET Code using Rijndael Managed:
Imports System.Security
Imports System.Security.Cryptography
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Text.RegularExpressions
Imports System.Text
Module Encrypt2
Public saltBytes As Byte()
Dim hashAlgorithm As String = "SHA1"
Public Sub GenerateSalt()
' Define min and max salt sizes.
Dim minSaltSize As Integer
Dim maxSaltSize As Integer
minSaltSize = 8
maxSaltSize = 8
' Generate a random number for the size of the salt.
Dim random As Random
random = New Random()
Dim saltSize As Integer
saltSize = random.Next(minSaltSize, maxSaltSize)
' Allocate a byte array, which will hold the salt.
saltBytes = New Byte(saltSize - 1) {}
' Initialize a random number generator.
Dim rng As RNGCryptoServiceProvider
rng = New RNGCryptoServiceProvider()
' Fill the salt with cryptographically strong byte values.
rng.GetNonZeroBytes(saltBytes)
End Sub
Public Function Encrypt(ByVal plainText As String, ByVal keyword As String) As String
Dim hashAlgorithm As String = "SHA1"
Dim passwordIterations As Integer = 128
Dim initVector As String = "#7781157e2629b09"
Dim keySize As Integer = 256
Dim initVectorBytes As Byte() = Encoding.ASCII.GetBytes(initVector)
Dim plainTextBytes As Byte() = Encoding.ASCII.GetBytes(plainText)
GenerateSalt()
Dim password As New Rfc2898DeriveBytes(keyword, saltBytes, passwordIterations)
Dim keyBytes As Byte() = password.GetBytes(keySize \ 8)
Dim symmetricKey As New RijndaelManaged()
symmetricKey.Mode = CipherMode.CBC
Dim encryptor As ICryptoTransform = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes)
Dim memoryStream As New MemoryStream()
Dim cryptoStream As New CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length)
cryptoStream.FlushFinalBlock()
Dim cipherTextBytes As Byte() = memoryStream.ToArray()
memoryStream.Close()
cryptoStream.Clear()
Dim cipherText As String = Convert.ToBase64String(cipherTextBytes)
Return (cipherText)
End Function
Public Function Decrypt(ByVal cipherText As String, ByVal keyword As String) As String
Dim hashAlgorithm As String = "SHA1"
Dim passwordIterations As Integer = 128
Dim initVector As String = "#7781157e2629b09"
Dim keySize As Integer = 256
' Convert strings defining encryption key characteristics into Byte
' arrays. Let us assume that strings only contain ASCII codes.
' If strings include Unicode characters, use Unicode, UTF7, or UTF8
' encoding
Dim initVectorBytes As Byte() = Encoding.ASCII.GetBytes(initVector)
' Convert the Ciphertext into a Byte array
Dim cipherTextBytes As Byte() = Encoding.ASCII.GetBytes(cipherText)
' First we must create a password, from which the key will be
' derived. This password will be generated from the specified
' passphrase and salt value. The password will be created using
' the specified hash algorithm. Password creation can be done in
' several iterations
GenerateSalt()
Dim password As New Rfc2898DeriveBytes(keyword, saltBytes, passwordIterations)
' Use the password to generate pseudo-random bytes for the encryption
' key. Specify the size of the key in bytes (instead of bits)
Dim keyBytes As Byte() = password.GetBytes(keySize \ 8)
' Create uninitialized Rijndael encryption object
Dim symmetricKey As New RijndaelManaged()
' It is reasonable to set encryption mode to Cipher Block Chaining
' (CBC). Use default options for other symmetric key parameters.
symmetricKey.Mode = CipherMode.CBC
' Generate decryptor from the existing key bytes and initialization
' vector. Key size will be defined based on the number of the key
'bytes
Dim decryptor As ICryptoTransform = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes)
' Define memory stream which will be used to hold encrypted data.
Dim memoryStream As New MemoryStream(cipherTextBytes)
' Define cryptographic stream (always user Read mode for encryption).
Dim cryptoStream As New CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)
' Since at this point we don't know what the size of decrypted data
' will be, allocate the buffer long enough to hold ciphertext;
' plaintext is never longer than ciphertext
Dim plainTextBytes As Byte() = New Byte(cipherTextBytes.Length - 1) {}
' Start decrypting.
Dim decryptedByteCount As Integer = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length)
' Close both streams
memoryStream.Close()
cryptoStream.Close()
' Convert decrypted data into a string
' Let us assume that the original plaintext string was UTF8-Encoded.
Dim plainText As String = Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount)
' Return the decrypted string
Return (plainText)
End Function
End Module
You'll notice that the IV is static and that the password iterations are low. This is because I'm not sure how to generate the IV in the same method as FBEncryptor. You may also notice that when attempting to decrypt, there are problems with the byte length.
Any help will be appreciated! Thanks!
One note. I looked in FBEncryptor. It uses some bad practices. So, if security is important for you I would recommend read up a little bit more regarding that and choose another code which does encryption (or write it on your own).
FBEncryptor does following for encryption
a) Convert string to byte array (based on UTF8 encoding)
b) Convert key to byte array (based on UTF encoding).
c) Copy only first 16 bytes of key
d) Create IV which is 16 butes of 0x00
e) Encrypt byte array from item a) using AES/CBC/PKCS7Padding with IV created in d)
f) The resulting encrypted data is converted to Base64
And it does following for decryption
a) Convert encrypt string from Base64 to byte array
b) Convert key to byte array (based on UTF encoding).
c) Copy only first 16 bytes of key
d) Create IV which is 16 butes of 0x00
e) Decrypt byte array from item a) using AES/CBC/PKCS7Padding with IV created in d)
f) Convert encrypted data from e) to a string using UTF8 encoding
You should do exactly the same thing in VB.NET. I think you have almost all parts
- You will need to drop all SHA1 and salt related stuff and use equivalent of step b).
And you will need to research PKCS7Padding for VB.NET

Resources