I have to create PKCS10 certificate request for RSA keypair stored on cryptocard with access via PKCS11 interface. The thing is that I can't use standard RSA algorithms in PKCS10 creation process and I can't get private key from cryptocard, so crypto operations should be done on cryptocard side. How can I prepare (maybe manually) PKCS10 request using PKCS11 interface for signing?
#Edit
Now I have an error like this:
Exception in thread "main" java.security.ProviderException: Initialization failed
at sun.security.pkcs11.P11Signature.initialize(P11Signature.java:310)
at sun.security.pkcs11.P11Signature.engineInitSign(P11Signature.java:391)
at java.security.Signature$Delegate.engineInitSign(Signature.java:1127)
at java.security.Signature.initSign(Signature.java:511)
at pkcs11.Main.stworzPkcs10(Main.java:65)
at pkcs11.Main.main(Main.java:53)
Caused by: sun.security.pkcs11.wrapper.PKCS11Exception: CKR_GENERAL_ERROR
at sun.security.pkcs11.wrapper.PKCS11.C_SignInit(Native Method)
at sun.security.pkcs11.P11Signature.initialize(P11Signature.java:302)
... 5 more
Java Result: 1
and code:
String zawartoscPlikuKonfiguracyjnego = new String(
"name=PKCS11\n" +
"library=" + sciezkaDoBibliotekiPkcs11);
FileOutputStream plikKonfiguracyjny = new FileOutputStream("pkcs11.cfg");
plikKonfiguracyjny.write(zawartoscPlikuKonfiguracyjnego.getBytes());
plikKonfiguracyjny.close();
File test = new File("pkcs11.cfg");
dostawcaPkcs11 = new SunPKCS11("pkcs11.cfg");
Security.addProvider(dostawcaPkcs11);
interfejsPkcs11 = KeyStore.getInstance("PKCS11",dostawcaPkcs11);
pin = new JPasswordField();
JOptionPane.showConfirmDialog(null, pin, "Podaj pin do tokena", JOptionPane.OK_CANCEL_OPTION);
interfejsPkcs11.load(null, pin.getPassword());
Key kluczPrywatny = null;
Key kluczPubliczny = null;
Enumeration<String> aliasy = interfejsPkcs11.aliases();
while(aliasy.hasMoreElements()) {
String alias = aliasy.nextElement();
if(interfejsPkcs11.isKeyEntry(alias)) {
kluczPrywatny = interfejsPkcs11.getKey(alias, pin.getPassword());
} else {
kluczPubliczny = interfejsPkcs11.getCertificate(alias).getPublicKey();
}
}
PKCS10 pkcs10 = new PKCS10((PublicKey) kluczPubliczny );
Signature sygnatura = Signature.getInstance("SHA1WithRSA", dostawcaPkcs11);
sygnatura.initSign((PrivateKey) kluczPrywatny);
X500Name nazwaX500 = new X500Name("Certyfikat testowy", "Developer", "Developer", "Warszawa", "Mazovia", "PL");
pkcs10.encodeAndSign(nazwaX500, sygnatura);
I would recommend you try using the Sun PKCS#11 provider. You will need to follow the documentation to understand how to configure the provider to use the DLL supplied by your cryptocard supplier. (Also worth asking them their recommended approach to accessing the cryptocard from Java).
Once you have this working, you should be able to load your public and private keys from a keystore (again see the docs for how this works). Finally, you can use the JCE libraries to produce the signing request:
String CN = "Mr Foo";
String OU = "Foo Department";
String O = "Foo Ltd.";
String L = "Foosville";
String S = "Foouisiana";
String C = "GB";
PublicKey publicKey = //... load public key from keystore
PrivateKey privateKey = //... load private key from keystore
PKCS10 pkcs10 = new PKCS10(publicKey);
Signature signature = Signature.getInstance("SHA1WithRSA"); // or whatever
signature.initSign(privateKey);
X500Name x500Name = new X500Name(CN, OU, O, L, S, C);
pkcs10.encodeAndSign(new X500Signer(signature, x500Name));
try (ByteArrayOutputStream bs = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(bs)) {
pkcs10.print(ps);
byte[] c = bs.toByteArray(); // <-- this is it! (save to disk maybe?)
}
Related
I need to encrypt the data using public key located in Safenet HSM Luna SA device and also need to decrypt the data using private key which also located in HSM device in JAVA.
I'm completely new to the HSM device. I have encrypted/ decrypted data using keys which are located in epass e-token device as follows:
private void loadKeys() {
logger.info("In loadKeys method at "+new Date());
try {
char password[] = hsmServiceAppProps.getDigiSigPfxPassword().toCharArray();
Provider userProvider = new sun.security.pkcs11.SunPKCS11(this.getClass().getClassLoader().getResourceAsStream("/pkcs11.cfg"));
Security.addProvider(userProvider);
KeyStore ks = KeyStore.getInstance("PKCS11");
ks.load(null, password);
String alias = null;
/*X509Certificate userCert = null;
PrivateKey userCertPrivKey = null;
PublicKey userCertPubKey = null;
Enumeration<String> e = ks.aliases();
while (e.hasMoreElements()) {
alias = (String) e.nextElement();
logger.info("Alias of the e-Token : " + alias);
userCert = (X509Certificate) ks.getCertificate(alias);
userCertPubKey = (PublicKey) ks.getCertificate(alias).getPublicKey();
userCertPrivKey = (PrivateKey) ks.getKey(alias, password);
}*/
alias = "*************************************";
//X509Certificate certificate = (X509Certificate) ks.getCertificate(alias);
publicKey = (PublicKey) ks.getCertificate(alias).getPublicKey();
privateKey = (PrivateKey) ks.getKey(alias, password);
} catch (Exception e) {
logger.error("Error while getting public and private keys ->> ",e);
}
}
private String performEncryption(String content,PublicKey publicKey) throws Exception {
logger.debug("Encrypting using public key : "+content);
Cipher publicEncryptCipher = Cipher.getInstance("RSA");
publicEncryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBinaryData = publicEncryptCipher.doFinal(content.getBytes());
Base64 encoder = new Base64();
String encodedEncryptedContent = new String(encoder.encode(encryptedBinaryData),"UTF-8");
logger.debug("Encrypted Content ->> "+encodedEncryptedContent);
return encodedEncryptedContent;
}
private String performDecryption(String encodedEncryptedContent, PrivateKey privateKey) throws Exception {
logger.debug("Decrypting with private key ->> "+encodedEncryptedContent);
Base64 decoder = new Base64();
byte[] encryptedString = decoder.decode(encodedEncryptedContent.getBytes());
Cipher privateDecryptCipher = Cipher.getInstance("RSA");
privateDecryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedBinaryData = privateDecryptCipher.doFinal(encryptedString);
String decryptedContent = new String(decryptedBinaryData,"UTF-8");
logger.debug("Decrypted Content ->> "+decryptedContent);
return decryptedContent;
}
In the same way I need to do encryption/decryption using HSM device. I have installed Luna client software and imported keys to the HSM device.
Could any one please help me
Once you have successfully installed Luna client. you can use use either Luna JSP or JCProv libraries to perform cryptographic operation on HSM by using keys residing on HSM.
To check if Luna client is installed and registered with the remote HSM correctly, you can run the following command: "VTL.exe verify" from your luna client directory.
Output of successfully VTL verify
Here is an example of Encryption and Decryption using public and private keys of RSA.
void asymetricEncDec(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE hPublicKey,
CK_OBJECT_HANDLE hPrivateKey)
{
//session - handle to an open session
//hPublicKey - handle to public asymetric key to use for encryption
//hPrivateKey - handle to private asymetric key to use for decryption
String startString = "this is 16 bytes";
byte[] plainText = startString.getBytes();
byte[] cipherText = null;
LongRef lRefEnc = new LongRef();
LongRef lRefDec = new LongRef();
//mechanism to use
CK_MECHANISM mechanism = new CK_MECHANISM(CKM.RSA_PKCS);
/* get ready to encrypt */
CryptokiEx.C_EncryptInit(session, mechanism, hPublicKey);
/* get the size of the cipher text */
CryptokiEx.C_Encrypt(session, plainText, plainText.length, null,
lRefEnc);
/* allocate space */
cipherText = new byte[(int)lRefEnc.value];
/* encrypt */
CryptokiEx.C_Encrypt(session, plainText, plainText.length, cipherText,
lRefEnc);
/* get ready to decrypt */
CryptokiEx.C_DecryptInit(session, mechanism, hPrivateKey);
/* get the size of the plain text */
CryptokiEx.C_Decrypt(session, cipherText, lRefEnc.value, null, lRefDec);
/* allocate space */
plainText = new byte[(int)lRefDec.value];
/* decrypt */
CryptokiEx.C_Decrypt(session, cipherText, lRefEnc.value, plainText,
lRefDec);
/* make sure that we end up with what we started with */
String endString = new String(plainText, 0, (int)lRefDec.value);
if (startString.compareTo(endString) == 0)
{
println("Decrypted string matches original string - hurray");
}
else
{
println("Decrypted string does not match original string - boo");
}
}
This example is using JCProv library provided by luna client. Note: JCProv lower level library close to the 'C' implementation of PKCS#11.
You can also use the IAIK PKCS#11 wrapper to make all kinds of operations on the HSM. The wrapper is open source. It is well documented and working code examples are available.
Ref: https://jce.iaik.tugraz.at/products/core-crypto-toolkits/pkcs11-wrapper/
I am trying to digitally sign xml document and verify the signature with the original xml file with public key and signed document. I have a java code for reference. I need to convert java code to C# where I have java code like this:
certList = new ArrayList<X509Certificate>();
certList.add(signerCert);
certStore = new JcaCertStore(certList);
signedDataGenerator = new CMSSignedDataGenerator();
ContentSigner sha2Signer = new JcaContentSignerBuilder("SHA512with" + privateKey.getAlgorithm()).build(privateKey);
ignedDataGenerator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).setDirectSignature(true).build(sha2Signer, signerCert));
signedDataGenerator.addCertificates(certStore);
CMSSignedData sigData = signedDataGenerator.generate(new CMSProcessableFile(inputXmlFile), false);
signedBytes = sigData.getEncoded();
I have converted java code to C# like this:
X509Store my = new X509Store(StoreName.My, StoreLocation.LocalMachine);
my.Open(OpenFlags.ReadOnly);
// Find the certificate we’ll use to sign
RSACryptoServiceProvider csp = null;
foreach (X509Certificate2 cert in my.Certificates)
{
if (cert.Subject.Contains(certSubject))
{
// We found it.
// Get its associated CSP and private key
csp = (RSACryptoServiceProvider)cert.PrivateKey;
}
}
if (csp == null)
{
throw new Exception("oppose no valid application was found");
}
// Hash the data
SHA512Managed sha1 = new SHA512Managed();
UnicodeEncoding encoding = new UnicodeEncoding();
byte[] data = encoding.GetBytes(text);
byte[] hash = sha1.ComputeHash(data);
// Sign the hash
return csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
I am trying to convert it since two days, It is generating sign byte array but not been able to verify. While verifying it is throwing bad hash\r\n error I shall be highly grateful for any assistance. I know I am somewhere wrong in converting the java code to C#. I am able to verify the code but not been able to sign the document
I have generated Signature using System.Security.Cryptography.Pkcs library like this
public static byte[] Sign(byte[] data, X509Certificate2 certificate)
{
if (data == null)
throw new ArgumentNullException("data");
if (certificate == null)
throw new ArgumentNullException("certificate");
// setup the data to sign
ContentInfo content = new ContentInfo(data);
SignedCms signedCms = new SignedCms(content, false);
CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, certificate);
// create the signature
signedCms.ComputeSignature(signer);
return signedCms.Encode();
}
and verify the signature like this
private static bool VerifySignatures(FileInfo contentFile, Stream signedDataStream)
{
CmsProcessable signedContent = null;
CmsSignedData cmsSignedData = null;
Org.BouncyCastle.X509.Store.IX509Store store = null;
ICollection signers = null;
bool verifiedStatus = false;
try
{
//Org.BouncyCastle.Security.addProvider(new BouncyCastleProvider());
signedContent = new CmsProcessableFile(contentFile);
cmsSignedData = new CmsSignedData(signedContent, signedDataStream);
store = cmsSignedData.GetCertificates("Collection");//.getCertificates();
IX509Store certStore = cmsSignedData.GetCertificates("Collection");
signers = cmsSignedData.GetSignerInfos().GetSigners();
foreach (var item in signers)
{
SignerInformation signer = (SignerInformation)item;
var certCollection = certStore.GetMatches(signer.SignerID);
IEnumerator iter = certCollection.GetEnumerator();
iter.MoveNext();
var cert = (Org.BouncyCastle.X509.X509Certificate)iter.Current;
verifiedStatus = signer.Verify(cert.GetPublicKey());
}
}
catch (Exception e)
{
throw e;
}
return verifiedStatus;
}
It is working for me
I try to revoke certificates over cmp (certificate management protocol) at a certificate authority (the cmp server) and getting the error code "invalid signature key code". I think its becouse of the way i sign the cmp message, something went wrong there.
I build the header with org.bouncycastle.asn1.cmp.PKIHeaderBuilder and the body with org.bouncycastle.asn1.crmf.CertTemplateBuilder:
CertTemplateBuilder builderCer = new CertTemplateBuilder();
// cert to revoke
builderCer.setIssuer(issuer);
builderCer.setSerialNumber(serial);
//body
ArrayList revDetailsList = new ArrayList();
revDetailsList.add(new RevDetails(builderCer.build()));
RevReqContent revReqContent = new RevReqContent((RevDetails[]) revDetailsList.toArray(new RevDetails[revDetailsList.size()]));
PKIBody body = new PKIBody(PKIBody.TYPE_REVOCATION_REQ, revReqContent);
// header
X509Name recipient = new X509Name("CN=recipient");
X509Name sender = new X509Name("CN=sender");
int pvno = 1;
PKIHeaderBuilder builderHeader = new PKIHeaderBuilder(pvno, new GeneralName(sender), new GeneralName(recipient));
AlgorithmIdentifier algId = new AlgorithmIdentifier(new ASN1ObjectIdentifier("1.2.840.10045.4.1"));
builderHeader.setProtectionAlg(algId);
PKIHeader header = builderHeader.build();
Then i have to sign the whole message and there seems to be different ways for it. In extracerts (CMPCertificate) i have to add the public key of the signature, the signature have to be verifiable with this publickey. How do i sign this message for this kind of requisition correctly? I tried org.bouncycastle.asn1.cmp.PKIMessages and org.bouncycastle.cert.cmp.ProtectedPKIMessage.
PKIMessages:
DERBitString signature = new DERBitString(createSignature("signature".getBytes()));
X509Certificate signercert = convertToX509Cert(certPEM);
CMPCertificate cmpCert = new CMPCertificate(org.bouncycastle.asn1.x509.Certificate.getInstance(signercert.getEncoded()));
PKIMessage message = new PKIMessage(header, body, signature, new CMPCertificate[] { cmpCert });
// createsignature()
private static byte[] createSignature(byte[] str){
Signature dsa = Signature.getInstance("SHA256WithRSA");
dsa.initSign(privateKey);
dsa.update(str, 0, str.length);
signature = dsa.sign();
return signature;
--> error from cmp server: "SIGNATURE_INVALID_KEY_CODE"
ProtectedPKIMessage:
ContentSigner signer = new JcaContentSignerBuilder("SHA1WithRSAEncryption").setProvider(BouncyCastleProvider.PROVIDER_NAME).build((PrivateKey) ks.getKey(KEYSTORE_ALIAS, KEYSTORE_PWD.toCharArray()));
ProtectedPKIMessage message = new ProtectedPKIMessageBuilder(pvno, new GeneralName(sender), new GeneralName(recipient))
.addCMPCertificate(new X509CertificateHolder(ks.getCertificate(KEYSTORE_ALIAS).getEncoded()))
.setBody(body).build(signer);
--> error from cmp server: "ERROR_READING_CMS_OBJECT_CODE"
Is the way how i sign the cmp request message correct?
What is the difference between PKIMessage and the 'protection' argument and org.bouncycastle.cert.cmp.ProtectedPKIMessage?
this is the way i use to sign cmp requests
GeneralName generalName = new GeneralName(subjectDN);
ProtectedPKIMessageBuilder pbuilder = new
ProtectedPKIMessageBuilder(generalName,
protectedPKIMessage.getHeader().getSender());
pbuilder.setBody(pkibody);
ContentSigner msgsigner = new
JcaContentSignerBuilder(contentSignerBuilder)//
.setProvider("BC")//
.build(getKey().getPrivate());
ProtectedPKIMessage message = pbuilder.build(msgsigner)
;
I also found another solution by using PKIMessage (not ProtectedPKIMessage):
// ProtectedPart from bouncy castle
ProtectedPart protectedPart = new ProtectedPart(header, body);
Signature signature = Signature.getInstance("1.2.840.113549.1.1.11", "BC");
signature.initSign((PrivateKey) key);
signature.update(protectedPart.getEncoded());
byte[] sigBytes = signature.sign();
DERBitString signatureDER = new DERBitString(sigBytes);
PKIMessage message = new PKIMessage(header, body, signatureDER, new CMPCertificate[] { cmpCert });
Here what I have so far generating a Certificate for a User
try {
Security.addProvider(new BouncyCastleProvider()); // adding provider
// to
String pathtoSave = "D://sureshtest.cer";
KeyPair keyPair = generateKeypair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
X509Certificate trustCert = createCertificate(null, "CN=CompanyName",
"CN=Owner", publicKey, privateKey);
java.security.cert.Certificate[] outChain = { trustCert, };
trustCert.checkValidity();
KeyStore outStore = KeyStore.getInstance("PKCS12");
outStore.load(null, null);
outStore.setKeyEntry("my own certificate", privateKey,
"admin123".toCharArray(), outChain);
OutputStream outputStream = new FileOutputStream(pathtoSave);
outStore.store(outputStream, "admin123".toCharArray());
outputStream.flush();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
The above code generate a certificate with a private and public key.
Now I want to sign that certificate with a signing certificate I've been issued by a certificate authority (CA). After that I'll grant that certificate to user.
I got some input from here and it seems that is not the required answer with my case.
No need for a full implementation, just a valid procedure or some hints will greatly help.
You need to generate a CSR so you can invoke the code from Sign CSR using Bouncy Castle which is using the BC API. Add this to your code above:
final PKCS10 request = new PKCS10(publicKey);
final String sigAlgName = "SHA1WithRSA"; // change this to SHA1WithDSA if it's a DSA key
final Signature signature = Signature.getInstance(sigAlgName);
signature.initSign(privateKey);
final X500Name subject = new X500Name(trustCert.getSubjectDN().toString());
final X500Signer signer = new X500Signer(signature, subject);
// Sign the request and base-64 encode it
request.encodeAndSign(signer);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final PrintStream writer = new PrintStream(baos);
request.print(writer);
// Remove -----BEGIN NEW CERTIFICATE REQUEST----- and -----END NEW CERTIFICATE REQUEST-----
final String requestBase64 = new String(baos.toByteArray());
String withoutTags = requestBase64.substring(41);
withoutTags = withoutTags.substring(0, withoutTags.length() - 39);
// org.bouncycastle.pkcs.PKCS10CertificationRequestHolder
final PKCS10CertificationRequest holder = new PKCS10CertificationRequest(Base64.decode(withoutTags));
// Feed this into https://stackoverflow.com/questions/7230330/sign-csr-using-bouncy-castle
i have a web service running on JBOSS. that web service has a method called public X509Certificate provideCertificate(String name, PublicKey key){ ... } that provides clients with a fresh certificate so they communicate with other parties.
problem is, i cant receive a PublicKey because JAXB complains its an interface and i cant return a X509Certificate because it doesnt have an empty constructor (or so JBOSS says).
i've tried encapsulating those objects on some kind of DTO object but it didnt work as well.
i know maybe this is not the way to do it, so any lights on the subject would be greatly appretiated.
my web service code:
#javax.jws.WebService
public class CAWebService
{
#javax.jws.WebMethod
public X509Certificate addOperatorPublicKey(PublicKeyReqResDTO req)
{
PublicKey key = req.getPublicKey();
String operador = req.getNome();
X509CertImpl cert = null;
try
{
// used algorithm
String algorithm = "MD5WithRSA";
// create certificate for this request
PrivateKey privateKey = caKeyPair.getPrivate();
X509CertInfo info = new X509CertInfo();
// 3600000 = 1 hour maximum duration
Date from = new Date();
Date to = new Date(from.getTime() + 3600000L);
CertificateValidity interval = new CertificateValidity(from, to);
BigInteger sn = new BigInteger(64, new SecureRandom());
X500Name owner = new X500Name(operador);
info.set(X509CertInfo.VALIDITY, interval);
info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
info.set(X509CertInfo.ISSUER, new CertificateIssuerName(new X500Name("CA")));
info.set(X509CertInfo.KEY, new CertificateX509Key(key));
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));
// signs the certificate using this web service private key
cert = new X509CertImpl(info);
cert.sign(privateKey, algorithm);
// updates and re-signs
algo = (AlgorithmId)cert.get(X509CertImpl.SIG_ALG);
info.set(CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM, algo);
cert = new X509CertImpl(info);
cert.sign(privateKey, algorithm);
}
//catch all the exceptions, its like 10 diffente ones
catch( .... )
{
}
//is the name already on the valid cert list?
boolean found = false; int index = 0;
for(int i = 0; i < validList.size(); i++)
{
if(validList.get(i).getSubjectX500Principal().getName().equals(operador))
{
found = true; index = i;
break;
}
}
//the cert was already on the valid list, so we put it on the black list
if(found)
{
blackList.add(validList.get(index));
validList.remove(index);
validList.add(cert);
}
else
{
//didnt find so no need to put on the black list
validList.add(cert);
}
return cert;
}
also im using ArrayList to control the black and valid certificate lists because for some reason X509CRL doesnt include an .add() method..
also im not interested in persistency, i just want it to work while the web service is online, the time is goes offline i dont care if eveything siezes to exist.
thanks in advance.
It will be easy if you will return X509Certificate as byte[] to client from web-service and recreate X509Certificate from byte[] at client side.
It can be done by following way.
Convert to byte[]:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput out = new ObjectOutputStream(bos);
out.writeObject(certificate);
byte[] data = bos.toByteArray();
bos.close();
Recreating X509Certificate from byte[]:
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate x509Certificate = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(data));