I'm working on webservice client which requires authentication and xml signing.
I've read a lot of articles but it looks like my one looks different.
I need to send a request containing tag with some certificate details.
I received from Service provider few files (certificate.crt , certificate.p12 , certificate.pem)
I managed to attach crt file into request using follString providerName =
System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
Reference ref = fac.newReference("", fac.newDigestMethod(
DigestMethod.SHA1, null), Collections.singletonList(fac
.newTransform(Transform.ENVELOPED, (XMLStructure) null)), null,
null);
// Create the SignedInfo
SignedInfo si = fac.newSignedInfo(
fac.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE_WITH_COMMENTS, (C14NMethodParameterSpec) null),
fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null), Collections.singletonList(ref));
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair kp = kpg.generateKeyPair();
KeyInfoFactory kif = fac.getKeyInfoFactory();
KeyValue kv = kif.newKeyValue(kp.getPublic());
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(new FileInputStream(new File("certificate.crt")));
X509Data x509d = kif.newX509Data(Collections.singletonList(cert));
KeyInfo ki = kif.newKeyInfo(Arrays.asList(x509d, kv));
Document doc = (Document) result.getNode();
DOMSignContext dsc = new DOMSignContext(kp.getPrivate(), doc.getDocumentElement());
XMLSignature signature = fac.newXMLSignature(si, ki);
signature.sign(dsc);
But RSA key is generated and different each call. It is obvious because I'm using KeyPairGenerator. Is it possible to import my private key from P12 file instead?
When I run this application on tomcat do I need to configure it somehow to enable the HTTPS calls to the service?
You can use KeySotre
Load the file:
KeyStore ks = KeyStore.getInstance("PKCS12");
FileInputStream ksin = new FileInputStream("myfile.p12");
ks.load(ksin, "password");
getKey("keyalis", "password");
Related
I've been trying on this for a couple of days and I'm hopelessly stuck.
To fully understand how java key store works, i've been trying to create my own keystore, put some stuff inside it, then retrieve them from another program.
Here's my keystore generator :
{
//generate a X509 certificate
Security.addProvider(new BouncyCastleProvider());
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", "BC");
X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(new FileInputStream("certificate.cer"));
LOGGER.debug("BouncyCastle provider & X509 certificate added.");
//generate a private & a public key
KeyPair keyPair = generateRSAKeyPair();
RSAPrivateKey priv = (RSAPrivateKey) keyPair.getPrivate();
RSAPublicKey pub = (RSAPublicKey) keyPair.getPublic();
//generate a keystore
KeyStore ks = KeyStore.getInstance("PKCS12");
char[] keyStorePassword = "keystore_password".toCharArray();
ks.load(null, keyStorePassword);
try (FileOutputStream fos = new FileOutputStream("TestKeyStore.jks")) {
ks.store(fos, keyStorePassword);
}
ks.load(new FileInputStream("TestKeyStore.jks"), keyStorePassword);
//Symmetric key
SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey();
KeyStore.SecretKeyEntry secretKeyEntry = new KeyStore.SecretKeyEntry((secretKey));
KeyStore.ProtectionParameter protectionParameter = new KeyStore.PasswordProtection(keyStorePassword);
ks.setEntry("symmetric_key", secretKeyEntry, protectionParameter);
//Asymmetric key
X509Certificate[] x509Certificates = new X509Certificate[1];
x509Certificates[0] = certificate;
ks.setKeyEntry("asymmetric key", priv, keyStorePassword, x509Certificates);
//certificate
ks.setCertificateEntry("test_certif", certificate);
Key key = ks.getKey("symmetric_key", keyStorePassword);
System.out.println("I have this symmetric key : " + key);
X509Certificate certificate1 = (X509Certificate) ks.getCertificate("test_certif");
System.out.println("I have this certificate : " + certificate1);
System.out.println(ks.aliases().nextElement());
LOGGER.debug("all went well");
}
As you probably noticed, it's not all polished: my goal for now is only to put some stuff inside the keystore. But the point here, from the last System.out.println(ks.aliases().nextElement());, is just to see there is indeed something inside the keystore. And this is working just fine, it gives back symmetric_key.
Now, from the same folder is another class that is supposed to read from that keystore.
Note: there is no issue regarding the file (I've tested by moving its localization) so it can't be that.
KeyStore ks = KeyStore.getInstance("PKCS12");
char[] keyStorePassword = "keystore_password".toCharArray();
ks.load(new FileInputStream("TestKeyStore.jks"), keyStorePassword);
System.out.println(ks.containsAlias("symmetric_key"));
This always gets me: false
If I try this: System.out.println(ks.aliases());, it's always null
If I try this:
if (!keystore.aliases().hasMoreElements()) {
System.out.println("nothing inside the keystore");
}
it gives me back nothing inside the keystore.
Even though it's not the case in the generator.
Any clue?
Thank you
The problem is that you are setting the entries after writing the keystore.
If you move:
try (FileOutputStream fos = new FileOutputStream("TestKeyStore.jks")) {
ks.store(fos, keyStorePassword);
}
Till after this line:
ks.setCertificateEntry("test_certif", certificate);
Everything should work fine.
Bellow you have an working example with some code removed for clarity:
public static void main(String[] args) throws Exception{
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
final KeyPair keyPair = keyGen.genKeyPair();
RSAPrivateKey priv = (RSAPrivateKey) keyPair.getPrivate();
//generate a keystore
KeyStore ks = KeyStore.getInstance("PKCS12");
char[] keyStorePassword = PASSWORD;
ks.load(null, keyStorePassword);
X509Certificate[] chain = {generateCertificate("cn=Unknown", keyPair, 365, "SHA256withRSA")};
// saving one keypair in keystore object
ks.setKeyEntry("asymmetric key", keyPair.getPrivate(), keyStorePassword, chain);
//Symmetric key
SecretKey secretKey = KeyGenerator.getInstance("AES").generateKey();
KeyStore.SecretKeyEntry secretKeyEntry = new KeyStore.SecretKeyEntry((secretKey));
KeyStore.ProtectionParameter protectionParameter = new KeyStore.PasswordProtection(keyStorePassword);
// saving symmetric key in keystore object
ks.setEntry("symmetric_key", secretKeyEntry, protectionParameter);
// Saving our keystore object into the filesystem
try (FileOutputStream fos = new FileOutputStream("TestKeyStore.p12")) {
ks.store(fos, keyStorePassword);
}
// The rest of the code
}
The reason why it works in the method that saves the keystore is because the changed keystore is still in memory, but not on the filesystem. Also, since you are creating a PKCS12 keystore, I would avoid the .jks extension and go for something like .pkcs12.
need some tips/advice about (optimization) and how to sign XML with a certificate in the fastest way possible. On some databases (Oracle) it needs 30 msec to sign a file.. But on another database, it needs 1.2 sec. This function/method is called in a loop. It signs some XML data and returns a signed XML in a String. (Java method is exposed as PL/SQL function).
public static String signXML(String vhodniXml) throws Exception {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setNamespaceAware(true);
Document doc = dbFactory.newDocumentBuilder().parse(new ByteArrayInputStream(vhodniXml.getBytes()));
NodeList nl = doc.getElementsByTagName("fu:BusinessPremiseRequest");
Node node = nl.item(0);
((Element) node).setIdAttribute("Id", true);
Enumeration e = p12.aliases();
String alias = (String) e.nextElement();
Key privateKey = p12.getKey(alias, geslo.toCharArray());
KeyStore.PrivateKeyEntry keyEntry
= (KeyStore.PrivateKeyEntry) p12.getEntry(alias, new KeyStore.PasswordProtection(geslo.toCharArray()));
X509Certificate cert = (X509Certificate) keyEntry.getCertificate();
PublicKey publicKey = cert.getPublicKey();
final XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM");
// Create a Reference to the enveloped document
Reference ref = sigFactory.newReference("#data",
sigFactory.newDigestMethod(DigestMethod.SHA256, null),
Collections.singletonList(sigFactory.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)),
null,
null);
SignedInfo si = sigFactory.newSignedInfo(sigFactory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null), sigFactory.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", null), Collections.singletonList(ref));
// Create a KeyValue containing the RSA PublicKey
KeyInfoFactory keyInfoFactory = sigFactory.getKeyInfoFactory();
X509IssuerSerial x509IssuerSerial = keyInfoFactory.newX509IssuerSerial(cert.getSubjectX500Principal().getName(), cert.getSerialNumber());
List x509Content = new ArrayList();
x509Content.add(cert.getSubjectX500Principal().getName());
x509Content.add(x509IssuerSerial);
KeyValue keyValue = keyInfoFactory.newKeyValue(publicKey);
X509Data xd = keyInfoFactory.newX509Data(x509Content);
// Create a KeyInfo and add the KeyValue to it
KeyInfo keyInfo = keyInfoFactory.newKeyInfo(Collections.singletonList(xd));
// Create a DOMSignContext and specify the RSA PrivateKey and
// location of the resulting XMLSignature's parent element
DOMSignContext dsc = new DOMSignContext(
privateKey,
node
);
// Create the XMLSignature (but don't sign it yet)
XMLSignature signature = sigFactory.newXMLSignature(si, keyInfo);
// Marshal, generate (and sign) the enveloped signature
signature.sign(dsc);
ByteArrayOutputStream os = new ByteArrayOutputStream();
Transformer trans = TransformerFactory.newInstance()
.newTransformer();
trans.transform(new DOMSource(doc), new StreamResult(os));
return new String(os.toByteArray());
}
The first thing to do is to remove some IO operations by storing your key related objects in fields because reading the KeyStore to get keys takes time.
I would like to encapsulate SignedData in EnvelopedData.
I am using BouncyCastle.
After looking in an example, I wrote something like this:
CMSSignedData signedData = gen.generate(new CMSProcessableByteArray(out.toByteArray()), true);
FileInputStream inputStream = new FileInputStream(args[3]);
CertificateFactory factory = CertificateFactory.getInstance("X.509", "BC");
X509Certificate certificate = (X509Certificate) factory.generateCertificate(inputStream);
CMSEnvelopedDataGenerator engen = new CMSEnvelopedDataGenerator();
engen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(certificate).setProvider("BC"));
CMSEnvelopedData envelopedData =engen.generate( new CMSProcessableByteArray(signedData.getEncoded()),
new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES256_CBC).setProvider("BC").build());
But I think this is wrong. Where can I specify the ContentType of ContentInfo? I need to specify the content type of EnvelopedData as SignedData, but it's not possible with the CMSSignedData class.
I have an issue with XML Signature value mismatch error. Full Scenario
I generated a private 1024 bit key with OpenSSL.
I generated the CSR with that private key and sent it to VISA, they have signed the document with their root CA certificate and sent me a signed certificate in the .pem format.
I use the following java code to sign an XML document.
When i send the data back to VISA they verify the signature value and send an error of mismatch.
XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());
DigestMethod digestMethod = factory.newDigestMethod(DigestMethod.SHA1, null);
factory.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null);
Reference reference = factory.newReference("#" + paresId, digestMethod, null, null,null);
CanonicalizationMethod canonicalizationMethod = factory.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null);
SignatureMethod signatureMethod = factory.newSignatureMethod(SignatureMethod.RSA_SHA1, null);
SignedInfo signedInfo = factory.newSignedInfo(canonicalizationMethod, signatureMethod, Collections.singletonList(reference));
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(512);
KeyPair keyPair = kpg.generateKeyPair();
KeyInfoFactory keyInfoFactory = factory.getKeyInfoFactory();
KeyStore keyStore = KeyStore.getInstance("JKS"); //PKC#7
keyStore.load(new FileInputStream(keystorePath), "dell12345".toCharArray());
KeyStore.PrivateKeyEntry keyEntry =
(KeyStore.PrivateKeyEntry) keyStore.getEntry("deskey", new KeyStore.PasswordProtection("dell12345".toCharArray()));
//PrivateKey m_objRequestSigningKey = (PrivateKey) obj_keyStore.getKey(str_alias, "password".toCharArray());
KeyStore.TrustedCertificateEntry rootEntry = (KeyStore.TrustedCertificateEntry) keyStore.getEntry("root", null);
X509Certificate rootCertificate = (X509Certificate) rootEntry.getTrustedCertificate();
KeyStore.TrustedCertificateEntry intermediateEntry = (KeyStore.TrustedCertificateEntry) keyStore.getEntry("intermediate", null);
X509Certificate intermediateCertificate = (X509Certificate) intermediateEntry.getTrustedCertificate();
KeyStore.TrustedCertificateEntry signEntry = (KeyStore.TrustedCertificateEntry) keyStore.getEntry("sign", null);
X509Certificate sigingCertificate = (X509Certificate) signEntry.getTrustedCertificate();
List<X509Certificate> x509 = new ArrayList<X509Certificate>();
x509.add(rootCertificate );
x509.add(intermediateCertificate );
x509.add(sigingCertificate );
X509Data x509Data = keyInfoFactory.newX509Data(x509);
List<X509Data> items = new ArrayList<X509Data>();
items.add(x509Data);
KeyInfo keyInfo = keyInfoFactory.newKeyInfo(items);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Document doc = dbf.newDocumentBuilder().parse(IOUtils.toInputStream(inputXml));
DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), doc.getDocumentElement());
XMLSignature signature = factory.newXMLSignature(signedInfo, keyInfo);
signature.sign(dsc);
Sorry, after a long time I am going to answer the question which I have posted 5 years back.
Actually, the problem was basically with the XML formatting, I removed the next line and carriage return characters from XML and send back to Visa and then my signature was match properly.
I don't know why they giving the error even I mentioned the CanonicalizationMethod properly.
Hi I just have contructed an org.apache.xml.security.signature.XMLSignature from a xml that complies with xmldsig w3c recommendation, and I can see that the xml contains all certificate chain in
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
elements, but using the XMLSignature API I can see that I can only access the user certificate and the issuer one, but not the complete chain, is there an straightforward way to get this done through xmlsec API?
I found a solution for this, not the most clean, but it works:
XMLSignature signature = new XMLSignature(sigElement,
null);
KeyInfo keyInfo = signature.getKeyInfo();
NodeList x509Certificates = keyInfo.getElement().getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "X509Certificate");
ArrayList<X509Certificate> allCertificates = new ArrayList<X509Certificate>();
for (int i = 0; i < x509Certificates.getLength(); i++) {
Node x509CertificateElement = x509Certificates.item(i);
byte[] decodedX509Certificate = Base64.decode(x509CertificateElement.getTextContent());
X509Certificate x509Certificate = (X509Certificate) CertificateFactory.getInstance("X509").generateCertificate(new ByteArrayInputStream(decodedX509Certificate));
allCertificates.add(x509Certificate);
}
// now you have all certificates in allCertificates