-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathXMLSigner.java
150 lines (128 loc) · 5.94 KB
/
XMLSigner.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package br.gov.bcb.pi.dict.xml;
import com.google.common.base.Preconditions;
import lombok.SneakyThrows;
import org.w3c.dom.Node;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import javax.xml.crypto.dsig.keyinfo.X509IssuerSerial;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
import static javax.xml.crypto.dsig.CanonicalizationMethod.EXCLUSIVE;
class XMLSigner {
private static final String KEY_INFO_ID = "key-info-id";
private static final String ROOT_URI = "";
private static final String KEY_STORE_TYPE = "PKCS12";
private final XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM");
private final KeyStore store;
private final String password;
XMLSigner(String keyStoreFile, String password) {
this.password = password;
try {
InputStream keystoreIS = new FileInputStream(keyStoreFile);
store = KeyStore.getInstance(KEY_STORE_TYPE);
store.load(keystoreIS, this.password.toCharArray());
Preconditions.checkArgument(store.size() == 1, "Esperado que keystore tivesse apenas 1 alias");
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException e) {
throw new RuntimeException(e);
}
}
void sign(Node node) {
try {
XMLSignature xmlSignature = newXMLSignature(newArrayList(newReferenceKeyInfo(), newReferenceRoot()));
DOMSignContext signContext = newDOMSignContext(node);
xmlSignature.sign(signContext);
} catch (Exception e) {
throw new RuntimeException("Erro ao assinar XML", e);
}
}
private DOMSignContext newSignContext(Node node, PrivateKey privateKey) {
// No DICT, elemento 'Signature' é sempre colocado como filho da tag raiz
DOMSignContext dsc = new DOMSignContext(privateKey, node.getFirstChild(), node.getFirstChild().getFirstChild());
dsc.setDefaultNamespacePrefix("ds");
return dsc;
}
private Reference newReferenceRoot() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
return xmlSignatureFactory.newReference(
ROOT_URI,
xmlSignatureFactory.newDigestMethod(DigestMethod.SHA256, null),
Arrays.asList(
xmlSignatureFactory.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null),
xmlSignatureFactory.newCanonicalizationMethod(EXCLUSIVE, (C14NMethodParameterSpec) null)
),
null,
null
);
}
private DOMSignContext newSignContext(Node node) {
return newSignContext(node, getPrivateKey());
}
private Reference newReferenceKeyInfo() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
return xmlSignatureFactory.newReference(
"#" + KEY_INFO_ID,
xmlSignatureFactory.newDigestMethod(DigestMethod.SHA256, null),
Collections.singletonList(xmlSignatureFactory.newCanonicalizationMethod(EXCLUSIVE, (C14NMethodParameterSpec) null)),
null,
null
);
}
private XMLSignature newXMLSignature(List<Reference> referenceList) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
KeyInfoFactory kif = xmlSignatureFactory.getKeyInfoFactory();
KeyInfo keyInfo = kif.newKeyInfo(Collections.singletonList(getX509Data(kif)), KEY_INFO_ID);
return xmlSignatureFactory.newXMLSignature(
newSignedInfo(referenceList),
keyInfo,
null,
null,
null
);
}
private X509Data newX509Data(KeyInfoFactory kif, X509Certificate certificate) {
X509IssuerSerial x509IssuerSerial = kif.newX509IssuerSerial(certificate.getIssuerDN().toString(), certificate.getSerialNumber());
return kif.newX509Data(Collections.singletonList(x509IssuerSerial));
}
private X509Data getX509Data(KeyInfoFactory kif) {
return newX509Data(kif, getCertificate());
}
private SignedInfo newSignedInfo(List<Reference> referenceList) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
return xmlSignatureFactory.newSignedInfo(
xmlSignatureFactory.newCanonicalizationMethod(EXCLUSIVE, (C14NMethodParameterSpec) null),
xmlSignatureFactory.newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", null),
referenceList
);
}
private DOMSignContext newDOMSignContext(Node node) {
return newSignContext(node);
}
@SneakyThrows
private X509Certificate getCertificate() {
String alias = store.aliases().nextElement();
return (X509Certificate) store.getCertificate(alias);
}
@SneakyThrows
private PrivateKey getPrivateKey() {
String alias = store.aliases().nextElement();
return (PrivateKey) store.getKey(alias, this.password.toCharArray());
}
}