Hopp til hovedinnhold
difi.github.io/difi-sikker-digital-post-klient-java

Sikker Digital Post Java

difi.github.io/difi-sikker-digital-post-klient-java

Sekundærmeny

  • GitHub
  • Issues

Java-bibliotek for sending av sikker digital post for offentlig sektor

Last ned
Gå til http://mvnrepository.com/artifact/no.difi.sdp

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), hvor KeyStore inneholder virksomhetssertifikat og trust-sertifikater eller
  • Noekkelpar.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()
                )
        );

    }
}

Innhold

v5 v4 v3 v2.1 v1.2

  • Kontakt: idporten@difi.no
  • Telefon: +47 95 73 61 03
  • E-post: postmottak@difi.no
  • Org.no: 991 825 827
  • Difis nettsteder
  • Personvern
Go to top of page
↑