6702a44138c92bb4b2dcf2e3fcde06e339228e61
[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.io.ByteArrayOutputStream;
11 import java.io.DataOutputStream;
12 import java.io.IOException;
13 import java.math.BigInteger;
14 import java.security.KeyFactory;
15 import java.security.NoSuchAlgorithmException;
16 import java.security.NoSuchProviderException;
17 import java.security.PublicKey;
18 import java.security.interfaces.DSAParams;
19 import java.security.interfaces.DSAPublicKey;
20 import java.security.interfaces.RSAPublicKey;
21 import java.security.spec.DSAPublicKeySpec;
22 import java.security.spec.ECPoint;
23 import java.security.spec.ECPublicKeySpec;
24 import java.security.spec.InvalidKeySpecException;
25 import java.security.spec.RSAPublicKeySpec;
26 import java.util.Arrays;
27 import java.util.HashMap;
28 import java.util.Map;
29 import org.apache.sshd.common.util.Base64;
30 import org.apache.sshd.common.util.SecurityUtils;
31 import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
32 import org.bouncycastle.jce.ECNamedCurveTable;
33 import org.bouncycastle.jce.ECPointUtil;
34 import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
35 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
36
37
38 /**
39  * FIXME: This should be probably located at AAA library
40  */
41 public class AuthorizedKeysDecoder {
42
43     private static final String KEY_FACTORY_TYPE_RSA = "RSA";
44     private static final String KEY_FACTORY_TYPE_DSA = "DSA";
45     private static final String KEY_FACTORY_TYPE_ECDSA = "EC";
46
47     private static Map<String, String> ECDSA_CURVES = new HashMap<>();
48
49     static {
50         ECDSA_CURVES.put("nistp256", "secp256r1");
51         ECDSA_CURVES.put("nistp384", "secp384r1");
52         ECDSA_CURVES.put("nistp512", "secp512r1");
53     }
54
55     private static String ECDSA_SUPPORTED_CURVE_NAME = "nistp256";
56     private static String ECDSA_SUPPORTED_CURVE_NAME_SPEC = ECDSA_CURVES.get(ECDSA_SUPPORTED_CURVE_NAME);
57
58     private static final String KEY_TYPE_RSA = "ssh-rsa";
59     private static final String KEY_TYPE_DSA = "ssh-dss";
60     private static final String KEY_TYPE_ECDSA = "ecdsa-sha2-" + ECDSA_SUPPORTED_CURVE_NAME;
61
62     private byte[] bytes = new byte[0];
63     private int pos = 0;
64
65
66     public PublicKey decodePublicKey(String keyLine) throws InvalidKeySpecException, NoSuchAlgorithmException, NoSuchProviderException {
67
68         // look for the Base64 encoded part of the line to decode
69         // both ssh-rsa and ssh-dss begin with "AAAA" due to the length bytes
70         bytes = Base64.decodeBase64(keyLine.getBytes());
71         if (bytes.length == 0)
72             throw new IllegalArgumentException("No Base64 part to decode in " + keyLine);
73         pos = 0;
74
75         String type = decodeType();
76         if (type.equals(KEY_TYPE_RSA))
77             return decodeAsRSA();
78
79         if (type.equals(KEY_TYPE_DSA))
80             return decodeAsDSA();
81
82         if (type.equals(KEY_TYPE_ECDSA))
83             return decodeAsECDSA();
84
85         throw new IllegalArgumentException("Unknown decode key type " + type + " in " + keyLine);
86     }
87
88     private PublicKey decodeAsECDSA()
89             throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
90         KeyFactory ecdsaFactory = SecurityUtils.getKeyFactory(KEY_FACTORY_TYPE_ECDSA);
91
92         ECNamedCurveParameterSpec spec256r1 = ECNamedCurveTable.getParameterSpec(ECDSA_SUPPORTED_CURVE_NAME_SPEC);
93         ECNamedCurveSpec params256r1 = new ECNamedCurveSpec(ECDSA_SUPPORTED_CURVE_NAME_SPEC, spec256r1.getCurve(), spec256r1.getG(), spec256r1.getN());
94         // copy last 65 bytes from ssh key.
95         ECPoint point = ECPointUtil.decodePoint(params256r1.getCurve(), Arrays.copyOfRange(bytes, 39, bytes.length));
96         ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params256r1);
97
98         return ecdsaFactory.generatePublic(pubKeySpec);
99     }
100
101     private PublicKey decodeAsDSA() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
102         KeyFactory dsaFactory = SecurityUtils.getKeyFactory(KEY_FACTORY_TYPE_DSA);
103         BigInteger p = decodeBigInt();
104         BigInteger q = decodeBigInt();
105         BigInteger g = decodeBigInt();
106         BigInteger y = decodeBigInt();
107         DSAPublicKeySpec spec = new DSAPublicKeySpec(y, p, q, g);
108
109         return dsaFactory.generatePublic(spec);
110     }
111
112     private PublicKey decodeAsRSA() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
113         KeyFactory rsaFactory = SecurityUtils.getKeyFactory(KEY_FACTORY_TYPE_RSA);
114         BigInteger exponent = decodeBigInt();
115         BigInteger modulus = decodeBigInt();
116         RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
117
118         return rsaFactory.generatePublic(spec);
119     }
120
121     private String decodeType() {
122         int len = decodeInt();
123         String type = new String(bytes, pos, len);
124         pos += len;
125         return type;
126     }
127
128     private int decodeInt() {
129         return ((bytes[pos++] & 0xFF) << 24) | ((bytes[pos++] & 0xFF) << 16)
130                 | ((bytes[pos++] & 0xFF) << 8) | (bytes[pos++] & 0xFF);
131     }
132
133     private BigInteger decodeBigInt() {
134         int len = decodeInt();
135         byte[] bigIntBytes = new byte[len];
136         System.arraycopy(bytes, pos, bigIntBytes, 0, len);
137         pos += len;
138         return new BigInteger(bigIntBytes);
139     }
140
141     public static String encodePublicKey(PublicKey publicKey) throws IOException {
142         String publicKeyEncoded;
143         ByteArrayOutputStream byteOs = new ByteArrayOutputStream();
144         if (publicKey.getAlgorithm().equals(KEY_FACTORY_TYPE_RSA)) {
145             RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
146             DataOutputStream dout = new DataOutputStream(byteOs);
147             dout.writeInt(KEY_TYPE_RSA.getBytes().length);
148             dout.write(KEY_TYPE_RSA.getBytes());
149             dout.writeInt(rsaPublicKey.getPublicExponent().toByteArray().length);
150             dout.write(rsaPublicKey.getPublicExponent().toByteArray());
151             dout.writeInt(rsaPublicKey.getModulus().toByteArray().length);
152             dout.write(rsaPublicKey.getModulus().toByteArray());
153         } else if (publicKey.getAlgorithm().equals(KEY_FACTORY_TYPE_DSA)) {
154             DSAPublicKey dsaPublicKey = (DSAPublicKey) publicKey;
155             DSAParams dsaParams = dsaPublicKey.getParams();
156             DataOutputStream dout = new DataOutputStream(byteOs);
157             dout.writeInt(KEY_TYPE_DSA.getBytes().length);
158             dout.write(KEY_TYPE_DSA.getBytes());
159             dout.writeInt(dsaParams.getP().toByteArray().length);
160             dout.write(dsaParams.getP().toByteArray());
161             dout.writeInt(dsaParams.getQ().toByteArray().length);
162             dout.write(dsaParams.getQ().toByteArray());
163             dout.writeInt(dsaParams.getG().toByteArray().length);
164             dout.write(dsaParams.getG().toByteArray());
165             dout.writeInt(dsaPublicKey.getY().toByteArray().length);
166             dout.write(dsaPublicKey.getY().toByteArray());
167         } else if (publicKey.getAlgorithm().equals(KEY_FACTORY_TYPE_ECDSA)) {
168             BCECPublicKey ecPublicKey = (BCECPublicKey) publicKey;
169             DataOutputStream dout = new DataOutputStream(byteOs);
170             dout.writeInt(KEY_TYPE_ECDSA.getBytes().length);
171             dout.write(KEY_TYPE_ECDSA.getBytes());
172             dout.writeInt(ECDSA_SUPPORTED_CURVE_NAME.getBytes().length);
173             dout.write(ECDSA_SUPPORTED_CURVE_NAME.getBytes());
174
175             byte[] x = ecPublicKey.getQ().getAffineXCoord().getEncoded();
176             byte[] y = ecPublicKey.getQ().getAffineYCoord().getEncoded();
177             dout.writeInt(x.length + y.length + 1);
178             dout.writeByte(0x04);
179             dout.write(x);
180             dout.write(y);
181         } else {
182             throw new IllegalArgumentException("Unknown public key encoding: " + publicKey.getAlgorithm());
183         }
184         publicKeyEncoded = new String(Base64.encodeBase64(byteOs.toByteArray()));
185         return publicKeyEncoded;
186
187     }
188 }