package io.pivotal.labs.cfenv.crypto;

import javax.xml.bind.DatatypeConverter;
import java.io.ByteArrayInputStream;
import java.security.Key;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CryptoParser {

    private static final Pattern KEY_PATTERN = Pattern.compile("-----BEGIN PRIVATE KEY-----\n(.*)\n-----END PRIVATE KEY-----\n?", Pattern.DOTALL);
    private static final byte[] RSA_SIGNATURE = bytes("06 09 2a 86 48 86 f7 0d 01 01 01");
    private static final byte[] EC_SIGNATURE = bytes("06 07 2a 86 48 ce 3d 02 01");
    private static final byte[] DSA_SIGNATURE = bytes("06 07 2a 86 48 ce 38 04 01");
    private static final byte[] DH_SIGNATURE = bytes("06 09 2a 86 48 86 f7 0d 01 03 01");

    private static byte[] bytes(String hex) {
        return DatatypeConverter.parseHexBinary(hex.replace(" ", ""));
    }

    public static Certificate parseCertificate(String certificateString) throws CertificateException {
        return X509CertificateFactory.INSTANCE.generateCertificate(toStream(certificateString));
    }

    private static ByteArrayInputStream toStream(String string) {
        return new ByteArrayInputStream(string.getBytes());
    }

    public static Key parseKey(String keyString) throws InvalidKeySpecException {
        Matcher matcher = KEY_PATTERN.matcher(keyString);
        if (!matcher.matches()) throw new IllegalArgumentException("bad or unsupported PEM encoding: " + keyString);
        String keyBytesString = matcher.group(1);

        byte[] keyBytes = Base64.getMimeDecoder().decode(keyBytesString);
        KeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);

        if (contains(keyBytes, RSA_SIGNATURE)) {
            return RSAKeyFactory.INSTANCE.generatePrivate(keySpec);
        } else if (contains(keyBytes, EC_SIGNATURE)) {
            return ECKeyFactory.INSTANCE.generatePrivate(keySpec);
        } else if (contains(keyBytes, DSA_SIGNATURE)) {
            return DSAKeyFactory.INSTANCE.generatePrivate(keySpec);
        } else if (contains(keyBytes, DH_SIGNATURE)) {
            return DHKeyFactory.INSTANCE.generatePrivate(keySpec);
        } else {
            throw new IllegalArgumentException("unsupported algorithm: " + keyString);
        }
    }

    private static boolean contains(byte[] haystack, byte[] needle) {
        bytes:
        for (int i = 0; i < haystack.length; i++) {
            for (int j = 0; j < needle.length; j++) {
                if (haystack[i + j] != needle[j]) continue bytes;
            }
            return true;
        }
        return false;
    }

}

@@ -136,16 +136,49 @@
 ty.Key;%0A
+import java.security.KeyFactory;%0A
 import j
@@ -208,16 +208,16 @@
 ficate;%0A
-
 import j
@@ -655,16 +655,17 @@
 OTALL);%0A
+%0A
     priv
@@ -1927,53 +1927,167 @@
     
-if (contains(keyBytes, RSA_SIGNATURE)) %7B%0A    
+KeyFactory keyFactory = chooseKeyFactory(keyBytes);%0A        if (keyFactory == null) throw new IllegalArgumentException(%22unsupported algorithm: %22 + keyString);%0A
     
@@ -2101,12 +2101,9 @@
 urn 
-RSAK
+k
 eyFa
@@ -2104,33 +2104,24 @@
  keyFactory.
-INSTANCE.
 generatePriv
@@ -2134,34 +2134,100 @@
 ySpec);%0A    
+%7D%0A%0A
     
-%7D else
+private static KeyFactory chooseKeyFactory(byte%5B%5D keyBytes) %7B%0A       
  if (contain
@@ -2238,18 +2238,19 @@
 yBytes, 
-EC
+RSA
 _SIGNATU
@@ -2250,37 +2250,105 @@
 SIGNATURE)) 
-%7B%0A           
+return RSAKeyFactory.INSTANCE;%0A        else if (contains(keyBytes, EC_SIGNATURE))
  return ECKe
@@ -2368,33 +2368,8 @@
 ANCE
-.generatePrivate(keySpec)
 ;%0A  
@@ -2365,34 +2365,32 @@
 NSTANCE;%0A       
- %7D
  else if (contai
@@ -2413,38 +2413,24 @@
 _SIGNATURE))
- %7B%0A           
  return DSAK
@@ -2451,33 +2451,8 @@
 ANCE
-.generatePrivate(keySpec)
 ;%0A  
@@ -2452,26 +2452,24 @@
 NCE;%0A       
- %7D
  else if (co
@@ -2499,30 +2499,16 @@
 NATURE))
- %7B%0A           
  return 
@@ -2532,33 +2532,8 @@
 ANCE
-.generatePrivate(keySpec)
 ;%0A  
@@ -2541,114 +2541,26 @@
     
- %7D
  else 
-%7B%0A            throw new IllegalArgumentException(%22unsupported algorithm: %22 + keyString);%0A        %7D
+return null;
 %0A   
@@ -2675,16 +2675,17 @@
 = 0; i %3C
+=
  haystac
@@ -2692,16 +2692,32 @@
 k.length
+ - needle.length
 ; i++) %7B

Refactor CryptoParser a bit