I have spent all day trying to make a usable pbkdf2 password for the mosquitto-auth-plug. This program sets it up exactly the way it should be stored in the mysql database. I have a password hash generated by the program that comes with auth-plug and mosquitto loves it. I just can't replicate it in c#, if anyone can help please let me know.
public string CreatePasswordHash(string password)
{
var salt = GenerateRandomSalt();
var iterationCount = GetIterationCount();
var hashValue = GenerateHashValue(password, salt, iterationCount);
string result = "PBKDF2$sha256$" + iterationCount + "$" + Convert.ToBase64String(salt) + "$" + Convert.ToBase64String(hashValue);
return result;
}
private int GetIterationCount()
{
return 901;
}
private static byte[] GenerateRandomSalt()
{
var csprng = new RNGCryptoServiceProvider();
var salt = new byte[SaltByteLength];
csprng.GetBytes(salt);
return salt;
//return GetLetter();
}
private static byte[] GenerateHashValue(string password, byte[] salt, int iterationCount)
{
byte[] hashValue;
var valueToHash = string.IsNullOrEmpty(password) ? string.Empty : password;
using (var pbkdf2 = new Rfc2898DeriveBytes(valueToHash, salt, iterationCount))
{
hashValue = pbkdf2.GetBytes(DerivedKeyLength);
}
return hashValue;
}
---EDIT-----
Rfc2898DeriveBytes states --
Implements password-based key derivation functionality, PBKDF2, by using psuedo-random number generator based on HMACSHA1.
The program/auth-plug seems to be using sha256 is there a c# PBKDF2 that uses this.
As you have already stated in your edit, the problem appears to be the difference in hash functions used between the mosquitto plugin (which only supports SHA-256, according to the source code) and the .NET implementation (which can only do SHA-1).
A more flexible implementation of PBKDF2 is available in BouncyCastle, a more lightweight implementation can be found here. If you are not satisfied with those two, you could chose to implemnt PBKDF2 yourself, which is not really hard.
Well it appears there really wasn't an answer and there weren't many who had tried. I figured out the problem and after contacting the mosquitto-auth-plug author he felt it would be good for us to add my solution to his contrib folder on the plugins github repo.
So now if you need a c# hashing algorithm for mosquitto-auth-plug just go to the repo in git here
https://github.com/jpmens/mosquitto-auth-plug/tree/master/contrib/C%23
and follow my instructions --- Let me know if you have any problems
Related
I'm trying summarize: I have a self-signed certificate (created by OpenSSL) being used in a aspnetCore API to verify hash (or message digest, if you prefer) signature. I can run tests to sign the hash and run in the API and everything goes well.
Now, I'm trying do my client sign such hashs and consume the API. It's an application running on Mono. I'm tried do exactly the same thing as my tests, but Mono has a bug in X509Certificate2, when pfx certificate is protected by password. So, I replaced it the by the famous BouncyCastle. However, the results are different... Checking the pk algorithm, I can see some differences, but nothing so remarkable (at least to me).
Can you give me advices? I gonna put the codes:
Good code running on tests (dotNet Core):
Console.WriteLine("Entry text:");
var text = Console.ReadLine();
X509Certificate2 certificate = new X509Certificate2((byte[])Resource1._2dbb1721_281d_4990_836c_7e46909b8767, "1509a96c-d56b-4e17-af5b-c11f5f214c74");
SHA1Managed sha1Hasher = new SHA1Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1Hasher.ComputeHash(data);
RSA provider = (RSA)certificate.PrivateKey;
var signatureData = provider.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
var signature = Convert.ToBase64String(signatureData);
Bad code, running on Mono and BouncyCastle:
SHA1Managed sha1Hasher = new SHA1Managed();
var encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(uuid);
byte[] hash = sha1Hasher.ComputeHash(data);
var rsaParameters = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)privateKeyParam);
var provider = RSA.Create();
provider.ImportParameters(rsaParameters);
var signatureData = provider.SignHash(hash, HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1);
var signature = Convert.ToBase64String(signatureData);
Both certificates are the same. Debugging, I could see the dotNet instantiates on provider as RSACng. The SignatureAlgorithm property is "RSA" as the KeyExchangeAlgorithm so.
In the BoncyCastle over Mono, the provider is RSACrytoServiceProvider. According some articles that I read, this shouldn't make any difference. However, SignatureAlgorithm is "http://www.w3.org/2000/09/xmldsig#rsa-sha1", while KeyExchangeAlgorithm is "RSA-PKCS1-KeyEx".
Thanks in advance
My bad: I've just realized my program wasn't using the string it is supposed might use to calculate the hash...
The Mono's version using BouncyCastle is working perfect fine.
Good morning, I am trying to sign a PDF document using ItextSharp 5.5.13.2 and SHA-256 for the signature process but at the moment of signing I get the error "Specified Algorithm is Invalid". This error does not happen when I use SHA-1 with .NetFramework 4.7.2 to sign the PDF, the next method is the one I use to sign the PDF.
public void SignPDF(string PathSourceDoc, string PathTargetDoc, X509Certificate2 certificate, string pathLogo)
{
using (PdfReader reader = new PdfReader(PathSourceDoc))
using (var writer = new FileStream(PathTargetDoc, FileMode.Create, FileAccess.Write))
using (var stamper = PdfStamper.CreateSignature(reader, writer, '\0', null, true))
{
var signature = stamper.SignatureAppearance;
signature.CertificationLevel = PdfSignatureAppearance.NOT_CERTIFIED;
signature.Reason = "My Reason";
signature.Location = "My Location";
signature.SignDate = DateTime.Now;
signature.Acro6Layers = true;
PdfSignature objSignature = new PdfSignature(PdfName.ADOBE_PPKMS, PdfName.ADBE_PKCS7_SHA1);
objSignature.Date = new PdfDate(signature.SignDate);
signature.CryptoDictionary = objSignature;
var bcCert = DotNetUtilities.FromX509Certificate(certificate);
string name = CertificateInfo.GetSubjectFields(bcCert).GetField("CN");
string industry = CertificateInfo.GetSubjectFields(bcCert).GetField("O");
string position = CertificateInfo.GetSubjectFields(bcCert).GetField("T");
DateTime date = DateTime.Now;
signature.Layer2Text = "Digital Signed by: " + name + "\n" +
"Reason: " + "My Reason" + "\n" +
"Date: " + date;
signature.Layer2Font = new Font(iTextSharp.text.Font.FontFamily.TIMES_ROMAN, 8);
Image img = Image.GetInstance(pathLogo);
signature.SignatureGraphic = img;
signature.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION;
Rectangle rect = new Rectangle(50, 50, 300, 110);
signature.SetVisibleSignature(rect, 1, null);
var standard = CryptoStandard.CADES;
/*I tried this way but I get an error of type Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'The requested operation is not supported.'*/
X509Certificate cert = certificate;
X509Certificate2 signatureCert = new X509Certificate2(cert);
var pk = Org.BouncyCastle.Security.DotNetUtilities.GetKeyPair(signatureCert.PrivateKey).Private;// the error is generated in this line (Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'The requested operation is not supported.').
IExternalSignature es = new PrivateKeySignature(pk, "SHA-256");
/****************************************************************************************************************************************************************/
X509Certificate2Signature externalSignature = new X509Certificate2Signature(certificate, DigestAlgorithms.SHA1); /*throws an exception on this line of type "System.ArgumentException: 'Unknown encryption algorithm System.Security.Cryptography.RSACng'"
* when using .NET Core 3.1. this always happens, it doesn't matter if I use sha1 or use any other algorithm, it always generates error*/
MakeSignature.SignDetached(signature, externalSignature, new[] { bcCert }, null, null, null, 0, standard);
}
}
The method receives as input parameters the path of the PDF that I need to sign, the path of the pdf that will be created when signing, the certificate and the path of the logo for displaying the image in the signature. Actually I don't know what I'm doing wrong since I've been researching related questions and it should work with SHA-1 and SHA-256 and .NetFramework.
Then I migrated the project to .NetCore 3.1 in order to try to fix the problem but instead I got a new error (It is commented in the code). My goal is to use .NetCore and to allow me to sign a pdf using sha256. I have no problem modifying the SignPdf method in order to make the program work.
Any contribution or information link is appreciated. Thanks for your help.
Psdt: This is the stacktrace is as follows...
this is the stacktrace image of the error when the project was migrated to .Net Core 3.1
Apparently the Microsoft Crypto APIs used by X509Certificate2Signature do not support SHA-256.
You use a X509Certificate2Signature instance as IExternalSignature argument of MakeSignature.SignDetached.
If the PrivateKey of your X509Certificate2 is a RSACryptoServiceProvider (which appears to be the case for your certificate), X509Certificate2Signature.Sign calls
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)certificate.PrivateKey;
return rsa.SignData(message, hashAlgorithm);
RSACryptoServiceProvider.SignData at first apparently does support "SHA256" because before forwarding execution to SignHash it must have calculated the hash value to sign.
RSACryptoServiceProvider.SignHash, though, does not support SHA-256, according to the Microsoft docs on that method:
Remarks
This method creates a digital signature that is verified using the VerifyHash method.
The valid hash algorithms are SHA1 and MD5. The algorithm identifier can be derived from the hash name by using the MapNameToOID method.
Due to collision problems with SHA1 and MD5, Microsoft recommends a security model based on SHA256 or better.
(Viewed 2021-06-24, 11:07)
Thus, you get the "Specified Algorithm is Invalid" error.
Essentially, therefore, one should not base one's IExternalSignature implementation for RSA signing on RSACryptoServiceProvider anymore.
Maybe this is why X509Certificate2Signature was not ported to iText version 7...
An alternative approach might be to check whether the PrivateKey is a RSACng first and in that case use it as a RSACng. This Cryptography Next Generation (CNG) implementation of the RSA algorithm should support better hashing algorithms...
I encountered an error while using cppwinrt. When I use CryptographicEngine::SignHashedData function to sign a hash value, it returns WinRT originate error - 0x80004001 : 'not implemented'.
Here is the code:
#include "pch.h"
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Security::Cryptography;
using namespace Windows::Security::Cryptography::Core;
using namespace Windows::Storage::Streams;
using namespace std;
int main()
{
init_apartment();
IBuffer buffKeyPair;
IBuffer buffPublicKey;
IBuffer nullBuff(nullptr);
uint32_t asymmetricKeyLength = 512;
hstring strMsg = L"zzp yes!";
hstring strAsymmetricAlgName = AsymmetricAlgorithmNames::RsaPkcs1();
hstring strAlgNameH = HashAlgorithmNames::Md5();
AsymmetricKeyAlgorithmProvider objAlgProv = AsymmetricKeyAlgorithmProvider::OpenAlgorithm(strAsymmetricAlgName);
CryptographicKey keyPair = objAlgProv.CreateKeyPair(asymmetricKeyLength);
IBuffer buffUtf8Msg = CryptographicBuffer::ConvertStringToBinary(strMsg, BinaryStringEncoding::Utf8);
HashAlgorithmProvider objAlgH = HashAlgorithmProvider::OpenAlgorithm(strAlgNameH);
IBuffer buffHash = objAlgH.HashData(buffUtf8Msg);
IBuffer signedBuff = CryptographicEngine::SignHashedData(keyPair, buffHash);
hstring strHashHex = CryptographicBuffer::EncodeToHexString(signedBuff);
std::cout << "the strHashHex is: " << winrt::to_string(strHashHex) << std::endl;
}
Could you please help me find the problem? Thanks!
I'm not too experienced in this space, but I think I figured out a couple issues. This is happening because you aren't using an algorithm that can be used for signing, and you aren't using a hash algorithm compatible with it. The docs seem a bit... sparse... in this regard, but the algorithm names that have "sign" in them looked promising.
After a little experimentation, I got this to work.
using namespace Windows::Security::Cryptography;
using namespace Windows::Security::Cryptography::Core;
using namespace Windows::Storage::Streams;
using namespace std;
int main()
{
init_apartment();
auto algo_name = AsymmetricAlgorithmNames::RsaSignPkcs1Sha1();
auto asymmetric_provider = AsymmetricKeyAlgorithmProvider::OpenAlgorithm(algo_name);
auto keyPair = asymmetric_provider.CreateKeyPair(512);
auto md5_hasher = HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha1());
auto buffHash = md5_hasher.HashData(
CryptographicBuffer::ConvertStringToBinary(
L"Hello world", BinaryStringEncoding::Utf8));
auto signature = CryptographicEngine::SignHashedData(keyPair, buffHash);
auto publicKey = asymmetric_provider.ImportPublicKey(keyPair.ExportPublicKey());
auto match = CryptographicEngine::VerifySignatureWithHashInput(
publicKey, buffHash, signature);
}
Notice that I used RsaSignPkcs1Sha1 which seems to indicate that this algorithm can do signing. Also, for the hash algorithm, I used Sha1. Using Md5 gave a different runtime error (and Md5 is pretty weak, anyway). I also added the code to show how to use your key pair's public key to verify the signature.
After try your code and more tests, I have found that RsaSignPkcs1Sha1 is needed when you want to sign a content, the RsaPkcs1 does not support signing. Besides, I found that Windows only support RSA signing with SHA hashes, it does not support sign a md5 hash. Further more, the two names (RsaSignPkcs1Sha1 and Sha1) must match, or you will encounter an error. I don't known how it works in the back. I also found that sign a content with the sign function is equvalent with sign a content's hash with SignHashedData function, It seems that sign function first computes the hash before signing it.
I have visited and read all the Valence, and specifically the REST API, pages. I have one approved key already and a second key that has not yet been approved by D2L, and it's not clear how I request that approval.
The documentation contains a lot of information, but it is difficult to put all the pieces together. For example, in order to make any REST API call, I have to add several parameters to the end of the call. The parameters are documented in one place, but it isn't clear in some cases how to construct them (for example, one of the keys is to contain the url, timestamp, and the type of call being made, but how are they to be concatenated?). Then they have to be signed, and the documentation that tells how to sign the keys is in a completely different page that is not even referenced from the page that tells you that you have to sign the parameters. On top of that, the documentation is not extremely clear about how to do the signing, and offers no further explanation or examples. So to get anywhere, we have to jump around a lot through the documentation, and go through a whole lot of trial and error. It appears that the documentation assumes that the reader has expertise in several areas, which may or may not be true.
Code examples would make a huge difference.
There aren’t a lot of samples yet; we are working to add more, and to make the ones that are present more obvious. As one example, there is a Java Android app that has all the authentication stuff and some basic calls (including the call “whoami” which is a great test call).
The specific auth related files are available as well. From the D2LSigner class, you can see the signing algorithm we use:
Mac hmacSha256 = Mac.getInstance("hmacSHA256");
byte[] keyBytes = key.getBytes("UTF-8");
Key k = new SecretKeySpec(keyBytes, "hmacSHA256");
hmacSha256.init(k);
byte[] dataBytes = data.getBytes("UTF-8");
byte[] sig = hmacSha256.doFinal(dataBytes)
String sigString = base64Url( sig );
From D2LOperationSecurityImpl, you can see how the query string fits together:
//uppercase METHOD, lowercase PATH, timestamp as string
private static /*final*/ String BASE_STRING_TEMPLATE = "{0}&{1}&{2}";
private static /*final*/ String APP_ID_QUERY_NAME = "x_a";
private static /*final*/ String APP_SIG_QUERY_NAME = "x_c";
private static /*final*/ String USER_ID_QUERY_NAME = "x_b";
private static /*final*/ String USER_SIG_QUERY_NAME = "x_d";
private static /*final*/ String TIMESTAMP_QUERY_NAME = "x_t";
...
#Override
public Uri createAuthenticatedUri(String path, String httpMethod) {
long timestamp = System.currentTimeMillis() +
mServerSkewCorrectionMillis.longValue();
Long timestampObjectSeconds = new Long(timestamp/1000);
Object[]formatParms = {httpMethod.toUpperCase(),
path.toLowerCase(),
timestampObjectSeconds.toString()};
String signatureBaseString = MessageFormat.format(BASE_STRING_TEMPLATE,
formatParms);
String appSig = D2LSigner.base64URLSig(mAppKey, signatureBaseString);
String userSig = D2LSigner.base64URLSig(mUserKey, signatureBaseString);
if ((appSig == null) || (userSig == null)) {
return null;
}
String scheme = mEncryptOperations?ENCRYPED_SCHEME:PLAIN_SCHEME;
Uri.Builder b = new Uri.Builder();
b.scheme(scheme);
b.authority(mHostName);
b.path(path);
b.appendQueryParameter(APP_ID_QUERY_NAME, mAppID);
b.appendQueryParameter(APP_SIG_QUERY_NAME, appSig);
b.appendQueryParameter(USER_ID_QUERY_NAME, mUserID);
b.appendQueryParameter(USER_SIG_QUERY_NAME, userSig);
b.appendQueryParameter(TIMESTAMP_QUERY_NAME, timestampObjectSeconds.toString());
Uri securedURI = b.build();
return securedURI;
}
Also, you need to sign the first URL you use for logging in, but only with the application key (because you haven't yet established a user context). It uses a different base string (to protect the URL that is used during auth):
String signature = D2LSigner.base64URLSig(mAppKey, resultURLString);
BasicNameValuePair appID = new BasicNameValuePair(APP_ID_NAME, mAppID);
BasicNameValuePair appSig = new BasicNameValuePair(APP_SIG_NAME, signature);
BasicNameValuePair callbackURL = new BasicNameValuePair(CALLBACK_NAME, resultURLString);
AndroMDA uses the term "cartridge" (e.g. for out-of-the-box NHibernate support).
As I understood it, it takes an API/component, wrapps it, never adds new features, simplifies it, often taking away "the full power", but works well for most cases.
My questions:
Is the term widely used?
Can one properly define it?
Should the suffix "Cartridge" be used in class/method names?
An example: is the following Base64 helper a cartridge for Base64 conversion?
You give away all the power for performance-tuning, but if you simply want to decode a simple (and small) string it works fine:
Usage:
Base64StringCartridge.Decode(input);
Implementation
public static string Decode(string data)
{
try
{
System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding();
System.Text.Decoder utf8Decode = encoder.GetDecoder();
byte[] todecode_byte = System.Convert.FromBase64String(data);
int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);
char[] decoded_char = new char[charCount];
utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0);
string result = new String(decoded_char);
return result;
}
catch
{
return "";
}
}
It's called the Facade Pattern. Presumably the AndroMDA folks are big fans of old video game machines...