Skip to content

Commit

Permalink
fix cert hash (#40)
Browse files Browse the repository at this point in the history
* fix cert hash

* claim signature

* checkstyle

* tan expiration
  • Loading branch information
a-trzewik authored May 6, 2021
1 parent de42c2e commit 8f31788
Show file tree
Hide file tree
Showing 7 changed files with 232 additions and 111 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ public class IssuanceConfigProperties {
private String certAlias;
private String privateKeyPassword;
private String countryCode;
private int tanExpirationHours = 24;

}
73 changes: 50 additions & 23 deletions src/main/java/eu/europa/ec/dgc/issuance/service/DgciService.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@
import eu.europa.ec.dgc.issuance.restapi.dto.EgdcCodeData;
import eu.europa.ec.dgc.issuance.restapi.dto.IssueData;
import eu.europa.ec.dgc.issuance.restapi.dto.SignatureData;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.MessageDigest;
Expand All @@ -54,6 +53,7 @@
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
Expand Down Expand Up @@ -140,12 +140,12 @@ public SignatureData finishDgci(long dgciId, IssueData issueData) throws Excepti
Optional<DgciEntity> dgciEntityOpt = dgciRepository.findById(dgciId);
if (dgciEntityOpt.isPresent()) {
var dgciEntity = dgciEntityOpt.get();
String signatureBase64 = certificateService.signHash(issueData.getHash());
String tan = tanService.generateNewTan();
dgciEntity.setHashedTan(tanService.hashTan(tan));
dgciEntity.setCertHash(signatureBase64);
dgciEntity.setCertHash(issueData.getHash());
dgciRepository.saveAndFlush(dgciEntity);
log.info("signed for " + dgciId);
String signatureBase64 = certificateService.signHash(issueData.getHash());
return new SignatureData(tan, signatureBase64);
} else {
log.warn("can not find dgci with id " + dgciId);
Expand Down Expand Up @@ -180,10 +180,11 @@ public DidDocument getDidDocument(String hash) {

/**
* compute cose sign hash.
*
* @param coseMessage cose message
* @return hash value
*/
public byte[] computeCoseSignHash(byte[] coseMessage) {
public byte[] computeCoseSignHash(byte[] coseMessage) {
try {
CBORObject coseForSign = CBORObject.NewArray();
CBORObject cborCose = CBORObject.DecodeFromBytes(coseMessage);
Expand Down Expand Up @@ -212,11 +213,11 @@ public ClaimResponse claimUpdate(ClaimRequest claimRequest) {
}

/**
* TODO: Add Comment.
* claim dgci to wallet app.
* means bind dgci with some public key from wallet app
* @param claimRequest claim request
*/
public void claim(ClaimRequest claimRequest)
throws IOException, NoSuchAlgorithmException, SignatureException,
InvalidKeySpecException, InvalidKeyException {
public void claim(ClaimRequest claimRequest) {
if (!verifySignature(claimRequest)) {
throw new WrongRequest("signature verification failed");
}
Expand All @@ -237,6 +238,11 @@ public void claim(ClaimRequest claimRequest)
dgciRepository.saveAndFlush(dgciEntity);
throw new WrongRequest("tan mismatch");
}
ZonedDateTime tanExpireTime = dgciEntity.getCreatedAt()
.plus(Duration.ofHours(issuanceConfigProperties.getTanExpirationHours()));
if (tanExpireTime.isBefore(ZonedDateTime.now())) {
throw new WrongRequest("tan expired");
}
dgciEntity.setClaimed(true);
dgciEntity.setRetryCounter(dgciEntity.getRetryCounter() + 1);
dgciEntity.setPublicKey(claimRequest.getPublicKey().getValue());
Expand All @@ -249,26 +255,47 @@ public void claim(ClaimRequest claimRequest)
}
}

private boolean verifySignature(ClaimRequest claimRequest)
throws IOException, NoSuchAlgorithmException, SignatureException,
InvalidKeyException, InvalidKeySpecException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write(claimRequest.getDgci().getBytes());
bos.write(Base64.getDecoder().decode(claimRequest.getTanHash()));
private boolean verifySignature(ClaimRequest claimRequest) {
byte[] keyBytes = Base64.getDecoder().decode(claimRequest.getPublicKey().getValue());
bos.write(keyBytes);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance(claimRequest.getPublicKey().getType());
PublicKey publicKey = kf.generatePublic(spec);
Signature signature = Signature.getInstance(claimRequest.getSigAlg());
signature.initVerify(publicKey);
signature.update(bos.toByteArray());
byte[] sigBytes = Base64.getDecoder().decode(claimRequest.getSignature());
return signature.verify(sigBytes);
KeyFactory kf;
try {
kf = KeyFactory.getInstance(claimRequest.getPublicKey().getType());
} catch (NoSuchAlgorithmException e) {
throw new WrongRequest("key type not supported: '" + claimRequest.getPublicKey().getType()
+ "', try RSA or EC");
}
PublicKey publicKey;
try {
publicKey = kf.generatePublic(spec);
} catch (InvalidKeySpecException e) {
throw new WrongRequest("invalid key");
}
Signature signature;
try {
signature = Signature.getInstance(claimRequest.getSigAlg());
} catch (NoSuchAlgorithmException e) {
throw new WrongRequest("signature algorithm not supported: '" + claimRequest.getSigAlg() + "'");
}
StringBuilder dataToSign = new StringBuilder();
dataToSign.append(claimRequest.getTanHash())
.append(claimRequest.getCertHash())
.append(claimRequest.getPublicKey().getValue());
try {
signature.initVerify(publicKey);
signature.update(dataToSign.toString().getBytes(StandardCharsets.UTF_8));
byte[] sigBytes = Base64.getDecoder().decode(claimRequest.getSignature());
return signature.verify(sigBytes);
} catch (InvalidKeyException e) {
throw new WrongRequest("invalid key for signature");
} catch (SignatureException e) {
throw new WrongRequest("can not validity signature", e);
}
}

/**
* Create edgc in backend.
*
* @param eudgc certificate
* @return edgc qr code and tan
*/
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ issuance:
certAlias: edgc_dev_ec
privateKeyPassword: dgca
countryCode: DE
tanExpirationHours: 24
dgc:
gateway:
connector:
Expand Down
121 changes: 121 additions & 0 deletions src/test/java/eu/europa/ec/dgc/issuance/GenerateWalletRequestTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*-
* ---license-start
* EU Digital Green Certificate Issuance Service / dgca-issuance-service
* ---
* Copyright (C) 2021 T-Systems International GmbH and all other contributors
* ---
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ---license-end
*/

package eu.europa.ec.dgc.issuance;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import eu.europa.ec.dgc.issuance.restapi.dto.ClaimRequest;
import eu.europa.ec.dgc.issuance.restapi.dto.PublicKey;
import eu.europa.ec.dgc.issuance.service.TanService;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Base64;
import org.junit.Test;

public class GenerateWalletRequestTest {
// This can be used to generate valid json structure for claim
@Test
public void testGenerateWalletRequest() throws Exception {
TanService tanService = new TanService();

// Please adapt this to your certificate (the values can be get from browser network log
// see POST /dgci
// and PUT /dgci/{id}
String dgci = "dgci:V1:DE:2e974b3b-d932-4bc9-bbae-d387f93f8bf3:edbcb873196f24be";
String certHash = "mfg0MI7wPFexNkOa4n9OKojrzhe9a9lcim4JzJO3WtY=";
String tan = "U7ULCYZY";
String tanHash = tanService.hashTan(tan);

KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(2048);
KeyPair keyPair = keyPairGen.generateKeyPair();
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
String sigAlg = "SHA256WithRSA";

ClaimRequest claimRequest = new ClaimRequest();
claimRequest.setDgci(dgci);
claimRequest.setTanHash(tanHash);
PublicKey publicKeyDTO = new PublicKey();
publicKeyDTO.setType(keyPair.getPublic().getAlgorithm());
publicKeyDTO.setValue(Base64.getEncoder().encodeToString(publicKeyBytes));
claimRequest.setPublicKey(publicKeyDTO);

claimRequest.setSigAlg(sigAlg);
claimRequest.setCertHash(certHash);
createClaimSignature(claimRequest, keyPair.getPrivate(), sigAlg);

ObjectMapper objectMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
System.out.println(objectMapper.writeValueAsString(claimRequest));
}

private void createClaimSignature(ClaimRequest claimRequest, PrivateKey privateKey, String sigAlg) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
StringBuilder sigValue = new StringBuilder();
sigValue.append(claimRequest.getTanHash())
.append(claimRequest.getCertHash())
.append(claimRequest.getPublicKey().getValue());
Signature signature = Signature.getInstance(sigAlg);
signature.initSign(privateKey);
signature.update(sigValue.toString().getBytes(StandardCharsets.UTF_8));
byte[] sigData = signature.sign();
claimRequest.setSignature(Base64.getEncoder().encodeToString(sigData));
}

@Test
public void testGenerateWalletRequestEC() throws Exception {
TanService tanService = new TanService();

// Please adapt this to your certificate (the values can be get from browser network log
// see POST /dgci
// and PUT /dgci/{id}
String dgci = "dgci:V1:DE:2e974b3b-d932-4bc9-bbae-d387f93f8bf3:edbcb873196f24be";
String certHash = "mfg0MI7wPFexNkOa4n9OKojrzhe9a9lcim4JzJO3WtY=";
String tan = "U7ULCYZY";
String tanHash = tanService.hashTan(tan);

KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
keyPairGen.initialize(256);
KeyPair keyPair = keyPairGen.generateKeyPair();
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
String sigAlg = "SHA256withECDSA";

ClaimRequest claimRequest = new ClaimRequest();
claimRequest.setDgci(dgci);
claimRequest.setTanHash(tanHash);
PublicKey publicKeyDTO = new PublicKey();
publicKeyDTO.setType(keyPair.getPublic().getAlgorithm());
publicKeyDTO.setValue(Base64.getEncoder().encodeToString(publicKeyBytes));
claimRequest.setPublicKey(publicKeyDTO);

claimRequest.setSigAlg(sigAlg);
claimRequest.setCertHash(certHash);
createClaimSignature(claimRequest,keyPair.getPrivate(),sigAlg);

ObjectMapper objectMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
System.out.println(objectMapper.writeValueAsString(claimRequest));
}

}

This file was deleted.

Loading

0 comments on commit 8f31788

Please sign in to comment.