diff options
Diffstat (limited to 'src/main/java/at/gv/util/BpkUtil.java')
-rw-r--r-- | src/main/java/at/gv/util/BpkUtil.java | 364 |
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() { + } + +} |