summaryrefslogtreecommitdiff
path: root/src/main/java/at/gv/util/BpkUtil.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/at/gv/util/BpkUtil.java')
-rw-r--r--src/main/java/at/gv/util/BpkUtil.java364
1 files changed, 364 insertions, 0 deletions
diff --git a/src/main/java/at/gv/util/BpkUtil.java b/src/main/java/at/gv/util/BpkUtil.java
new file mode 100644
index 0000000..5c256c4
--- /dev/null
+++ b/src/main/java/at/gv/util/BpkUtil.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2011 Federal Chancellery Austria and
+ * Graz University of Technology
+ *
+ * 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.
+ */
+package at.gv.util;
+
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Date;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.StringUtils;
+
+import at.gv.util.data.BPK;
+import at.gv.util.data.WBPK;
+import at.gv.util.ex.InternalErrorException;
+
+/**
+ * Utility class for sector specific PINs (Bereichsspezifische
+ * Personenkennzeichen).
+ *
+ * @author <a href="mailto:Arne.Tauber@egiz.gv.at">Arne Tauber</a>
+ *
+ */
+public final class BpkUtil {
+
+ public static final String SECTOR_DELIVERY = "ZU";
+ public static final String PUBLIC_KEY_ZUSEKOPF_SN01_BASE64 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDfGFFEzWl1kSbzmk4pWVeftyD2aWTVQ8xSIwb6ECLdFTy4zE9LI6a87zlPbOOzvvdO+Nv1VCvT+WqD9HOCtPwealwwRKS2cHI9aqYyozqDOIGBRIz7MDBKuqaTwCvonFe0MUBxVWDDTmqhDIjkN0uDQidQtqqjzupEzuQ59lVpBQIDAQAB";
+
+ public static final String prefix = "urn:publicid:gv.at:cdid+";
+
+
+ private static String calcDigest(String data)
+ throws NoSuchAlgorithmException, UnsupportedEncodingException {
+ MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
+ byte[] digest = sha1.digest(data.getBytes("ISO-8859-1"));
+ return new String(Base64.encodeBase64(digest));
+ }
+
+
+
+ public static String calcBPK(String baseID, String sector) {
+ MiscUtil.assertNotEmpty(baseID, "BaseID");
+ MiscUtil.assertNotEmpty(sector, "Sector");
+ // prefix according to specification
+ String prefix = "urn:publicid:gv.at:cdid+";
+
+ // concatenation according to algorithm
+ String data;
+ if (sector.startsWith(prefix))
+ data = baseID + "+" + sector;
+ else
+ data = baseID + "+" + prefix + sector;
+
+ String hash = null;
+ try {
+ hash = calcDigest(data);
+ } catch (NoSuchAlgorithmException noSuchAlgorithmException) {
+ throw new InternalErrorException(noSuchAlgorithmException);
+ } catch (UnsupportedEncodingException unsupportedEncodingException) {
+ throw new InternalErrorException(unsupportedEncodingException);
+ }
+ return hash;
+ }
+
+ public static BPK createBPK(String baseID, String sector) {
+
+ if (sector.startsWith(prefix))
+ sector = sector.substring(prefix.length());
+
+ return new BPK(sector, calcBPK(baseID, sector));
+ }
+
+ public static String calcZBPK(String baseID) {
+ return calcBPK(baseID, SECTOR_DELIVERY);
+ }
+
+ public static BPK createZBPK(String baseID) {
+ return new BPK(SECTOR_DELIVERY, calcZBPK(baseID));
+ }
+
+ public static String calcWBPK(String baseID, String nameQualifier) {
+ MiscUtil.assertNotEmpty(baseID, "BaseID");
+ MiscUtil.assertNotEmpty(nameQualifier, "Name qualifier");
+ // concatenation according to algorithm
+ String data = baseID + "+" + nameQualifier;
+
+ String wBPK = null;
+ try {
+ wBPK = calcDigest(data);
+ } catch (NoSuchAlgorithmException e) {
+ throw new InternalErrorException(e);
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalErrorException(e);
+ }
+ return wBPK;
+ }
+
+ public static WBPK createWBPK(String baseID, String nameQualifier) {
+ return new WBPK(calcWBPK(baseID, nameQualifier), nameQualifier);
+ }
+
+ public static byte[] encrypt(byte[] inputBytes, PublicKey publicKey) {
+ byte[] result;
+ try {
+ Cipher cipher = null;
+ try {
+ cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); // try with bouncycastle
+ } catch(NoSuchAlgorithmException e) {
+ cipher = Cipher.getInstance("RSA/ECB/OAEP"); // try with iaik provider
+ }
+ cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+ result = cipher.doFinal(inputBytes);
+ } catch (InvalidKeyException e) {
+ throw new InternalErrorException(e);
+ } catch (IllegalBlockSizeException e) {
+ throw new InternalErrorException(e);
+ } catch (BadPaddingException e) {
+ throw new InternalErrorException(e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new InternalErrorException(e);
+ } catch (NoSuchPaddingException e) {
+ throw new InternalErrorException(e);
+ }
+ return result;
+ }
+
+ public static byte[] decrypt(byte[] encryptedBytes, PrivateKey privateKey) {
+ byte[] result;
+ try {
+ Cipher cipher = null;
+ try {
+ cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); // try with bouncycastle
+ } catch(NoSuchAlgorithmException e) {
+ cipher = Cipher.getInstance("RSA/ECB/OAEP"); // try with iaik provider
+ }
+ cipher.init(Cipher.DECRYPT_MODE, privateKey);
+ result = cipher.doFinal(encryptedBytes);
+ } catch (InvalidKeyException e) {
+ throw new InternalErrorException(e);
+ } catch (IllegalBlockSizeException e) {
+ throw new InternalErrorException(e);
+ } catch (BadPaddingException e) {
+ throw new InternalErrorException(e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new InternalErrorException(e);
+ } catch (NoSuchPaddingException e) {
+ throw new InternalErrorException(e);
+ }
+ return result;
+ }
+
+ public static String encryptBPK(BPK bpk, PublicKey publicKey) {
+ MiscUtil.assertNotNull(bpk, "BPK");
+ MiscUtil.assertNotNull(publicKey, "publicKey");
+ String input = "V1::urn:publicid:gv.at:cdid+" + bpk.getSector() + "::"
+ + bpk.getBpk() + "::"
+ + DateTimeUtil.formatDate(new Date(), "yyyy-MM-dd'T'HH:mm:ss");
+ System.out.println(input);
+ byte[] result;
+ try {
+ byte[] inputBytes = input.getBytes("ISO-8859-1");
+ result = encrypt(inputBytes, publicKey);
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalErrorException(e);
+ }
+ return new String(Base64.encodeBase64(result)).replaceAll("\r\n", "");
+ }
+
+ public static BPK decryptBPK(String encryptedBpk, PrivateKey privateKey) {
+ MiscUtil.assertNotEmpty(encryptedBpk, "Encrypted BPK");
+ MiscUtil.assertNotNull(privateKey, "Private key");
+ String decryptedString;
+ try {
+ byte[] encryptedBytes = Base64.decodeBase64(encryptedBpk
+ .getBytes("ISO-8859-1"));
+ byte[] decryptedBytes = decrypt(encryptedBytes, privateKey);
+ decryptedString = new String(decryptedBytes, "ISO-8859-1");
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalErrorException(e);
+ }
+ String tmp = decryptedString.substring(decryptedString.indexOf('+') + 1);
+ String sector = tmp.substring(0, tmp.indexOf("::"));
+ tmp = tmp.substring(tmp.indexOf("::") + 2);
+ String bPK = tmp.substring(0, tmp.indexOf("::"));
+
+ return new BPK(sector, bPK);
+ }
+
+ public static String calcVZBPK(String baseID, PublicKey publicKey) {
+ MiscUtil.assertNotEmpty(baseID, "BaseID");
+ MiscUtil.assertNotNull(publicKey, "Public key");
+ String bpk = calcZBPK(baseID);
+ String input = "Ver:1::urn:publicid:gv.at:cdid+" + SECTOR_DELIVERY + "::"
+ + bpk + "::"
+ + DateTimeUtil.formatDate(new Date(), "yyyy-MM-dd'T'HH:mm:ss");
+ byte[] result;
+ try {
+ byte[] inputBytes = input.getBytes("ISO-8859-1");
+ result = encrypt(inputBytes, publicKey);
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalErrorException(e);
+ }
+ return new String(Base64.encodeBase64(result)).replaceAll("\r\n", "");
+ }
+
+ public static String calcVZBPK(String baseID) {
+ MiscUtil.assertNotEmpty(baseID, "BaseID");
+ PublicKey publicKey;
+ try {
+ byte[] publicKeyBytes = Base64
+ .decodeBase64(PUBLIC_KEY_ZUSEKOPF_SN01_BASE64.getBytes("ISO-8859-1"));
+ KeyFactory rsaKeyFac = KeyFactory.getInstance("RSA");
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
+ publicKey = (RSAPublicKey) rsaKeyFac.generatePublic(keySpec);
+ } catch (UnsupportedEncodingException e) {
+ throw new InternalErrorException(e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new InternalErrorException(e);
+ } catch (InvalidKeySpecException e) {
+ throw new InternalErrorException(e);
+ }
+ return calcVZBPK(baseID, publicKey);
+ }
+
+ /**
+ * @deprecated Use {@link BpkUtil#calcVZBPK(String, PublicKey)} instead.
+ */
+ public static String calcVzbpk(String baseID, String sector,
+ byte[] rsaPublicKey) {
+ String zbpk = calcZBPK(baseID);
+ return encryptBPK(zbpk, sector, new Date(), rsaPublicKey);
+ }
+
+ /**
+ * Calculates the check digit for commercial register digits.<br/>
+ * A commercial register number is build by concatinating the commercial
+ * register digits with the check digit (e.g. 123456 and i results in
+ * 123456i).
+ *
+ * @param commercialRegisterDigits
+ * The commercial register number consisting of 6 digits.
+ * @return The check digit.
+ */
+ public static char calcCheckDigitFromCommercialRegisterNumber(
+ String commercialRegisterDigits) {
+ final int[] WEIGHT = { 6, 4, 14, 15, 10, 1 };
+ final char[] CHECKDIGIT = { 'a', 'b', 'd', 'f', 'g', 'h', 'i', 'k', 'm',
+ 'p', 's', 't', 'v', 'w', 'x', 'y', 'z' };
+ if (commercialRegisterDigits == null) {
+ throw new NullPointerException("Commercial register number missing.");
+ }
+ commercialRegisterDigits = StringUtils.leftPad(commercialRegisterDigits, 6,
+ '0');
+ if (!commercialRegisterDigits.matches("\\d{6}")) {
+ throw new IllegalArgumentException(
+ "Invalid commercial register number provided.");
+ }
+ int sum = 0;
+ for (int i = 0; i < commercialRegisterDigits.length(); i++) {
+ int value = commercialRegisterDigits.charAt(i) - '0';
+ sum += WEIGHT[i] * value;
+ }
+ return CHECKDIGIT[sum % 17];
+ }
+
+ /**
+ * Checks the validity of a commercial register number.
+ *
+ * @param commercialRegisterNumber
+ * The commercial register number.
+ * @return {@code true} if the given commercial register number is valid,
+ * {@code false} if not.
+ */
+ public static boolean checkCommercialRegisterNumber(
+ String commercialRegisterNumber) {
+ if (commercialRegisterNumber == null) {
+ return false;
+ }
+ commercialRegisterNumber = StringUtils.leftPad(commercialRegisterNumber, 7,
+ '0');
+ if (!commercialRegisterNumber.matches("\\d{6}[abdfghikmpstvwxzy]")) {
+ return false;
+ }
+ String digits = commercialRegisterNumber.substring(0,
+ commercialRegisterNumber.length() - 1);
+ char checkDigit = commercialRegisterNumber.charAt(commercialRegisterNumber
+ .length() - 1);
+ boolean result = calcCheckDigitFromCommercialRegisterNumber(digits) == checkDigit;
+ return result;
+ }
+
+ /**
+ * @deprecated Use {@link BpkUtil#encryptBPK(BPK, PublicKey)} instead.
+ */
+ public static String encryptBPK(String bpk, String context, Date date,
+ byte[] binaryPublicKey) {
+ String inputData = "Ver:1::urn:publicid:gv.at:cdid+" + context + "::" + bpk
+ + "::" + DateTimeUtil.formatDate(date, "yyyy-MM-dd'T'HH:mm:ss");
+ byte[] plain = new String(inputData).getBytes();
+ // log.debug("inputData = " + inputData);
+ byte[] encrypted = null;
+
+ try {
+ KeyFactory rsaKeyFac = KeyFactory.getInstance("RSA");
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(binaryPublicKey);
+ PublicKey rsaPublicKey = (RSAPublicKey) rsaKeyFac.generatePublic(keySpec);
+ Cipher rsa = null;
+ try {
+ rsa = Cipher.getInstance("RSA/ECB/OAEPPadding"); // try with bouncycastle
+ } catch(NoSuchAlgorithmException e) {
+ rsa = Cipher.getInstance("RSA/ECB/OAEP"); // try with iaik provider
+ }
+ rsa.init(Cipher.ENCRYPT_MODE, rsaPublicKey);
+ encrypted = rsa.doFinal(plain);
+ } catch (InvalidKeyException e) {
+ throw new InternalErrorException(e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new InternalErrorException(e);
+ } catch (NoSuchPaddingException e) {
+ throw new InternalErrorException(e);
+ } catch (IllegalArgumentException e) {
+ throw new InternalErrorException(e);
+ } catch (IllegalBlockSizeException e) {
+ throw new InternalErrorException(e);
+ } catch (BadPaddingException e) {
+ throw new InternalErrorException(e);
+ } catch (InvalidKeySpecException e) {
+ throw new InternalErrorException(e);
+ }
+
+ return new String(Base64.encodeBase64(encrypted)).replaceAll("\r\n", "");
+ }
+
+ private BpkUtil() {
+ }
+
+}