Signing/verifying a file with a PEM file

0 votes
asked Feb 17, 2017 by RandyDellinger (190 points)

I'm not having any success with Rebex Security to sign a file with a PEM file.

Details:
I'm trying to create a digital signature of a file so that after sending the file, the receiver can verify the authenticity.

I've accomplished this in a Windows program using the OpenSSL command line utility as follows:
Signing a file named filetosign:

openssl dgst -sha512 -sign priv.pem -passin pass:secretpassword -out sign.sha filetosign

This outputs a binary signature file, sign.sha.

My priv.pem looks like this, but uses a different key ;)

    -----BEGIN EC PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,91D4745C4011066F6833B5B57C0907C6

iyzXSqyTvq8DTIc6uJwi5PkP73R3waHLld95lZNHZX3gF94jUx9MwIvOeB4Aqu4x
Q17l/bMDXtcsdlROwvQogLCSpanb6hQJ8rpXdbI16Pq+Z8BZ5FISBi5V7edXTDBv
VXloguKoo0isl6f/xAM8zAU/AK0yqNHb73ukoc6wWUgR34I5XiXCYfWEF0d0f2JG
hIBvHGHxivbsAeuw2Km54U5tzlZ+bH8EhlFmoX8tMtPTS3BQTdS7rBS2PNE11CmK
mwjK+bmif7P0ccQGwqICEw3HdpJ4yMEtLqxS1/siIzw=
-----END EC PRIVATE KEY-----

I'll skip the verification details.

I'm trying to duplicate this on a different VB.net program running on a pocket PC with Windows Mobile 5 (.NET compact framework 2). I'm having difficulty getting OpenSSL running on this platform, so I'd thought I'd give Rebex a try since I've had good luck with Rebex SFTP.

I downloaded a trial version of Rebex Security and tried using the example in Cryptographic Message Syntax (PKCS #7) SignedData but it's unable to parse my PEM file. I also tried using an unencrypted PEM, and tried converting my PEM to DER, but no joy.

This line in the example throws the exception "Invalid certificate format":

Dim cert = Certificate.LoadDer(AppPath & "\priv.pem")

I also saw Rebex Secure Mail has a signing function, but I don't think this applies to simply signing a file.

Any help would be appreciated!
Randy

3 Answers

0 votes
answered Feb 22, 2017 by Lukas Pokorny (94,670 points)
edited Mar 22, 2017 by Lukas Pokorny
 
Best answer

In 2017 R2, we introduced a new API that makes it much easier to create or verify OpenSSL signatures.

Creating a signature:

' load private key
Dim key = New PrivateKeyInfo()
key.Load("priv.pem", "secretpassword")

' load content to sign
Dim content As Byte() = File.ReadAllBytes("filetosign")

' create signature
Dim signature As Byte()
Using alg = New AsymmetricKeyAlgorithm()
    ' import the key
    alg.ImportKey(key)

    ' specify signature parameters
    Dim parameters = New SignatureParameters()
    parameters.Format = SignatureFormat.Pkcs

    ' create signature
    signature = alg.SignMessage(content, parameters)
End Using

' save the signature
File.WriteAllBytes("sign.sha", signature)

Verifying a signature:

' load public key
Dim key = New PublicKeyInfo()
key.Load("pub.pem")

' load content to sign
Dim content As Byte() = File.ReadAllBytes("filetosign")

' load signature
Dim signature As Byte() = File.ReadAllBytes("sign.sha")

' verify signature
Dim valid As Boolean
Using alg = New AsymmetricKeyAlgorithm()
    ' import the key
    alg.ImportKey(key)

    Dim parameters = New SignatureParameters()
    parameters.Format = SignatureFormat.Pkcs

    valid = alg.VerifyMessage(content, signature, parameters)
End Using

' show the result
Console.WriteLine("Valid: {0}", valid)
commented Feb 22, 2017 by RandyDellinger (190 points)
I assume the above code only works in the the 2017 R2 beta? The verify code doesn't compile:
Type 'SignatureParameters' is not defined.
Name 'SignatureFormat' is not declared.
'VerifyMessage' is not a member of 'Rebex.Security.Cryptography.AsymmetricKeyAlgorithm'.
commented Feb 22, 2017 by Lukas Pokorny (94,670 points)
Sorry for the confusion, I just sent a link to 2017 R2 beta to your email.
0 votes
answered Feb 20, 2017 by Lukas Pokorny (94,670 points)
edited Feb 21, 2017 by Lukas Pokorny

Rebex SFTP can do this, but it's not as straightforward as it seems.

  • Your priv.pem file is an encrypted private key, not a certificate. This means you have to use PrivateKeyInfo (part of Rebex.Common) or SshPrivateKey (part of Rebex.Networking) to load it.

  • Your private key seems to be an elliptic curve private key. To be able to use such keys on Windows Mobile 5 platform, you have to load an ECC plugin.

  • OpenSSL's dgst command uses ANS.1 encoded form of the signature (not a PKCS #7 SignedData blob). We don't have public API for this form yet. However, as a workaround, we can create a signature in the format used by SSH and convert that into the form used by OpenSSL's dgst.

  • I don't know which elliptic curve algorithm your key actually uses. Based on the choice of "SHA-512" hashing algorithm, I will assume it's a NIST P-521 curve key. If my guess is incorrect, the code bellow would not work.

  • Please get the code working on a desktop platform first before trying it on Windows Mobile 5. If it doesn't work on desktop, it won't work on Pocket PC. (Don't forget to load an ECC plugin before trying this on the mobile platform.)

  • The code below needs to reference Rebex.Common and Rebex.Networking assemblies.

This will load the private key and filetosign, create a signature using the CreateEncodedSignature helper method (see below) and save it into a file:

' load private key
Dim key = New SshPrivateKey("priv.pem", "secretpassword")

' load content to sign
Dim content As Byte() = File.ReadAllBytes("filetosign")

' create encoded signature
Dim signature As Byte() = CreateEncodedSignature(key, content)

' save the signature
File.WriteAllBytes("sign.sha", signature)

The CreateEncodedSignature helper method that creates a signature and converts it into desired format looks like this:

Public Shared Function CreateEncodedSignature(key As SshPrivateKey, message As Byte()) As Byte()
    Dim hashAlg As SignatureHashAlgorithm
    Select Case key.KeyAlgorithm
        Case SshHostKeyAlgorithm.ECDsaNistP256
            hashAlg = SignatureHashAlgorithm.SHA256
            Exit Select
        Case SshHostKeyAlgorithm.ECDsaNistP384
            hashAlg = SignatureHashAlgorithm.SHA384
            Exit Select
        Case SshHostKeyAlgorithm.ECDsaNistP521
            hashAlg = SignatureHashAlgorithm.SHA512
            Exit Select
        Case Else
            Throw New InvalidOperationException("Key algorithm not supported yet.")
    End Select

    ' sign the message
    Dim signature As Byte() = key.CreateSignature(message, hashAlg)

    ' convert the signature into a format used by "openssl dgst" command

    Dim offset As Integer = 30
    Dim length1 As Integer = signature(offset)
    Dim offset1 As Integer = offset + 1
    offset = offset + 1 + length1 + 3
    Dim length2 As Integer = signature(offset)
    Dim offset2 As Integer = offset + 1

    Dim result = New MemoryStream()

    ' ASN.1 sequence and length
    result.WriteByte(&H30)
    Dim length As Integer = length1 + length2 + 4
    If length < &H80 Then
        result.WriteByte(CByte(length))
    Else
        result.WriteByte(&H81)
        result.WriteByte(CByte(length))
    End If

    ' ASN.1 integer, length and content
    result.WriteByte(&H2)
    result.WriteByte(CByte(length1))
    result.Write(signature, offset1, length1)

    ' ASN.1 integer, length and content
    result.WriteByte(&H2)
    result.WriteByte(CByte(length2))
    result.Write(signature, offset2, length2)

    Return result.ToArray()
End Function
0 votes
answered Feb 20, 2017 by RandyDellinger (190 points)

Lukas, thanks for the quick and detailed response!

When I run your code on a desktop VB.net program, it outputs a binary signature file with the correct number of bytes (yeah!), but when I run the verification routine it fails.

Do I need to run an ECC plugin for my desktop app?

Yes, my private key is an elliptic curve private key, it was created using this command:

openssl ecparam -out priv.pem -noout -name secp521r1 -genkey

I'm unsure if this is the same as a NIST P-521 curve key.

Note I'm using the Rebex.Security.dll (2017 trial), but the other Rebex dlls are from a purchased 2016 R3 release. I referenced the files in
C:\Program Files (x86)\Rebex Components 2016 R3\bin\net-2.0

Do all the dlls need to be from the same release?

Switching to my mobile app:

When run it displays "Elliptic curve algorithms need a plugin on this platform". It sounds like I didn't install the plugin correctly. Here's how I installed it:

  • Downloaded the ECC plugins from your link
  • Added project references to

    • netcf-2.0\Rebex.Castle.dll
    • netcf-2.0\Rebex.Curve25519.dll
  • Copied these 2 dlls to mobile device's app folder, which already had Rebex.Sftp.dll, Rebex.Networking.dll, Rebex.Common.dll, Rebex.Security.dll (2017 trial) .
    Note: the other Rebex dlls are from a purchased 2016 R3 release.

At the top of my app I import:

Imports Rebex.Security.Cryptography
Imports Rebex.Security.Cryptography.EllipticCurveAlgorithm

I don't know how to convert this sample C code on the plugin page to VB:

// import NIST and Brainpool curves
AsymmetricKeyAlgorithm.Register(EllipticCurveAlgorithm.Create);

I added this to CreateEncodedSignature():

AsymmetricKeyAlgorithm.Register(EllipticCurveAlgorithm.Create)

But this gives a compile error:

  • error BC30455: Argument not specified for parameter 'algName' of 'Public Shared Function Create(algName As String) As Rebex.Security.Cryptography.EllipticCurveAlgorithm'.
commented Feb 21, 2017 by Lukas Pokorny (94,670 points)
The code to register the plugin should look like this in VB.NET:
  AsymmetricKeyAlgorithm.Register(AddressOf EllipticCurveAlgorithm.Create)

The secp521r1 and NIST P-521 are the same, fortunately.

You don't have to reference Rebex.Curve25519.dll or register Curve25519. Rebex.Castle.dll is sufficient for NIST P-521.

Mixing Rebex DLLs from different releases is problematic, I suggest using the same version. However, Rebex.Security.dll is not needed for this, and you already licensed Rebex SFTP which includes Rebex.Common.dll and Rebex.Networking.dll - this is all you need to make this work. There is no need to use a trial version.

ECC plugin should not be needed for the desktop app. The failure in the verification routine was most likely caused by a bug in my signature conversion code. Sorry for that! Please replace "Dim length As Integer = length1 + length2 + 6" with "Dim length As Integer = length1 + length2 + 4" and give it a try again. (I fixed this in the sample code I posted yesterday as well.)
commented Feb 21, 2017 by RandyDellinger (190 points)
edited Feb 21, 2017 by RandyDellinger
Success! After I changed the code to "Dim length As Integer = length1 + length2 + 4" and registered the ECC plugin on mobile, both my apps create a valid signature.

However, when I remove the reference to Rebex.Security, the app no longer compiles the line:
Dim hashAlg As SignatureHashAlgorithm

because of:
error BC30002: Type 'SignatureHashAlgorithm' is not defined
and it gives the hint to import Rebex.Security.Certificates.

The documentation at http://help.rebex.net/##RebexTotalPack.chm/Html/T_Rebex_Security_Certificates_SignatureHashAlgorithm.htm
suggests I need Rebex.Security. Is there a way around this? I just have "Imports Rebex.Net" at the top of my program.

Also, on mobile, to register the ECC plugin via:
AsymmetricKeyAlgorithm.Register(AddressOf EllipticCurveAlgorithm.Create)

it looks like I need
Imports Rebex.Security.Cryptography
commented Feb 21, 2017 by Lukas Pokorny (94,670 points)
This looks good!
Despite its name, the Rebex.Security.Certificates namespace (and SignatureHashAlgorithm) is actually declared in Rebex.Common.dll - you have to reference that in addition to Rebex.Networking.dll. You also have to add "Import Rebex.Security.Certificates" (in addition to "Imports Rebex.Security.Cryptography").
commented Feb 21, 2017 by RandyDellinger (190 points)
edited Feb 21, 2017 by RandyDellinger
Thank you! The signature part of my app is now fully functional.

Now on to the second half of my problem: how do I verify a file using the saved signature and my public key?

In the documentation I found the SshPublicKey.VerifySignature method, but it has a note that it only supports SHA1 at the moment. Can this be used to verify with my SHA512 signature? Do you have any sample code?

My original app verifies a file using this OpenSSL command:

OpenSSL dgst -sha512 -verify PublicKeyFile.pem -signature sign.sha filetoverify

where sign.sha is the binary signature file
commented Feb 22, 2017 by Lukas Pokorny (94,670 points)
SshPublicKey.VerifySignature actually supports other algorithms as well (even though it's not documented), but we would have to convert the signature from the CMS / PKCS #7 format used by OpenSSL to SSH format (the reverse of CreateEncodedSignature method) to be able to use that.

But instead of this, we thought it would be better to add a new API that's much easier to use - see my [new answer](http://forum.rebex.net/6813/signing-verifying-a-file-with-a-pem-file?show=6834#a6834) for details. The ugly signature conversion method is no longer needed - we use PrivateKeyInfo/PublicKeyInfo and AsymmetricKeyAlgorithm objects (from Rebex.Common.dll) instead of SshPrivateKey/SshPublicKey (which means that Rebex.Networking.dll is no longer needed).

Please give it a try - I just sent you a link to the current beta that supports this.
commented Feb 22, 2017 by RandyDellinger (190 points)
It works, thank you!
Can I deploy this beta version in the field, or is this a trial version that will expire?
commented Feb 23, 2017 by Lukas Pokorny (94,670 points)
You can deploy it, it won't expire. It should work fine, but if you notice any unusual behavior, let us know - it's still a beta.
commented Mar 22, 2017 by Lukas Pokorny (94,670 points)
We just released Rebex Security 2017 R2 that incorporates the new functionality.
commented Mar 22, 2017 by RandyDellinger (190 points)
Thanks again!
...