Sende post
For å sende post så må du først lage en DigitalPost
eller en FysiskPost
, så opprette en Forsendelse
og legge ved posten. Deretter sendes denne gjennom en SikkerDigitalPostKlient
. Når brevet er sendt så kan du spørre om status på en meldingskø. Hvis du mottar en kvittering så kan du sjekke innholdet og så bekrefte mottatt kvittering.
Opprette digital post
Sertifikat mottakerSertifikat = null; //Fås fra Oppslagstjenesten
String orgnrPostkasse = null; //Fås fra Oppslagstjenesten
String postkasseadresse = null; //Fås fra Oppslagstjenesten
Mottaker mottaker = Mottaker
.builder(
"99999999999",
postkasseadresse,
mottakerSertifikat,
Organisasjonsnummer.of(orgnrPostkasse))
.build();
SmsVarsel smsVarsel = SmsVarsel.builder("4799999999",
"Du har mottatt brev i din digitale postkasse")
.build();
EpostVarsel epostVarsel = EpostVarsel.builder("epost@example.com",
"Du har mottatt brev i din digitale postkasse")
.varselEtterDager(asList(1, 4, 10))
.build();
DigitalPost digitalPost = DigitalPost
.builder(mottaker, "Ikke-sensitiv tittel")
.virkningsdato(new Date())
.aapningskvittering(false)
.sikkerhetsnivaa(Sikkerhetsnivaa.NIVAA_3)
.epostVarsel(epostVarsel)
.smsVarsel(smsVarsel)
.build();
Opprette fysisk post
Sertifikat utskriftsleverandørSertifikat = null; //Printsertifikat fra Oppslagstjenesten
TekniskMottaker utskriftsleverandør =
new TekniskMottaker(Organisasjonsnummer.of("99999999"), utskriftsleverandørSertifikat);
FysiskPost fysiskPost = FysiskPost.builder()
.adresse(
KonvoluttAdresse.build("Ola Nordmann")
.iNorge("Fjellheimen 22", "", "", "0001", "Oslo")
.build())
.retur(
Returhaandtering.DIREKTE_RETUR.MAKULERING_MED_MELDING,
KonvoluttAdresse.build("Returkongen")
.iNorge("Returveien 3", "", "", "0002", "Oslo")
.build())
.sendesMed(Posttype.A_PRIORITERT)
.utskrift(Utskriftsfarge.FARGE, utskriftsleverandør)
.build();
Opprette selve forsendelsen
DigitalPost digitalPost = null; //Som initiert tidligere
Dokument hovedDokument = Dokument
.builder("Sensitiv brevtittel", new File("/sti/til/dokument"))
.mimeType("application/pdf")
.build();
Dokumentpakke dokumentpakke = Dokumentpakke.builder(hovedDokument)
.vedlegg(new ArrayList<Dokument>())
.build();
AvsenderOrganisasjonsnummer avsenderOrgnr =
AktoerOrganisasjonsnummer.of("999999999").forfremTilAvsender();
Avsender avsender = Avsender
.builder(avsenderOrgnr)
.build();
Forsendelse forsendelse = Forsendelse
.digital(avsender, digitalPost, dokumentpakke)
.konversasjonsId(UUID.randomUUID().toString())
.prioritet(Prioritet.NORMAL)
.mpcId("KøId")
.spraakkode("NO")
.build();
Sett en unik
Forsendelse.mpcId
for å unngå at det konsumeres kvitteringer på tvers av ulike avsendere med samme organisasjonsnummer. Dette er nyttig i større organisasjoner som har flere avsenderenheter. I tillegg kan det være veldig nyttig i utvikling for å unngå at utviklere og testmiljøer går i beina på hverandre.
Utvidelser
Difi har egne dokumenttyper, eller utvidelser, som kan sendes som metadata til hoveddokumenter. Disse utvidelsene er strukturerte xml-dokumenter med egne mime-typer. Disse utvidelsene benyttes av postkasseleverandørene til å gi en øket brukeropplevelse for innbyggere. Les mer om utvidelser på https://difi.github.io/felleslosninger/
Utvidelsene ligger som generert kode i sdp-shared
, som er en avhengighet av sikker-digital-post-klient-java
. Du kan selv lage kode
for å generere xml fra instanser av disse typene med JAXB, eller du kan lage xml på andre måter.
SDPLenke lenke = new SDPLenke();
lenke.setUrl("http://example.com");
StringResult result = new StringResult();
JAXBContext.newInstance(SDPLenke.class).createMarshaller().marshal(lenke, result);
MetadataDokument innkalling = MetadataDokument.builder(
"lenke.xml",
"application/vnd.difi.dpi.lenke+xml",
result.toString().getBytes()
).build();
Dokumentpakke dokumentpakke = Dokumentpakke.builder(hovedDokument)
.metadataDocument(innkalling)
.build();
Opprette klient og sende post
Forsendelse forsendelse = null; //Som initiert tidligere
KeyStore virksomhetssertifikat = null; //Last inn sertifikat her.
KlientKonfigurasjon klientKonfigurasjon = KlientKonfigurasjon
.builder(Miljo.FUNKSJONELT_TESTMILJO)
.connectionTimeout(20, TimeUnit.SECONDS)
.build();
DatabehandlerOrganisasjonsnummer databehandlerOrgnr =
AktoerOrganisasjonsnummer.of("555555555").forfremTilDatabehandler();
Databehandler databehandler = Databehandler
.builder(
databehandlerOrgnr,
Noekkelpar.fraKeyStoreUtenTrustStore(
virksomhetssertifikat,
"sertifikatAlias",
"sertifikatPassord"))
.build();
SikkerDigitalPostKlient sikkerDigitalPostKlient = new SikkerDigitalPostKlient(databehandler, klientKonfigurasjon);
try {
sikkerDigitalPostKlient.send(forsendelse);
} catch (SendException sendException) {
SendException.AntattSkyldig antattSkyldig = sendException.getAntattSkyldig();
String message = sendException.getMessage();
}
Mer informasjon om hvordan du oppretter et
Noekkelpar
finner du her.
Hent kvittering og bekreft
SikkerDigitalPostKlient sikkerDigitalPostKlient = null; //Som initiert tidligere
KvitteringForespoersel kvitteringForespoersel = KvitteringForespoersel.builder(Prioritet.NORMAL).mpcId("KøId").build();
ForretningsKvittering forretningsKvittering = sikkerDigitalPostKlient.hentKvittering(kvitteringForespoersel);
if (forretningsKvittering instanceof LeveringsKvittering) {
//Forsendelse er levert til digital postkasse
} else if (forretningsKvittering instanceof AapningsKvittering) {
//Forsendelse ble åpnet av mottaker
} else if (forretningsKvittering instanceof MottaksKvittering) {
//Kvittering på sending av fysisk post
} else if (forretningsKvittering instanceof ReturpostKvittering) {
//Forsendelse er blitt sendt i retur
} else if (forretningsKvittering instanceof Feil) {
//Feil skjedde under sending
}
sikkerDigitalPostKlient.bekreft(forretningsKvittering);
Husk at det ikke er mulig å hente nye kvitteringer før du har bekreftet mottak av nåværende.
Hent antall fakturerbare bytes
Hvis det er ønskelig å ha kontroll på hvor mange bytes det blir fakturert for i en forsendelse, så kan dette hentes ut fra resultatet av en sendoperasjon:
SikkerDigitalPostKlient sikkerDigitalPostKlient = null; //Som initiert tidligere
Forsendelse forsendelse = null; //Som initiert tidligere
SendResultat sendResultat = sikkerDigitalPostKlient.send(forsendelse);
long antallFakturerbareBytes = sendResultat.getFakturerbareBytes();
Husk at antall bytes det faktureres for ikke er synonymt med størrelsen på pakken som sendes. Faktureringsstørrelsen er beregnet fra opprinnelg størrelse på dokumentene og metainformasjonen gitt av manifest- og signaturfil.
Hvordan komme i gang
Tekniske krav
- Java 1.8 eller nyere
- Legge inn JCE Unlimited Strength JAR for å støtte lengre nøkkellengde på plattformen. Se java cryptography extension. Last ned og legg inn den som er riktig for din Java versjon. Se README i zipen for mer informasjon.
- Maven for å laste ned sikker-digital-post-klient-java
Logging av forespørsel og respons
Klienten støtter registrering av spring-ws interceptors som kan brukes til logging av request og respons, samt annen feilhåndtering.
Eksempelet under viser hvordan dette kan benyttes til å logge utgående requests til System.out
ved hjelp av javax.xml.transform
.
KlientKonfigurasjon klientKonfigurasjon = KlientKonfigurasjon.builder()
.soapInterceptors(new ClientInterceptor() {
@Override
public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException {
Source payloadSource = messageContext.getRequest().getPayloadSource();
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
StringWriter writer = new StringWriter();
transformer.transform(payloadSource, new StreamResult(writer));
System.out.println(writer.toString());
} catch (Exception e) {
System.err.print("Klarte ikke logge request");
e.printStackTrace();
}
return true;
}
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException { return true; }
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException { return true; }
public void afterCompletion(MessageContext messageContext, Exception ex) throws WebServiceClientException { }
})
.build();
Interceptors
I tillegg til spring-ws interceptors støtter også klienten registrering av HttpRequestInterceptor og HttpResponseInterceptor for direkte tilgang til underliggende http request og response:
KlientKonfigurasjon.builder()
.httpRequestInterceptors(new HttpRequestInterceptor() {
@Override
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
System.out.println("Utgående request!");
}
})
.httpResponseInterceptors(new HttpResponseInterceptor() {
@Override
public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
System.out.println("Innkommende request!");
}
})
.build();
Debugging
Merk: Innstillingene under er ikke anbefalt i produksjonsmiljøer.
Den underliggende http-klienten har støtte for å logge meldingene som sendes over nettverket. Sett org.apache.http.wire
til debug
eller lavere for å slå på denne loggingen.
Alternativt kan logging av requests gjøres ved hjelp av interceptors som beskrevet over.
Biblioteket har innebygd støtte for å outputte den genererte ASiC-E Dokumentpakken til disk for debug-formål:
try {
File tempFile = File.createTempFile("dokumentpakke", "debug");
CreateASiCE.debug_writeArchiveToDisk(tempFile);
System.out.println(tempFile);
} catch (IOException e) {
throw new RuntimeException("Kunne ikke lagre dokumentpakke", e);
}
Feilhandtering
Exception-hierarkiet i klienten er plassert under SikkerDigitalPostException
. Deretter er de grovt kategorisert i KonfigurasjonException
og SendException
.
KonfigurasjonException
er typisk feil relatert til konfigurasjon av klienten, som for eksempel manglende støtte for XML-standarder i Java-installasjonen, ugyldig keystore og lignende.
SendException
brukes for feil relatert til gjennomføringen av en sending. Disse blir forsøkt markert med om feilen skyldes forhold på klienten eller serveren.
Dersom en SendException
skyldes feil på klienten vil det generelt ikke være hensiktsmessig å gjøre en automatisk retry av forsendelsen.
Custom mapping av feil
Det er mulig å registrerte en ExceptionMapper
for oversetting av feil som oppstår i forbindelse med sending av post. Dette gjøres med SikkerDigitalPostKlient.setExceptionMapper()
.
Tips og triks
Validering av PDF-dokumenter som sendes til utskriftstjenesten
Dokumenter som sendes til utskriftstjenesten valideres før de printes. Derfor anbefaler vi at du bruker en egen printvalidator som kan benyttes for å validere printbarheten til PDF-dokumentet før det sendes til utskriftstjenesten.
Ytelse
For å forbedre ytelsen i klientbiblioteket, kan man med fordel sette følgende system properties på jvm-en. Ved å gjøre dette, unngår man unødvendig service loading:
javax.xml.parsers.DocumentBuilderFactory=
com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
javax.xml.transform.TransformerFactory=
com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl
com.sun.org.apache.xalan.internal.xsltc.dom.XSLTCDTMManager=
com.sun.org.apache.xalan.internal.xsltc.dom.XSLTCDTMManager
Nøkkelpar
For å kunne sende brev så må Databehandler
initieres med et Noekkelpar
som innehar virksomhetssertifikat i tillegg til sertifikater som er nødvendig for å validere responsen, såkalte trust-sertifikater. Det er to måter å gjøre det på, og den ene er hakket lettere enn den andre.
Opprett nøkkelpar med virksomhetssertifikat
Ved å bruke Noekkelpar.fraKeyStoreUtenTrustStore(KeyStore, String, String)
så trenger du bare å sende inn en KeyStore
som er initert med virksomhetssertifikatet til organisasjonen. Innebygd trust store blir da brukt for å validere responsen.
KeyStore keyStore;
String virksomhetssertifikatsti = "/sti/til/virksomhetssertifikat.p12";
String virksomhetssertifikatpassord = "virksomhetssertifikat_passord";
try {
keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(
new FileInputStream(virksomhetssertifikatsti),
virksomhetssertifikatpassord.toCharArray()
);
} catch (Exception e) {
throw new RuntimeException(
MessageFormat.format(
"Fant ikke virksomhetssertifikat på sti {0}.",
virksomhetssertifikatsti), e
);
}
Opprett nøkkelpar med virksomhetssertifikat og trust store
Hvis det er et ønske om å bygge sertifikatlageret for trust selv så kan det gjøres ved enten å bruke
Noekkelpar.fraKeyStore(KeyStore, String, String)
, hvorKeyStore
inneholder virksomhetssertifikat og trust-sertifikater ellerNoekkelpar.fraKeyStoreOgTrustStore(KeyStore, KeyStore, String, String)
, hvor virksomhetssertifikat ikke ligger sammen med trust-sertifikater. Sertifikatene kan lastes ned fra begrep.difi.no. Følgende eksempel viser hvordan du kan bygge en trust store.
Merk at oppretting av en trust store er kun for avsendere som ønsker full kontroll på hvilke sertifikater som man stoler på. Det er ellers ingen grunn til å gjøre det.
KeyStore trustStore;
try {
trustStore = KeyStore.getInstance("JCEKS");
trustStore.load(null, "".toCharArray());
} catch (Exception e) {
throw new SertifikatException(
MessageFormat.format(
"Oppretting av tom keystore feilet. Grunnen er {0}.",
e.toString()
)
);
}
List<X509Certificate> certificates = null; //Sertifikater lastet fra disk
for(X509Certificate cert : certificates){
String uniqueCertificateAlias = cert.getSerialNumber().toString() + Math.random();
try {
trustStore.setCertificateEntry(uniqueCertificateAlias, cert);
} catch (KeyStoreException e) {
throw new RuntimeException(
MessageFormat.format(
"Klarte ikke å legge til sertifikat til trust store. Grunnen er {0}",
e.toString()
)
);
}
}