|
19 | 19 | */ |
20 | 20 | package ro.kuberam.libs.java.crypto.digitalSignature; |
21 | 21 |
|
22 | | -import java.io.*; |
23 | | -import java.nio.file.Files; |
24 | | -import java.nio.file.Paths; |
| 22 | +import java.io.InputStream; |
25 | 23 | import java.security.KeyPair; |
26 | 24 | import java.security.KeyPairGenerator; |
27 | 25 | import java.security.KeyStore; |
28 | 26 | import java.security.PrivateKey; |
29 | 27 | import java.security.PublicKey; |
30 | 28 | import java.security.Signature; |
31 | 29 | import java.security.cert.X509Certificate; |
32 | | -import java.util.*; |
| 30 | +import java.util.ArrayList; |
| 31 | +import java.util.Arrays; |
| 32 | +import java.util.Collections; |
| 33 | +import java.util.List; |
33 | 34 |
|
34 | 35 | import javax.xml.crypto.XMLStructure; |
35 | 36 | import javax.xml.crypto.dom.DOMStructure; |
36 | | -import javax.xml.crypto.dsig.CanonicalizationMethod; |
37 | | -import javax.xml.crypto.dsig.DigestMethod; |
38 | 37 | import javax.xml.crypto.dsig.Reference; |
39 | | -import javax.xml.crypto.dsig.SignatureMethod; |
40 | 38 | import javax.xml.crypto.dsig.SignedInfo; |
41 | 39 | import javax.xml.crypto.dsig.Transform; |
42 | 40 | import javax.xml.crypto.dsig.XMLObject; |
|
52 | 50 | import javax.xml.crypto.dsig.spec.TransformParameterSpec; |
53 | 51 | import javax.xml.crypto.dsig.spec.XPathFilterParameterSpec; |
54 | 52 | import javax.xml.parsers.DocumentBuilderFactory; |
55 | | -import javax.xml.parsers.ParserConfigurationException; |
56 | 53 | import javax.xml.xpath.XPath; |
57 | 54 | import javax.xml.xpath.XPathConstants; |
58 | 55 | import javax.xml.xpath.XPathExpression; |
|
64 | 61 | import org.w3c.dom.bootstrap.DOMImplementationRegistry; |
65 | 62 | import org.w3c.dom.ls.DOMImplementationLS; |
66 | 63 | import org.w3c.dom.ls.LSSerializer; |
67 | | -import org.xml.sax.InputSource; |
68 | | -import org.xml.sax.SAXException; |
69 | 64 |
|
70 | 65 | public class GenerateSignature { |
71 | 66 |
|
72 | | - public static byte[] generateSignature(final byte[] data, final PrivateKey key, final String algorithm, String provider) |
73 | | - throws Exception { |
74 | | - final Signature signer = Signature.getInstance(algorithm); |
75 | | - signer.initSign(key); |
76 | | - signer.update(data); |
77 | | - return signer.sign(); |
78 | | - } |
79 | | - |
80 | | - public static String GenerateDigitalSignature(final org.w3c.dom.Document inputDoc, |
81 | | - final String canonicalizationAlgorithmURI, final String digestAlgorithmURI, final String signatureAlgorithmURI, |
82 | | - final String keyPairAlgorithm, final String signatureNamespacePrefix, final String signatureType, |
83 | | - final String xpathExprString, final String[] certificateDetails, final InputStream keyStoreInputStream) |
84 | | - throws Exception { |
85 | | - // Create a DOM XMLSignatureFactory |
86 | | - final String providerName = System.getProperty("jsr105Provider", |
87 | | - "org.jcp.xml.dsig.internal.dom.XMLDSigRI"); |
88 | | - final XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM"); |
89 | | - |
90 | | - // Create a Reference to the signed element |
91 | | - Node sigParent; |
92 | | - final List<Transform> transforms; |
93 | | - |
94 | | - if (xpathExprString == null) { |
95 | | - sigParent = inputDoc.getDocumentElement(); |
96 | | - transforms = Collections.singletonList(sigFactory.newTransform(Transform.ENVELOPED, |
97 | | - (TransformParameterSpec) null)); |
98 | | - } else { |
99 | | - final XPathFactory factory = XPathFactory.newInstance(); |
100 | | - final XPath xpath = factory.newXPath(); |
101 | | - // Find the node to be signed by PATH |
102 | | - final XPathExpression expr = xpath.compile(xpathExprString); |
103 | | - final NodeList nodes = (NodeList) expr.evaluate(inputDoc, XPathConstants.NODESET); |
104 | | - if (nodes.getLength() < 1) { |
105 | | - throw new Exception("Can't find node by this XPath expression: " + xpathExprString); |
106 | | - } |
107 | | - |
108 | | - // Node nodeToSign = nodes.item(0); |
109 | | - // sigParent = nodeToSign.getParentNode(); |
110 | | - sigParent = nodes.item(0); |
111 | | - /* |
112 | | - * if ( signatureType.equals( "enveloped" ) ) { sigParent = ( |
113 | | - * nodes.item(0) ).getParentNode(); } |
| 67 | + public static byte[] generateSignature(byte[] data, PrivateKey key, String algorithm, String provider) |
| 68 | + throws Exception { |
| 69 | + Signature signer = Signature.getInstance(algorithm); |
| 70 | + signer.initSign(key); |
| 71 | + signer.update(data); |
| 72 | + |
| 73 | + return signer.sign(); |
| 74 | + } |
| 75 | + |
| 76 | + public static String GenerateDigitalSignature(org.w3c.dom.Document inputDoc, String canonicalizationAlgorithmURI, |
| 77 | + String digestAlgorithmURI, String signatureAlgorithmURI, String keyPairAlgorithm, |
| 78 | + String signatureNamespacePrefix, String signatureType, String xpathExprString, String[] certificateDetails, |
| 79 | + InputStream keyStoreInputStream) throws Exception { |
| 80 | + // Create a DOM XMLSignatureFactory |
| 81 | + String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI"); |
| 82 | + XMLSignatureFactory sigFactory = XMLSignatureFactory.getInstance("DOM"); |
| 83 | + |
| 84 | + // Create a Reference to the signed element |
| 85 | + Node sigParent; |
| 86 | + List<Transform> transforms; |
| 87 | + |
| 88 | + if (xpathExprString == null) { |
| 89 | + sigParent = inputDoc.getDocumentElement(); |
| 90 | + transforms = Collections |
| 91 | + .singletonList(sigFactory.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)); |
| 92 | + } else { |
| 93 | + XPathFactory factory = XPathFactory.newInstance(); |
| 94 | + XPath xpath = factory.newXPath(); |
| 95 | + // Find the node to be signed by PATH |
| 96 | + XPathExpression expr = xpath.compile(xpathExprString); |
| 97 | + NodeList nodes = (NodeList) expr.evaluate(inputDoc, XPathConstants.NODESET); |
| 98 | + if (nodes.getLength() < 1) { |
| 99 | + throw new Exception("Can't find node by this XPath expression: " + xpathExprString); |
| 100 | + } |
| 101 | + |
| 102 | + // Node nodeToSign = nodes.item(0); |
| 103 | + // sigParent = nodeToSign.getParentNode(); |
| 104 | + sigParent = nodes.item(0); |
| 105 | + /* |
| 106 | + * if ( signatureType.equals( "enveloped" ) ) { sigParent = ( nodes.item(0) |
| 107 | + * ).getParentNode(); } |
114 | 108 | */ |
115 | | - transforms = Arrays.asList( |
116 | | - sigFactory.newTransform(Transform.XPATH, new XPathFilterParameterSpec( |
117 | | - xpathExprString)), |
118 | | - sigFactory.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)); |
119 | | - } |
120 | | - |
121 | | - final Reference ref = sigFactory.newReference("", sigFactory.newDigestMethod(digestAlgorithmURI, null), |
122 | | - transforms, null, null); |
123 | | - |
124 | | - // http://www.massapi.com/source/xml-security-1_4_4/src/org/jcp/xml/dsig/internal/dom/DOMXPathFilter2Transform.java.html |
125 | | - |
126 | | - // Create the SignedInfo |
127 | | - final SignedInfo si = sigFactory.newSignedInfo(sigFactory.newCanonicalizationMethod( |
128 | | - canonicalizationAlgorithmURI, (C14NMethodParameterSpec) null), sigFactory |
129 | | - .newSignatureMethod(signatureAlgorithmURI, null), Collections.singletonList(ref)); |
130 | | - |
131 | | - // generate key pair |
132 | | - final KeyInfo ki; |
133 | | - final PrivateKey privateKey; |
134 | | - if (certificateDetails[0].length() != 0) { |
135 | | - final KeyStore keyStore; |
136 | | - try { |
137 | | - keyStore = KeyStore.getInstance(certificateDetails[0]); |
138 | | - } catch (Exception ex) { |
139 | | - throw new Exception("The keystore type '" + certificateDetails[0] + "' is not supported!."); |
140 | | - } |
141 | | - keyStore.load(keyStoreInputStream, certificateDetails[1].toCharArray()); |
142 | | - String alias = certificateDetails[2]; |
143 | | - if (!keyStore.containsAlias(alias)) { |
144 | | - throw new Exception("Cannot find key for alias '" + alias + "' in given keystore!."); |
145 | | - } |
146 | | - privateKey = (PrivateKey) keyStore.getKey(alias, certificateDetails[3].toCharArray()); |
147 | | - |
148 | | - final X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias); |
149 | | - final PublicKey publicKey = cert.getPublicKey(); |
150 | | - final KeyInfoFactory kif = sigFactory.getKeyInfoFactory(); |
151 | | - |
152 | | - final List<XMLStructure> kiContent = new ArrayList<>(); |
153 | | - final KeyValue keyValue = kif.newKeyValue(publicKey); |
154 | | - kiContent.add(keyValue); |
155 | | - |
156 | | - final List<Object> x509Content = new ArrayList<>(); |
157 | | - final X509IssuerSerial issuer = kif.newX509IssuerSerial(cert.getIssuerX500Principal().getName(), |
158 | | - cert.getSerialNumber()); |
159 | | - x509Content.add(cert.getSubjectX500Principal().getName()); |
160 | | - x509Content.add(issuer); |
161 | | - x509Content.add(cert); |
162 | | - |
163 | | - final X509Data x509Data = kif.newX509Data(x509Content); |
164 | | - kiContent.add(x509Data); |
165 | | - ki = kif.newKeyInfo(kiContent); |
166 | | - } else { |
167 | | - final KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyPairAlgorithm); |
168 | | - kpg.initialize(512); |
169 | | - final KeyPair kp = kpg.generateKeyPair(); |
170 | | - final KeyInfoFactory kif = sigFactory.getKeyInfoFactory(); |
171 | | - final KeyValue kv = kif.newKeyValue(kp.getPublic()); |
172 | | - ki = kif.newKeyInfo(Collections.singletonList(kv)); |
173 | | - privateKey = kp.getPrivate(); |
174 | | - } |
| 109 | + transforms = Arrays.asList( |
| 110 | + sigFactory.newTransform(Transform.XPATH, new XPathFilterParameterSpec(xpathExprString)), |
| 111 | + sigFactory.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)); |
| 112 | + } |
| 113 | + |
| 114 | + Reference ref = sigFactory.newReference("", sigFactory.newDigestMethod(digestAlgorithmURI, null), transforms, |
| 115 | + null, null); |
| 116 | + |
| 117 | + // http://www.massapi.com/source/xml-security-1_4_4/src/org/jcp/xml/dsig/internal/dom/DOMXPathFilter2Transform.java.html |
| 118 | + |
| 119 | + // Create the SignedInfo |
| 120 | + SignedInfo si = sigFactory.newSignedInfo( |
| 121 | + sigFactory.newCanonicalizationMethod(canonicalizationAlgorithmURI, (C14NMethodParameterSpec) null), |
| 122 | + sigFactory.newSignatureMethod(signatureAlgorithmURI, null), Collections.singletonList(ref)); |
| 123 | + |
| 124 | + // generate key pair |
| 125 | + KeyInfo ki; |
| 126 | + PrivateKey privateKey; |
| 127 | + if (certificateDetails[0].length() != 0) { |
| 128 | + KeyStore keyStore; |
| 129 | + try { |
| 130 | + keyStore = KeyStore.getInstance(certificateDetails[0]); |
| 131 | + } catch (Exception ex) { |
| 132 | + throw new Exception("The keystore type '" + certificateDetails[0] + "' is not supported!."); |
| 133 | + } |
| 134 | + keyStore.load(keyStoreInputStream, certificateDetails[1].toCharArray()); |
| 135 | + String alias = certificateDetails[2]; |
| 136 | + if (!keyStore.containsAlias(alias)) { |
| 137 | + throw new Exception("Cannot find key for alias '" + alias + "' in given keystore!."); |
| 138 | + } |
| 139 | + privateKey = (PrivateKey) keyStore.getKey(alias, certificateDetails[3].toCharArray()); |
| 140 | + |
| 141 | + X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias); |
| 142 | + PublicKey publicKey = cert.getPublicKey(); |
| 143 | + KeyInfoFactory kif = sigFactory.getKeyInfoFactory(); |
| 144 | + |
| 145 | + List<XMLStructure> kiContent = new ArrayList<>(); |
| 146 | + KeyValue keyValue = kif.newKeyValue(publicKey); |
| 147 | + kiContent.add(keyValue); |
| 148 | + |
| 149 | + List<Object> x509Content = new ArrayList<>(); |
| 150 | + X509IssuerSerial issuer = kif.newX509IssuerSerial(cert.getIssuerX500Principal().getName(), |
| 151 | + cert.getSerialNumber()); |
| 152 | + x509Content.add(cert.getSubjectX500Principal().getName()); |
| 153 | + x509Content.add(issuer); |
| 154 | + x509Content.add(cert); |
| 155 | + |
| 156 | + X509Data x509Data = kif.newX509Data(x509Content); |
| 157 | + kiContent.add(x509Data); |
| 158 | + ki = kif.newKeyInfo(kiContent); |
| 159 | + } else { |
| 160 | + KeyPairGenerator kpg = KeyPairGenerator.getInstance(keyPairAlgorithm); |
| 161 | + kpg.initialize(512); |
| 162 | + KeyPair kp = kpg.generateKeyPair(); |
| 163 | + KeyInfoFactory kif = sigFactory.getKeyInfoFactory(); |
| 164 | + KeyValue kv = kif.newKeyValue(kp.getPublic()); |
| 165 | + ki = kif.newKeyInfo(Collections.singletonList(kv)); |
| 166 | + privateKey = kp.getPrivate(); |
| 167 | + } |
175 | 168 |
|
176 | 169 | /* |
177 | 170 | * <element name="X509Data" type="ds:X509DataType"/> <complexType |
178 | 171 | * name="X509DataType"> <sequence maxOccurs="unbounded"> <choice> SOLVED |
179 | | - * <element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/> |
180 | | - * <element name="X509SKI" type="base64Binary"/> SOLVED <element |
181 | | - * name="X509SubjectName" type="string"/> SOLVED <element |
182 | | - * name="X509Certificate" type="base64Binary"/> <element name="X509CRL" |
183 | | - * type="base64Binary"/> <any namespace="##other" |
| 172 | + * <element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/> <element |
| 173 | + * name="X509SKI" type="base64Binary"/> SOLVED <element name="X509SubjectName" |
| 174 | + * type="string"/> SOLVED <element name="X509Certificate" type="base64Binary"/> |
| 175 | + * <element name="X509CRL" type="base64Binary"/> <any namespace="##other" |
184 | 176 | * processContents="lax"/> </choice> </sequence> </complexType> > |
185 | 177 | */ |
186 | 178 |
|
187 | | - // Create a DOMSignContext and specify the location of the resulting |
188 | | - // XMLSignature's parent element |
189 | | - final DOMSignContext dsc; |
190 | | - final XMLSignature signature; |
191 | | - Document signatureDoc = null; |
192 | | - if (signatureType.equals("enveloped")) { |
193 | | - dsc = new DOMSignContext(privateKey, sigParent); |
194 | | - signature = sigFactory.newXMLSignature(si, ki); |
195 | | - } else if (signatureType.equals("detached")) { |
196 | | - final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); |
197 | | - dbf.setNamespaceAware(true); |
198 | | - sigParent = dbf.newDocumentBuilder().newDocument(); |
199 | | - dsc = new DOMSignContext(privateKey, sigParent); |
200 | | - signature = sigFactory.newXMLSignature(si, ki); |
201 | | - } else if (signatureType.equals("enveloping")) { |
202 | | - final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); |
203 | | - dbf.setNamespaceAware(true); |
204 | | - signatureDoc = dbf.newDocumentBuilder().newDocument(); |
205 | | - final XMLStructure content = new DOMStructure(sigParent); |
206 | | - final XMLObject xmlobj = sigFactory.newXMLObject(Collections.singletonList(content), "object", null, |
207 | | - null); |
208 | | - dsc = new DOMSignContext(privateKey, signatureDoc); |
209 | | - signature = sigFactory.newXMLSignature(si, ki, Collections.singletonList(xmlobj), null, null); |
210 | | - } else { |
211 | | - //TODO(AR) error, as below will cause NPE... |
212 | | - dsc = null; |
213 | | - signature = null; |
214 | | - } |
215 | | - |
216 | | - dsc.setDefaultNamespacePrefix(signatureNamespacePrefix); |
217 | | - |
218 | | - // Marshal, generate and sign |
219 | | - signature.sign(dsc); |
220 | | - |
221 | | - final DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); |
222 | | - final DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS"); |
223 | | - final LSSerializer serializer = impl.createLSSerializer(); |
224 | | - if (signatureType.equals("enveloping")) { |
225 | | - return serializer.writeToString(signatureDoc); |
226 | | - } else { |
227 | | - return serializer.writeToString(sigParent); |
228 | | - } |
229 | | - } |
230 | | - |
231 | | - public static void main(final String[] args) throws ParserConfigurationException, SAXException, IOException, |
232 | | - Exception { |
233 | | - final String docString = "<data><a xml:id=\"type\"><b>23</b><c><d/></c></a></data>"; |
234 | | - final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); |
235 | | - dbf.setNamespaceAware(true); |
236 | | - |
237 | | - try (final Reader reader = new StringReader(docString)) { |
238 | | - final Document inputDoc = dbf.newDocumentBuilder().parse(new InputSource(reader)); |
239 | | - |
240 | | - final String[] certificateDetails = { |
241 | | - "JKS", |
242 | | - "ab987c", |
243 | | - "eXist", |
244 | | - "kpi135" |
245 | | - }; |
246 | | - |
247 | | - try (final InputStream is = Files.newInputStream(Paths.get("/home/claudius/mykeystoreEXist.ks"))) { |
248 | | - |
249 | | - final String domString = GenerateDigitalSignature(inputDoc, CanonicalizationMethod.EXCLUSIVE, |
250 | | - DigestMethod.SHA1, SignatureMethod.DSA_SHA1, "DSA", "ds", "enveloped", "//b", |
251 | | - certificateDetails, is); |
252 | | - System.out.print(domString + "\n"); |
253 | | - } |
254 | | - } |
255 | | - } |
| 179 | + // Create a DOMSignContext and specify the location of the resulting |
| 180 | + // XMLSignature's parent element |
| 181 | + DOMSignContext dsc; |
| 182 | + XMLSignature signature; |
| 183 | + Document signatureDoc = null; |
| 184 | + if (signatureType.equals("enveloped")) { |
| 185 | + dsc = new DOMSignContext(privateKey, sigParent); |
| 186 | + signature = sigFactory.newXMLSignature(si, ki); |
| 187 | + } else if (signatureType.equals("detached")) { |
| 188 | + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); |
| 189 | + dbf.setNamespaceAware(true); |
| 190 | + sigParent = dbf.newDocumentBuilder().newDocument(); |
| 191 | + dsc = new DOMSignContext(privateKey, sigParent); |
| 192 | + signature = sigFactory.newXMLSignature(si, ki); |
| 193 | + } else if (signatureType.equals("enveloping")) { |
| 194 | + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); |
| 195 | + dbf.setNamespaceAware(true); |
| 196 | + signatureDoc = dbf.newDocumentBuilder().newDocument(); |
| 197 | + XMLStructure content = new DOMStructure(sigParent); |
| 198 | + XMLObject xmlobj = sigFactory.newXMLObject(Collections.singletonList(content), "object", null, null); |
| 199 | + dsc = new DOMSignContext(privateKey, signatureDoc); |
| 200 | + signature = sigFactory.newXMLSignature(si, ki, Collections.singletonList(xmlobj), null, null); |
| 201 | + } else { |
| 202 | + // TODO(AR) error, as below will cause NPE... |
| 203 | + dsc = null; |
| 204 | + signature = null; |
| 205 | + } |
| 206 | + |
| 207 | + dsc.setDefaultNamespacePrefix(signatureNamespacePrefix); |
| 208 | + |
| 209 | + // Marshal, generate and sign |
| 210 | + signature.sign(dsc); |
| 211 | + |
| 212 | + DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); |
| 213 | + DOMImplementationLS impl = (DOMImplementationLS) registry.getDOMImplementation("LS"); |
| 214 | + LSSerializer serializer = impl.createLSSerializer(); |
| 215 | + if (signatureType.equals("enveloping")) { |
| 216 | + return serializer.writeToString(signatureDoc); |
| 217 | + } else { |
| 218 | + return serializer.writeToString(sigParent); |
| 219 | + } |
| 220 | + } |
256 | 221 | } |
0 commit comments