by Mikael Henriksson
17. October 2010 11:28
This post should have been written ages ago. I had such a hard time figuring out how to solve this one I really want to share the working solution with everyone but have had to live life. I’ll write something about how to live life in a future post. Right now I’ll focus on OpenSSL and the PHP implementation of openssl_sign and how to implement openssl_verify to verify what was signed.
To start with there needs to be keys exchanged. Both parties should exchange public keys. The public keys should be used for verifying what the other end signed with their private key. Do not make the mistake of sending private keys. Then you need to generate a new one
.
Prerequisites
Before coding
First we need to create a new set of keys. This is done in two steps, first we generate a new private key and then we generate a public key using the private key as base.
#Private:
openssl genrsa -out private.pem 2048
#Public:
openssl rsa -pubout -in private.pem -out public.pem -outform PEM
Now we can send our public key to wherever it needs to go and start coding.
The code
using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
namespace System.Cryptography
{
public class Crypt
{
///<summary>
/// Verifies that data and the openssl_signed signature can be verified with the given publicKey
///</summary>
///<param name = "msg">Raw data extracted on our side.</param>
///<param name = "signature">The data signed with other sides private key.</param>
///<param name = "publicKey">Other sides public key stored safely with us for authentication purposes.
/// <remarks>
/// either as a file path reference or raw text.
/// </remarks>
///</param>
///<returns></returns>
public static bool Verify(String msg, String signature, String publicKey)
{
RsaKeyParameters remotepubkey = GetRsaPublicKey(publicKey);
ISigner signer = SignerUtilities.GetSigner("SHA1withRSA");
signer.Init(false, remotepubkey);
var bytes = Convert.FromBase64String(signature);
var msgBytes = Encoding.UTF8.GetBytes(msg);
signer.BlockUpdate(msgBytes, 0, msgBytes.Length);
return signer.VerifySignature(bytes);
}
/// <summary>
/// Signs a string with our private key. The string can later be verified with our public key at receiving end.
/// </summary>
/// <param name = "data">The data that shall be signed.</param>
/// <param name = "privateKey">Our private key, either as a file path reference or raw text.</param>
/// <returns></returns>
public static String Sign(String data, String privateKey)
{
RsaPrivateCrtKeyParameters privKey = GetPrivateKey(privateKey);
ISigner sig = SignerUtilities.GetSigner("SHA1withRSA");
sig.Init(true, privKey);
var bytes = Encoding.UTF8.GetBytes(data);
sig.BlockUpdate(bytes, 0, bytes.Length);
byte[] signature = sig.GenerateSignature();
var signedString = Convert.ToBase64String(signature);
return signedString;
}
/// <summary>
/// Returns an instance of <see cref = "RsaKeyParameters" /> based on the supplied pemFile or pemstring
/// </summary>
/// <param name = "pemFile">Either the file path or the file content as text.</param>
/// <returns></returns>
public static RsaKeyParameters GetPublicKey(String pemFile)
{
return GetRsaPublicKey(pemFile);
}
/// <summary>
/// Returns an instance of <see cref = "RsaPrivateCrtKeyParameters" /> based on the supplied pemFile or pemstring
/// </summary>
/// <param name = "pemFile">Either the file path or the file content as text.</param>
/// <returns></returns>
public static RsaPrivateCrtKeyParameters GetPrivateKey(String pemFile)
{
if (string.IsNullOrEmpty(pemFile))
throw new ArgumentNullException("pemFile");
string privateKey = File.Exists(pemFile) ? File.ReadAllText(pemFile) : pemFile;
var reader = new PemReader(new StringReader(privateKey));
RsaPrivateCrtKeyParameters privkey = null;
Object obj = reader.ReadObject();
if (obj is AsymmetricCipherKeyPair) {
privkey = (RsaPrivateCrtKeyParameters) ((AsymmetricCipherKeyPair) obj).Private;
}
return privkey;
}
/// <summary>
/// Returns an instance of <see cref = "RsaKeyParameters" /> based on the supplied PEM or DER file location or the PEM string
/// </summary>
/// <param name = "pemFile">Either the file path or the file content as text.</param>
/// <returns></returns>
public static RsaKeyParameters GetRsaPublicKey(string pemFile)
{
RsaKeyParameters pubkey = null;
if (!File.Exists(pemFile)) {
pubkey = GetPemPublicKey(pemFile);
}
else if (pemFile.EndsWith(".pem")) {
pubkey = GetPemPublicKey(File.ReadAllText(pemFile));
}
return pubkey;
}
private static RsaKeyParameters GetPemPublicKey(string key)
{
var reader = new PemReader(new StringReader(key));
object obj = reader.ReadObject();
if (obj is RsaKeyParameters) {
return (RsaKeyParameters) obj;
}
return null;
}
}
}