|
2 | 2 |
|
3 | 3 | import static org.apache.commons.compress.utils.IOUtils.toByteArray;
|
4 | 4 |
|
5 |
| -import COSE.CoseException; |
6 |
| -import COSE.Message; |
7 |
| -import COSE.MessageTag; |
8 |
| -import COSE.OneKey; |
9 |
| -import COSE.Sign1Message; |
| 5 | +import COSE.*; |
| 6 | +import com.fasterxml.jackson.databind.ObjectMapper; |
10 | 7 | import com.upokecenter.cbor.CBORObject;
|
| 8 | + |
11 | 9 | import java.io.ByteArrayInputStream;
|
12 | 10 | import java.io.ByteArrayOutputStream;
|
13 | 11 | import java.io.IOException;
|
| 12 | + |
| 13 | +import ehn.techiop.hcert.model.CertificatePayload; |
14 | 14 | import nl.minvws.encoding.Base45;
|
15 | 15 | import org.apache.commons.compress.compressors.CompressorException;
|
16 | 16 | import org.apache.commons.compress.compressors.CompressorInputStream;
|
17 | 17 | import org.apache.commons.compress.compressors.CompressorStreamFactory;
|
18 | 18 |
|
19 | 19 | public class GreenCertificateDecoder {
|
20 | 20 |
|
21 |
| - private final OneKey publicKey; |
| 21 | + private final CertificateProvider certificateProvider; |
| 22 | + private final String prefix; |
22 | 23 |
|
23 |
| - public GreenCertificateDecoder(OneKey publicKey) { |
24 |
| - this.publicKey = publicKey; |
25 |
| - } |
| 24 | + public GreenCertificateDecoder(CertificateProvider certificateProvider) { |
| 25 | + this(certificateProvider, "HC1"); |
| 26 | + } |
26 | 27 |
|
| 28 | + public GreenCertificateDecoder(CertificateProvider certificateProvider, String prefix) { |
| 29 | + this.certificateProvider = certificateProvider; |
| 30 | + this.prefix = prefix; |
| 31 | + } |
27 | 32 |
|
28 | 33 |
|
29 |
| - /** |
30 |
| - * Decodes base45 encoded string -> Deflate -> COSE -> CBOR -> arbitrary Json String |
31 |
| - * |
32 |
| - * @param base45String |
33 |
| - * @return |
34 |
| - * @throws CompressorException |
35 |
| - * @throws IOException |
36 |
| - * @throws CoseException |
37 |
| - */ |
38 |
| - public String decode(String base45String) throws CompressorException, IOException, CoseException { |
39 |
| - if(!base45String.startsWith("HC1")) |
40 |
| - throw new RuntimeException("Base45 string not valid according to specification"); |
| 34 | + /** |
| 35 | + * Decodes base45 encoded string -> Deflate -> COSE -> CBOR -> arbitrary Json String |
| 36 | + * |
| 37 | + * @param base45String |
| 38 | + * @return |
| 39 | + * @throws CompressorException |
| 40 | + * @throws IOException |
| 41 | + * @throws CoseException |
| 42 | + */ |
| 43 | + public String decode(String base45String) throws CompressorException, IOException, CoseException { |
| 44 | + if (!base45String.startsWith(prefix)) |
| 45 | + throw new RuntimeException("Base45 string not valid according to specification"); |
41 | 46 |
|
42 |
| - base45String = base45String.substring(3); |
43 |
| - byte[] decodedBytes = Base45.getDecoder().decode(base45String); |
| 47 | + base45String = base45String.substring(3); |
| 48 | + byte[] decodedBytes = Base45.getDecoder().decode(base45String); |
44 | 49 |
|
45 |
| - byte[] coseBytes = getCoseBytes(decodedBytes); |
| 50 | + byte[] coseBytes = decompress(decodedBytes); |
46 | 51 |
|
47 |
| - byte[] cborBytes = getCborBytes(coseBytes); |
| 52 | + return toJsonPayload(coseBytes); |
| 53 | + } |
48 | 54 |
|
49 |
| - return getJsonString(cborBytes); |
50 |
| - } |
| 55 | + private String toJsonPayload(byte[] coseBytes) throws CoseException, IOException { |
| 56 | + Sign1Message msg = (Sign1Message) Message.DecodeFromBytes(coseBytes, MessageTag.Sign1); |
51 | 57 |
|
52 |
| - private String getJsonString(byte[] cborBytes) throws IOException { |
53 |
| - CBORObject cborObject = CBORObject.DecodeFromBytes(cborBytes); |
54 |
| - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); |
55 |
| - cborObject.WriteJSONTo(byteArrayOutputStream); |
| 58 | + String kid = msg.getProtectedAttributes().get(HeaderKeys.KID.AsCBOR()).AsString(); |
56 | 59 |
|
57 |
| - return byteArrayOutputStream.toString("UTF-8"); |
58 |
| - } |
| 60 | + CBORObject cborObject = CBORObject.DecodeFromBytes(msg.GetContent()); |
| 61 | + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); |
| 62 | + cborObject.WriteJSONTo(byteArrayOutputStream); |
59 | 63 |
|
60 |
| - private byte[] getCborBytes(byte[] coseBytes) throws CoseException { |
61 |
| - Sign1Message msg = (Sign1Message) Message.DecodeFromBytes(coseBytes, MessageTag.Sign1); |
62 |
| - if (!msg.validate(publicKey)) { |
63 |
| - throw new RuntimeException("Could not verify COSE signature"); |
64 |
| - } |
| 64 | + String json = byteArrayOutputStream.toString("UTF-8"); |
65 | 65 |
|
66 |
| - return msg.GetContent(); |
67 |
| - } |
| 66 | + ObjectMapper objectMapper = new ObjectMapper(); |
| 67 | + CertificatePayload certificatePayload = objectMapper.readValue(json, CertificatePayload.class); |
| 68 | + String issuer = certificatePayload.getIss(); |
68 | 69 |
|
69 |
| - private byte[] getCoseBytes(byte[] decodedBytes) throws CompressorException, IOException { |
70 | 70 |
|
71 |
| - CompressorInputStream compressedStream = new CompressorStreamFactory() |
72 |
| - .createCompressorInputStream(CompressorStreamFactory.DEFLATE, |
73 |
| - new ByteArrayInputStream(decodedBytes)); |
| 71 | + if (!msg.validate(new OneKey(certificateProvider.provideKey(kid, issuer), null))) { |
| 72 | + throw new RuntimeException("Could not verify COSE signature"); |
| 73 | + } |
74 | 74 |
|
75 |
| - return toByteArray(compressedStream); |
76 |
| - } |
| 75 | + return json; |
| 76 | + } |
| 77 | + |
| 78 | + private byte[] decompress(byte[] decodedBytes) throws CompressorException, IOException { |
| 79 | + |
| 80 | + CompressorInputStream compressedStream = new CompressorStreamFactory() |
| 81 | + .createCompressorInputStream(CompressorStreamFactory.DEFLATE, |
| 82 | + new ByteArrayInputStream(decodedBytes)); |
| 83 | + |
| 84 | + return toByteArray(compressedStream); |
| 85 | + } |
77 | 86 | }
|
0 commit comments