CSR generation using java or BouncyCastle without using Private key - java

Want to create a CSR file in java, when the private/public key pair are getting generated in HSM(Hardware Security Module).
On trying out the examples in Bouncy Castle, the generation of CSR requires both the private key and public key.As the generation of keys is happening in HSM, i have only the public key and the private key sham object.
Can i generate CSR in java without having the private key?
Please find the code sample i was trying.
KeyPair pair = generateKeyPair();
PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
new X500Principal("CN=Requested Test Certificate"), pair.getPublic());
JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA");
ContentSigner signer = csBuilder.build(pair.getPrivate());
PKCS10CertificationRequest csr = p10Builder.build(signer);
I am pretty new to HSM, and any input or reference will be helpful.

You can generate a CSR without having the value of the private key. You do need a reference to the private key, and the key must be capable of signing. References to private keys are just special versions of classes that implement PrivateKey. They don't contain the data, just the reference. Calling getEncoded or retrieving a private exponent of an RSA key will however (usually - it may depend on the key generation parameters and PKCS#11 middleware) fail with an exception.
The way these keys can be used is by just providing them to an init method of a newly generated Signature instance. The Java runtime will then search for the right SignatureSpi implementation in the right provider (the one for your HSM). This is called delayed provider selection as it only searches for an implementation after the init method is called. Of course in your case this will all happen out of sight by the ContentSigner.
The private key data should not leave your HSM at any time, unless wrapped for backup or sharing between HSM's.

Related

Is there any way to get the plain text from signed data using private key?

The plain text is signed using java.security.Signature. Below is the code used to sign the plain text
public String getSignature(String plainText) throws Exception
{
KeyStore keyStore = loadKeyStore(); // A local method to read the keystore file from file system.
PrivateKey privateKey = (PrivateKey) keyStore.getKey(KEY_ALIAS_IN_KEYSTORE, KEYSTORE_PASSWORD.toCharArray());
Signature privateSignature = Signature.getInstance(SIGNATUREALGO);
privateSignature.initSign(privateKey);
privateSignature.update(plainText.getBytes("UTF-8"));
byte[] signature = privateSignature.sign();
return String.valueOf(signature);
// KEY_ALIAS_IN_KEYSTORE, KEYSTORE_PASSWORD and SIGNATUREALGO are all constant Strings
}
Note 1: I found online a way to verify the signature using the public key Java Code Examples for java.security.Signature#verify(). But this is not what I require.
Note 2: I also found a ways to encrypt and decrypt as mentioned here RSA Signing and Encryption in Java. But the use case I have in hand is to get the original plain text from a signed data. Is that possible?
No, you can't retrieve the original content from just the signature.
The signature alone does not contain enough information to restore the original clear text, no matter what keys you have access to.
The basic idea of a signature is to send it together with the clear text. That means the clear text will be visible, but the signature can be used to verify that the message was written (or at least signed) by who claims to have done so and has not been tampered with since then.
Signing something is different from encrypting it. The two often uses the same or related technologies and both fall under cryptography.

AWS Encryption SDK Encrypt/Decrypt with Data Key

I'm attempting to encrypt data using KMS and the AWS Encryption SDK. Looking at the example provided in the AWS documentation, it appears that there is nowhere to explicitly set the data key.
I've found API documentation for the EncryptionMaterialsRequest class that allows you to set the plaintext key using the associated builder class, EncryptionMaterialsRequest.Builder, and this class has a method that returns an instance of EncryptionMaterials. I can't find anywhere to use the EncryptionMaterials instance when executing the encrypt operation.
Here is the code I have so far. Note that the EncryptionMaterials instance isn't used in the request.
public static void encryptData(String dataToEncrypt, String keyID) {
final KmsMasterKeyProvider prov = new KmsMasterKeyProvider(keyID);
DefaultCryptoMaterialsManager manager = new DefaultCryptoMaterialsManager(prov);
byte[] plaintextKey = generateDataKey(keyID);
EncryptionMaterialsRequest request = EncryptionMaterialsRequest
.newBuilder()
.setPlaintext(plaintextKey)
.build();
EncryptionMaterials materials = manager.getMaterialsForEncrypt(request);
AwsCrypto crypto = new AwsCrypto();
String encryptedString = crypto.encryptString(manager, dataToEncrypt).getResult();
}
public byte[] generateDataKey(String keyID) {
GenerateDataKeyRequest dataKeyRequest = new GenerateDataKeyRequest();
dataKeyRequest.setKeyId(keyID);
dataKeyRequest.setKeySpec(DataKeySpec.AES_256);
GenerateDataKeyResult dataKeyResult = kmsClient.generateDataKey(dataKeyRequest);
ByteBuffer encryptedKey = dataKeyResult.getCiphertextBlob();
byte[] arr = new byte[encryptedKey.remaining()];
encryptedKey.get(arr);
return arr;
}
What is the recommended approach encrypting data using the AWS Encryption SDK with a data key generated by KMS?
#Viccari is correct, but it sounds like some context around the intended use of these constructs would help explain why.
Unless you are building a custom cryptographic materials manager you should not be creating EncryptionMaterials; the client and management components take care of that for you.
The client asks the cryptographic materials manager for encryption materials on every encrypt call. Depending on the cryptographic materials manager, what exactly happens next might be different.
In the case of the DefaulCryptoMaterialsManager, it then asks the provided master key provider for all of the master keys to use, then uses those master keys to generate and encrypt the data key (one is used to generate and encrypt, any additional are used to encrypt).
In the case of the CachingCryptoMaterialsManager, it adds a caching layer between the client and another cryptographic materials manager.
If you want to use the AWS Encryption SDK with AWS KMS, the recommended approach is to simply provide an instance of KmsMasterKey or KmsMasterKeyProvider, or a cryptographic materials manager that ultimately uses one of those, in the encrypt call. All of the details are taken care of by the client.
If you're interested in more details about how these concepts fit together, our concepts documentation[1] would be a good place to start.
[1] https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/concepts.html
My question for you would be: why not using the DefaultCryptoMaterialsManager, which should generate a new data key from the master key for each encryption operation? Why are you going to reuse the data keys? This does not sound like a sound approach from the security perspective.
But if you want to do that, you need to provide an implementation of the CryptoMaterialsManager interface.
Instead of using DefaultCryptoMaterialsManager, create a new class, let's say, MyCryptoMaterialsManager, that implements the interface above.
Something like this would do it:
public static void encryptData(String dataToEncrypt, String keyID) {
// not sure whether you need this or where you're getting the data key from.
final KmsMasterKeyProvider prov = new KmsMasterKeyProvider(keyID);
MyCryptoMaterialsManager manager = new MyCryptoMaterialsManager(prov);
byte[] plaintextKey = generateDataKey(keyID);
EncryptionMaterialsRequest request = EncryptionMaterialsRequest
.newBuilder()
.setPlaintext(plaintextKey)
.build();
// this, you told you know how to do:
EncryptionMaterials materials = manager.getMaterialsForEncrypt(request);
AwsCrypto crypto = new AwsCrypto();
String encryptedString = crypto.encryptString(manager, dataToEncrypt).getResult();
}
public byte[] generateDataKey(String keyID) {
GenerateDataKeyRequest dataKeyRequest = new GenerateDataKeyRequest();
dataKeyRequest.setKeyId(keyID);
dataKeyRequest.setKeySpec(DataKeySpec.AES_256);
GenerateDataKeyResult dataKeyResult = kmsClient.generateDataKey(dataKeyRequest);
ByteBuffer encryptedKey = dataKeyResult.getCiphertextBlob();
byte[] arr = new byte[encryptedKey.remaining()];
encryptedKey.get(arr);
return arr;
}
If cost or number of calls to KMS is a concern, you could also use the CachingCryptoMaterialsManager instead. It provides guarantees like making sure that a data key is not used an indefinite number of times.

Why does the Java KeyStore fail at loading an OpenPGP key?

I am willing to spend some amount of time developing yet another license manager for desktop Java application. After some looking around I discovered JCPUID by Iakin that is free to use and should work at most operating systems with native libs that I found here.
My idea is to do two modules: main application that will show popup window with CPU ID and verification text field and key generator app. User will pass CPU ID to keygen owner, who will return verification code (generated with keygen) to user. After user submits correct verification code, license file with that code will be created at filesystem. Every time the application starts up, it will check the existence and correctness of that file and load main application screen after that.
What about code verification, I think the best option will be to use asymmetric cryptography, in particular RSA. The public key will be built-in into application and secret will be built-in into key generator. So CPUID will be passed to key generator owner and then signed with RSA. That signature will be transferred back to user, who will verify its validity with built-in public key.
I generated gpg key pairs using Kleopatra and gpg Linux command line tool itself. Then I tried to sign something using this method:
private byte[] createSignature(byte[] file) {
byte[] signature = null;
try {
java.security.KeyStore keyStoreFile = java.security.KeyStore
.getInstance("PKCS12");
keyStoreFile.load(getClass().getClassLoader().getResourceAsStream("/secret.asc"),
"******".toCharArray());
PrivateKey privateKey = (PrivateKey) keyStoreFile.getKey(
"My Name Here", "******".toCharArray());
Signature dsa = Signature.getInstance("SHA1withRSA");
dsa.initSign(privateKey);
dsa.update(file, 0, file.length);
signature = dsa.sign();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return signature;
}
But the privateKey initialization throws exception:
java.security.InvalidKeyException: Key must not be null
I guess it's because of wrong instance format here:
java.security.KeyStore keyStoreFile = java.security.KeyStore
.getInstance("PKCS12");
I would like to know:
How good is this approach at all?
What difference exists between different OpenPGP key formats and which will be the best to use at this case? How to know the format of existing OpenPGP file?
The Java crypto framework does not support OpenPGP. X.509 keys, for example in the PKCS12 format, are incompatible with OpenPGP -- although they rely on (mostly) the same cryptographic algorithms.
Either use X.509 certificates (you could also create your own CA for this purpose), or rely on an implementation of OpenPGP for Java. In terms of open source libraries, you can choose between the native Java implementation BouncyCastle (MIT license), or interface GnuPG (GPL) through the Java GPGME binding (LGPL).
BouncyCastle is probably the better way to go, as all you need to do is add another Java library, not install another software into the system.

Why does KeyValue take a public key?

I am trying to sign an XML document using Java and I'm following along with this tutorial. I was trying to sign the document using my private key, but when I looked at the API it says
KeyValue only takes a PublicKey as the parameter. Also in the tutorial it has me sign a DOMSignContext with the private key and then the XMLSignature with the public key.
DOMSignContext dsc = new DOMSignContext(kp.getPrivate(), doc.getDocumentElement());
KeyValue kv = kif.newKeyValue(kp.getPublic());
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));
XMLSignature signature = fac.newXMLSignature(si, ki);
I thought the whole point of a private key was so that people would trust you? Why would you need a public key for anything here? Can somebody explain the details here a little bit better for me?
In general, when someone wants to verify/decrypt something encrypted with a private key, the associated public key must be known (which is the whole point of public-key/asymmetric cryptography).
In the XMLSignature context, someone wanting to verify a document with a XMLSignature will need to know what public key to use; thus the public key can be included in a XMLSignature structure for convenience.
Successful verification of the XMLSignature means one can trust that the data signed has not been modified since the signature was created. If the public key is known to be associated with a particular party, then one can trust that party created the signature.
As noted in the tutorial, you still have to "sign" the signature with:
signature.sign(dsc);

Capicom and SHA1 - Help translating a java code to Delphi

I have a java application that signs a string using a certificate. It works encrypting the string it with SHA1. I am trying to translate the code to Delphi 2010, but I have no idea how to get it working the same way the java app does (using sha1). So far, I have found this:
Delphi 7 access Windows X509 Certificate Store
It does work, but it does not use sha1 and I get different results when I run the java app.
Java code
char[] pass = (char[]) null;
PrivateKey key = (PrivateKey) getKeyStore().getKey(alias, pass);
Certificate[] chain = getKeyStore().getCertificateChain(alias);
CertStore certsAndCRLs = CertStore.getInstance("Collection", new CollectionCertStoreParameters(Arrays.asList(chain)), "BC");
X509Certificate cert = (X509Certificate) chain[0];
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
gen.addSigner(key, cert, CMSSignedDataGenerator.DIGEST_SHA1);
gen.addCertificatesAndCRLs(certsAndCRLs);
CMSProcessable data = new CMSProcessableByteArray(conteudoParaAssinar);
CMSSignedData signed = gen.generate(data, true, "SunMSCAPI");
byte[] envHex = signed.getEncoded();
CertInfo certInfo = new CertInfo();
certInfo.Hash = new BigInteger(envHex).toString(16);
return certInfo;
Delphi Code
var
lSigner: TSigner;
lSignedData: TSignedData;
fs: TFileStream;
qt: integer;
ch: PChar;
msg : WideString;
content : string;
cert: TCertificate;
begin
cert := Self.GetCert;
content := 'test';
lSigner := TSigner.Create(self);
lSigner.Certificate := cert.DefaultInterface;
lSignedData := TSignedData.Create(self);
lSignedData.content := content;
msg := lSignedData.Sign(lSigner.DefaultInterface, false, CAPICOM_ENCODE_BASE64);
lSignedData.Free;
lSigner.Free;
EDIT
Based on the java code, should I get the cert info in binary format, apply sha1 on it and them convert it to hex? Is this the right order and the same thing the java code does? I can see some SHA1 constants in the capicom tlb as well as a hash class, maybe I should use those classes, but I dont know how.
We use DCPCrypt in some delphi apps that interface with our Java Tomcat App and are able to get SHA-256 compatible hashes. I suspect SHA1 is also easy.
Here's an example
function Sha256FileStreamHash(fs : TFileStream): String;
var
Hash: TDCP_sha256;
Digest: array[0..31] of byte; // RipeMD-160 produces a 160bit digest (20bytes)
i: integer;
s: string;
begin
if fs <> nil then
begin
fs.Seek(0, soFromBeginning);
Hash:= TDCP_sha256.Create(nil); // create the hash
try
Hash.Init; // initialize it
Hash.UpdateStream(fs,fs.Size); // hash the stream contents
Hash.Final(Digest); // produce the digest
s:= '';
for i:= 0 to 31 do
s:= s + IntToHex(Digest[i],2);
Result:= s; // display the digest
finally
Hash.Free;
end;
end;
end;
First, what makes you think you're not using SHA-1 ? I'm asking because CAPICOM's sign function only works with SHA-1 signature.
Second, how do you know that you're getting a different result ? Have you tried to validate the answer ? If yes, using what ?
Third, there is something that you MUST know about CAPICOM: the "content" property is a widestring. This has various implication, including the fact that all content will be padded to 16-bits. If your input data is of different size, you'll get a different result.
Based on the java code, should I get the cert info in binary format, apply sha1 on it and them convert it to hex?
No. You get an interface to an instance of a ICertificate object (or, more likely, ICertificate2) and you just use that directly. If you have the B64 encoded version of the certificate, you can create a new ICertificate instance and then call the ICertificate.Import method. The hash of the certificate itself is only used by the signing authority to sign that specific cert.
The hash algorythm is actually used during the data signature process: the library reads the data, creates a hash of that data (using SHA-1 in case of CAPICOM) and then digitally sign that hash value. This reduction is necessary because signing the whole data block would be far too slow and because, that way, you only have to carry the hash if you're using a hardware crypto system.
Is this the right order and the same thing the java code does?
Yes and no. The Java code does all the necessary steps in explicit details, something you don't have (and actually cannot) do with CAPICOM. It should result in compatible result, though.
It also has an additional step not related to the signature itself: I'm not sure what it does because it seems to create a dummy certificate information data and store the SHA-1 hash value of the signed CMS message and return the resulting instance. I suppose that it's a way the Java dev has found to pass the hash value back to the caller.
I can see some SHA1 constants in the capicom tlb as well as a hash class, maybe I should use those classes, but I dont know how.
The HashedData class is used to (surprise) hash data. It has the same limitation as Signeddata i.e. it only works on widestrings so compatibility with other frameworks is dodgy at best.
Final note: Windows offers access to much more comprehensive cryptographic functions through the CAPI group of functions. CAPICOM is only an interface to that library that is used (mostly) in script language (JavaScript on web pages, VB, etc). You should do yourself a favor and try using it instead of CAPICOM because there is a good chance you'll encounter something that you simply cannot do properly using CAPICOM. At that stage, you will have to rewrite part for all of your application using CAPI (or another library). So save time now and skip CAPICOM if you don't have a requirement to use it.

Categories