|
1 | 1 | package ch.swisscom.mid.client.impl; |
2 | 2 |
|
| 3 | +import ch.swisscom.mid.client.SignatureValidator; |
| 4 | +import ch.swisscom.mid.client.config.ConfigurationException; |
| 5 | +import ch.swisscom.mid.client.config.SignatureValidationConfiguration; |
| 6 | +import ch.swisscom.mid.client.model.SignatureValidationFailureReason; |
| 7 | +import ch.swisscom.mid.client.model.SignatureValidationResult; |
| 8 | +import ch.swisscom.mid.client.model.Traceable; |
3 | 9 | import org.bouncycastle.cert.X509CertificateHolder; |
4 | 10 | import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; |
5 | 11 | import org.bouncycastle.cms.CMSException; |
|
18 | 24 | import java.nio.charset.StandardCharsets; |
19 | 25 | import java.security.KeyStore; |
20 | 26 | import java.security.Security; |
21 | | -import java.security.cert.CertPathValidator; |
22 | | -import java.security.cert.CertificateExpiredException; |
23 | | -import java.security.cert.CertificateFactory; |
24 | | -import java.security.cert.CertificateNotYetValidException; |
25 | | -import java.security.cert.PKIXParameters; |
26 | | -import java.security.cert.X509Certificate; |
| 27 | +import java.security.cert.*; |
27 | 28 | import java.util.Base64; |
28 | 29 | import java.util.LinkedList; |
29 | 30 | import java.util.List; |
30 | 31 | import java.util.regex.Matcher; |
31 | 32 | import java.util.regex.Pattern; |
32 | 33 |
|
33 | | -import ch.swisscom.mid.client.SignatureValidator; |
34 | | -import ch.swisscom.mid.client.config.ConfigurationException; |
35 | | -import ch.swisscom.mid.client.config.SignatureValidationConfiguration; |
36 | | -import ch.swisscom.mid.client.model.SignatureValidationFailureReason; |
37 | | -import ch.swisscom.mid.client.model.SignatureValidationResult; |
38 | | -import ch.swisscom.mid.client.model.Traceable; |
39 | | - |
40 | | -import static ch.swisscom.mid.client.utils.Utils.assertNotEmpty; |
41 | | -import static ch.swisscom.mid.client.utils.Utils.printTrace; |
| 34 | +import static ch.swisscom.mid.client.utils.Utils.*; |
42 | 35 |
|
43 | 36 | /** |
44 | 37 | * Default implementation of {@link SignatureValidator}. |
@@ -67,6 +60,7 @@ public SignatureValidationResult validateSignature(String base64SignatureContent |
67 | 60 | assertNotEmpty(requestedDtbs, "The requestedDtbs parameter cannot be NULL" + printTrace(trace)); |
68 | 61 |
|
69 | 62 | SignatureValidationResult result = new SignatureValidationResult(); |
| 63 | + // 4 criteria to be met in parallel to mark digital signature as valid |
70 | 64 | result.setSignatureValid(false); |
71 | 65 | result.setSignerCertificateValid(false); |
72 | 66 | result.setSignerCertificatePathValid(false); |
@@ -107,7 +101,7 @@ public SignatureValidationResult validateSignature(String base64SignatureContent |
107 | 101 | } |
108 | 102 |
|
109 | 103 | // extract data from the signature |
110 | | - result.setMobileIdSerialNumber(getMIDSerialNumber(signerCert)); |
| 104 | + result.setMobileIdSerialNumber(extractMIDSerialNumber(signerCert)); |
111 | 105 | result.setSignedDtbs(getSignedDtbs(cmsSignedData)); |
112 | 106 |
|
113 | 107 | // check that the certificate is valid. It is if the current date and time are within the validity period |
@@ -166,16 +160,48 @@ public SignatureValidationResult validateSignature(String base64SignatureContent |
166 | 160 | return result; |
167 | 161 | } |
168 | 162 |
|
| 163 | + @Override |
| 164 | + public String getMIDSerialNumber(String base64SignatureContent, Traceable trace) { |
| 165 | + assertNotEmpty(base64SignatureContent, "The base64SignatureContent parameter cannot be NULL" + printTrace(trace)); |
| 166 | + CMSSignedData cmsSignedData; |
| 167 | + X509Certificate signerCert = null; |
| 168 | + List<X509Certificate> signerCertChain; |
| 169 | + SignerInformation signerInfo; |
| 170 | + try { |
| 171 | + final JcaX509CertificateConverter x509CertificateConverter = new JcaX509CertificateConverter(); |
| 172 | + cmsSignedData = new CMSSignedData(Base64.getDecoder().decode(base64SignatureContent)); |
| 173 | + |
| 174 | + // Find the signer certificate and the entire certificate chain from the CMS content |
| 175 | + final SignerInformationStore signerInfoStore = cmsSignedData.getSignerInfos(); |
| 176 | + signerInfo = signerInfoStore.getSigners().iterator().next(); |
| 177 | + signerCertChain = new LinkedList<>(); |
| 178 | + |
| 179 | + for (final X509CertificateHolder currentCertHolder : cmsSignedData.getCertificates().getMatches(null)) { |
| 180 | + X509Certificate currentCert = x509CertificateConverter.getCertificate(currentCertHolder); |
| 181 | + signerCertChain.add(currentCert); |
| 182 | + if (signerInfo.getSID().match(currentCertHolder)) { |
| 183 | + signerCert = currentCert; |
| 184 | + } |
| 185 | + } |
| 186 | + } catch (Exception e) { |
| 187 | + log.warn("Failed to extract the signing certificate from the Base64 CMS content{}", printTrace(trace), e); |
| 188 | + return null; |
| 189 | + } |
| 190 | + return extractMIDSerialNumber(signerCert); |
| 191 | + |
| 192 | + } |
| 193 | + |
169 | 194 | // ---------------------------------------------------------------------------------------------------- |
170 | 195 |
|
171 | 196 | private static final Pattern SERIAL_NUMBER_PATTERN = Pattern.compile(".*SERIALNUMBER=(.{16}).*"); |
172 | 197 |
|
173 | 198 | /** |
174 | | - * Get the user's unique Mobile ID SerialNumber from the signer certificate's SubjectDN |
| 199 | + * Get the user's unique Mobile ID serial number from the signer certificate's SubjectDN |
175 | 200 | * |
176 | 201 | * @return the user's unique Mobile ID serial number. |
177 | 202 | */ |
178 | | - private String getMIDSerialNumber(X509Certificate signerCert) { |
| 203 | + private String extractMIDSerialNumber(X509Certificate signerCert) { |
| 204 | + assertNotNull(signerCert, "The signerCert param for Mobile ID serial number extraction cannot be NULL"); |
179 | 205 | Matcher matcher = SERIAL_NUMBER_PATTERN.matcher(signerCert.getSubjectX500Principal().toString().toUpperCase()); |
180 | 206 | if (matcher.find()) { |
181 | 207 | return matcher.group(1); |
|
0 commit comments