I have the following problem. I wrote simple test class that encrypts and decrypts XML files in java. But when i try to decrypt a file that is loaded from disc, the following error appears:
Oct 04, 2017 5:41:49 PM com.sun.org.apache.xml.internal.security.encryption.XMLCipher doFinal
SEVERE: Source element unexpectedly null...
Exception in thread "main" java.lang.NullPointerException
at com.sun.org.apache.xml.internal.security.encryption.XMLCipher$Factory.newEncryptedData(XMLCipher.java:2190)
at com.sun.org.apache.xml.internal.security.encryption.XMLCipher.decryptToByteArray(XMLCipher.java:1677)
at com.sun.org.apache.xml.internal.security.encryption.XMLCipher.decryptElement(XMLCipher.java:1616)
at com.sun.org.apache.xml.internal.security.encryption.XMLCipher.doFinal(XMLCipher.java:936)
at EncryptionDecryption.decryptDocument(EncryptionDecryption.java:134)
at EncryptionDecryption.main(EncryptionDecryption.java:42)
Class:
import com.sun.org.apache.xml.internal.security.encryption.XMLCipher;
import com.sun.org.apache.xml.internal.security.utils.EncryptionConstants;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
public class EncryptionDecryption {
public static void main(String[] args) throws Exception {
com.sun.org.apache.xml.internal.security.Init.init();
byte[] key = ("i love stackoverflow").getBytes("UTF-8");
MessageDigest sha = MessageDigest.getInstance("SHA-512");
key = sha.digest(key);
key = Arrays.copyOf(key, 16); // use only first 128 bit
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
Document document = getDocument("classes.xml");
Document encryptedDoc = encryptDocument(document, secretKeySpec,
XMLCipher.AES_256);
saveDocumentTo(encryptedDoc, "encrypted.xml");
encryptedDoc = getDocument("encrypted.xml");
Document decryptedDoc = decryptDocument(encryptedDoc,
secretKeySpec, XMLCipher.AES_256);
saveDocumentTo(decryptedDoc, "decrypted.xml");
}
public static void saveSecretKey(SecretKey secretKey, String fileName) {
byte[] keyBytes = secretKey.getEncoded();
File keyFile = new File(fileName);
FileOutputStream fOutStream = null;
try {
fOutStream = new FileOutputStream(keyFile);
fOutStream.write(keyBytes);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fOutStream != null) {
try {
fOutStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static String keyToString(SecretKey secretKey) {
/* Get key in encoding format */
byte encoded[] = secretKey.getEncoded();
/*
* Encodes the specified byte array into a String using Base64 encoding
* scheme
*/
String encodedKey = Base64.getEncoder().encodeToString(encoded);
return encodedKey;
}
public static SecretKey getSecretKey(String algorithm) {
KeyGenerator keyGenerator = null;
try {
keyGenerator = KeyGenerator.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return keyGenerator.generateKey();
}
public static Document getDocument(String xmlFile) throws Exception {
/* Get the instance of BuilderFactory class. */
DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance();
/* Instantiate DocumentBuilder object. */
DocumentBuilder docBuilder = builder.newDocumentBuilder();
/* Get the Document object */
Document document = docBuilder.parse(xmlFile);
return document;
}
public static void saveDocumentTo(Document document, String fileName)
throws Exception {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(new File(fileName));
transformer.transform(source, result);
}
public static Document encryptDocument(Document document, SecretKey secretKey, String algorithm) throws Exception {
/* Get Document root element */
Element rootElement = document.getDocumentElement();
String algorithmURI = algorithm;
XMLCipher xmlCipher = XMLCipher.getInstance(algorithmURI);
/* Initialize cipher with given secret key and operational mode */
xmlCipher.init(XMLCipher.ENCRYPT_MODE, secretKey);
/* Process the contents of document */
xmlCipher.doFinal(document, rootElement, true);
return document;
}
public static Document decryptDocument(Document document, SecretKey secretKey, String algorithm) throws Exception {
Element encryptedDataElement = (Element) document.
getElementsByTagNameNS(EncryptionConstants.EncryptionSpecNS, EncryptionConstants._TAG_ENCRYPTEDDATA).item(0);
XMLCipher xmlCipher = XMLCipher.getInstance();
xmlCipher.init(XMLCipher.DECRYPT_MODE, secretKey);
xmlCipher.doFinal(document, encryptedDataElement);
return document;
}
}
When i comment out the line 40:
/*encryptedDoc = getDocument("encrypted.xml");*/
Then the problem does not appear. So it's like when i use a xml document encrypted while the program is running without loading from disc, the XMLCipher decrypts it succesfully :)
You need to set your DocumentBuilder to be namespace aware:
public static Document getDocument(String xmlFile) throws Exception {
/* Get the instance of BuilderFactory class. */
DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance();
builder.setNamespaceAware(true);
// ...
Without this setting the library can't find the metadata it stored when it encrypted the document.
Related
We need to send a hash / digest of an XML file to a remote signing service.
The signing service returns a PKCS#7 response. This includes the signature and the short-lived-x509 certificate that was used.
Question: what is the easiest solution to apply the information from the PKCS#7 to the XML file so that it is correctly signed? I am looking for an example (Plain Java or Apache Santuario).
Update:
This is the code used for signing (xml-dsig) an XML with Apache Santuario given a local keystore:
package test.signer.signer;
import org.apache.commons.io.IOUtils;
import org.apache.xml.security.Init;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.transforms.Transforms;
import org.apache.xml.security.utils.Constants;
import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Key;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
/**
* from: https://stackoverflow.com/a/15911581/5658642
*/
public class CreateSignature
{
private static final String PRIVATE_KEY_ALIAS = "sgw-sign-client-keystore";
private static final String PRIVATE_KEY_PASS = "password";
private static final String KEY_STORE_PASS = "";
private static final String KEY_STORE_TYPE = "JKS";
public static void main(String... unused) throws Exception
{
final InputStream fileInputStream = Files.newInputStream(Paths.get("unsigned_DEV.xml"));
try
{
output(signFile(fileInputStream, new File("sgw-sign-client-keystore.jks")), "signed-test.xml");
} finally
{
IOUtils.closeQuietly(fileInputStream);
}
}
public static ByteArrayOutputStream signFile(InputStream xmlFile, File privateKeyFile) throws Exception
{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(xmlFile);
Init.init();
final KeyStore keyStore = loadKeyStore(privateKeyFile);
final XMLSignature sig = new XMLSignature(doc, null, XMLSignature.ALGO_ID_SIGNATURE_RSA);
doc.getDocumentElement().appendChild(sig.getElement());
final Transforms transforms = new Transforms(doc);
transforms.addTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE);
sig.addDocument("", transforms, Constants.ALGO_ID_DIGEST_SHA1);
// TODO replace with external signature
final Key privateKey = keyStore.getKey(PRIVATE_KEY_ALIAS, PRIVATE_KEY_PASS.toCharArray());
final X509Certificate cert = (X509Certificate) keyStore.getCertificate(PRIVATE_KEY_ALIAS);
sig.addKeyInfo(cert);
sig.addKeyInfo(cert.getPublicKey());
sig.sign(privateKey);
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_WITH_COMMENTS).canonicalizeSubtree(doc));
return outputStream;
}
private static KeyStore loadKeyStore(File privateKeyFile) throws Exception
{
final InputStream fileInputStream = Files.newInputStream(privateKeyFile.toPath());
try
{
final KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE);
keyStore.load(fileInputStream, null);
return keyStore;
} finally
{
IOUtils.closeQuietly(fileInputStream);
}
}
private static void output(ByteArrayOutputStream signedOutputStream, String fileName) throws IOException
{
final OutputStream fileOutputStream = Files.newOutputStream(Paths.get(fileName));
try
{
fileOutputStream.write(signedOutputStream.toByteArray());
fileOutputStream.flush();
} finally
{
IOUtils.closeQuietly(fileOutputStream);
}
}
}
This works so far. I now must replace the local keystore with a "remote keystore". The remote signing service takes a digest / hash and signs it. The response is a valid PKCS#7, which contains the certificate and the signature.
I am able to extract both with the following code (based on CMSSignedData from BouncyCastle):
String hashB64 = Base64.getEncoder().encodeToString(digest);
byte[] pkcs7Signature = remoteServiceClientService.sign(hashB64);
CMSSignedData cms = null;
try
{
cms = new CMSSignedData(pkcs7Signature);
SignerInformationStore signers = cms.getSignerInfos();
Collection<SignerInformation> c = signers.getSigners();
for (SignerInformation signer : c) {
// this collection will contain the signer certificate, if present
Collection<X509CertificateHolder> signerCol = cms.getCertificates().getMatches(signer.getSID());
SIG_CERT = new JcaX509CertificateConverter().getCertificate(signerCol.stream().findFirst().get());
}
List<byte[]> signatures = cms.getSignerInfos().getSigners().stream().map(SignerInformation::getSignature)
.collect(Collectors.toList());
byte[] pkcs1Signature = signatures.get(0);
SOPLogger.log("Plain Signature", pkcs1Signature);
return pkcs1Signature;
} catch (CMSException e)
{
throw new RuntimeException(e);
} catch (CertificateException e)
{
throw new RuntimeException(e);
}
I have no idea how to apply the extracted information instead of using a local keystore and are happy to try any hints.
I have integrated my web site with TFIM for SSO.
SSO is working fine but i am unable to get the Signature in SAMLResponse.
it's getting null. but it is already there in SAMLResponse.
When am trying to get the signature value from samlresponse it giving me nullpointerexception
package com.saml;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.opensaml.Configuration;
import org.opensaml.DefaultBootstrap;
import org.opensaml.saml2.core.Response;
import org.opensaml.saml2.core.Subject;
import org.opensaml.security.SAMLSignatureProfileValidator;
import org.opensaml.xml.ConfigurationException;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.io.Unmarshaller;
import org.opensaml.xml.io.UnmarshallerFactory;
import org.opensaml.xml.io.UnmarshallingException;
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.security.x509.BasicX509Credential;
import org.opensaml.xml.signature.SignatureValidator;
import org.apache.commons.codec.binary.Base64;
import org.opensaml.xml.validation.ValidationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
public class ReceiveSAMLResponse {
public String receiveSAMLResponse(HttpServletRequest request)
throws ParserConfigurationException, SAXException, IOException,
UnmarshallingException, ValidationException, CertificateException {
/* Getting the response string from HTTP Request object */
String responseString = (String) request.getParameter("SAMLResponse");
/* Decoding Base64 response string to get the XML string */
String responseXml = new String(Base64.decodeBase64(responseString
.getBytes()));
System.out.println(responseXml);
/* Generating SAML Response object from XML string */
try {
DefaultBootstrap.bootstrap();
} catch (ConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder docBuilder = documentBuilderFactory
.newDocumentBuilder();
ByteArrayInputStream is = new ByteArrayInputStream(
responseXml.getBytes());
Document document = docBuilder.parse(is);
Element element = document.getDocumentElement();
UnmarshallerFactory unmarshallerFactory = Configuration
.getUnmarshallerFactory();
Unmarshaller unmarshaller = unmarshallerFactory
.getUnmarshaller(element);
XMLObject xmlObj = unmarshaller.unmarshall(element);
Response response = (Response) xmlObj;
/* Validating the signature on the response */
// validateSignature(response);
/* If validation was successful, get the username from the response. */
Subject subject = response.getAssertions().get(0).getSubject();
String username = subject.getNameID().getValue();
return username;
}
private void validateSignature(Response response)
throws ValidationException, FileNotFoundException,
CertificateException {
SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();
try {
profileValidator.validate(response.getSignature());
} catch (ValidationException e) {
/* Indicates signature did not conform to SAML Signature profile */
e.printStackTrace();
throw e;
}
Credential verificationCredential = getVerificationCredential();
SignatureValidator sigValidator = new SignatureValidator(
verificationCredential);
try {
sigValidator.validate(response.getSignature());
} catch (ValidationException e) {
e.printStackTrace();
throw e;
}
}
private Credential getVerificationCredential()
throws FileNotFoundException, CertificateException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
"/pathToYourCertificte"));
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
BasicX509Credential x509Credential = new BasicX509Credential();
x509Credential.setPublicKey(cert.getPublicKey());
x509Credential.setEntityCertificate(cert);
Credential credential = x509Credential;
return credential;
}
}
....................................
saml response in xml file
<samlp:Response xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Destination="https://10.44.90.29:8443/SAMLShareFile/saml/samlresponse" ID="FIMRSP_604af2be-0150-1ff0-adad-8154af08b58c" InResponseTo="-5346144739450824145" IssueInstant="2015-10-13T08:22:15Z" Version="2.0"><saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://10.44.189.168:444/apjct/sps/NewRelic/saml20</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"></samlp:StatusCode></samlp:Status><saml:Assertion ID="Assertion-uuid604af281-0150-1512-8c38-8154af08b58c" IssueInstant="2015-10-13T08:22:15Z" Version="2.0"><saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://10.44.189.168:444/apjct/sps/NewRelic/saml20</saml:Issuer><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="uuid604af289-0150-1dab-a25e-8154af08b58c"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod><ds:Reference URI="#Assertion-uuid604af281-0150-1512-8c38-8154af08b58c"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"><xc14n:InclusiveNamespaces xmlns:xc14n="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="xs saml xsi"></xc14n:InclusiveNamespaces></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod><ds:DigestValue>pMf0E/z1rS9OkTOLc+0aoD7cl30=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>SW9BaJm0rGJAOG62Il1v46YsqocHXNpmcQKAmSIKDX4tRN3EbUHeqFcVfJmmUGDe4uC1H115SOCehQAkJ35lLBnVsda2WHgu4kWdGC8j+kaw0y9zjzngrHZljBpzU2h87zk4X+fGXvtCmBUH7xfrID4tQ6ODdhoWjd6K8s21S50=</ds:SignatureValue><ds:KeyInfo><ds:X509Data><ds:X509Certificate>MIICBzCCAXCgAwIBAgIEQH26vjANBgkqhkiG9w0BAQQFADBIMQswCQYDVQQGEwJVUzEPMA0GA1UEChMGVGl2b2xpMQ4wDAYDVQQLEwVUQU1lQjEYMBYGA1UEAxMPZmltZGVtby5pYm0uY29tMB4XDTA0MDQxNDIyMjcxMFoXDTE3MTIyMjIyMjcxMFowSDELMAkGA1UEBhMCVVMxDzANBgNVBAoTBlRpdm9saTEOMAwGA1UECxMFVEFNZUIxGDAWBgNVBAMTD2ZpbWRlbW8uaWJtLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAiZ0D1X6rk8+ZwNBTVZt7C85m421a8A52Ksjw40t+jNvbLYDp/W66AMMYD7rB5qgniZ5K1p9W8ivM9WbPxc2u/60tFPg0e/Q/r/fxegW1K1umnay+5MaUvN3p4XUCRrfg79OvurvXQ7GZa1/wOp5vBIdXzg6i9CVAqL29JGi6GYUCAwEAATANBgkqhkiG9w0BAQQFAAOBgQBXiAhxm91I4m+g3YX+dyGc352TSKO8HvAIBkHHFFwIkzhNgO+zLhxg5UMkOg12X9ucW7leZ1IB0Z6+JXBrXIWmU3UPum+QxmlaE0OG9zhp9LEfzsE5+ff+7XpS0wpJklY6c+cqHj4aTGfOhSE6u7BLdI26cZNdzxdhikBMZPgdyQ==</ds:X509Certificate></ds:X509Data></ds:KeyInfo></ds:Signature><saml:Subject><saml:NameID Format="urn:ibm:names:ITFIM:5.1:accessmanager">musaddique</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData InResponseTo="-5346144739450824145" NotOnOrAfter="2015-10-13T08:32:15Z" Recipient="https://10.44.90.29:8443/SAMLShareFile/saml/samlresponse"></saml:SubjectConfirmationData></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2015-10-13T08:12:15Z" NotOnOrAfter="2015-10-13T08:32:15Z"><saml:AudienceRestriction><saml:Audience>musaddique</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant="2015-10-13T08:22:15Z" SessionIndex="uuid604af260-0150-14b6-8127-8154af08b58c" SessionNotOnOrAfter="2015-10-13T09:22:15Z"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute Name="AuthenticatingAuthority" NameFormat="urn:oasis:names:tc:SAML:2.0:assertion"><saml:AttributeValue xsi:type="xs:string">musaddique</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion></samlp:Response>
Your SAML has only assertion part () being signed, so you should get signature from assertion object not response object: try response.getAssertions().get(0).getSignature().
Base on SAML 2.0 specification, SAML response has to be signed, but not both response and sssertion are mandatory.
I wrote this code in my SamlProvider. I didn't used it because the Idp was not requesting the HTTPS protocol and certificates in saml so it's not a "certified solution" I load the certificate from a file so you migth directly inject your BufferedInputStream.
private Credential getCredential() {
BasicX509Credential credential = null;
try {
// read private key
File privateKeyFile = new File(derFile);
FileInputStream inputStreamPrivateKey = new FileInputStream(privateKeyFile);
byte[] encodedPrivateKey = new byte[(int) privateKeyFile.length()];
inputStreamPrivateKey.read(encodedPrivateKey);
inputStreamPrivateKey.close();
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
RSAPrivateKey privateKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(
privateKeySpec);
// read the certificate
InputStream inStream = new FileInputStream(pemFile);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);
// create credential
credential = new BasicX509Credential();
credential.setEntityCertificate((java.security.cert.X509Certificate) cert);
credential.setPrivateKey(privateKey);
} catch (Exception e) {
Logger.error("failed getting credential!", e);
}
return credential;
}
Hope it helps.
When running below program I am getting this exception. When I make
byte key_bytes[] = new byte[X]; with X<16, I get same error but with
Invalid AES key length: X bytes
If I write this with X>=16 I get the error that I wrote in the title.
Here is the exception i get and after that my code
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 444 bytes
at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:495)
at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1062)
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1020)
at javax.crypto.Cipher.init(Cipher.java:1225)
at javax.crypto.Cipher.init(Cipher.java:1166)
at Main.main(Main.java:87)
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.nio.ByteBuffer;
import java.security.*;
import java.security.cert.CertificateException;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class Main
{
public static void main(String[] args) throws IOException, UnrecoverableKeyException, KeyStoreException, NoSuchAlgorithmException, NoSuchPaddingException, CertificateException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, SignatureException{
//open the file containing keys
File file = new File("keys/ks_file.jks");
//cipher object that will hold the information
Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
//create keystore object from stored keys inside the file
KeyStore keystore = loadKeyStore(file, "sergey", "JKS");
//messageDigest instance
MessageDigest md = MessageDigest.getInstance("SHA1");
//singanture instance
Signature dsa = Signature.getInstance("SHA1withDSA");
//params for getting keys
String allias = "enc_key", password = "sergey";
SecureRandom s_random = SecureRandom.getInstance("SHA1PRNG");
//create random bytes for semtric key
byte key_bytes[] = new byte[16];
s_random.setSeed(711);
s_random.nextBytes(key_bytes);
Key key = new SecretKeySpec(key_bytes, "AES");
Key key_enc = keystore.getKey(allias, password.toCharArray());
KeyPair enc_key = null;
if (key_enc instanceof PrivateKey) {
// Get certificate of public key
java.security.cert.Certificate cert = keystore.getCertificate(allias);
// Get public key
PublicKey publicKey = cert.getPublicKey();
enc_key = new KeyPair(publicKey, (PrivateKey) key_enc);
}
//cipher the file
aes.init(Cipher.ENCRYPT_MODE, key);
FileInputStream fis;
FileOutputStream fos;
CipherInputStream cis;
fis = new FileInputStream("tmp/a.txt");
cis = new CipherInputStream(fis, aes);
fos = new FileOutputStream("tmp/b.txt");
byte[] b = new byte[8];
int i = cis.read(b);
byte[] bytes = ByteBuffer.allocate(4).putInt(i).array();
//update message digest for signature
md.update(bytes);
while (i != -1) {
fos.write(b, 0, i);
i = cis.read(b);
bytes = ByteBuffer.allocate(4).putInt(i).array();
md.update(bytes);
}
fis.close();
cis.close();
fos.close();
//encode the secret key
aes.init(Cipher.ENCRYPT_MODE, (Key)enc_key.getPublic());
byte[] cipherKey = aes.doFinal(key.toString().getBytes());
//we save the final digest
byte[] hash = md.digest();
//init singature with private key
dsa.initSign(enc_key.getPrivate());
//update the signature with the hash aster digest
dsa.update(hash);
//final signature
byte[] sig = dsa.sign();
//creating config xml
try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
// root elements
Document doc = docBuilder.newDocument();
Element rootElement = doc.createElement("config");
doc.appendChild(rootElement);
// signature elements
Element sig_xml = doc.createElement("sig");
rootElement.appendChild(sig_xml);
sig_xml.setAttribute("value", sig.toString());
// key element
Element key_xml = doc.createElement("key");
rootElement.appendChild(key_xml);
key_xml.setAttribute("value", cipherKey.toString());
// write the content into xml file
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File("./config.xml"));
transformer.transform(source, result);
System.out.println("File saved!");
} catch (ParserConfigurationException pce) {
pce.printStackTrace();
} catch (TransformerException tfe) {
tfe.printStackTrace();
}
}
/**
* Reads a Java keystore from a file.
*
* #param keystoreFile
* keystore file to read
* #param password
* password for the keystore file
* #param keyStoreType
* type of keystore, e.g., JKS or PKCS12
* #return the keystore object
* #throws KeyStoreException
* if the type of KeyStore could not be created
* #throws IOException
* if the keystore could not be loaded
* #throws NoSuchAlgorithmException
* if the algorithm used to check the integrity of the keystore
* cannot be found
* #throws CertificateException
* if any of the certificates in the keystore could not be loaded
*/
public static KeyStore loadKeyStore(final File keystoreFile,
final String password, final String keyStoreType)
throws KeyStoreException, IOException, NoSuchAlgorithmException,
CertificateException {
if (null == keystoreFile) {
throw new IllegalArgumentException("Keystore url may not be null");
}
final URI keystoreUri = keystoreFile.toURI();
final URL keystoreUrl = keystoreUri.toURL();
final KeyStore keystore = KeyStore.getInstance(keyStoreType);
InputStream is = null;
try {
is = keystoreUrl.openStream();
keystore.load(is, null == password ? null : password.toCharArray());
} finally {
if (null != is) {
is.close();
}
}
return keystore;
}
}
Try installing the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files this is one of the main issues behind the invalid key length.
It's available at Oracle http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html
Download the local_policy and US_export_policy and put them in your
Java\jdk1.6.0_45\jre\lib\security
I'm writing a program that decrypt a file that was encrypted.
i recive the folowing output:
==============================
== Receiver\Decryptor side ==
==============================
Exception in thread "main" java.lang.Error: Unresolved compilation problems:
Base64 cannot be resolved
Base64 cannot be resolved
Base64 cannot be resolved
at Decryptor.DecryptFileAndValidateSignature(Decryptor.java:97)
at Decryptor.main(Decryptor.java:65)
the error happens somwhere at these 3 lines -
ks.load(ksStream, receiverConfigurations.get("keyStorePass").toCharArray());
/* Loads private key from keyStore, needed to decrypt the symmetric key from configuration file */
PrivateKey receiverPrivateKey = (PrivateKey) ks.getKey(receiverConfigurations.get("receiverAlias"), receiverConfigurations.get("receiverKeyPass").toCharArray()); //private key of receiver
i guess something is wring with my imports..
as you can see i tried a lot. any help will be aprricated tnx
here is my code -
import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.util.HashMap;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.security.KeyStore;
import java.security.Key;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Base64InputStream;
public class Decryptor {
public static void main(String args[]) throws Exception
{
if (args.length!=2)
{
System.out.println("Wrong number of parameters, please insert the path of encryptedTextFile.txt, cipherConfigurations.xml and the path for keystore file.\n" +
"Make sure that path is ended with '\\\\'. Example: c:\\\\test\\\\crypto\\\\keyStore.jks c:\\\\test\\\\crypto\\\\"
+ "\nAborting...\n");
return;
}
if (!Files.exists(Paths.get(args[0])))
{
System.out.println("Path "+args[0]+" doesn't exist.\nAborting...\n");
return;
}
String basePath = args[0];
String keyStore = args[1];
basePath = "c:\\test\\crypto\\";
System.out.println("-------------------------------------------------------------");
System.out.println("==============================");
System.out.println("== Receiver\\Decryptor side ==");
System.out.println("==============================");
/* Configurations that are known to the receiver */
HashMap<String,String> receiverConfigurations = new HashMap<>();
receiverConfigurations.put("encryptedTextFile" ,basePath+"encryptedTextFile.txt");
receiverConfigurations.put("configurationFile",basePath+"cipherConfigurations.xml");
receiverConfigurations.put("keyStorePass","gilperryB");
receiverConfigurations.put("receiverAlias","b_side");
receiverConfigurations.put("receiverKeyPass","gilperryB");
receiverConfigurations.put("decryptedTextFile",basePath+"decryptedText.txt");
receiverConfigurations.put("keyStorePath",keyStore);
receiverConfigurations.put("senderAlias","a_side");
DecryptFileAndValidateSignature(receiverConfigurations);
System.out.println("-------------------------------------------------------------");
}
/*
* Decrypts and validates a signature of an encrypted file that was sent between a sender and a receiver, in the following way:
* 1. Uses private key to encrypt a symmetric key from a configuration file.
* 2. Uses the symmetric key to decrypt the message sent in a different file
* 3. Calculates digital signature over the file, and compares it with the one received in the configuration file.
* 4. If the signatures match - returns true, else - returns false
* */
static public boolean DecryptFileAndValidateSignature(HashMap<String,String> receiverConfigurations) throws Exception
{
/* Load data from keyStore .jks file */
KeyStore ks = KeyStore.getInstance("jks"); // Load public key from keyStore
FileInputStream ksStream = new FileInputStream(receiverConfigurations.get("keyStorePath"));
ks.load(ksStream, receiverConfigurations.get("keyStorePass").toCharArray());
/* Loads private key from keyStore, needed to decrypt the symmetric key from configuration file */
PrivateKey receiverPrivateKey = (PrivateKey) ks.getKey(receiverConfigurations.get("receiverAlias"), receiverConfigurations.get("receiverKeyPass").toCharArray()); //private key of receiver
/* Load data received by the cipher configurations XML sent by sender */
HashMap<String,String> cipherConfigurations = ReadConfigurationXML(receiverConfigurations.get("configurationFile"));
if (cipherConfigurations == null)
{
System.out.println("Error reading cipher configurations XML.\nAborting...");
}
System.out.println("Read data Cipher configurations XML.");
/* Initialize the encryptor */
Cipher encryptor = Cipher.getInstance(cipherConfigurations.get("encryptionAlgoForSymmetricKey"), cipherConfigurations.get("encryptionAlgoForSymmetricKeyProvider"));
/* Get data from cipher configurations XML*/
byte[] symetricKeyEncrypted = Base64.decodeBase64(cipherConfigurations.get("symetricKeyEncrypted"));
/* Initialize the symmetric key encryptor */
Cipher rsaEncryptor = Cipher.getInstance(cipherConfigurations.get("encryptionAlgoForSendingSharedKey"), cipherConfigurations.get("encryptionAlgoForSendingSharedKeyProvider")); // encryptor for the secret key
byte[] symetricKeyDecrypted = DecryptText(symetricKeyEncrypted,rsaEncryptor,receiverPrivateKey, null);
byte[] ivConfig =Base64.decodeBase64(cipherConfigurations.get("ivspec"));
byte[] iv = DecryptText(ivConfig, rsaEncryptor, receiverPrivateKey, null);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
byte[] digitalSignature = Base64.decodeBase64(cipherConfigurations.get("digitalSignature"));
Key symmetricKeyAfterDecription = new SecretKeySpec(symetricKeyDecrypted, cipherConfigurations.get("encryptionAlgoForKeyGeneration")); //build a new secret key from text
System.out.println("Decrypted symmetric key using his own private key");
/* Decrypt file into decryptedFile */
DecryptFile(receiverConfigurations.get("encryptedTextFile"), receiverConfigurations.get("decryptedTextFile"), encryptor, symmetricKeyAfterDecription,ivSpec);
System.out.println("Decrypted text file "+receiverConfigurations.get("encryptedTextFile")+" into "+receiverConfigurations.get("decryptedTextFile"));
/* Verify digital signature */
PublicKey senderPublicKey = ks.getCertificate(receiverConfigurations.get("senderAlias")).getPublicKey(); //publicKey holds the public key for sender
boolean signatureValidated = ValidateDigitalSignature(receiverConfigurations.get("decryptedTextFile"),cipherConfigurations.get("digitalSignatureAlgorithm"),senderPublicKey,digitalSignature);
if (!signatureValidated)
{
System.out.println("Error decrypting text or validating digital signature.\nAborting...");
return false;
}
else
{
System.out.println("File was successfully decrypted, digital signature was successfully validated.\n");
return true;
}
}
/*
* Simulates the process where the receiver is calculating the signature of a message he received
* and compares it to the signature sent to him by sender.
* Calculates the digital signature over the decrypted file, using the digital signature algorithm in digitalSignatureAlgorithm,
* and public key in senderPublicKey.
* returns true iff the signatures match.
* */
private static boolean ValidateDigitalSignature(String decryptedFile,
String digitalSignatureAlgorithm, PublicKey senderPublicKey, byte[] signatureToVerify) throws Exception {
Signature dsa = Signature.getInstance(digitalSignatureAlgorithm); /* Initializing the object with the digital signature algorithm */
dsa.initVerify(senderPublicKey);
/* Update and sign the data */
FileInputStream fis = new FileInputStream(decryptedFile);
byte[] block = new byte[8];
int i;
while ((i = fis.read(block)) != -1) { //read all blocks in file
dsa.update(block); // update digital signature after each block
}
fis.close();
return dsa.verify(signatureToVerify);
}
/*
* Reads an encrypted text file and decrypts it using a Cipher object (encryptor).
* The decrypted file will be the returned value.
* Decryption process contains also Base64 encoding of the text.
*/
private static void DecryptFile(String inputFile,String outputFile, Cipher encryptor, Key key, IvParameterSpec ivspec) throws Exception
{
assert (CreateFileIfNecessery(outputFile) == true); //creates output file if necessery
FileInputStream fis = new FileInputStream(inputFile);
Base64InputStream b64os = new Base64InputStream(fis);
CipherInputStream cis = new CipherInputStream(b64os, encryptor);
FileOutputStream fos = new FileOutputStream(outputFile);
encryptor.init(Cipher.DECRYPT_MODE, key, ivspec); //initilize cipher in decryption mode with IV
byte[] block = new byte[8];
int i;
while ((i = cis.read(block)) != -1) { //read all blocks in file
{
fos.write(block,0,i); // write each block encrypted to the output file
}
}
b64os.close();
fos.close(); // close output file
cis.close(); // close input file
}
/*
* Reads the configuration XML from file in 'path'.
* Retuns a HashMap containing the entries and their value.
* if not possible - returns null.
*/
public static HashMap<String,String> ReadConfigurationXML(String path) throws Exception
{
HashMap<String,String> cipherConfigurations = new HashMap<>();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc;
try{
doc = builder.parse(path);
}catch (Exception e)
{
System.out.println("Error reading configurations file "+path+"\nAborting...");
return null;
}
NodeList mainNode = null;
try
{
mainNode = doc.getElementsByTagName("CipherConfigurations");
}
catch (Exception e)
{
System.out.println("Could not find element EncryptionConfigurations in the configurations file.\nAborting...");
return null;
}
if (mainNode.getLength()!=1)
{
System.out.println("Wrong structure of cipher configutarion element.\nAborting...");
return null;
}
NodeList cipherConfigurationsRoot = (NodeList) mainNode.item(0); // get the root element of the configurations
for (int i = 0; i < cipherConfigurationsRoot.getLength(); ++i)
{
Element elem = (Element) cipherConfigurationsRoot.item(i);
String paramName = elem.getNodeName();
String innerText = elem.getTextContent();
cipherConfigurations.put(paramName, innerText);
}
return cipherConfigurations;
}
/*
* Reads an encrypted text and decrypts it using a Cipher object (encryptor).
* The decrypted text will be the returned value.
* Decryption process contains also Base64 encoding of the text.
*/
public static byte[] DecryptText(byte[] text, Cipher encryptor, Key key, IvParameterSpec ivspec) throws Exception
{
OutputStream os = new ByteArrayOutputStream();
InputStream is = new ByteArrayInputStream(text);
Base64InputStream b64is = new Base64InputStream(is);
CipherInputStream cis = new CipherInputStream(b64is, encryptor);
encryptor.init(Cipher.DECRYPT_MODE, key, ivspec); //initilize cipher in decryption mode with IV
byte[] block = new byte[8];
int i;
while ((i = cis.read(block)) != -1) { //read all blocks in file
{
os.write(block,0, i); // write each block encrypted to the output file
}
}
b64is.close();
os.close(); // close output file
is.close(); // close input file
cis.close();
return ((ByteArrayOutputStream) os).toByteArray();
}
/*
* Creates file and its paths if not exist.
* Example: path = c:\\test\\test1\\foo.txt
* Method will check if this path and file exists, if not - will create full hierarchy.
*/
private static boolean CreateFileIfNecessery(String path) throws Exception
{
File f = new File(path);
if (!f.mkdirs()) return false; //creates the directories for the file
if (!f.createNewFile()) return false; // creates the output file
return true;
}
}
You're missing Apache's commons-codec-1.10.jar
or
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
if you're using Maven
I am using WSS4J to create a soap request signer with a certificate from the keystore. My keystore format is .pkcs and everything works with the first signing, but if I try to run the program to sign more than one document and it doesn't find the certificate in the keystore. It seems to me that the certificate is possibly consumed during signing, which means that it no longer exists for the current environment. If the program stops and starts again it will sign the first request but the second request fails. I have stepped through multiple times and cannot figure out what the cause is. This code is the last thing being called before the certificate disappears from the keystore this.certUri = getWsConfig().getIdAllocator().createSecureId("X509-", certs[0]); this is found on line 556 in WSSecSignature.class. Here is my code.
package com.soapsigner;
import java.io.*;
import java.util.Properties;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.ws.security.*;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.components.crypto.Merlin;
import org.apache.ws.security.message.WSSecHeader;
import org.apache.ws.security.message.WSSecSignature;
import org.apache.ws.security.message.WSSecTimestamp;
import org.w3c.dom.Document;
import org.xml.sax.*;
public class SoapSigner {
private Crypto crypto;
private WSSecSignature sig;
private WSSecTimestamp time;
private WSSecHeader header;
private String alias;
private String password;
private String keyFile;
private String keyFileType;
private Document signedDoc;
private String lastError;
{
Logger rootLogger = Logger.getRootLogger();
rootLogger.setLevel(Level.INFO);
rootLogger.addAppender(new ConsoleAppender(
new PatternLayout("%-6r [%p] %c - %m%n")));
}
//constructor
public SoapSigner(String XML){
try {
alias = "myalias";
password = "mypassword";
keyFile = "/keystore/mykeystore.pkcs";
keyFileType = "pkcs12";
sig = new WSSecSignature();
time = new WSSecTimestamp();
header = new WSSecHeader();
signedDoc = null;
lastError = "";
Merlin merlin = new Merlin(getCryptoProperties());
System.out.println("real signing keystore object: "+merlin.getKeyStore().getCertificate(alias).toString().length()); //Passed
crypto = merlin;
signDocument(xmlToDoc(XML));
Merlin test = new Merlin(getCryptoProperties());
System.out.println("test keystore object: "+test.getKeyStore().getCertificate(alias).toString().length()); //Failed, this is null
} catch (Exception e) {
setLastError(e);
}
}
//properties
public Properties getCryptoProperties(){
Properties cryptoProperties = new Properties();
cryptoProperties.setProperty("org.apache.ws.security.crypto.merlin.keystore.alias", alias);
cryptoProperties.setProperty("org.apache.ws.security.crypto.merlin.keystore.password", password);
cryptoProperties.setProperty("org.apache.ws.security.crypto.merlin.keystore.type", keyFileType);
cryptoProperties.setProperty("org.apache.ws.security.crypto.merlin.keystore.file", keyFile);
return cryptoProperties;
}
//sign the document
public void signDocument(Document doc){
try {
header.setMustUnderstand(true);
sig.setSignatureAlgorithm(WSConstants.C14N_EXCL_OMIT_COMMENTS);
sig.setSignatureAlgorithm(WSConstants.RSA);
sig.setUserInfo(alias, password);
sig.setKeyIdentifierType(WSConstants.BST_DIRECT_REFERENCE);
header.insertSecurityHeader(doc);
sig.build(doc, crypto, header);
time.build(doc, header);
signedDoc = doc;
} catch (Exception e) {
setLastError(e);
}
}
//get the signed document
public Document getDocument(){
return signedDoc;
}
//get the signed xml
public String getXML(){
return getStringFromDoc(getDocument()).trim();
}
//get last error
public String getLastError(){
return lastError;
}
//set last error
private void setLastError(Throwable e){
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
lastError += " NEXT ERROR "+sw.toString();
e.printStackTrace();
}
//document to string
public String getStringFromDoc(Document doc){
try
{
DOMSource domSource = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.transform(domSource, result);
writer.flush();
return writer.toString();
}
catch(Exception e)
{
setLastError(e);
return null;
}
}
//string to document
public Document xmlToDoc(String XML){
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db;
db = dbf.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(XML));
Document doc = db.parse(is);
return doc;
} catch (Exception e) {
setLastError(e);
return null;
}
}
//main
public static void main(String[] args){
String XML1 = "<?xml version='1.0' encoding='UTF-8'?><soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'> <soap:Body> <test1></test1> </soap:Body> </soap:Envelope>";
String XML2 = "<?xml version='1.0' encoding='UTF-8'?><soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'> <soap:Body> <test2></test2> </soap:Body> </soap:Envelope>";
new SoapSigner(XML1);
new SoapSigner(XML2);
}
}
Also, I would like to turn the doDebug mode on in the WSSecBase.class but it won't showup in the variable viewer to toggle the value. I created a breakpoint in the constructor and set it to be watched but it never shows up to toggle.
Your testcase works fine for me with a .p12 from WSS4J test source on the latest WSS4J SNAPSHOT code. Are you using an older version of WSS4J? If so please try with the latest releases + see if it works. If not, then please create a test-case with your .p12 file + create a new issue in the WSS4J JIRA:
https://issues.apache.org/jira/browse/WSS
Colm.