2222
2323package eu .webeid .example .service ;
2424
25+ import com .fasterxml .jackson .databind .ObjectMapper ;
2526import eu .webeid .example .config .YAMLConfig ;
2627import eu .webeid .example .security .WebEidAuthentication ;
2728import eu .webeid .example .service .dto .CertificateDTO ;
2829import eu .webeid .example .service .dto .DigestDTO ;
2930import eu .webeid .example .service .dto .FileDTO ;
31+ import eu .webeid .example .service .dto .SignatureAlgorithmDTO ;
3032import eu .webeid .example .service .dto .SignatureDTO ;
33+ import eu .webeid .security .authtoken .SupportedSignatureAlgorithm ;
3134import eu .webeid .security .certificate .CertificateData ;
3235import jakarta .servlet .http .HttpSession ;
3336import jakarta .xml .bind .DatatypeConverter ;
4851import org .springframework .core .io .ByteArrayResource ;
4952import org .springframework .security .access .annotation .Secured ;
5053import org .springframework .stereotype .Service ;
54+ import org .springframework .web .servlet .support .ServletUriComponentsBuilder ;
5155
5256import java .io .IOException ;
5357import java .io .InputStream ;
5458import java .security .NoSuchAlgorithmException ;
5559import java .security .cert .CertificateException ;
5660import java .security .cert .X509Certificate ;
61+ import java .util .Base64 ;
62+ import java .util .List ;
63+ import java .util .Map ;
5764import java .util .Objects ;
5865
5966import static eu .webeid .example .security .WebEidAuthenticationProvider .ROLE_USER ;
@@ -65,13 +72,17 @@ public class SigningService {
6572 private static final String SESSION_ATTR_FILE = "file-to-sign" ;
6673 private static final String SESSION_ATTR_CONTAINER = "container-to-sign" ;
6774 private static final String SESSION_ATTR_DATA = "data-to-sign" ;
75+ public static final String CERTIFICATE_URI = "/sign/mobile/certificate" ;
76+ public static final String SIGNATURE_URI = "/sign/mobile/signature" ;
6877 private static final Logger LOG = LoggerFactory .getLogger (SigningService .class );
6978 private final Configuration signingConfiguration ;
7079
7180 private final ObjectFactory <HttpSession > httpSessionFactory ;
81+ private final YAMLConfig yamlConfig ;
7282
7383 public SigningService (ObjectFactory <HttpSession > httpSessionFactory , YAMLConfig yamlConfig ) {
7484 this .httpSessionFactory = httpSessionFactory ;
85+ this .yamlConfig = yamlConfig ;
7586 signingConfiguration = Configuration .of (yamlConfig .getUseDigiDoc4jProdConfiguration () ?
7687 Configuration .Mode .PROD : Configuration .Mode .TEST );
7788 // Use automatic AIA OCSP URL selection from certificate for signatures.
@@ -179,6 +190,81 @@ private Container getContainerToSign(FileDTO fileDTO) {
179190 .build ();
180191 }
181192
193+ public Map <String , Object > buildMobileInitResponse (WebEidAuthentication authentication ) throws IOException , CertificateException , NoSuchAlgorithmException {
194+ String signingCertificate = authentication .getSigningCertificate ();
195+ var supportedSignatureAlgorithms = authentication .getSupportedSignatureAlgorithms ();
196+
197+ boolean hasCertificateData = signingCertificate != null
198+ && supportedSignatureAlgorithms != null
199+ && !supportedSignatureAlgorithms .isEmpty ();
200+
201+ boolean requestSigningCert = yamlConfig .getRequestSigningCert ();
202+ String nextPath = requestSigningCert
203+ ? CERTIFICATE_URI
204+ : SIGNATURE_URI ;
205+
206+ if (!requestSigningCert && hasCertificateData ) {
207+ CertificateDTO certificateDTO = new CertificateDTO ();
208+ certificateDTO .setCertificate (signingCertificate );
209+ certificateDTO .setSupportedSignatureAlgorithms (mapSupportedAlgorithms (supportedSignatureAlgorithms ));
210+ prepareContainer (certificateDTO , authentication );
211+ } else {
212+ LOG .info ("Skipping container preparation — no certificate available." );
213+ }
214+
215+ String responseUri = ServletUriComponentsBuilder .fromCurrentContextPath ()
216+ .path (nextPath )
217+ .build ()
218+ .toUriString ();
219+
220+ return Map .of ("sign_uri" , buildDeepLink (Map .of ("response_uri" , responseUri )));
221+ }
222+
223+ public Map <String , Object > buildMobileCertificateResponse (CertificateDTO certificateDTO , WebEidAuthentication authentication ) throws IOException , CertificateException , NoSuchAlgorithmException {
224+ certificateDTO .setSupportedSignatureAlgorithms (mapSupportedAlgorithms (certificateDTO .getSupportedSignatureAlgorithms ()));
225+ DigestDTO digest = prepareContainer (certificateDTO , authentication );
226+ String responseUri = ServletUriComponentsBuilder .fromCurrentContextPath ()
227+ .path (SIGNATURE_URI )
228+ .build ()
229+ .toUriString ();
230+
231+ Map <String , Object > payload = Map .of (
232+ "response_uri" , responseUri ,
233+ "sign_certificate" , certificateDTO .getCertificate (),
234+ "hash" , digest .getHash (),
235+ "hash_function" , digest .getHashFunction ()
236+ );
237+
238+ return Map .of (
239+ "sign_uri" , buildDeepLink (payload ),
240+ "hash" , digest .getHash (),
241+ "hashFunction" , digest .getHashFunction ()
242+ );
243+ }
244+
245+ private List <SignatureAlgorithmDTO > mapSupportedAlgorithms (List <?> algorithms ) {
246+ if (algorithms == null ) return List .of ();
247+ return algorithms .stream ().map (item -> {
248+ if (item instanceof SignatureAlgorithmDTO dto ) {
249+ return dto ;
250+ } else if (item instanceof SupportedSignatureAlgorithm ssa ) {
251+ var dto = new SignatureAlgorithmDTO ();
252+ dto .setCryptoAlgorithm (ssa .getCryptoAlgorithm ());
253+ dto .setHashFunction (ssa .getHashFunction ());
254+ dto .setPaddingScheme (ssa .getPaddingScheme ());
255+ return dto ;
256+ } else {
257+ throw new IllegalArgumentException ("Unsupported algorithm type: " + item .getClass ());
258+ }
259+ }).toList ();
260+ }
261+
262+ private String buildDeepLink (Map <String , Object > payload ) throws IOException {
263+ String payloadJson = new ObjectMapper ().writeValueAsString (payload );
264+ String encoded = Base64 .getUrlEncoder ().withoutPadding ().encodeToString (payloadJson .getBytes ());
265+ return "web-eid-mobile://sign#" + encoded ;
266+ }
267+
182268 private String generateContainerName (String fileName ) {
183269 return FilenameUtils .removeExtension (fileName ) + ".asice" ;
184270 }
0 commit comments