How to persist public key created by RSACng to Local machine or Key Storage provider(KSP)? - cryptography

I have a Public key and private key pair generated by RSACng class. I am able to persist private key into my KSP(MicrosoftSoftwareKeyStorageProvider) under local machine->(Program Data-> Crypto->RSA->Keys) .But, i am unable to persist public key generated by RSACng. How to persist public key in RSACng to KSP(MicrosoftSoftwareKeyStorageProvider)?
I have already tried persisting public key using CngKey, But it is throwing me 'The operation is not supported'.Please find below the code.
public static void SaveKeyPairToKSP(KeyGenerationResult keyData, string keyName)
{
var myKSP = CngProvider.MicrosoftSoftwareKeyStorageProvider;
const bool MachineKey = true;
if (!CngKey.Exists(keyName, myKSP))
{
var keyParams = new CngKeyCreationParameters
{
ExportPolicy = CngExportPolicies.AllowArchiving,
KeyCreationOptions = (MachineKey) ? CngKeyCreationOptions.MachineKey : CngKeyCreationOptions.None,
Provider = myKSP
};
keyParams.Parameters.Add(new CngProperty("Length", BitConverter.GetBytes(keyData.KeySize), CngPropertyOptions.None));
keyParams.Parameters.Add(new CngProperty(CngKeyBlobFormat.GenericPrivateBlob.Format, keyData.PrivateBytes, CngPropertyOptions.Persist));
//Here is my public key that i want to store in my KSP
keyParams.Parameters.Add(new CngProperty(CngKeyBlobFormat.GenericPublicBlob.Format, keyData.PublicBytes, CngPropertyOptions.Persist));
CngKey.Create(CngAlgorithm.Rsa, keyName, keyParams);
}
}
But , with above code, it throws me "The operation is not supported" exception.
In case, only private key is only added for persistence without public key, code works fine.
Expected result-> I want to persist public key as well as private key in my KSP.
actual result-> Only private key is getting persisted. Please do help me on the same. Thanks in advance! Can you please help me out with this?

Microsoft CNG does not support exporting of public keys for assymetric encryption as indicated at https://learn.microsoft.com/en-us/windows/win32/seccng/key-import-and-export
which states:
"For BCryptExportKey to create a persisted key pair, the input key BLOB must contain a private key. Public keys are not persisted."

Related

confused on validation of token on auth0 JWT java library

I read public/private key is so you can
create JWT token with private / public key
hand out your public key only to 3rd parties
3rd parties can now validate users JWT tokens via the public key
However, their example with private / public key requires the private key to validate which seems odd ->
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJhdXRoMCJ9.AbIJTDMFc7yUa5MhvcP03nJPyCPzZtQcGEp-zWfOkEE";
RSAPublicKey publicKey = //Get the key instance
RSAPrivateKey privateKey = //Get the key instance
try {
Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer("auth0")
.build(); //Reusable verifier instance
DecodedJWT jwt = verifier.verify(token);
} catch (JWTVerificationException exception){
//Invalid signature/claims
}
Is there no way to validate with just the public key?
On this line:
Algorithm algorithm = Algorithm.RSA256(publicKey, privateKey);
pass the privateKey as null. Private keys are for signing.
Algorithm algorithm = Algorithm.RSA256(publicKey, null);

DDD: Can I pass a Domain-Service as a parameter of aggregate constructor

I have a domain that maintains accounts for other systems(Media).
At first, I derived the following aggregate root
public class Account extends Entity {
private AccountId accountId;
private TenantId tenantId;
private LoginAccount loginAccount;
private Media media;
private LoginValidity validity;
public Account(TenatId shopId, Media media, LoginAccount loginAccount) {
this.accountId = new AccountId();
this.setTenatId(shopId);
this.set(media);
this.setLoginValidity(LoginValidity.NOT_REQUESTED);
}
public void validateLogin(LoginValidationService loginValidationService) {
LoginValidity validity = loginValidationService.validateLoginFor(media,loginAccount);
setLoginValidity(validity);
//TO-DO EVENT PUBLISHING
}
public void changeLoginAccount(LoginAccount loginAccount) {
setLoginAccount(loginAccount);
setLoginValidity(LoginValidity.NOT_REQUESTED);
//TO-DO EVENT PUBLISHING?
}
...Setters
}
And I also derived the LoginValidationService as a Domain-Service.
LoginValidationService determines strategy(policy) using Media
And then I also derive two business logic(invariant)
When the User adds a Account login validation must occur.
When the User changes LoginAccount login validation must occur.
My question is that for the first invariant,LoginValidationService(A Domain-Service) could be a parameter for aggregate root's constructor like this
public class AccountApplicationService {
private LoginValidationService loginValidationService;
private AccountRepository accountRepository;
public Account createAccount(CreateAccountCommand command) {
TenantId tenantId = new TenantId(command.getTenantId());
Media media = mediaService.mediaFrom(command.getMediaId());
Account account = new Account(tenantId,
media,
command.getLoginAccount(),
loginValidationService);
accountRepository.save(account);
return ccount;
}
...
}
public class Account extends Entity {
private AccountId accountId;
private TenantId tenantId;
private LoginAccount loginAccount;
private Media media;
private LoginValidity validity;
public Account(TenatId shopId,
Media media,
LoginAccount
loginAccount,LoginValidationService loginValidationService) {
this.accountId = new AccountId();
this.setTenatId(shopId);
this.set(media);
LoginValidity validity =
loginValidationService.validateLoginFor(media,loginAccount);
this.setLoginValidity(validity);
}
....
}
Is exist the Pattern above? (passing the domain-service to the constructor) and is it the right approach of DDD?
Do I have to derive the first invariant as a use-case? like this,
public class AccountApplicationService {
private LoginValidationService loginValidationService;
private AccountRepository accountRepository;
public Account createAccountAndValidateLogin(CreateAccountAndValidateLoginCommand command) {
TenantId tenantId = new TenantId(command.getTenantId());
Media media = mediaService.mediaFrom(command.getMediaId());
MediaAccount mediaAccount = new MediaAccount(tenantId,media,command.getLoginAccount());
mediaAccount.validateLogin(loginValidationService);
mediaAccountRepository.save(mediaAccount);
}
...
}
Please give me any advice.
-----------Edit---------
I add the Account's constructor code. LoginValidationService is not a member of Account.
Would be a solution for you to pass the LoginValidity as Account aggregate constructor parameter?
public Account createAccount(CreateAccountCommand command) {
TenantId tenantId = new TenantId(command.getTenantId());
Media media = mediaService.mediaFrom(command.getMediaId());
LoginValidity loginValidity =
loginValidationService.validateLoginFor(media,command.getLoginAccount());
Account account = new Account(tenantId,
media,
command.getLoginAccount(),
loginValidity);
accountRepository.save(account);
return ccount;
}
I think that to validate an account is something that the entity cannot do by yourself so it is clear is domain service's responsibility, then I would see in the use case flow logic, or you can create the account with a domain service responsible of validate and create the account and you had the business logic encapsulates in domain service instead of use case.
Can I pass a Domain-Service as a parameter of aggregate constructor
Yes, but I would expect that to make your life harder in the long run.
An AGGREGATE is a cluster of associated objects that we treat as a unit for the purpose of data changes. -- Evans, 2003
Domain Services, however, don't change -- they are stateless, and "any client can use any instance of a particular SERVICE without regard to the instance's individual history."
So mixing the patterns in this way is a bit odd on two fronts; including the service as a member of the aggregate suggests that it changes, and also it implies that this aggregate has a special relationship with a particular instance of the service, which contradicts the notion that they are not interchangeable.
It's not clear to me what compensating advantage you get in a design where the service is embedded within your aggregate.
I found an additional invariant for Account creating that Account can't hold itself so I derived AccountProvisionService(Domain-Service). The Application-Service code is as follows.
...
public Account createAccount(CreateAccountCommand command) {
return AccountProvisioningService.provisionAccount(
command.getTenantId(), command.getMediaId(), command.getLoginAccount());
}
public void changeLoginAccount(ChangeLoginAccountCommand command) {
Account account = existingAccount(command.getShopId(),command.getAccountId());
LoginValidity loginValidity = loginValidationService.validateLoginFor(Account.media(), command.getLoginAccount());
account.changeLoginAccount(command.getLoginAccount(),loginValidity);
AccountRepository.save(account);
}
...

Redis defaultDatabase Argument Not Working on Adds

So I am adding a key to a redis database (using StackExchange.Redis) that has a connection string as follows:
server:6379,ssl=false,abortConnect=false,password=*****,defaultDatabase=1
The behavior I am seeing is that keys are added to the default database of 0 not what I specify in my connection string, however it appears the default database in the string is where keys are attempted to be retrieved from. Any thoughts before I go littering my class with explicit database sets?
EDIT
To get objects from cache:
public object Get(string key, Type t = null)
{
var cacheStore = Connection.GetDatabase();
var cachedItem = cacheStore.StringGet(key);
return !cachedItem.IsNull ? JsonConvert.DeserializeObject(cachedItem, t) : null;
}
To add objects to cache:
public void Add(string key, object value, TimeSpan? expiry)
{
var cacheStore = Connection.GetDatabase();
var serializedData = JsonConvert.SerializeObject(value);
cacheStore.StringSet(key, serializedData, expiry);
}
As mentioned if I pass the database id to Connection.GetDatabase() it works without issue, if I rely on the defaultDatabase argument in the connection string items get added to database 0 and it attempts to retrieve from database 1.

Saving JWT token in static filed is best practice?

Saving KeyForHmacSha256, TokenIssuer, TokenAudience and TokenLifetimeMinutes in static filed is best practice or read these value from config file.
public class SecurityConstants
{
public static readonly byte[] KeyForHmacSha256 = new byte[64];
public static readonly string TokenIssuer = string.Empty;
public static readonly string TokenAudience = string.Empty;
public static readonly double TokenLifetimeMinutes = 1;
static SecurityConstants()
{
RNGCryptoServiceProvider cryptoProvider = new RNGCryptoServiceProvider();
cryptoProvider.GetNonZeroBytes(KeyForHmacSha256);
TokenIssuer = "issuer";
TokenAudience = "http://localhost:90";
}
}
As with about anything, the answer is "it depends."
I would certainly make the argument that the KeyForHmacSha256 variable is pulled from a config file or environment variable, just to keep it out of source control.
Personally, I usually pull in issuer and audience dynamically. The issuer is pulled from the environment so that I don't have to manually set it in each deploy and the audience is determined by who is requesting the token.
The token lifetime has the best case for just being a static definition. If you have a need to make it dynamic, you will need to handle that, but setting it explicitly isn't a security issue.

Windows 8 Metro RSA Encryption: AsymmetricKeyAlgorithmProvider ImportPublicKey Fails

I am attempting to pass some encrypted data between a Win 8 Metro app and a RESTful WCF service. Initially the Metro app requests a public key and the WCF service returns it as a raw Stream as to avoid any pesky formatting issues. The Base 64 encoded public key is decoded in the metro app into a byte array. Here is where the problem occurs. When I attempted to call AsymmetricKeyAlgorithmProvider.ImportPublicKey I get the error "ASN1 bad tag value met".
I am using RSA PKCS1 for the encryption. Here is the relevant code:
WCF Service
string keyName = "This is passed in via a parameter";
var key = !CngKey.Exists(keyName) ? CngKey.Create(CngAlgorithm2.Rsa, keyName) : CngKey.Open(keyName);
// Create the RSA container to get keys and then dispose
using (var rsaCng = new RSACng(key) { EncryptionPaddingMode = AsymmetricPaddingMode.Pkcs1, KeySize = 2048 })
{
byte[] publicBlob = rsaCng.Key.Export(CngKeyBlobFormat.GenericPublicBlob);
publicKey = Convert.ToBase64String(publicBlob);
}
Metro App
public static string Encrypt(IBuffer dataBuffer, string publicKeyString)
{
var asymmAlg = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
// The next line fails with ASN1 bad tag value met
var publicKey = asymmAlg.ImportPublicKey(CryptographicBuffer.DecodeFromBase64String(publicKeyString), CryptographicPublicKeyBlobType.Pkcs1RsaPublicKey);
var encryptedData = CryptographicEngine.Encrypt(publicKey, dataBuffer, null);
return CryptographicBuffer.EncodeToBase64String(encryptedData);
}
EDIT 1: More information below
Exporting the public key from a 2048bit key pair from the WCF service yields a 283 bit length key blob, while exporting the same type of public key from the Metro app is only 270 bits. When I import the Metro generated public key it succeeds. Any idea why the WCF service has 13 extra bits on its public key? I think those extra 13 bits are causing the failure.
Here is the Metro code that yields the shorter public key blob:
var provider = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
CryptographicKey standardKeyPair = provider.CreateKeyPair(2048);
byte[] standardKey = standardKeyPair.ExportPublicKey(CryptographicPublicKeyBlobType.Pkcs1RsaPublicKey).ToArray();
Quite late, but maybe it will help you or saves someone's time...
Change the type of blob type during import. It's really wierd, but I had success with it, after experimenting.
Your code in WCF may stay as it is.
Change just the Metro code:
public static string Encrypt(IBuffer dataBuffer, string publicKeyString)
{
var asymmAlg = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.RsaPkcs1);
// The next line fails with ASN1 bad tag value met
var publicKey = asymmAlg.ImportPublicKey(CryptographicBuffer.DecodeFromBase64String(publicKeyString), CryptographicPublicKeyBlobType.BCryptPublicKey);
var encryptedData = CryptographicEngine.Encrypt(publicKey, dataBuffer, null);
return CryptographicBuffer.EncodeToBase64String(encryptedData);
}
So the only change here is the BCryptPublicKey during the importing. Then it works. But do not ask me why :-).