I try to create keys with the python package ecdsa, export it to der and use them with BouncyCastle under .Net (and vice versa).
This is my code in Python:
import base64
from ecdsa.keys import SigningKey
from ecdsa.curves import NIST521p, NIST384p, NIST256p
#classmethod
def CreateKey(self) -> SigningKey:
privateKey = SigningKey.generate(NIST256p)
return privateKey
#classmethod
def GetPublicKey(self, privateKey: SigningKey) -> str:
publicKey = privateKey.get_verifying_key()
der = publicKey.to_der()
return base64.b64encode(der)
I get two strings that I want to import in .Net:
Const plainDerBase64Pub = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEyJW32bO4wswhE9ZC5klCjRNDJQSB0lIBphe9wIa/W2n3fBJ0cDpSpy9qlq2L6sa4W5lgRHYD9IyfETom6YOH/g=="
Const plainDerBase64Priv = "MHcCAQEEIKjIPCG9CGMunu34jXnDZg1LmNYrcJo8EqzKbRu2E24MoAoGCCqGSM49AwEHoUQDQgAEyJW32bO4wswhE9ZC5klCjRNDJQSB0lIBphe9wIa/W2n3fBJ0cDpSpy9qlq2L6sa4W5lgRHYD9IyfETom6YOH/g=="
Dim keyPriv = PrivateKeyFactory.DecryptKey({}, Base64.Decode(plainDerBase64Priv))
Dim keyPub = PrivateKeyFactory.DecryptKey({}, Base64.Decode(plainDerBase64Pub))
But I get an exception:
Test method LicenseProtectorTest.KeyManagementTest.ImportKeyFromPythonTest threw exception:
System.ArgumentException: Wrong number of elements in sequence
Parametername: seq
Result StackTrace:
bei Org.BouncyCastle.Asn1.Pkcs.EncryptedPrivateKeyInfo..ctor(Asn1Sequence seq) in C:\BouncyCastle\crypto\src\asn1\pkcs\EncryptedPrivateKeyInfo.cs:Zeile 18.
bei Org.BouncyCastle.Asn1.Pkcs.EncryptedPrivateKeyInfo.GetInstance(Object obj) in C:\BouncyCastle\crypto\src\asn1\pkcs\EncryptedPrivateKeyInfo.cs:Zeile 42.
bei Org.BouncyCastle.Security.PrivateKeyFactory.DecryptKey(Char[] passPhrase, Asn1Object asn1Object) in C:\BouncyCastle\crypto\src\security\PrivateKeyFactory.cs:Zeile 196.
bei Org.BouncyCastle.Security.PrivateKeyFactory.DecryptKey(Char[] passPhrase, Byte[] encryptedPrivateKeyInfoData) in C:\BouncyCastle\crypto\src\security\PrivateKeyFactory.cs:Zeile 182.
bei LicenseProtectorTest.KeyManagementTest.ImportKeyFromPythonTest() in ...
Any idea what I'm doing wrong here?
I'm not familiar with the python library, but I was able to guess at the formats and the following code will parse the example data:
using System;
using Org.BouncyCastle.Asn1.Sec;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
namespace BCTests
{
class MainClass
{
public static void Main(string[] args)
{
var plainDerBase64Pub = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEyJW32bO4wswhE9ZC5klCjRNDJQSB0lIBphe9wIa/W2n3fBJ0cDpSpy9qlq2L6sa4W5lgRHYD9IyfETom6YOH/g==";
var plainDerBase64Priv = "MHcCAQEEIKjIPCG9CGMunu34jXnDZg1LmNYrcJo8EqzKbRu2E24MoAoGCCqGSM49AwEHoUQDQgAEyJW32bO4wswhE9ZC5klCjRNDJQSB0lIBphe9wIa/W2n3fBJ0cDpSpy9qlq2L6sa4W5lgRHYD9IyfETom6YOH/g==";
var pubKeyBytes = Base64.Decode(plainDerBase64Pub);
var privKeyBytes = Base64.Decode(plainDerBase64Priv);
var pubKey = PublicKeyFactory.CreateKey(pubKeyBytes);
var privKeyStruct = ECPrivateKeyStructure.GetInstance(privKeyBytes);
var x9 = ECNamedCurveTable.GetByName("P-256");
var ec = new ECDomainParameters(x9.Curve, x9.G, x9.N, x9.H, x9.GetSeed());
var privKey = new ECPrivateKeyParameters(privKeyStruct.GetKey(), ec);
}
}
}
pubKey and privKey are then ECPublicKeyParameters and ECPrivateKeyParameters that you can use with various algorithms.
Note that DER is an encoding, not really a format. For the public key, the python code is producing a SubjectPublicKeyInfo, which is the standard X.509 format for public keys.
There ought to be an option with the private key of generating a PrivateKeyInfo (or EncryptedPrivateKeyInfo using a password), both of these are formats from the PKCS#8 standard. If the python code can output one of these, then decoding the private key would be easier as just PrivateKeyFactory.CreateKey(bytes) or .DecryptKey(password, bytes).
Related
I need to have a python code and a swift code exchange encrypted message.
Here's what I tried:
Fernet
After a review of the options, I thought that a symetric key algorithm could work well.
In python (as usual), it is straightforward to encrypt and decrypt:
Fernet(key).encrypt(b"mdg") # encrypt
Fernet(key).decrypt(encryptedMsg) # decrypt
In swift, it seemed initially straightforward with something along the lines of:
func encrypt(key: String, msg: String) throws -> String {
let data = Data(base64URL: key)!
let symetricKey = try! SymmetricKey(data: d)
let msgUtf8 = msg.data(using: .utf8)!
let sealBox = try! AES.GCM.seal(msgUtf8, using: symetricKey, nonce: nil)
return sealBox.combined.base64EncodedString();
}
However, I have been unable to find the algorithm in swift matching python's Fernet.
ChaCha
While searching for the problem, I landed on this amazing answer from Bram. Very unfortunately it only solves one side of my problem : encrypting messages in python and decoding them in swift. I also need the reverse process.
How to solve this?
To start, we first need a way to create secure random values to generate the IV and keys. You can also generate the keys using CryptoKit's SymmetricKey and extract the data from them, but for now, I'll use this function.
extension Data {
static func secureRandom(ofSize size: Int) -> Data {
var output = [UInt8](repeating: 0, count: size)
_ = SecRandomCopyBytes(kSecRandomDefault, size, &output)
return Data(output)
}
}
We then require the possibility to compute the AES CBC ciphertext, which can be done using CommonCrypto.
func encrypt(plaintext: Data, key: Data, iv: Data) -> Data {
var encryptor: CCCryptorRef?
defer {
CCCryptorRelease(encryptor)
}
var key = Array(key)
var iv = Array(iv)
var plaintext = Array(plaintext)
CCCryptorCreate(CCOperation(kCCEncrypt), CCAlgorithm(kCCAlgorithmAES), CCOperation(kCCOptionPKCS7Padding), &key, key.count, &iv, &encryptor)
var outputBytes = [UInt8](repeating: 0, count: CCCryptorGetOutputLength(encryptor, plaintext.count, false))
CCCryptorUpdate(encryptor, &plaintext, plaintext.count, &outputBytes, outputBytes.count, nil)
var movedBytes = 0
var finalBytes = [UInt8](repeating: 0, count: CCCryptorGetOutputLength(encryptor, 0, true))
CCCryptorFinal(encryptor, &finalBytes, finalBytes.count, &movedBytes)
return Data(outputBytes + finalBytes[0 ..< movedBytes])
}
and the HMAC with the SHA-256 hash function. I recommend using CryptoKit's HMAC implementation here, but to keep things simple, I went with the CommonCrypto implementation.
func computeHMAC(_ data: Data, using key: Data) -> Data {
var data = Array(data)
var key = Array(key)
var macOut = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), &key, key.count, &data, data.count, &macOut)
return Data(macOut)
}
This brings all of this together into the following
let plaintext = Data("Hello world!".utf8)
let signingKey = Data.secureRandom(ofSize: kCCKeySizeAES128)
let cryptoKey = Data.secureRandom(ofSize: kCCKeySizeAES128)
let fernetKey = (signingKey + cryptoKey).base64EncodedString()
let version: [UInt8] = [0x80]
let timestamp: [UInt8] = {
let timestamp = Int(Date().timeIntervalSince1970).bigEndian
return withUnsafeBytes(of: timestamp, Array.init)
}()
let iv = Data.secureRandom(ofSize: kCCBlockSizeAES128)
let ciphertext = encrypt(plaintext: plaintext, key: cryptoKey, iv: iv)
let hmac = computeHMAC(version + timestamp + iv + ciphertext, using: signingKey)
let fernetToken = (version + timestamp + iv + ciphertext + hmac).base64EncodedString()
print("Fernet key: \(fernetKey)")
print("Fernet token: \(fernetToken)")
An example output can be
Fernet key: 7EwFlYNKTGfj+2fSgL3AUqtrRqRs4D1TWNK7t2XbGJQ=
Fernet token: gAAAAABivCLM0y0poDtGOohT1yK4XTDJppYPJdu4fuDTZ5tb9P9KP5ACgX8aJq4imsSdbzOCcvY3Tueo4FYbwyG+ZugozILL+Q==
We can use this in python using cryptography.io's implementation
from cryptography.fernet import Fernet
key = b'7EwFlYNKTGfj+2fSgL3AUqtrRqRs4D1TWNK7t2XbGJQ='
token = b'gAAAAABivCLM0y0poDtGOohT1yK4XTDJppYPJdu4fuDTZ5tb9P9KP5ACgX8aJq4imsSdbzOCcvY3Tueo4FYbwyG+ZugozILL+Q=='
Fernet(key).decrypt(token)
# b'Hello world!'
I'm trying to sign message with RSA in Python and then verifying data in C#, but getting fail still after hours of testing/fighting.
Python code for signing:
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
message = b"Test string"
with open("id_rsa", "rb") as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None,
)
signature = private_key.sign(
message,
padding.PKCS1v15(),
hashes.SHA256()
)
signaturefile = open('signed.dat', 'wb')
signaturefile.write(signature)
signaturefile.close()
datafile = open('message.dat', 'wb')
datafile.write(message)
datafile.close()
And C# code for verifying:
private void button_Click(object sender, EventArgs e)
{
byte[] data = File.ReadAllBytes("message.dat");
byte[] signature = File.ReadAllBytes("signed.dat");
try
{
using (var reader = File.OpenText("id_rsa.pub"))
{
var pem = new PemReader(reader);
var o = (RsaKeyParameters)pem.ReadObject();
using (var rsa = new RSACryptoServiceProvider())
{
var parameters = new RSAParameters();
parameters.Modulus = o.Modulus.ToByteArray();
parameters.Exponent = o.Exponent.ToByteArray();
rsa.ImportParameters(parameters);
bool ok = rsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
if (ok) Console.WriteLine("Verified");
}
}
}
catch (CryptographicException exc)
{
Console.WriteLine(exc.Message);
}
}
I'm getting always fail from rsa.VerifyData. Could someone point me what is the problem?
Same files can be verified well in Python.
RSAParameters#Modulus and RSAParameters#Exponent expect modulus and exponent unsigned, so Org.BouncyCastle.Math.BigInteger#ToByteArrayUnsigned() must be used instead of Org.BouncyCastle.Math.BigInteger#ToByteArray(). With this change, verification is successful.
Also, the big endian byte order is required, but this applies to both methods.
Note that System.Numerics.BigInteger#ToByteArray() returns the data signed with little endian order.
Your current approach uses BC for key import and built-in .NET methods for verification. Alternatively, BC classes can also be used for verification, making for a slightly more efficient implementation overall.
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Security;
...
ISigner signer = SignerUtilities.GetSigner("SHA256withRSA");
signer.Init(false, o);
signer.BlockUpdate(data, 0, data.Length);
bool verified = signer.VerifySignature(signature);
I'm struggling to encode a string to SHA-256 with a specific key.
There is a python code that is used a reference.
The problem is that I cannot achieve the same result in C#.
import base64
import hmac
import hashlib
# Base64 encoded key (get it with /hook/{hookId}/key request)
webhook_key_base64 = 'JcyVhjHCvHQwufz+IHXolyqHgEc5MoayBfParl6Guoc='
# notification parameters
data = '643|1|IN|+79165238345|13353941550'
webhook_key = base64.b64decode(s))
print(hmac.new(webhook_key, data.encode('utf-8'), hashlib.sha256).hexdigest())
C#
public class Program
{
public static void Main()
{
var key = "JcyVhjHCvHQwufz+IHXolyqHgEc5MoayBfParl6Guoc=";
var message = "643|1|IN|+79165238345|13353941550";
var decodedKey = Base64Decode(key);
var result = HmacSha256Digest(message, decodedKey);
Console.WriteLine(result);
Console.ReadLine();
}
private static string HmacSha256Digest(string message, string secret)
{
var keyBytes = Encoding.UTF8.GetBytes(secret);
var messageBytes = Encoding.UTF8.GetBytes(message);
var cryptographer = new HMACSHA256(keyBytes);
var bytes = cryptographer.ComputeHash(messageBytes);
return BitConverter.ToString(bytes).Replace("-", "").ToLower();
}
private static string Base64Decode(string base64EncodedData)
{
var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
return Encoding.UTF8.GetString(base64EncodedBytes);
}
}
What could be wrong?
Thanks
I am wondering if Crypto++ only signs files using SHA (RSASSA_PKCS1v15_SHA_Signer)?
I have been using pyCryptodome to do the signing and verifying, but I want to make a C++ application that does the same. In Python, I can sign the files with any of the supported hashing (SHA3/BLAKE2B etc..) algorithms. At the very least I want to support signing using SHA256 in C++.
std::string Hasher::sign(const std::string& message)
{
RSASSA_PKCS1v15_SHA_Signer signer(m_privateKey);
size_t length = signer.MaxSignatureLength();
SecByteBlock signature(length);
length = signer.SignMessage(rng, (CryptoPP::byte*)message.c_str(), message.size(), signature);
signature.resize(length);
//Want the signature as a hex
return toHex(signature, signature.size());
}
However, I want to be able to do something similar I do in Python:
def(message, key, passphrase, hashType):
rsakey = RSA.importKey(key,passphrase)
signer = PKCS1_v1_5.new(rsakey)
# Get the hash class based os user given hashType ex ("SHA256" returns "SHA256.New()")
digest = getHash(hashType)
digest.update(message.encode("ascii"))
return signer.sign(digest).hex()
If I chose the same private key, and use the hashType "SHA" I get the same signature result as my C++ code would.
So I found an answer to it. So partially following the tutorial found here
and from RSASS<PSS, SHA256>::Signer signer(privateKey); you could add the PKCS1v15 padding as such RSASS<PKCS1v15, SHA256>::Signer signer(privateKey);
So the final code would look something like this:
std::string Hasher::sign(const std::string& message)
{
CryptoPP::RSASS<CryptoPP::PKCS1v15, CryptoPP::SHA256>::Signer signer(m_privateKey);
size_t length = signer.MaxSignatureLength();
SecByteBlock signature(length);
length = signer.SignMessage(rng, (CryptoPP::byte*)message.c_str(), message.size(), signature);
signature.resize(length);
//Want the signature as a hex
return toHex(signature, signature.size());
}
So super similar, I still don't know why RSASSA_PKCS1v15_SHA_Signer class still exists. To throw in something extra my final goal, as I could find nothing on it except a random 13-year-old question on a mailing list :
How to sign and verify a file with Crypto++ (Like you sign files in pyCrypto)
Signing file:
std::string Hasher::sign()
{
CryptoPP::RSASS<CryptoPP::PKCS1v15, CryptoPP::SHA256>::Signer signer(m_privateKey);
size_t length = signer.MaxSignatureLength();
SecByteBlock signature(length);
// Get bytes of the file
const auto& data = getFileBytes();
//AFAIK you need to create a signer PK_MessageAccumulator object to acheive this
CryptoPP::PK_MessageAccumulator* pSigMsgAcc = signer.NewSignatureAccumulator(rng);
//Update it with your data, toBinary_const is just a short for (const CryptoPP::byte*)data.data()
pSigMsgAcc->Update(toBinary_const(data), data.size());
length = signer.Sign(rng, pSigMsgAcc, signature);
signature.resize(length);
//I return it as hex as people can actually read the signature and verify it themself
return toHex(signature, signature.size());
}
Verifying a file
bool Hasher::verify(const std::string& signature, const std::string& type)
{
CryptoPP::RSASS<CryptoPP::PKCS1v15, CryptoPP::SHA256>::Verifier verifier(m_publicKey);
const auto& data = getFileBytes();
//Create a verifyer PK_MessageAccumulator object
CryptoPP::PK_MessageAccumulator* pVrfyMsgAcc = verifier.NewVerificationAccumulator();
pVrfyMsgAcc->Update(toBinary_const(data), data.size());
//Here comes the down side of using hex as signatrue, I convert it to a binary string
auto toVerify = toBinaryString(signature);
//Then I have to convert the binary string to binary
// (It does not like me to convert the hex directly to binary)
verifier.InputSignature(*pVrfyMsgAcc, toBinary_const(toVerify), toVerify.size());
return verifier.Verify(pVrfyMsgAcc);
}
I've a django app which serves encrypted media files to Flash apps. Encryption in python is done with PyCrypto as follows (I include description too in case useful):
def encrypt_aes(key, text):
try:
raw = _pad(text)
iv = Random.new().read(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(raw))
except Exception as e:
print e.message
return text
def decrypt_aes(key, text):
try:
enc = base64.b64decode(text)
iv = enc[:AES.block_size]
cipher = AES.new(key, AES.MODE_CBC, iv)
return _unpad(cipher.decrypt(enc[AES.block_size:]))
except Exception as e:
print e.message
return text
def _pad(s):
bs = 16
return s + (bs - len(s) % bs) * chr(bs - len(s) % bs)
def _unpad(s):
return s[:-ord(s[len(s) - 1:])]
I cannot yet decrypt the Python provided media files (downloaded with LoaderMax by GreenSock, using 'DataLoader'). My AS3 code (using AS3Crypto) is as follows:
public static function decipher(_key:String):void{
key=_key;
cipher = Crypto.getCipher("simple-aes-cbc", Hex.toArray(key));
cipher.decrypt(ByteArray(_content));
}
I get
RangeError: Error #2006
One suspicion is that in Python I have 64bit base but I think that AS3 ByteArray is 32bit base. I have tried the below, but get the same Error.
cipher.decrypt(ByteArray(com.hurlant.util.Base64.decodeToByteArray(_content)));
Another suspicion is that I have not appropriately removed 'padding' from the _content / set up my IV appropriately (which is specified by the padding I must remove from the _content). This should be done via that "simple" statement however. I have been trying this, but with no success:
var pad:IPad = new PKCS5
cipher = Crypto.getCipher("simple-aes", Hex.toArray(key),pad);
pad.setBlockSize(cipher.getBlockSize());
Could anyone advise on how I can fix this ? :)
Many thanks!
OK finally figured out what was going wrong. Besides some AS3 tweaks, we wrongly were transmitting files as MP3/image (should have been text/html).
Our Python remains as above. Our AS3 is tweaked to the below.
Here's the AS3 class we used:
package com.xperiment.preloader
{
import com.greensock.loading.DataLoader;
import com.hurlant.crypto.Crypto;
import com.hurlant.crypto.symmetric.ICipher;
import com.hurlant.util.Base64;
import flash.events.Event;
import flash.utils.ByteArray;
public class EncryptedDataLoader extends DataLoader
{
private static var backlog:Vector.<EncryptedDataLoader>;
private static var cipher:ICipher;
private var decrypted:Boolean = true;
public function EncryptedDataLoader(urlOrRequest:*, vars:Object=null)
{
this.addEventListener(Event.COMPLETE,decryptL);
super(urlOrRequest, vars);
}
public function decryptL(e:Event):void {
trace("start decrypt");
e.stopImmediatePropagation();
this.removeEventListener(Event.COMPLETE,decryptL);
backlog ||= new Vector.<EncryptedDataLoader>;
backlog.push(this);
if(cipher) pingBacklog();
}
public function decipher():void
{
_content = Base64.decodeToByteArray( _content );
cipher.decrypt( _content );
decrypted=true;
this.dispatchEvent(new Event(Event.COMPLETE));
}
public static function setCipher(_key:String):void{
var keyBA:ByteArray = new ByteArray;
keyBA.writeMultiByte(_key, "iso-8859-1");
cipher = Crypto.getCipher("simple-aes", keyBA);
pingBacklog();
}
public static function kill():void{
cipher.dispose();
cipher = null;
}
public static function pingBacklog():void{
if(backlog){
var encrypted:EncryptedDataLoader;
while(backlog.length>0){
encrypted=backlog.shift();
encrypted.decipher();
}
backlog=null;
}
}
}
}