BUG-1422 Introduce Call-Home functionality for the NETCONF Topology.
[netconf.git] / netconf / callhome-protocol / src / main / java / org / opendaylight / netconf / callhome / protocol / AuthorizedKeysDecoder.java
1 /*
2  * Copyright (c) 2016 Brocade Communication Systems and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.callhome.protocol;
9
10 import java.math.BigInteger;
11 import java.security.KeyFactory;
12 import java.security.NoSuchAlgorithmException;
13 import java.security.NoSuchProviderException;
14 import java.security.PublicKey;
15 import java.security.spec.DSAPublicKeySpec;
16 import java.security.spec.ECPoint;
17 import java.security.spec.ECPublicKeySpec;
18 import java.security.spec.InvalidKeySpecException;
19 import java.security.spec.RSAPublicKeySpec;
20 import java.util.Arrays;
21 import java.util.HashMap;
22 import java.util.Map;
23 import org.apache.sshd.common.util.Base64;
24 import org.apache.sshd.common.util.SecurityUtils;
25 import org.bouncycastle.jce.ECNamedCurveTable;
26 import org.bouncycastle.jce.ECPointUtil;
27 import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
28 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
29
30 /**
31  *
32  * FIXME: This should be probably located at AAA library
33  *
34  */
35 public class AuthorizedKeysDecoder {
36
37     private static final String KEY_FACTORY_TYPE_RSA = "RSA";
38     private static final String KEY_FACTORY_TYPE_DSA = "DSA";
39     private static final String KEY_FACTORY_TYPE_ECDSA = "EC";
40
41     private static Map<String,String> ECDSA_CURVES = new HashMap<>();
42     static {
43         ECDSA_CURVES.put("nistp256", "secp256r1");
44         ECDSA_CURVES.put("nistp384", "secp384r1");
45         ECDSA_CURVES.put("nistp512", "secp512r1");
46     }
47     private static String ECDSA_SUPPORTED_CURVE_NAME = "nistp256";
48     private static String ECDSA_SUPPORTED_CURVE_NAME_SPEC = ECDSA_CURVES.get(ECDSA_SUPPORTED_CURVE_NAME);
49
50     private static final String KEY_TYPE_RSA = "ssh-rsa";
51     private static final String KEY_TYPE_DSA = "ssh-dss";
52     private static final String KEY_TYPE_ECDSA = "ecdsa-sha2-" + ECDSA_SUPPORTED_CURVE_NAME;
53
54     private byte[] bytes = new byte[0];
55     private int pos = 0;
56
57
58     public PublicKey decodePublicKey(String keyLine) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchProviderException {
59
60         // look for the Base64 encoded part of the line to decode
61         // both ssh-rsa and ssh-dss begin with "AAAA" due to the length bytes
62         bytes = Base64.decodeBase64(keyLine.getBytes());
63         if (bytes.length == 0)
64             throw new IllegalArgumentException("No Base64 part to decode in " + keyLine);
65         pos = 0;
66
67         String type = decodeType();
68         if (type.equals(KEY_TYPE_RSA))
69             return decodeAsRSA();
70
71         if (type.equals(KEY_TYPE_DSA))
72             return decodeAsDSA();
73
74         if(type.equals(KEY_TYPE_ECDSA))
75             return decodeAsECDSA();
76
77         throw new IllegalArgumentException("Unknown decode key type " + type + " in " + keyLine);
78     }
79
80     private PublicKey decodeAsECDSA()
81             throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
82         KeyFactory ecdsaFactory = SecurityUtils.getKeyFactory(KEY_FACTORY_TYPE_ECDSA);
83
84         ECNamedCurveParameterSpec spec256r1 = ECNamedCurveTable.getParameterSpec(ECDSA_SUPPORTED_CURVE_NAME_SPEC);
85         ECNamedCurveSpec params256r1 = new ECNamedCurveSpec(ECDSA_SUPPORTED_CURVE_NAME_SPEC, spec256r1.getCurve(), spec256r1.getG(), spec256r1.getN());
86         // copy last 65 bytes from ssh key.
87         ECPoint point =  ECPointUtil.decodePoint(params256r1.getCurve(), Arrays.copyOfRange(bytes,39,bytes.length));
88         ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params256r1);
89
90         return ecdsaFactory.generatePublic(pubKeySpec);
91     }
92
93     private PublicKey decodeAsDSA() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
94         KeyFactory dsaFactory = SecurityUtils.getKeyFactory(KEY_FACTORY_TYPE_DSA);
95         BigInteger p = decodeBigInt();
96         BigInteger q = decodeBigInt();
97         BigInteger g = decodeBigInt();
98         BigInteger y = decodeBigInt();
99         DSAPublicKeySpec spec = new DSAPublicKeySpec(y, p, q, g);
100
101         return dsaFactory.generatePublic(spec);
102     }
103
104     private PublicKey decodeAsRSA() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
105         KeyFactory rsaFactory = SecurityUtils.getKeyFactory(KEY_FACTORY_TYPE_RSA);
106         BigInteger exponent = decodeBigInt();
107         BigInteger modulus = decodeBigInt();
108         RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
109
110         return rsaFactory.generatePublic(spec);
111     }
112
113     private String decodeType() {
114         int len = decodeInt();
115         String type = new String(bytes, pos, len);
116         pos += len;
117         return type;
118     }
119
120     private int decodeInt() {
121         return ((bytes[pos++] & 0xFF) << 24) | ((bytes[pos++] & 0xFF) << 16)
122                 | ((bytes[pos++] & 0xFF) << 8) | (bytes[pos++] & 0xFF);
123     }
124
125     private BigInteger decodeBigInt() {
126         int len = decodeInt();
127         byte[] bigIntBytes = new byte[len];
128         System.arraycopy(bytes, pos, bigIntBytes, 0, len);
129         pos += len;
130         return new BigInteger(bigIntBytes);
131     }
132 }