private void Init(byte[] data) { GXAsn1Sequence seq = (GXAsn1Sequence)GXAsn1Converter.FromByteArray(data); if (seq.Count < 3) { throw new System.ArgumentException("Wrong number of elements in sequence."); } if (!(seq[0] is sbyte)) { PkcsType type = GXAsn1Converter.GetCertificateType(data, seq); switch (type) { case PkcsType.Pkcs10: throw new GXDLMSCertificateException("Invalid Certificate. This is PKCS 10 certification requests, not PKCS 8."); case PkcsType.x509Certificate: throw new GXDLMSCertificateException("Invalid Certificate. This is PKCS x509 certificate, not PKCS 8."); } throw new GXDLMSCertificateException("Invalid Certificate Version."); } Version = (CertificateVersion)seq[0]; GXAsn1Sequence tmp = (GXAsn1Sequence)seq[1]; Algorithm = X9ObjectIdentifierConverter.FromString(tmp[0].ToString()); PrivateKey = GXPrivateKey.FromRawBytes((byte[])((GXAsn1Sequence)seq[2])[1]); if (PrivateKey == null) { throw new Exception("Invalid private key."); } PublicKey = GXPublicKey.FromRawBytes(((GXAsn1BitString)((List <object>)((GXAsn1Sequence)seq[2])[2])[0]).Value); GXEcdsa.Validate(PublicKey); }
private void Init(byte[] data) { GXAsn1Sequence seq = (GXAsn1Sequence)GXAsn1Converter.FromByteArray(data); if (seq.Count < 3) { throw new System.ArgumentException("Wrong number of elements in sequence."); } ///////////////////////////// // CertificationRequestInfo ::= SEQUENCE { // version INTEGER { v1(0) } (v1,...), // subject Name, // subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }}, // attributes [0] Attributes{{ CRIAttributes }} // } GXAsn1Sequence reqInfo = (GXAsn1Sequence)seq[0]; Version = (CertificateVersion)(sbyte)reqInfo[0]; Subject = GXAsn1Converter.GetSubject((GXAsn1Sequence)reqInfo[1]); // subject Public key info. GXAsn1Sequence subjectPKInfo = (GXAsn1Sequence)reqInfo[2]; if (reqInfo.Count > 3) { Attributes = reqInfo[3]; } GXAsn1Sequence tmp = (GXAsn1Sequence)subjectPKInfo[0]; Algorithm = PkcsObjectIdentifierConverter.FromString(tmp[0].ToString()); if ((PkcsObjectIdentifier)Algorithm == PkcsObjectIdentifier.None) { Algorithm = X9ObjectIdentifierConverter.FromString(tmp[0].ToString()); } byte[] encodedKey = GXAsn1Converter.ToByteArray(subjectPKInfo); PublicKey = GXPublicKey.FromRawBytes(encodedKey); ///////////////////////////// // signatureAlgorithm GXAsn1Sequence sign = (GXAsn1Sequence)seq[1]; SignatureAlgorithm = HashAlgorithmConverter.FromString(sign[0].ToString()); if (sign.Count != 1) { SignatureParameters = sign[1]; } ///////////////////////////// // signature Signature = ((GXAsn1BitString)seq[2]).Value; GXEcdsa e = new GXEcdsa(PublicKey); if (!e.Verify(GXAsn1Converter.ToByteArray(reqInfo), data)) { throw new ArgumentException("Invalid Signature."); } }
private void Init(byte[] data) { GXAsn1Sequence seq = (GXAsn1Sequence)GXAsn1Converter.FromByteArray(data); if (seq.Count < 3) { throw new System.ArgumentException("Wrong number of elements in sequence."); } Version = (CertificateVersion)seq[0]; GXAsn1Sequence tmp = (GXAsn1Sequence)seq[1]; Algorithm = X9ObjectIdentifierConverter.FromString(tmp[0].ToString()); PrivateKey = GXPrivateKey.FromRawBytes((byte[])((GXAsn1Sequence)seq[2])[1]); if (PrivateKey == null) { throw new Exception("Invalid private key."); } PublicKey = GXPublicKey.FromRawBytes(((GXAsn1BitString)((List <object>)((GXAsn1Sequence)seq[2])[2])[0]).Value); GXEcdsa.Validate(PublicKey); }
private void Init(byte[] data) { Attributes = new List <KeyValuePair <PkcsObjectIdentifier, object[]> >(); GXAsn1Sequence seq = (GXAsn1Sequence)GXAsn1Converter.FromByteArray(data); if (seq.Count < 3) { throw new System.ArgumentException("Wrong number of elements in sequence."); } ///////////////////////////// // CertificationRequestInfo ::= SEQUENCE { // version INTEGER { v1(0) } (v1,...), // subject Name, // subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }}, // attributes [0] Attributes{{ CRIAttributes }} // } GXAsn1Sequence reqInfo = (GXAsn1Sequence)seq[0]; Version = (CertificateVersion)(sbyte)reqInfo[0]; Subject = GXAsn1Converter.GetSubject((GXAsn1Sequence)reqInfo[1]); // subject Public key info. GXAsn1Sequence subjectPKInfo = (GXAsn1Sequence)reqInfo[2]; if (reqInfo.Count > 3) { //PkcsObjectIdentifier foreach (GXAsn1Sequence it in (GXAsn1Context)reqInfo[3]) { List <object> values = new List <object>(); foreach (object v in (List <object>)((KeyValuePair <object, object>)it[1]).Key) { values.Add(v); } Attributes.Add(new KeyValuePair <PkcsObjectIdentifier, object[]>(PkcsObjectIdentifierConverter.FromString(it[0].ToString()), values.ToArray())); } } GXAsn1Sequence tmp = (GXAsn1Sequence)subjectPKInfo[0]; Algorithm = X9ObjectIdentifierConverter.FromString(tmp[0].ToString()); if (Algorithm != X9ObjectIdentifier.IdECPublicKey) { object algorithm = Algorithm; if (Algorithm == X9ObjectIdentifier.None) { algorithm = PkcsObjectIdentifierConverter.FromString(tmp[0].ToString()); if ((PkcsObjectIdentifier)algorithm == PkcsObjectIdentifier.None) { algorithm = tmp[0].ToString(); } } throw new Exception("Invalid PKCS #10 certificate algorithm. " + algorithm); } PublicKey = GXPublicKey.FromRawBytes(((GXAsn1BitString)subjectPKInfo[1]).Value); GXEcdsa.Validate(PublicKey); ///////////////////////////// // signatureAlgorithm GXAsn1Sequence sign = (GXAsn1Sequence)seq[1]; SignatureAlgorithm = HashAlgorithmConverter.FromString(sign[0].ToString()); if (SignatureAlgorithm != HashAlgorithm.Sha256WithEcdsa && SignatureAlgorithm != HashAlgorithm.Sha384WithEcdsa) { throw new GXDLMSCertificateException("Invalid signature algorithm. " + sign[0].ToString()); } if (sign.Count != 1) { SignatureParameters = sign[1]; } ///////////////////////////// // signature Signature = ((GXAsn1BitString)seq[2]).Value; GXEcdsa e = new GXEcdsa(PublicKey); GXAsn1Sequence tmp2 = (GXAsn1Sequence)GXAsn1Converter.FromByteArray(Signature); GXByteBuffer bb = new GXByteBuffer(); int size = SignatureAlgorithm == HashAlgorithm.Sha256WithEcdsa ? 32 : 48; //Some implementations might add extra byte. It must removed. bb.Set(((GXAsn1Integer)tmp2[0]).Value, ((GXAsn1Integer)tmp2[0]).Value.Length == size ? 0 : 1, size); bb.Set(((GXAsn1Integer)tmp2[1]).Value, ((GXAsn1Integer)tmp2[1]).Value.Length == size ? 0 : 1, size); if (!e.Verify(bb.Array(), GXAsn1Converter.ToByteArray(reqInfo))) { throw new ArgumentException("Invalid Signature."); } }
/// <summary> /// Decrypt data. /// </summary> /// <param name="p">Decryption parameters</param> /// <returns>Decrypted data.</returns> public static byte[] DecryptAesGcm(AesGcmParameter p, GXByteBuffer data) { if (data == null || data.Size < 2) { throw new ArgumentOutOfRangeException("cryptedData"); } byte[] tmp; int len; Command cmd = (Command)data.GetUInt8(); switch (cmd) { case Command.GeneralGloCiphering: case Command.GeneralDedCiphering: len = GXCommon.GetObjectCount(data); if (len != 0) { p.SystemTitle = new byte[len]; data.Get(p.SystemTitle); if (p.Xml != null && p.Xml.Comments) { p.Xml.AppendComment(GXCommon.SystemTitleToString(Standard.DLMS, p.SystemTitle, true)); } } if (p.SystemTitle == null || p.SystemTitle.Length != 8) { if (p.Xml == null) { throw new ArgumentNullException("Invalid sender system title."); } else { p.Xml.AppendComment("Invalid sender system title."); } } break; case Command.GeneralCiphering: case Command.GloInitiateRequest: case Command.GloInitiateResponse: case Command.GloReadRequest: case Command.GloReadResponse: case Command.GloWriteRequest: case Command.GloWriteResponse: case Command.GloGetRequest: case Command.GloGetResponse: case Command.GloSetRequest: case Command.GloSetResponse: case Command.GloMethodRequest: case Command.GloMethodResponse: case Command.GloEventNotification: case Command.DedInitiateRequest: case Command.DedInitiateResponse: case Command.DedGetRequest: case Command.DedGetResponse: case Command.DedSetRequest: case Command.DedSetResponse: case Command.DedMethodRequest: case Command.DedMethodResponse: case Command.DedEventNotification: case Command.DedReadRequest: case Command.DedReadResponse: case Command.DedWriteRequest: case Command.DedWriteResponse: case Command.GloConfirmedServiceError: case Command.DedConfirmedServiceError: break; default: throw new ArgumentOutOfRangeException("cryptedData"); } int value = 0; GXPrivateKey key = null; GXPublicKey pub = null; GXByteBuffer transactionId = null; if (cmd == Command.GeneralCiphering) { transactionId = new GXByteBuffer(); len = GXCommon.GetObjectCount(data); GXCommon.SetObjectCount(len, transactionId); transactionId.Set(data, len); p.TransactionId = transactionId.GetUInt64(1); len = GXCommon.GetObjectCount(data); if (len != 0) { tmp = new byte[len]; data.Get(tmp); p.SystemTitle = tmp; } if (p.SystemTitle == null || p.SystemTitle.Length != 8) { if (p.Xml == null) { throw new ArgumentNullException("Invalid sender system title."); } else { p.Xml.AppendComment("Invalid sender system title."); } } len = GXCommon.GetObjectCount(data); tmp = new byte[len]; data.Get(tmp); p.RecipientSystemTitle = tmp; // Get date time. len = GXCommon.GetObjectCount(data); if (len != 0) { tmp = new byte[len]; data.Get(tmp); p.DateTime = tmp; } // other-information len = data.GetUInt8(); if (len != 0) { tmp = new byte[len]; data.Get(tmp); p.OtherInformation = tmp; } // KeyInfo OPTIONAL len = data.GetUInt8(); // AgreedKey CHOICE tag. data.GetUInt8(); // key-parameters len = data.GetUInt8(); value = data.GetUInt8(); p.KeyParameters = value; if (value == (int)KeyAgreementScheme.OnePassDiffieHellman) { // key-ciphered-data len = GXCommon.GetObjectCount(data); GXByteBuffer bb = new GXByteBuffer(); bb.Set(data, len); if (p.Xml != null) { p.KeyCipheredData = bb.Array(); //Find key agreement key using subject. string subject = GXAsn1Converter.SystemTitleToSubject(p.SystemTitle); foreach (KeyValuePair <GXPkcs8, GXx509Certificate> it in p.Settings.Keys) { if (it.Key != null && it.Value.KeyUsage == ASN.Enums.KeyUsage.KeyAgreement && it.Value.Subject.Contains(subject)) { key = it.Key.PrivateKey; //Get recipient Ephemeral public key. subject = GXAsn1Converter.SystemTitleToSubject(p.RecipientSystemTitle); foreach (KeyValuePair <GXPkcs8, GXx509Certificate> it2 in p.Settings.Keys) { if (it2.Value != null && it2.Value.KeyUsage == ASN.Enums.KeyUsage.KeyAgreement && it2.Value.Subject.Contains(subject)) { pub = it2.Value.PublicKey; break; } } break; } } if (key == null) { //Find key agreement key using subject. subject = GXAsn1Converter.SystemTitleToSubject(p.RecipientSystemTitle); foreach (KeyValuePair <GXPkcs8, GXx509Certificate> it in p.Settings.Keys) { if (it.Key != null && it.Value.KeyUsage == ASN.Enums.KeyUsage.KeyAgreement && it.Value.Subject.Contains(subject)) { key = it.Key.PrivateKey; break; } } } } else { key = p.Settings.Cipher.KeyAgreementKeyPair.Key; } if (key != null && pub == null) { //Get Ephemeral public key. int keySize = len / 2; pub = GXPublicKey.FromRawBytes(bb.SubArray(0, keySize)); } } else if (value == (int)KeyAgreementScheme.StaticUnifiedModel) { len = GXCommon.GetObjectCount(data); if (len != 0) { throw new ArgumentException("Invalid key parameters"); } if (p.Xml != null) { //Find key agreement key using subject. string subject = GXAsn1Converter.SystemTitleToSubject(p.RecipientSystemTitle); foreach (KeyValuePair <GXPkcs8, GXx509Certificate> it in p.Settings.Keys) { if (it.Value.KeyUsage == ASN.Enums.KeyUsage.KeyAgreement && it.Value.Subject.Contains(subject)) { key = it.Key.PrivateKey; break; } } if (key != null) { //Find key agreement key using subject. subject = GXAsn1Converter.SystemTitleToSubject(p.Settings.SourceSystemTitle); foreach (KeyValuePair <GXPkcs8, GXx509Certificate> it in p.Settings.Keys) { if (it.Value.KeyUsage == ASN.Enums.KeyUsage.KeyAgreement && it.Value.Subject.Contains(subject)) { pub = it.Value.PublicKey; break; } } } } else { key = p.Settings.Cipher.KeyAgreementKeyPair.Key; pub = p.Settings.Cipher.KeyAgreementKeyPair.Value; } } else { throw new ArgumentException("key-parameters"); } } len = GXCommon.GetObjectCount(data); if (len > data.Available) { throw new Exception("Not enought data."); } p.CipheredContent = data.Remaining(); byte sc = data.GetUInt8(); p.SecuritySuite = (SecuritySuite)(sc & 0x3); p.Security = (Security)(sc & 0x30); if ((sc & 0x80) != 0) { System.Diagnostics.Debug.WriteLine("Compression is used."); } if ((sc & 0x40) != 0) { System.Diagnostics.Debug.WriteLine("Error: Key_Set is used."); } if ((sc & 0x20) != 0) { System.Diagnostics.Debug.WriteLine("Encryption is applied."); } if (key != null) { if (value == (int)KeyAgreementScheme.OnePassDiffieHellman) { GXEcdsa c = new GXEcdsa(key); //Get Ephemeral signing key and verify it. byte[] z = c.GenerateSecret(pub); System.Diagnostics.Debug.WriteLine("Originator ephemeral public key: " + pub.ToHex()); System.Diagnostics.Debug.WriteLine("Recipient private agreement key: " + key.ToHex()); System.Diagnostics.Debug.WriteLine("Shared secret:" + GXCommon.ToHex(z, true)); GXByteBuffer kdf = new GXByteBuffer(); kdf.Set(GXSecure.GenerateKDF(p.SecuritySuite, z, p.SecuritySuite == SecuritySuite.Ecdsa256 ? AlgorithmId.AesGcm128 : AlgorithmId.AesGcm256, p.SystemTitle, p.RecipientSystemTitle, null, null)); System.Diagnostics.Debug.WriteLine("KDF:" + kdf.ToString()); p.BlockCipherKey = kdf.SubArray(0, 16); } else if (value == (int)KeyAgreementScheme.StaticUnifiedModel) { GXEcdsa c = new GXEcdsa(key); byte[] z = c.GenerateSecret(pub); System.Diagnostics.Debug.WriteLine("Shared secret:" + GXCommon.ToHex(z, true)); GXByteBuffer kdf = new GXByteBuffer(); kdf.Set(GXSecure.GenerateKDF(p.SecuritySuite, z, p.SecuritySuite == SecuritySuite.Ecdsa256 ? AlgorithmId.AesGcm128 : AlgorithmId.AesGcm256, p.SystemTitle, transactionId.Array(), p.RecipientSystemTitle, null)); System.Diagnostics.Debug.WriteLine("KDF:" + kdf.ToString()); p.BlockCipherKey = kdf.SubArray(0, 16); } else { throw new ArgumentOutOfRangeException("Invalid Key-id value."); } } UInt32 invocationCounter = data.GetUInt32(); p.InvocationCounter = invocationCounter; System.Diagnostics.Debug.WriteLine("Decrypt settings: " + p.ToString()); System.Diagnostics.Debug.WriteLine("Encrypted: " + GXCommon.ToHex(data.Data, false, data.Position, data.Size - data.Position)); byte[] tag = new byte[12]; byte[] encryptedData; int length; if (p.Security == Security.Authentication) { length = data.Size - data.Position - 12; encryptedData = new byte[length]; data.Get(encryptedData); data.Get(tag); // Check tag. EncryptAesGcm(p, encryptedData); if (!GXDLMSChipperingStream.TagsEquals(tag, p.CountTag)) { if (p.Xml == null) { throw new GXDLMSException("Decrypt failed. Invalid tag."); } else { p.Xml.AppendComment("Decrypt failed. Invalid tag."); } } return(encryptedData); } byte[] ciphertext = null; if (p.Security == Security.Encryption) { length = data.Size - data.Position; ciphertext = new byte[length]; data.Get(ciphertext); } else if (p.Security == Security.AuthenticationEncryption) { length = data.Size - data.Position - 12; ciphertext = new byte[length]; data.Get(ciphertext); data.Get(tag); } byte[] aad = GetAuthenticatedData(p, ciphertext), iv = GetNonse(invocationCounter, p.SystemTitle); GXDLMSChipperingStream gcm = new GXDLMSChipperingStream(p.Security, true, p.BlockCipherKey, aad, iv, tag); gcm.Write(ciphertext); byte[] decrypted = gcm.FlushFinalBlock(); System.Diagnostics.Debug.WriteLine("Decrypted: " + GXCommon.ToHex(decrypted, true)); if (p.Security != Security.Encryption) { if (!GXCommon.Compare(gcm.GetTag(), tag)) { if (p.Xml == null) { throw new Exception("Decrypt failed. Invalid authentication tag."); } p.Xml.AppendComment("Decrypt failed. Invalid authentication tag."); } } return(decrypted); }
// https://tools.ietf.org/html/rfc5280#section-4.1 private void Init(byte[] data) { rawData = data; GXAsn1Sequence seq = (GXAsn1Sequence)GXAsn1Converter.FromByteArray(data); if (seq.Count != 3) { throw new GXDLMSCertificateException("Invalid Certificate Version. Wrong number of elements in sequence."); } if (!(seq[0] is GXAsn1Sequence)) { PkcsType type = GXAsn1Converter.GetCertificateType(data, seq); switch (type) { case PkcsType.Pkcs8: throw new GXDLMSCertificateException("Invalid Certificate. This is PKCS 8 private key, not x509 certificate."); case PkcsType.Pkcs10: throw new GXDLMSCertificateException("Invalid Certificate. This is PKCS 10 certification requests, not x509 certificate."); } throw new GXDLMSCertificateException("Invalid Certificate Version."); } GXAsn1Sequence reqInfo = (GXAsn1Sequence)seq[0]; if ((reqInfo[0] is GXAsn1Integer)) { throw new GXDLMSCertificateException("Invalid Certificate. DLMS certificate version number must be 3."); } Version = (CertificateVersion)((GXAsn1Context)reqInfo[0])[0]; if (reqInfo[1] is sbyte) { SerialNumber = Convert.ToUInt64(reqInfo[1]); } else { SerialNumber = new BigInteger(((GXAsn1Integer)reqInfo[1]).Value); } string tmp = ((GXAsn1Sequence)reqInfo[2])[0].ToString(); // Signature Algorithm SignatureAlgorithm = HashAlgorithmConverter.FromString(tmp); if (SignatureAlgorithm != HashAlgorithm.Sha256WithEcdsa) { throw new Exception("DLMS certificate must be signed with ecdsa-with-SHA256."); } // Optional. if (((GXAsn1Sequence)reqInfo[2]).Count > 1) { SignatureParameters = ((GXAsn1Sequence)reqInfo[2])[1]; } // Issuer Issuer = GXAsn1Converter.GetSubject((GXAsn1Sequence)reqInfo[3]); bool basicConstraintsExists = false; // Validity ValidFrom = (DateTime)((GXAsn1Sequence)reqInfo[4])[0]; ValidTo = (DateTime)((GXAsn1Sequence)reqInfo[4])[1]; Subject = GXAsn1Converter.GetSubject((GXAsn1Sequence)reqInfo[5]); string CN = X509NameConverter.GetString(X509Name.CN); // Subject public key Info GXAsn1Sequence subjectPKInfo = (GXAsn1Sequence)reqInfo[6]; PublicKey = GXPublicKey.FromRawBytes(((GXAsn1BitString)subjectPKInfo[1]).Value); GXEcdsa.Validate(PublicKey); // Get Standard Extensions. if (reqInfo.Count > 7) { foreach (GXAsn1Sequence s in (GXAsn1Sequence)((GXAsn1Context)reqInfo[7])[0]) { GXAsn1ObjectIdentifier id = (GXAsn1ObjectIdentifier)s[0]; object value = s[1]; X509CertificateType t = X509CertificateTypeConverter.FromString(id.ToString()); switch (t) { case X509CertificateType.SubjectKeyIdentifier: SubjectKeyIdentifier = (byte[])value; break; case X509CertificateType.AuthorityKeyIdentifier: foreach (GXAsn1Context it in (GXAsn1Sequence)value) { switch (it.Index) { case 0: //Authority Key Identifier. AuthorityKeyIdentifier = (byte[])it[0]; break; case 1: { StringBuilder sb = new StringBuilder(); //authorityCertIssuer foreach (KeyValuePair <object, object> it2 in ((GXAsn1Sequence)((GXAsn1Context)it[0])[0])) { if (sb.Length != 0) { sb.Append(", "); } sb.Append(X509NameConverter.FromString(Convert.ToString(it2.Key))); sb.Append("="); sb.Append(Convert.ToString(it2.Value)); } AuthorityCertIssuer = sb.ToString(); } break; case 2: //Authority cert serial number . AuthorityCertificationSerialNumber = (byte[])it[0]; break; default: throw new ArgumentOutOfRangeException("Invalid context." + it.Index); } } break; case X509CertificateType.KeyUsage: if (value is GXAsn1BitString) { // critical is optional. BOOLEAN DEFAULT FALSE, KeyUsage = (KeyUsage)((GXAsn1BitString)value).ToInteger(); } else if (value is bool?) { value = s[2]; KeyUsage = (KeyUsage)((GXAsn1BitString)value).ToInteger(); } else { throw new ArgumentException("Invalid key usage."); } break; case X509CertificateType.BasicConstraints: basicConstraintsExists = true; if (value is GXAsn1Sequence) { if (((GXAsn1Sequence)value).Count != 0) { BasicConstraints = (bool)((GXAsn1Sequence)value)[0]; } } else if (value is bool?) { BasicConstraints = (bool)value; } else { throw new ArgumentException("Invalid key usage."); } break; default: System.Diagnostics.Debug.WriteLine("Unknown extensions: " + t.ToString()); break; } } } if (!basicConstraintsExists) { // Verify that subject Common Name includes system title. bool commonNameFound = false; foreach (KeyValuePair <object, object> it in (GXAsn1Sequence)reqInfo[5]) { if (CN == it.Key.ToString()) { if (Convert.ToString(it.Value).Length != 16) { throw new GXDLMSCertificateException("System title is not included in Common Name."); } commonNameFound = true; break; } } if (!commonNameFound) { throw new GXDLMSCertificateException("Common name doesn't exist."); } } if (KeyUsage == KeyUsage.None) { throw new Exception("Key usage not present. It's mandotory."); } if ((KeyUsage & (KeyUsage.KeyCertSign | KeyUsage.CrlSign)) != 0 && !basicConstraintsExists) { throw new Exception("Basic Constraints value not present. It's mandotory."); } PublicKeyAlgorithm = HashAlgorithmConverter.FromString(((GXAsn1Sequence)seq[1])[0].ToString()); if (PublicKeyAlgorithm != HashAlgorithm.Sha256WithEcdsa) { throw new Exception("DLMS certificate must be signed with ecdsa-with-SHA256."); } // Optional. if (((GXAsn1Sequence)seq[1]).Count > 1) { PublicKeyParameters = ((GXAsn1Sequence)seq[1])[1]; } ///////////////////////////// //Get signature. Signature = ((GXAsn1BitString)seq[2]).Value; }
// https://tools.ietf.org/html/rfc5280#section-4.1 private void Init(byte[] data) { GXAsn1Sequence seq = (GXAsn1Sequence)GXAsn1Converter.FromByteArray(data); if (seq.Count != 3) { throw new ArgumentException("Wrong number of elements in sequence."); } GXAsn1Sequence reqInfo = (GXAsn1Sequence)seq[0]; Version = (CertificateVersion)((GXAsn1Context)reqInfo[0])[0]; if (reqInfo[1] is sbyte) { SerialNumber = new GXAsn1Integer(Convert.ToUInt64(reqInfo[1])); } else { SerialNumber = (GXAsn1Integer)reqInfo[1]; } string tmp = ((GXAsn1Sequence)reqInfo[2])[0].ToString(); // Signature Algorithm SignatureAlgorithm = HashAlgorithmConverter.FromString(tmp); if (SignatureAlgorithm != HashAlgorithm.Sha256withecdsa) { throw new Exception("DLMS certificate must be signed with ecdsa-with-SHA256."); } // Optional. if (((GXAsn1Sequence)reqInfo[2]).Count > 1) { SignatureParameters = ((GXAsn1Sequence)reqInfo[2])[1]; } // Issuer Issuer = GXAsn1Converter.GetSubject((GXAsn1Sequence)reqInfo[3]); // Validity ValidFrom = (DateTime)((GXAsn1Sequence)reqInfo[4])[0]; ValidTo = (DateTime)((GXAsn1Sequence)reqInfo[4])[1]; Subject = GXAsn1Converter.GetSubject((GXAsn1Sequence)reqInfo[5]); // Subject public key Info GXAsn1Sequence subjectPKInfo = (GXAsn1Sequence)reqInfo[6]; PublicKey = GXPublicKey.FromRawBytes(((GXAsn1BitString)subjectPKInfo[1]).Value); // Get Standard Extensions. if (reqInfo.Count > 7) { foreach (GXAsn1Sequence s in (GXAsn1Sequence)((GXAsn1Context)reqInfo[7])[0]) { GXAsn1ObjectIdentifier id = (GXAsn1ObjectIdentifier)s[0]; object value = s[1]; Enums.X509CertificateType t = Enums.X509CertificateTypeConverter.FromString(id.ToString()); switch (t) { case Enums.X509CertificateType.SubjectKeyIdentifier: SubjectKeyIdentifier = (byte[])value; break; case Enums.X509CertificateType.AuthorityKeyIdentifier: AuthorityKeyIdentifier = (byte[])((GXAsn1Sequence)value)[0]; break; case Enums.X509CertificateType.KeyUsage: if (value is GXAsn1BitString) { // critical is optional. BOOLEAN DEFAULT FALSE, KeyUsage = (KeyUsage)((GXAsn1BitString)value).Value[0]; } else if (value is bool?) { value = s[2]; KeyUsage = (KeyUsage)((GXAsn1BitString)value).Value[0]; } else { throw new ArgumentException("Invalid key usage."); } break; default: System.Diagnostics.Debug.WriteLine("Unknown extensions: " + t.ToString()); break; } } } if (KeyUsage == KeyUsage.None) { throw new Exception("Key usage not present."); } PublicKeySignature = HashAlgorithmConverter.FromString(((GXAsn1Sequence)seq[1])[0].ToString()); // Optional. if (((GXAsn1Sequence)seq[1]).Count > 1) { SignatureParameters = ((GXAsn1Sequence)seq[1])[1]; } // signature Signature = ((GXAsn1BitString)seq[2]).Value; }
byte[] IGXDLMSBase.Invoke(GXDLMSSettings settings, ValueEventArgs e) { if (e.Index == 1) { SecurityPolicy = (SecurityPolicy)e.Parameters; } else if (e.Index == 2) { try { foreach (List <object> item in e.Parameters as List <object> ) { GlobalKeyType type = (GlobalKeyType)Convert.ToInt32(item[0]); byte[] data = (byte[])item[1]; //if settings.Cipher is null non secure server is used. //Keys are take in action after reply is generated. switch (type) { case GlobalKeyType.UnicastEncryption: GXDLMSSecureClient.Decrypt(settings.Kek, data); break; case GlobalKeyType.BroadcastEncryption: //Invalid type e.Error = ErrorCode.ReadWriteDenied; break; case GlobalKeyType.Authentication: GXDLMSSecureClient.Decrypt(settings.Kek, data); break; case GlobalKeyType.Kek: GXDLMSSecureClient.Decrypt(settings.Kek, data); break; default: //Invalid type e.Error = ErrorCode.ReadWriteDenied; break; } } } catch (Exception) { e.Error = ErrorCode.ReadWriteDenied; } } else if (e.Index == 3) { // key_agreement try { List <Object> tmp = (List <Object>)(e.Parameters as List <Object>)[0]; byte keyId = (byte)tmp[0]; if (keyId != 0) { e.Error = ErrorCode.InconsistentClass; } else { byte[] data = (byte[])tmp[0]; // ephemeral public key GXByteBuffer data2 = new GXByteBuffer(65); data2.SetUInt8(keyId); data2.Set(data, 0, 64); GXByteBuffer sign = new GXByteBuffer(); sign.Set(data, 64, 64); GXPublicKey pk = null; string subject = SystemTitleToSubject(settings.SourceSystemTitle); foreach (GXx509Certificate it in settings.Cipher.Certificates) { if ((it.KeyUsage & KeyUsage.DigitalSignature) != 0 && it.Subject == subject) { pk = it.PublicKey; break; } } if (pk == null) //TODO:|| !GXSecure.ValidateEphemeralPublicKeySignature(data2.Array(), sign.Array(), pk)) { e.Error = ErrorCode.InconsistentClass; settings.TargetEphemeralKey = null; } else { settings.TargetEphemeralKey = GXPublicKey.FromRawBytes(data2.SubArray(1, 64)); // Generate ephemeral keys. KeyValuePair <GXPrivateKey, GXPublicKey> eKpS = settings.Cipher.EphemeralKeyPair; if (eKpS.Key == null) { eKpS = GXEcdsa.GenerateKeyPair(GetEcc(SecuritySuite)); settings.Cipher.EphemeralKeyPair = eKpS; } // Generate shared secret. return(null); } } } catch (Exception) { e.Error = ErrorCode.InconsistentClass; } } else if (e.Index == 4) { // generate_key_pair CertificateType key = (CertificateType)(int)e.Parameters; KeyValuePair <GXPrivateKey, GXPublicKey> value = GXEcdsa.GenerateKeyPair(GetEcc(SecuritySuite)); switch (key) { case CertificateType.DigitalSignature: settings.Cipher.SigningKeyPair = value; break; case CertificateType.KeyAgreement: settings.Cipher.KeyAgreementKeyPair = value; break; default: e.Error = ErrorCode.InconsistentClass; break; } } else if (e.Index == 5) { // generate_certificate_request CertificateType key = (CertificateType)(int)e.Parameters; try { KeyValuePair <GXPrivateKey, GXPublicKey> kp = default(KeyValuePair <GXPrivateKey, GXPublicKey>); switch (key) { case CertificateType.DigitalSignature: kp = settings.Cipher.SigningKeyPair; break; case CertificateType.KeyAgreement: kp = settings.Cipher.KeyAgreementKeyPair; break; default: break; } if (kp.Key != null) { GXPkcs10 pkc10 = GXPkcs10.CreateCertificateSigningRequest(kp, SystemTitleToSubject(settings.Cipher.SystemTitle)); return(pkc10.Encoded); } else { e.Error = ErrorCode.ReadWriteDenied; } } catch (Exception) { e.Error = ErrorCode.ReadWriteDenied; } } else if (e.Index == 6) { // import_certificate GXx509Certificate cert = new GXx509Certificate((byte[])e.Parameters); if (cert.KeyUsage == 0) { // At least one bit must be used. e.Error = ErrorCode.InconsistentClass; } else { settings.Cipher.Certificates.Add(cert); } } else if (e.Index == 7) { // export_certificate List <Object> tmp = (List <Object>)e.Parameters; short type = (short)tmp[0]; GXx509Certificate cert = null; lock (settings.Cipher.Certificates) { if (type == 0) { tmp = (List <Object>)tmp[1]; cert = FindCertificateByEntity(settings, (CertificateEntity)tmp[0], (CertificateType)tmp[1], (byte[])tmp[2]); } else if (type == 1) { tmp = (List <Object>)tmp[1]; cert = FindCertificateBySerial(settings, (byte[])tmp[1], ASCIIEncoding.ASCII.GetString((byte[])tmp[2])); } if (cert == null) { e.Error = ErrorCode.InconsistentClass; } else { return(cert.Encoded); } } } else if (e.Index == 8) { // remove_certificate List <Object> tmp = (List <Object>)((List <object>)e.Parameters)[0]; short type = (short)tmp[0]; GXx509Certificate cert = null; lock (settings.Cipher.Certificates) { if (type == 0) { cert = FindCertificateByEntity(settings, (CertificateEntity)tmp[1], (CertificateType)tmp[2], (byte[])tmp[3]); } else if (type == 1) { cert = FindCertificateBySerial(settings, (byte[])tmp[1], ASCIIEncoding.ASCII.GetString((byte[])tmp[2])); } if (cert == null) { e.Error = ErrorCode.InconsistentClass; } else { settings.Cipher.Certificates.Remove(cert); } } } else { e.Error = ErrorCode.ReadWriteDenied; } //Return standard reply. return(null); }
/// <summary> /// Update ephemeral keys. /// </summary> /// <param name="client">DLMS Client.</param> /// <param name="value">Received reply from the server.</param> /// <returns>List of Parsed key id and GUAK. This is for debugging purpose.</returns> public List <KeyValuePair <GlobalKeyType, byte[]> > UpdateEphemeralKeys(GXDLMSSecureClient client, GXByteBuffer value) { if (client == null) { throw new ArgumentNullException(nameof(client)); } if (value.GetUInt8() != (byte)DataType.Array) { throw new ArgumentOutOfRangeException("Invalid tag."); } GXEcdsa c = new GXEcdsa(client.Ciphering.EphemeralKeyPair.Key); int count = GXCommon.GetObjectCount(value); List <KeyValuePair <GlobalKeyType, byte[]> > list = new List <KeyValuePair <GlobalKeyType, byte[]> >(); for (int pos = 0; pos != count; ++pos) { if (value.GetUInt8() != (byte)DataType.Structure) { throw new ArgumentOutOfRangeException("Invalid tag."); } if (value.GetUInt8() != 2) { throw new ArgumentOutOfRangeException("Invalid length."); } if (value.GetUInt8() != (byte)DataType.Enum) { throw new ArgumentOutOfRangeException("Invalid key id data type."); } int keyId = value.GetUInt8(); if (keyId > 4) { throw new ArgumentOutOfRangeException("Invalid key type."); } if (value.GetUInt8() != (byte)DataType.OctetString) { throw new ArgumentOutOfRangeException("Invalid tag."); } if (GXCommon.GetObjectCount(value) != 128) { throw new ArgumentOutOfRangeException("Invalid length."); } //Get ephemeral public key server. GXByteBuffer key = new GXByteBuffer(); key.SetUInt8(4); key.Set(value, 64); GXPublicKey targetEphemeralKey = GXPublicKey.FromRawBytes(key.Array()); //Get ephemeral public key signature server. byte[] signature = new byte[64]; value.Get(signature); key.SetUInt8(0, (byte)keyId); //Verify signature. if (!GXSecure.ValidateEphemeralPublicKeySignature(key.Array(), signature, client.Ciphering.SigningKeyPair.Value)) { throw new GXDLMSCipherException("Invalid signature."); } byte[] z = c.GenerateSecret(targetEphemeralKey); System.Diagnostics.Debug.WriteLine("Shared secret:" + GXCommon.ToHex(z, true)); GXByteBuffer kdf = new GXByteBuffer(); kdf.Set(GXSecure.GenerateKDF(client.SecuritySuite, z, AlgorithmId.AesGcm128, client.Ciphering.SystemTitle, client.Settings.SourceSystemTitle, null, null)); System.Diagnostics.Debug.WriteLine("KDF:" + kdf.ToString()); list.Add(new KeyValuePair <GlobalKeyType, byte[]>((GlobalKeyType)keyId, kdf.SubArray(0, 16))); } //Update ephemeral keys. foreach (KeyValuePair <GlobalKeyType, byte[]> it in list) { switch (it.Key) { case GlobalKeyType.UnicastEncryption: client.Settings.EphemeralBlockCipherKey = it.Value; break; case GlobalKeyType.BroadcastEncryption: client.Settings.EphemeralBroadcastBlockCipherKey = it.Value; break; case GlobalKeyType.Authentication: client.Settings.EphemeralAuthenticationKey = it.Value; break; case GlobalKeyType.Kek: client.Settings.EphemeralKek = it.Value; break; } } return(list); }